1 package com.trendmicro.grid.acl.ds.cifs;
2
3 import com.trendmicro.grid.acl.commons.Processes;
4 import jcifs.UniAddress;
5 import jcifs.smb.*;
6 import org.slf4j.Logger;
7 import org.slf4j.LoggerFactory;
8
9 import java.net.MalformedURLException;
10 import java.net.UnknownHostException;
11 import java.util.concurrent.Semaphore;
12 import java.util.concurrent.TimeUnit;
13
14
15
16
17
18
19
20 public class CIFSLoginHandler {
21
22 private static final Logger log = LoggerFactory.getLogger(CIFSLoginHandler.class);
23
24 private final String hostname;
25 private final String shareName;
26
27 private String cifsRepositoryBaseURI;
28 private SmbFile repositoryBasePath;
29
30 private UniAddress cifsAddress;
31 private NtlmPasswordAuthentication authentication;
32
33 private final Semaphore loginLock = new Semaphore(1);
34
35
36
37
38
39
40
41 public CIFSLoginHandler(String hostname, String shareName) {
42 this.hostname = hostname;
43 this.shareName = shareName;
44 }
45
46
47
48
49
50
51
52
53 public SmbFile getAuthenticatedRepositoryBasePath() throws MalformedURLException, SmbException {
54 reLoginIfRequired();
55 return repositoryBasePath;
56 }
57
58
59
60
61
62
63
64
65 public String getAuthenticatedRepositoryBaseURI() throws MalformedURLException, SmbException {
66 reLoginIfRequired();
67 return cifsRepositoryBaseURI;
68 }
69
70
71
72
73
74
75
76
77
78
79
80 public synchronized void login(String domain, String username, String password)
81 throws MalformedURLException, UnknownHostException, SmbException {
82 doLogin(domain, username, password);
83 }
84
85
86
87
88
89
90
91
92
93
94 public void loginAsynchronously(final String domain, final String username, final String password) {
95 acquireLoginLock();
96 Processes.getInstance().submit(new Runnable() {
97
98 long retryWaitMinutes;
99 boolean needsUnlock = true;
100
101 public void run() {
102 try {
103 doLogin(domain, username, password);
104 log.info("TMACL-01190:Successfully connected with CIFS host {}, repository {}",
105 hostname, cifsRepositoryBaseURI);
106 } catch (UnknownHostException e) {
107 log.error("TMACL-01200:Failed to connect the CIFS host '" + hostname +
108 "', the hostname is unknown");
109 } catch (Exception e) {
110 CIFSFileRepository.fixExceptionChain(e);
111 final Object cifsHost = cifsAddress == null ? hostname : cifsAddress;
112
113 if (e.getCause() != null && e.getCause().getCause() instanceof java.net.ConnectException) {
114 log.error("TMACL-01210:Failed to connect the CIFS host '{}', " +
115 "the socket connection could not be established, reason: \"{}\". " +
116 "This is usually caused by a firewall or an invalid IP address.",
117 cifsHost, e.getCause().getCause().getMessage());
118 } else {
119 log.error("TMACL-01220:Failed to connect the CIFS host '" + cifsHost +
120 "' the remote file repostory may be unavailable", e);
121 }
122 } finally {
123 if (cifsRepositoryBaseURI == null) {
124 retryWaitMinutes += 5;
125 Processes.getInstance().schedule(this, retryWaitMinutes, TimeUnit.MINUTES);
126 if (log.isDebugEnabled()) {
127 log.debug("Scheduled a retry to connect to CIFS host '{}' in {} minutes",
128 hostname, retryWaitMinutes);
129 }
130 } else {
131 if (needsUnlock) {
132 loginLock.release();
133 needsUnlock = false;
134 }
135 }
136 }
137 }
138 });
139 }
140
141
142
143
144 public void forceReLogin() {
145 acquireLoginLock();
146 try {
147 repositoryBasePath = null;
148 } finally {
149 loginLock.release();
150 }
151 }
152
153 private void reLoginIfRequired() throws MalformedURLException, SmbException {
154 if (repositoryBasePath == null) {
155 acquireLoginLock();
156 try {
157 loginAndSetBasePathIfRequired();
158 } finally {
159 loginLock.release();
160 }
161 }
162 }
163
164 private void acquireLoginLock() {
165 try {
166 loginLock.acquire();
167 } catch (InterruptedException e) {
168 Thread.interrupted();
169 throw new RuntimeException(e);
170 }
171 }
172
173 private synchronized void doLogin(String domain, String username, String password)
174 throws SmbException, UnknownHostException, MalformedURLException {
175
176 if (log.isTraceEnabled())
177 log.trace("Resolving CIFS address for hostname '{}'", hostname);
178
179 cifsAddress = UniAddress.getByName(hostname);
180
181 if (username == null || username.isEmpty()) {
182 authentication = new NtlmPasswordAuthentication(null);
183 if (log.isTraceEnabled()) {
184 log.trace("Logon to CIFS host using default credentials: " +
185 "domain: '{}' username: '{}' and password: '{}'", new Object[]{
186 authentication.getDomain(), authentication.getUsername(), authentication.getPassword()
187 });
188 }
189 } else {
190
191
192
193 domain = (domain == null || domain.isEmpty() || domain.equals("*")) ? "?" : domain;
194 authentication = new NtlmPasswordAuthentication(domain, username, password);
195 }
196
197 log.info("TMACL-01160:Connecting with CIFS server 'smb://{}/{}', using '{}\\\\{}'",
198 new Object[]{hostname, shareName, authentication.getDomain(), authentication.getUsername()});
199
200 loginAndSetBasePathIfRequired();
201 cifsRepositoryBaseURI = repositoryBasePath.getCanonicalPath();
202 }
203
204 private void loginAndSetBasePathIfRequired() throws SmbException, MalformedURLException {
205 if (repositoryBasePath == null) {
206 synchronized (this) {
207 if (cifsAddress == null) {
208 throw new IllegalStateException("A connection with the CIFS server was not initiated. " +
209 "No valid address to the cifs server was defined. " +
210 "See previous log output for further details what failed.");
211 }
212
213 if (repositoryBasePath == null) {
214 try {
215 if (log.isTraceEnabled())
216 log.trace("Trying to logon to {}, using {}", cifsAddress, authentication);
217 SmbSession.logon(cifsAddress, authentication);
218 } catch (SmbException e) {
219 logLoginException(e);
220 throw e;
221 }
222
223 String baseURI = String.format("smb://%s/%s", cifsAddress.getHostAddress(), shareName);
224 if (log.isTraceEnabled()) log.trace("Looking up base path under {}", baseURI);
225 SmbFile repositoryBasePath = new SmbFile(baseURI, authentication);
226
227 boolean basePathExists = false;
228 try {
229 basePathExists = repositoryBasePath.isDirectory();
230 } catch (SmbException e) {
231 logAccessException(baseURI, e);
232 }
233
234 if (!basePathExists) {
235 log.warn("TMACL-01180:The repository base path doesn't exist under '{}', " +
236 "file operations will probably fail.", repositoryBasePath);
237 }
238
239 this.repositoryBasePath = repositoryBasePath;
240 }
241 }
242 }
243 }
244
245 private void logAccessException(String baseURI, SmbException e) throws SmbException {
246 switch (e.getNtStatus()) {
247 case NtStatus.NT_STATUS_BAD_NETWORK_NAME:
248 log.error("TMACL-01170:The specified CIFS share '{}' is not known. " +
249 "Please create the share on the CIFS server before submitting " +
250 "files to the ACL.", baseURI);
251 break;
252 case NtStatus.NT_STATUS_ACCESS_DENIED:
253 log.error("TMACL-01290:Access was denied to the specified CIFS share '{}'. " +
254 "Please GRANT read & write access to the user '{}'.", baseURI, authentication);
255 break;
256 default:
257 throw e;
258 }
259 }
260
261 private void logLoginException(SmbException e) {
262 switch (e.getNtStatus()) {
263 case NtStatus.NT_STATUS_LOGON_FAILURE:
264 log.error("TMACL-01330:The logon failed with user '{}'." +
265 "Please verify domain, username and password and retry.",
266 authentication);
267 break;
268 case NtStatus.NT_STATUS_WRONG_PASSWORD:
269 log.error("TMACL-01340:The specified password is wrong for user '{}'." +
270 "Please use a correct password to access the CIFS share.",
271 authentication);
272 break;
273 case NtStatus.NT_STATUS_PASSWORD_EXPIRED:
274 case NtStatus.NT_STATUS_PASSWORD_MUST_CHANGE:
275 log.error("TMACL-01350:The specified password is expired or must be changed for " +
276 "user '{}'. Specify a new password to access the CIFS share.",
277 authentication);
278 break;
279 case NtStatus.NT_STATUS_ACCOUNT_LOCKED_OUT:
280 case NtStatus.NT_STATUS_ACCOUNT_DISABLED:
281 log.error("TMACL-01300:The account of user '{}' was disabled or locked out." +
282 "Please re-enable it or use another user to access the CIFS share.",
283 authentication);
284 break;
285 case NtStatus.NT_STATUS_INVALID_LOGON_HOURS:
286 log.error("TMACL-01310:The account of user '{}' is used within invalid logon hours." +
287 "Please remove logon hour limits in order to access the CIFS share.",
288 authentication);
289 break;
290 case NtStatus.NT_STATUS_INVALID_WORKSTATION:
291 log.error("TMACL-01320:This machine was not granted access to the CIFS share." +
292 "Please grant access to the CIFS share for this workstation " +
293 "(e.g. using its FQDN).");
294 break;
295 }
296 }
297 }