xref: /onnv-gate/usr/src/lib/smbsrv/libsmbrdr/common/smbrdr_logon.c (revision 10966:37e5dcdf36d3)
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 2009 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_anonymous_logon(char *domain_controller, char *domain_name);
45 static int smbrdr_auth_logon(char *domain_controller, char *domain_name,
46     char *username);
47 static int smbrdr_session_setupx(struct sdb_logon *logon);
48 static boolean_t smbrdr_logon_validate(char *server, char *username);
49 static struct sdb_logon *smbrdr_logon_init(struct sdb_session *session,
50     char *username, unsigned char *pwd);
51 static int smbrdr_logon_user(char *server, char *username, unsigned char *pwd);
52 static int smbrdr_authenticate(char *, char *, char *, unsigned char *);
53 
54 /*
55  * If the username is SMBRDR_ANON_USER, an anonymous session will be
56  * established. Otherwise, an authenticated session will be established
57  * based on the specified credentials.
58  */
59 int
60 smbrdr_logon(char *domain_controller, char *domain, char *username)
61 {
62 	int rc;
63 
64 	if (strcmp(username, SMBRDR_ANON_USER) == 0)
65 		rc = smbrdr_anonymous_logon(domain_controller, domain);
66 	else
67 		rc = smbrdr_auth_logon(domain_controller, domain, username);
68 
69 	return (rc);
70 }
71 
72 /*
73  * smbrdr_anonymous_logon
74  *
75  * Set up an anonymous session. If the session to the resource domain
76  * controller appears to be okay we shouldn't need to do anything here.
77  * Otherwise we clean up the stale session and create a new one.
78  */
79 static int
80 smbrdr_anonymous_logon(char *domain_controller, char *domain_name)
81 {
82 	if (smbrdr_logon_validate(domain_controller, SMBRDR_ANON_USER))
83 		return (0);
84 
85 	if (smbrdr_negotiate(domain_controller, domain_name) != 0) {
86 		syslog(LOG_DEBUG, "smbrdr_anonymous_logon: negotiate failed");
87 		return (-1);
88 	}
89 
90 	if (smbrdr_logon_user(domain_controller, SMBRDR_ANON_USER, 0) < 0) {
91 		syslog(LOG_DEBUG, "smbrdr_anonymous_logon: logon failed");
92 		return (-1);
93 	}
94 
95 	return (0);
96 }
97 
98 /*
99  * Get the user session key from an already open named pipe.
100  * The RPC library needs this.  See ndr_rpc_get_ssnkey()
101  *
102  * Returns zero (success) or an errno.
103  */
104 int
105 smbrdr_get_ssnkey(int fid, unsigned char *ssn_key, size_t key_len)
106 {
107 	struct sdb_logon *logon;
108 	struct sdb_session *session;
109 	struct sdb_netuse *netuse;
110 	struct sdb_ofile *ofile;
111 
112 	if (ssn_key == NULL || key_len < SMBAUTH_SESSION_KEY_SZ)
113 		return (EINVAL);
114 
115 	ofile = smbrdr_ofile_get(fid);
116 	if (ofile == NULL)
117 		return (EBADF);
118 
119 	netuse = ofile->netuse;
120 	session = netuse->session;
121 	logon = &session->logon;
122 
123 	if (key_len > SMBAUTH_SESSION_KEY_SZ)
124 		bzero(ssn_key, key_len);
125 	bcopy(logon->ssn_key, ssn_key,
126 	    SMBAUTH_SESSION_KEY_SZ);
127 
128 	smbrdr_ofile_put(ofile);
129 	return (0);
130 }
131 
132 /*
133  * smbrdr_auth_logon
134  *
135  * Set up a user session. If the session to the resource domain controller
136  * appears to be okay we shouldn't need to do anything here. Otherwise we
137  * clean up the stale session and create a new one. Once a session is
138  * established, we leave it intact. It should only need to be set up again
139  * due to an inactivity timeout or a domain controller reset.
140  */
141 static int
142 smbrdr_auth_logon(char *domain_controller, char *domain_name, char *username)
143 {
144 	int erc;
145 	uint8_t pwd_hash[SMBAUTH_HASH_SZ];
146 
147 	if (username == NULL || *username == 0) {
148 		syslog(LOG_DEBUG, "smbrdr_auth_logon: no username");
149 		return (-1);
150 	}
151 
152 	smb_ipc_get_passwd(pwd_hash, SMBAUTH_HASH_SZ);
153 	if (*pwd_hash == 0) {
154 		syslog(LOG_DEBUG, "smbrdr_auth_logon: no password");
155 		return (-1);
156 	}
157 
158 	if (smbrdr_logon_validate(domain_controller, username))
159 		return (0);
160 
161 	if (smbrdr_negotiate(domain_controller, domain_name) != 0) {
162 		syslog(LOG_DEBUG, "smbrdr_auth_logon: negotiate failed");
163 		return (-1);
164 	}
165 
166 	erc = smbrdr_authenticate(domain_controller, domain_name, username,
167 	    pwd_hash);
168 	return ((erc == AUTH_USER_GRANT) ? 0 : -1);
169 }
170 
171 /*
172  * smbrdr_authenticate
173  *
174  * Authenticate primary_domain\account_name.
175  *
176  * Returns:
177  * 0	User access granted
178  * 1	Guest access granted
179  * 2	IPC access granted
180  * (<0) Error
181  */
182 static int
183 smbrdr_authenticate(char *domain_controller, char *primary_domain,
184     char *account_name, unsigned char *pwd)
185 {
186 	if (pwd == NULL)
187 		return (AUTH_USER_GRANT | AUTH_IPC_ONLY_GRANT);
188 
189 	/*
190 	 * Ensure that the domain name is uppercase.
191 	 */
192 	(void) smb_strupr(primary_domain);
193 	return (smbrdr_logon_user(domain_controller, account_name, pwd));
194 }
195 
196 /*
197  * smbrdr_logon_user
198  *
199  * This is the entry point for logging  a user onto the domain. The
200  * session structure should have been obtained via a successful call
201  * to smbrdr_smb_connect. We allocate a logon structure to hold the
202  * user details and attempt to logon using smbrdr_session_setupx.
203  * Note that we expect the password fields to have been encrypted
204  * before this call.
205  *
206  * On success, the logon structure will be returned. Otherwise a null
207  * pointer will be returned.
208  */
209 static int
210 smbrdr_logon_user(char *server, char *username, unsigned char *pwd)
211 {
212 	struct sdb_session *session;
213 	struct sdb_logon *logon;
214 	struct sdb_logon old_logon;
215 	int ret;
216 
217 	if ((server == NULL) || (username == NULL) ||
218 	    ((strcmp(username, SMBRDR_ANON_USER) != 0) && (pwd == NULL)))
219 		return (-1);
220 
221 	session = smbrdr_session_lock(server, 0, SDB_SLCK_WRITE);
222 	if (session == NULL) {
223 		syslog(LOG_DEBUG, "smbrdr_logon_user: %s: no session with %s",
224 		    username, server);
225 		return (-1);
226 	}
227 
228 	bzero(&old_logon, sizeof (struct sdb_logon));
229 
230 	logon = &session->logon;
231 	if (logon->type != SDB_LOGON_NONE) {
232 		if (strcasecmp(logon->username, username) == 0) {
233 			/* The requested user has already been logged in */
234 			smbrdr_session_unlock(session);
235 			return ((logon->type == SDB_LOGON_GUEST)
236 			    ? AUTH_GUEST_GRANT : AUTH_USER_GRANT);
237 		}
238 
239 		old_logon = *logon;
240 	}
241 
242 	logon = smbrdr_logon_init(session, username, pwd);
243 
244 	if (logon == NULL) {
245 		syslog(LOG_DEBUG, "smbrdr_logon_user: %s: %s",
246 		    username, strerror(ENOMEM));
247 		smbrdr_session_unlock(session);
248 		return (-1);
249 	}
250 
251 	if (smbrdr_session_setupx(logon) < 0) {
252 		free(logon);
253 		smbrdr_session_unlock(session);
254 		return (-1);
255 	}
256 
257 
258 	ret = (logon->type == SDB_LOGON_GUEST)
259 	    ? AUTH_GUEST_GRANT : AUTH_USER_GRANT;
260 
261 	session->logon = *logon;
262 	free(logon);
263 
264 	if (old_logon.type != SDB_LOGON_NONE)
265 		(void) smbrdr_logoffx(&old_logon);
266 
267 	smbrdr_session_unlock(session);
268 	return (ret);
269 }
270 
271 
272 /*
273  * smbrdr_session_setupx
274  *
275  * Build and send an SMB session setup command. This is used to log a
276  * user onto the domain. See CIFS section 4.1.2.
277  *
278  * Returns 0 on success. Otherwise returns a -ve error code.
279  */
280 static int
281 smbrdr_session_setupx(struct sdb_logon *logon)
282 {
283 	struct sdb_session *session;
284 	smb_hdr_t smb_hdr;
285 	smbrdr_handle_t srh;
286 	smb_msgbuf_t *mb;
287 	char *native_os;
288 	char *native_lanman;
289 	unsigned short data_bytes;
290 	unsigned short guest;
291 	unsigned long capabilities;
292 	unsigned short null_size;
293 	size_t (*strlen_fn)(const char *s);
294 	DWORD status;
295 	int rc;
296 
297 	/*
298 	 * Paranoia check - we should never get this
299 	 * far without a valid session structure.
300 	 */
301 	if ((session = logon->session) == NULL)
302 		return (-1);
303 
304 	if (session->remote_caps & CAP_UNICODE) {
305 		strlen_fn = smb_wcequiv_strlen;
306 		null_size = sizeof (smb_wchar_t);
307 		session->smb_flags2 |= SMB_FLAGS2_UNICODE;
308 	} else {
309 		strlen_fn = strlen;
310 		null_size = sizeof (char);
311 	}
312 
313 	if (smbrdr_sign_init(session, logon) < 0) {
314 		syslog(LOG_DEBUG,
315 		    "smbrdr_session_setupx: smbrdr_sign_init failed");
316 		return (-1);
317 	}
318 
319 	status = smbrdr_request_init(&srh, SMB_COM_SESSION_SETUP_ANDX,
320 	    session, 0, 0);
321 
322 	if (status != NT_STATUS_SUCCESS) {
323 		smbrdr_sign_fini(session);
324 		syslog(LOG_DEBUG, "smbrdr_session_setupx: %s",
325 		    xlate_nt_status(status));
326 		return (-1);
327 	}
328 	mb = &srh.srh_mbuf;
329 
330 	/*
331 	 * Regardless of the server's capabilities or what's
332 	 * reported in smb_flags2, we should report our full
333 	 * capabilities.
334 	 */
335 	capabilities = CAP_UNICODE | CAP_NT_SMBS | CAP_STATUS32;
336 
337 	/*
338 	 * Compute the BCC for unicode or ASCII strings.
339 	 */
340 	data_bytes  = logon->auth.ci_len + logon->auth.cs_len + null_size;
341 	data_bytes += strlen_fn(session->native_os) + null_size;
342 	data_bytes += strlen_fn(session->native_lanman) + null_size;
343 
344 	if (logon->type == SDB_LOGON_ANONYMOUS) {
345 		/*
346 		 * Anonymous logon: no username or domain name.
347 		 * We still need to include two null characters.
348 		 */
349 		data_bytes += (2 * null_size);
350 
351 		rc = smb_msgbuf_encode(mb, "bb.wwwwlwwllwlu.u.",
352 		    13,				/* smb_wct */
353 		    0xff,			/* AndXCommand (none) */
354 		    32 + 26 + 3 + data_bytes,	/* AndXOffset */
355 		    SMBRDR_REQ_BUFSZ,		/* MaxBufferSize */
356 		    1,				/* MaxMpxCount */
357 		    0,				/* VcNumber */
358 		    0,				/* SessionKey */
359 		    1,				/* CaseInsensitivePassLength */
360 		    0,				/* CaseSensitivePassLength */
361 		    0,				/* Reserved */
362 		    capabilities,		/* Capabilities */
363 		    data_bytes,			/* smb_bcc */
364 		    0,				/* No user or domain */
365 		    session->native_os,		/* NativeOS */
366 		    session->native_lanman);	/* NativeLanMan */
367 	} else {
368 		data_bytes += strlen_fn(logon->username) + null_size;
369 		data_bytes += strlen_fn(session->domain) + null_size;
370 
371 		rc = smb_msgbuf_encode(mb, "bb.wwwwlwwllw#c#cuuu.u.",
372 		    13,				/* smb_wct */
373 		    0xff,			/* AndXCommand (none) */
374 		    32 + 26 + 3 + data_bytes,	/* AndXOffset */
375 		    SMBRDR_REQ_BUFSZ,		/* MaxBufferSize */
376 		    1,				/* MaxMpxCount */
377 		    session->vc,		/* VcNumber */
378 		    session->sesskey,		/* SessionKey */
379 		    logon->auth.ci_len,		/* CaseInsensitivePassLength */
380 		    logon->auth.cs_len,		/* CaseSensitivePassLength */
381 		    0,				/* Reserved */
382 		    capabilities,		/* Capabilities */
383 		    data_bytes,			/* smb_bcc */
384 		    logon->auth.ci_len,		/* ci length spec */
385 		    logon->auth.ci,		/* CaseInsensitivePassword */
386 		    logon->auth.cs_len,		/* cs length spec */
387 		    logon->auth.cs,		/* CaseSensitivePassword */
388 		    logon->username,		/* AccountName */
389 		    session->domain,		/* PrimaryDomain */
390 		    session->native_os,		/* NativeOS */
391 		    session->native_lanman);	/* NativeLanMan */
392 	}
393 
394 	if (rc <= 0) {
395 		syslog(LOG_DEBUG, "smbrdr_session_setupx: encode failed");
396 		smbrdr_handle_free(&srh);
397 		smbrdr_sign_fini(session);
398 		return (-1);
399 	}
400 
401 	status = smbrdr_exchange(&srh, &smb_hdr, 0);
402 	if (status != NT_STATUS_SUCCESS) {
403 		syslog(LOG_DEBUG, "smbrdr_session_setupx: %s",
404 		    xlate_nt_status(status));
405 		smbrdr_handle_free(&srh);
406 		smbrdr_sign_fini(session);
407 		return (-1);
408 	}
409 
410 	rc = smb_msgbuf_decode(mb, "5.w2.u", &guest, &native_os);
411 
412 	/*
413 	 * There was a problem in decoding response from
414 	 * a Samba 2.x PDC. This server sends strings in ASCII
415 	 * format and there is one byte with value 0 between
416 	 * native_os and native_lm:
417 	 *
418 	 *				 FF 53 4D 42 73 00		.SMBs.
419 	 * 00 00 00 88 01 00 00 00 00 00 00 00 00 00 00 00 ................
420 	 * 00 00 00 00 BB 00 64 00 00 00 03 FF 00 00 00 01 ......d.........
421 	 * 00 1C 00 55 6E 69 78 00 53 61 6D 62 61 20 32 2E ...Unix.Samba.2.
422 	 * 32 2E 38 61 00 53 41 4D 42 41 5F 44 4F 4D 00    2.8a.SAMBA_DOM.
423 	 *
424 	 * The byte doesn't seem to be padding because when change in
425 	 * native OS from Unix to Unix1 the 0 byte is still there:
426 	 *
427 	 *				 FF 53 4D 42 73 00		.SMBs.
428 	 * 00 00 00 88 01 00 00 00 00 00 00 00 00 00 00 00 ................
429 	 * 00 00 00 00 BB 00 64 00 00 00 03 FF 00 00 00 00 ......d.........
430 	 * 00 1D 00 55 6E 69 78 31 00 53 61 6D 62 61 20 32 ...Unix1.Samba.2
431 	 * 2E 32 2E 38 61 00 53 41 4D 42 41 5F 44 4F 4D 00 .2.8a.SAMBA_DOM.
432 	 */
433 	if (rc > 0) {
434 		if (session->remote_caps & CAP_UNICODE)
435 			rc = smb_msgbuf_decode(mb, "u", &native_lanman);
436 		else
437 			rc = smb_msgbuf_decode(mb, ".u", &native_lanman);
438 	}
439 
440 	if (rc <= 0) {
441 		syslog(LOG_DEBUG, "smbrdr_session_setupx: decode failed");
442 		smbrdr_handle_free(&srh);
443 		smbrdr_sign_fini(session);
444 		return (-1);
445 	}
446 
447 	session->remote_os = smbnative_os_value(native_os);
448 	session->remote_lm = smbnative_lm_value(native_lanman);
449 	session->pdc_type  = smbnative_pdc_value(native_lanman);
450 
451 	logon->uid = smb_hdr.uid;
452 	if (guest)
453 		logon->type = SDB_LOGON_GUEST;
454 
455 	smbrdr_handle_free(&srh);
456 	smbrdr_sign_unset_key(session);
457 
458 	logon->state = SDB_LSTATE_SETUP;
459 
460 	return (0);
461 }
462 
463 /*
464  * smbrdr_logoffx
465  *
466  * Build and send an SMB session logoff (SMB_COM_LOGOFF_ANDX) command.
467  * This is the inverse of an SMB_COM_SESSION_SETUP_ANDX. See CIFS
468  * section 4.1.3. The logon structure should have been obtained from a
469  * successful call to smbrdr_logon_user.
470  *
471  * Returns 0 on success. Otherwise returns a -ve error code.
472  */
473 int
474 smbrdr_logoffx(struct sdb_logon *logon)
475 {
476 	struct sdb_session *session;
477 	smbrdr_handle_t srh;
478 	smb_hdr_t smb_hdr;
479 	DWORD status;
480 	int rc;
481 
482 	if (logon->state != SDB_LSTATE_SETUP) {
483 		/* No user to logoff */
484 		bzero(logon, sizeof (struct sdb_logon));
485 		return (0);
486 	}
487 
488 	if ((session = logon->session) == 0) {
489 		bzero(logon, sizeof (struct sdb_logon));
490 		return (0);
491 	}
492 
493 	logon->state = SDB_LSTATE_LOGGING_OFF;
494 	smbrdr_netuse_logoff(logon->uid);
495 
496 	if ((session->state != SDB_SSTATE_NEGOTIATED) &&
497 	    (session->state != SDB_SSTATE_DISCONNECTING)) {
498 		bzero(logon, sizeof (struct sdb_logon));
499 		return (0);
500 	}
501 
502 	status = smbrdr_request_init(&srh, SMB_COM_LOGOFF_ANDX,
503 	    session, logon, 0);
504 
505 	if (status != NT_STATUS_SUCCESS) {
506 		logon->state = SDB_LSTATE_SETUP;
507 		syslog(LOG_DEBUG, "smbrdr_logoffx: %s: %s", logon->username,
508 		    xlate_nt_status(status));
509 		return (-1);
510 	}
511 
512 	rc = smb_msgbuf_encode(&srh.srh_mbuf, "bbbww", 2, 0xff, 0, 0, 0);
513 	if (rc < 0) {
514 		logon->state = SDB_LSTATE_SETUP;
515 		smbrdr_handle_free(&srh);
516 		syslog(LOG_DEBUG, "smbrdr_logoffx: %s: encode failed",
517 		    logon->username);
518 		return (rc);
519 	}
520 
521 	status = smbrdr_exchange(&srh, &smb_hdr, 0);
522 	if (status != NT_STATUS_SUCCESS) {
523 		syslog(LOG_DEBUG, "smbrdr_logoffx: %s: %s", logon->username,
524 		    xlate_nt_status(status));
525 		rc = -1;
526 	} else {
527 		rc = 0;
528 	}
529 
530 	bzero(logon, sizeof (struct sdb_logon));
531 	smbrdr_handle_free(&srh);
532 	return (rc);
533 }
534 
535 
536 /*
537  * smbrdr_logon_init
538  *
539  * Find a slot for account logon information. The account information
540  * is associated with a session so we need a valid session slot before
541  * calling this function. If we already have a record of the specified
542  * account, a pointer to that record is returned. Otherwise we attempt
543  * to allocate a new one.
544  */
545 static struct sdb_logon *
546 smbrdr_logon_init(struct sdb_session *session, char *username,
547     unsigned char *pwd)
548 {
549 	struct sdb_logon *logon;
550 	int64_t smbrdr_lmcompl;
551 	int rc;
552 
553 	logon = (struct sdb_logon *)malloc(sizeof (sdb_logon_t));
554 	if (logon == 0)
555 		return (0);
556 
557 	bzero(logon, sizeof (struct sdb_logon));
558 	logon->session = session;
559 
560 	(void) smb_config_getnum(SMB_CI_LM_LEVEL, &smbrdr_lmcompl);
561 
562 	if (strcmp(username, "IPC$") == 0) {
563 		logon->type = SDB_LOGON_ANONYMOUS;
564 		logon->auth.ci_len = 1;
565 		*(logon->auth.ci) = 0;
566 		logon->auth.cs_len = 0;
567 	} else {
568 		logon->type = SDB_LOGON_USER;
569 		rc = smb_auth_set_info(username, 0, pwd,
570 		    session->domain, session->challenge_key,
571 		    session->challenge_len, smbrdr_lmcompl, &logon->auth);
572 
573 		/* Generate (and save) the session key. */
574 		if (rc == 0) {
575 			rc = smb_auth_gen_session_key(&logon->auth,
576 			    logon->ssn_key);
577 		}
578 
579 		if (rc != 0) {
580 			free(logon);
581 			return (0);
582 		}
583 	}
584 
585 	(void) strlcpy(logon->username, username, MAX_ACCOUNT_NAME);
586 	logon->state = SDB_LSTATE_INIT;
587 	return (logon);
588 }
589 
590 /*
591  * smbrdr_logon_validate
592  *
593  * if session is there and it's alive and also the required
594  * user is already logged in don't need to do anything
595  * otherwise clear the session structure.
596  */
597 static boolean_t
598 smbrdr_logon_validate(char *server, char *username)
599 {
600 	struct sdb_session *session;
601 	boolean_t valid = B_FALSE;
602 
603 	session = smbrdr_session_lock(server, username, SDB_SLCK_WRITE);
604 	if (session) {
605 		if (nb_keep_alive(session->sock, session->port) == 0) {
606 			valid = B_TRUE;
607 		} else {
608 			session->state = SDB_SSTATE_STALE;
609 			syslog(LOG_DEBUG,
610 			    "smbrdr_logon_validate: stale session");
611 		}
612 
613 		smbrdr_session_unlock(session);
614 	}
615 
616 	return (valid);
617 }
618