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