xref: /onnv-gate/usr/src/lib/smbsrv/libsmbrdr/common/smbrdr_logon.c (revision 11963:061945695ce1)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 /*
22  * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 #include <pthread.h>
27 #include <string.h>
28 #include <strings.h>
29 #include <syslog.h>
30 #include <synch.h>
31 #include <sys/errno.h>
32 #include <sys/types.h>
33 #include <sys/socket.h>
34 #include <netinet/in.h>
35 #include <arpa/inet.h>
36 #include <smbsrv/wintypes.h>
37 #include <smbsrv/libsmbrdr.h>
38 #include <smbsrv/ntstatus.h>
39 #include <smbsrv/smb.h>
40 #include <smbrdr.h>
41 
42 #define	SMBRDR_ANON_USER	"IPC$"
43 
44 static int smbrdr_session_setupx(struct sdb_logon *);
45 static struct sdb_logon *smbrdr_logon_init(struct sdb_session *, char *,
46     uint8_t *);
47 static int smbrdr_authenticate(char *, char *, uint8_t *);
48 
49 /*
50  * If the username is SMBRDR_ANON_USER, an anonymous session will be
51  * established. Otherwise, an authenticated session will be established
52  * based on the specified credentials.
53  */
54 int
55 smbrdr_logon(char *domain_controller, char *domain, char *username)
56 {
57 	uint8_t pwd_hash[SMBAUTH_HASH_SZ];
58 	int rc;
59 
60 	if (username == NULL || *username == 0) {
61 		syslog(LOG_DEBUG, "smbrdr: no username");
62 		return (-1);
63 	}
64 
65 	bzero(pwd_hash, SMBAUTH_HASH_SZ);
66 	if (smb_strcasecmp(username, SMBRDR_ANON_USER, 0) != 0) {
67 		smb_ipc_get_passwd(pwd_hash, SMBAUTH_HASH_SZ);
68 		if (*pwd_hash == 0) {
69 			syslog(LOG_DEBUG, "smbrdr: no password");
70 			return (-1);
71 		}
72 	}
73 
74 	if (smbrdr_negotiate(domain_controller, domain) != 0) {
75 		syslog(LOG_DEBUG, "smbrdr: negotiate failed");
76 		return (-1);
77 	}
78 
79 	rc = smbrdr_authenticate(domain_controller, username, pwd_hash);
80 	return ((rc == AUTH_USER_GRANT) ? 0 : -1);
81 
82 }
83 
84 /*
85  * Get the user session key from an already open named pipe.
86  * The RPC library needs this.  See ndr_rpc_get_ssnkey()
87  *
88  * Returns zero (success) or an errno.
89  */
90 int
91 smbrdr_get_ssnkey(int fid, unsigned char *ssn_key, size_t key_len)
92 {
93 	struct sdb_logon *logon;
94 	struct sdb_session *session;
95 	struct sdb_netuse *netuse;
96 	struct sdb_ofile *ofile;
97 
98 	if (ssn_key == NULL || key_len < SMBAUTH_SESSION_KEY_SZ)
99 		return (EINVAL);
100 
101 	ofile = smbrdr_ofile_get(fid);
102 	if (ofile == NULL)
103 		return (EBADF);
104 
105 	netuse = ofile->netuse;
106 	session = netuse->session;
107 	logon = &session->logon;
108 
109 	if (key_len > SMBAUTH_SESSION_KEY_SZ)
110 		bzero(ssn_key, key_len);
111 	bcopy(logon->ssn_key, ssn_key,
112 	    SMBAUTH_SESSION_KEY_SZ);
113 
114 	smbrdr_ofile_put(ofile);
115 	return (0);
116 }
117 
118 /*
119  * smbrdr_authenticate
120  *
121  * This is the entry point for logging  a user onto the domain. The
122  * session structure should have been obtained via a successful call
123  * to smbrdr_smb_connect. We allocate a logon structure to hold the
124  * user details and attempt to logon using smbrdr_session_setupx.
125  * Note that we expect the password fields to have been encrypted
126  * before this call.
127  *
128  * On success, the logon structure will be returned. Otherwise a null
129  * pointer will be returned.
130  */
131 static int
132 smbrdr_authenticate(char *server, char *username, uint8_t *pwd)
133 {
134 	struct sdb_session *session;
135 	struct sdb_logon *logon;
136 	struct sdb_logon old_logon;
137 	int ret;
138 
139 	session = smbrdr_session_lock(server, SDB_SLCK_WRITE);
140 	if (session == NULL) {
141 		syslog(LOG_DEBUG, "smbrdr_authenticate: %s: no session with %s",
142 		    username, server);
143 		return (-1);
144 	}
145 
146 	bzero(&old_logon, sizeof (struct sdb_logon));
147 
148 	logon = &session->logon;
149 	if (logon->type != SDB_LOGON_NONE) {
150 		if (strcasecmp(logon->username, username) == 0) {
151 			/* The requested user has already been logged in */
152 			smbrdr_session_unlock(session);
153 			return ((logon->type == SDB_LOGON_GUEST)
154 			    ? AUTH_GUEST_GRANT : AUTH_USER_GRANT);
155 		}
156 
157 		old_logon = *logon;
158 	}
159 
160 	logon = smbrdr_logon_init(session, username, pwd);
161 
162 	if (logon == NULL) {
163 		syslog(LOG_DEBUG, "smbrdr_authenticate: %s: %m", username);
164 		smbrdr_session_unlock(session);
165 		return (-1);
166 	}
167 
168 	if (smbrdr_session_setupx(logon) < 0) {
169 		free(logon);
170 		smbrdr_session_unlock(session);
171 		return (-1);
172 	}
173 
174 
175 	ret = (logon->type == SDB_LOGON_GUEST)
176 	    ? AUTH_GUEST_GRANT : AUTH_USER_GRANT;
177 
178 	session->logon = *logon;
179 	free(logon);
180 
181 	if (old_logon.type != SDB_LOGON_NONE)
182 		(void) smbrdr_logoffx(&old_logon);
183 
184 	smbrdr_session_unlock(session);
185 	return (ret);
186 }
187 
188 
189 /*
190  * smbrdr_session_setupx
191  *
192  * Build and send an SMB session setup command. This is used to log a
193  * user onto the domain. See CIFS section 4.1.2.
194  *
195  * Returns 0 on success. Otherwise returns a -ve error code.
196  */
197 static int
198 smbrdr_session_setupx(struct sdb_logon *logon)
199 {
200 	struct sdb_session *session;
201 	smb_hdr_t smb_hdr;
202 	smbrdr_handle_t srh;
203 	smb_msgbuf_t *mb;
204 	char *native_os;
205 	char *native_lanman;
206 	unsigned short data_bytes;
207 	unsigned short guest;
208 	unsigned long capabilities;
209 	unsigned short null_size;
210 	size_t (*strlen_fn)(const char *s);
211 	DWORD status;
212 	int rc;
213 
214 	/*
215 	 * Paranoia check - we should never get this
216 	 * far without a valid session structure.
217 	 */
218 	if ((session = logon->session) == NULL)
219 		return (-1);
220 
221 	if (session->remote_caps & CAP_UNICODE) {
222 		strlen_fn = smb_wcequiv_strlen;
223 		null_size = sizeof (smb_wchar_t);
224 		session->smb_flags2 |= SMB_FLAGS2_UNICODE;
225 	} else {
226 		strlen_fn = strlen;
227 		null_size = sizeof (char);
228 	}
229 
230 	if (smbrdr_sign_init(session, logon) < 0) {
231 		syslog(LOG_DEBUG,
232 		    "smbrdr_session_setupx: smbrdr_sign_init failed");
233 		return (-1);
234 	}
235 
236 	status = smbrdr_request_init(&srh, SMB_COM_SESSION_SETUP_ANDX,
237 	    session, 0, 0);
238 
239 	if (status != NT_STATUS_SUCCESS) {
240 		smbrdr_sign_fini(session);
241 		syslog(LOG_DEBUG, "smbrdr_session_setupx: %s",
242 		    xlate_nt_status(status));
243 		return (-1);
244 	}
245 	mb = &srh.srh_mbuf;
246 
247 	/*
248 	 * Regardless of the server's capabilities or what's
249 	 * reported in smb_flags2, we should report our full
250 	 * capabilities.
251 	 */
252 	capabilities = CAP_UNICODE | CAP_NT_SMBS | CAP_STATUS32;
253 
254 	/*
255 	 * Compute the BCC for unicode or ASCII strings.
256 	 */
257 	data_bytes  = logon->auth.ci_len + logon->auth.cs_len + null_size;
258 	data_bytes += strlen_fn(session->native_os) + null_size;
259 	data_bytes += strlen_fn(session->native_lanman) + null_size;
260 
261 	if (logon->type == SDB_LOGON_ANONYMOUS) {
262 		/*
263 		 * Anonymous logon: no username or domain name.
264 		 * We still need to include two null characters.
265 		 */
266 		data_bytes += (2 * null_size);
267 
268 		rc = smb_msgbuf_encode(mb, "bb.wwwwlwwllwlu.u.",
269 		    13,				/* smb_wct */
270 		    0xff,			/* AndXCommand (none) */
271 		    32 + 26 + 3 + data_bytes,	/* AndXOffset */
272 		    SMBRDR_REQ_BUFSZ,		/* MaxBufferSize */
273 		    1,				/* MaxMpxCount */
274 		    0,				/* VcNumber */
275 		    0,				/* SessionKey */
276 		    1,				/* CaseInsensitivePassLength */
277 		    0,				/* CaseSensitivePassLength */
278 		    0,				/* Reserved */
279 		    capabilities,		/* Capabilities */
280 		    data_bytes,			/* smb_bcc */
281 		    0,				/* No user or domain */
282 		    session->native_os,		/* NativeOS */
283 		    session->native_lanman);	/* NativeLanMan */
284 	} else {
285 		data_bytes += strlen_fn(logon->username) + null_size;
286 		data_bytes += strlen_fn(session->domain) + null_size;
287 
288 		rc = smb_msgbuf_encode(mb, "bb.wwwwlwwllw#c#cuuu.u.",
289 		    13,				/* smb_wct */
290 		    0xff,			/* AndXCommand (none) */
291 		    32 + 26 + 3 + data_bytes,	/* AndXOffset */
292 		    SMBRDR_REQ_BUFSZ,		/* MaxBufferSize */
293 		    1,				/* MaxMpxCount */
294 		    session->vc,		/* VcNumber */
295 		    session->sesskey,		/* SessionKey */
296 		    logon->auth.ci_len,		/* CaseInsensitivePassLength */
297 		    logon->auth.cs_len,		/* CaseSensitivePassLength */
298 		    0,				/* Reserved */
299 		    capabilities,		/* Capabilities */
300 		    data_bytes,			/* smb_bcc */
301 		    logon->auth.ci_len,		/* ci length spec */
302 		    logon->auth.ci,		/* CaseInsensitivePassword */
303 		    logon->auth.cs_len,		/* cs length spec */
304 		    logon->auth.cs,		/* CaseSensitivePassword */
305 		    logon->username,		/* AccountName */
306 		    session->domain,		/* PrimaryDomain */
307 		    session->native_os,		/* NativeOS */
308 		    session->native_lanman);	/* NativeLanMan */
309 	}
310 
311 	if (rc <= 0) {
312 		syslog(LOG_DEBUG, "smbrdr_session_setupx: encode failed");
313 		smbrdr_handle_free(&srh);
314 		smbrdr_sign_fini(session);
315 		return (-1);
316 	}
317 
318 	status = smbrdr_exchange(&srh, &smb_hdr, 0);
319 	if (status != NT_STATUS_SUCCESS) {
320 		syslog(LOG_DEBUG, "smbrdr_session_setupx: %s",
321 		    xlate_nt_status(status));
322 		smbrdr_handle_free(&srh);
323 		smbrdr_sign_fini(session);
324 		return (-1);
325 	}
326 
327 	rc = smb_msgbuf_decode(mb, "5.w2.u", &guest, &native_os);
328 
329 	/*
330 	 * There was a problem in decoding response from
331 	 * a Samba 2.x PDC. This server sends strings in ASCII
332 	 * format and there is one byte with value 0 between
333 	 * native_os and native_lm:
334 	 *
335 	 *				 FF 53 4D 42 73 00		.SMBs.
336 	 * 00 00 00 88 01 00 00 00 00 00 00 00 00 00 00 00 ................
337 	 * 00 00 00 00 BB 00 64 00 00 00 03 FF 00 00 00 01 ......d.........
338 	 * 00 1C 00 55 6E 69 78 00 53 61 6D 62 61 20 32 2E ...Unix.Samba.2.
339 	 * 32 2E 38 61 00 53 41 4D 42 41 5F 44 4F 4D 00    2.8a.SAMBA_DOM.
340 	 *
341 	 * The byte doesn't seem to be padding because when change in
342 	 * native OS from Unix to Unix1 the 0 byte is still there:
343 	 *
344 	 *				 FF 53 4D 42 73 00		.SMBs.
345 	 * 00 00 00 88 01 00 00 00 00 00 00 00 00 00 00 00 ................
346 	 * 00 00 00 00 BB 00 64 00 00 00 03 FF 00 00 00 00 ......d.........
347 	 * 00 1D 00 55 6E 69 78 31 00 53 61 6D 62 61 20 32 ...Unix1.Samba.2
348 	 * 2E 32 2E 38 61 00 53 41 4D 42 41 5F 44 4F 4D 00 .2.8a.SAMBA_DOM.
349 	 */
350 	if (rc > 0) {
351 		if (session->remote_caps & CAP_UNICODE)
352 			rc = smb_msgbuf_decode(mb, "u", &native_lanman);
353 		else
354 			rc = smb_msgbuf_decode(mb, ".u", &native_lanman);
355 	}
356 
357 	if (rc <= 0) {
358 		syslog(LOG_DEBUG, "smbrdr_session_setupx: decode failed");
359 		smbrdr_handle_free(&srh);
360 		smbrdr_sign_fini(session);
361 		return (-1);
362 	}
363 
364 	session->remote_os = smbnative_os_value(native_os);
365 	session->remote_lm = smbnative_lm_value(native_lanman);
366 	session->pdc_type  = smbnative_pdc_value(native_lanman);
367 
368 	logon->uid = smb_hdr.uid;
369 	if (guest)
370 		logon->type = SDB_LOGON_GUEST;
371 
372 	smbrdr_handle_free(&srh);
373 	smbrdr_sign_unset_key(session);
374 
375 	logon->state = SDB_LSTATE_SETUP;
376 
377 	return (0);
378 }
379 
380 /*
381  * smbrdr_logoffx
382  *
383  * Build and send an SMB session logoff (SMB_COM_LOGOFF_ANDX) command.
384  * This is the inverse of an SMB_COM_SESSION_SETUP_ANDX. See CIFS
385  * section 4.1.3. The logon structure should have been obtained from a
386  * successful call to smbrdr_authenticate.
387  *
388  * Returns 0 on success. Otherwise returns a -ve error code.
389  */
390 int
391 smbrdr_logoffx(struct sdb_logon *logon)
392 {
393 	struct sdb_session *session;
394 	smbrdr_handle_t srh;
395 	smb_hdr_t smb_hdr;
396 	DWORD status;
397 	int rc;
398 
399 	if (logon->state != SDB_LSTATE_SETUP) {
400 		/* No user to logoff */
401 		bzero(logon, sizeof (struct sdb_logon));
402 		return (0);
403 	}
404 
405 	if ((session = logon->session) == 0) {
406 		bzero(logon, sizeof (struct sdb_logon));
407 		return (0);
408 	}
409 
410 	logon->state = SDB_LSTATE_LOGGING_OFF;
411 	smbrdr_netuse_logoff(logon->uid);
412 
413 	if ((session->state != SDB_SSTATE_NEGOTIATED) &&
414 	    (session->state != SDB_SSTATE_DISCONNECTING)) {
415 		bzero(logon, sizeof (struct sdb_logon));
416 		return (0);
417 	}
418 
419 	status = smbrdr_request_init(&srh, SMB_COM_LOGOFF_ANDX,
420 	    session, logon, 0);
421 
422 	if (status != NT_STATUS_SUCCESS) {
423 		logon->state = SDB_LSTATE_SETUP;
424 		syslog(LOG_DEBUG, "smbrdr_logoffx: %s: %s", logon->username,
425 		    xlate_nt_status(status));
426 		return (-1);
427 	}
428 
429 	rc = smb_msgbuf_encode(&srh.srh_mbuf, "bbbww", 2, 0xff, 0, 0, 0);
430 	if (rc < 0) {
431 		logon->state = SDB_LSTATE_SETUP;
432 		smbrdr_handle_free(&srh);
433 		syslog(LOG_DEBUG, "smbrdr_logoffx: %s: encode failed",
434 		    logon->username);
435 		return (rc);
436 	}
437 
438 	status = smbrdr_exchange(&srh, &smb_hdr, 0);
439 	if (status != NT_STATUS_SUCCESS) {
440 		syslog(LOG_DEBUG, "smbrdr_logoffx: %s: %s", logon->username,
441 		    xlate_nt_status(status));
442 		rc = -1;
443 	} else {
444 		rc = 0;
445 	}
446 
447 	bzero(logon, sizeof (struct sdb_logon));
448 	smbrdr_handle_free(&srh);
449 	return (rc);
450 }
451 
452 
453 /*
454  * smbrdr_logon_init
455  *
456  * Find a slot for account logon information. The account information
457  * is associated with a session so we need a valid session slot before
458  * calling this function. If we already have a record of the specified
459  * account, a pointer to that record is returned. Otherwise we attempt
460  * to allocate a new one.
461  */
462 static struct sdb_logon *
463 smbrdr_logon_init(struct sdb_session *session, char *username, uint8_t *pwd)
464 {
465 	struct sdb_logon *logon;
466 	int64_t smbrdr_lmcompl;
467 	int rc;
468 
469 	logon = (struct sdb_logon *)malloc(sizeof (sdb_logon_t));
470 	if (logon == 0)
471 		return (0);
472 
473 	bzero(logon, sizeof (struct sdb_logon));
474 	logon->session = session;
475 
476 	(void) smb_config_getnum(SMB_CI_LM_LEVEL, &smbrdr_lmcompl);
477 
478 	if (strcmp(username, "IPC$") == 0) {
479 		logon->type = SDB_LOGON_ANONYMOUS;
480 		logon->auth.ci_len = 1;
481 		*(logon->auth.ci) = 0;
482 		logon->auth.cs_len = 0;
483 	} else {
484 		logon->type = SDB_LOGON_USER;
485 		rc = smb_auth_set_info(username, 0, pwd,
486 		    session->domain, session->challenge_key,
487 		    session->challenge_len, smbrdr_lmcompl, &logon->auth);
488 
489 		/* Generate (and save) the session key. */
490 		if (rc == 0) {
491 			rc = smb_auth_gen_session_key(&logon->auth,
492 			    logon->ssn_key);
493 		}
494 
495 		if (rc != 0) {
496 			free(logon);
497 			return (0);
498 		}
499 	}
500 
501 	(void) strlcpy(logon->username, username, MAX_ACCOUNT_NAME);
502 	logon->state = SDB_LSTATE_INIT;
503 	return (logon);
504 }
505