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 2008 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 /*
27  * NETR SamLogon and SamLogoff RPC client functions.
28  */
29 
30 #include <stdio.h>
31 #include <strings.h>
32 #include <stdlib.h>
33 #include <time.h>
34 #include <alloca.h>
35 #include <unistd.h>
36 #include <netdb.h>
37 #include <thread.h>
38 
39 #include <smbsrv/libsmb.h>
40 #include <smbsrv/libsmbrdr.h>
41 #include <smbsrv/libmlrpc.h>
42 #include <smbsrv/libmlsvc.h>
43 #include <smbsrv/ndl/netlogon.ndl>
44 #include <smbsrv/netrauth.h>
45 #include <smbsrv/ntstatus.h>
46 #include <smbsrv/smbinfo.h>
47 #include <smbsrv/smb_token.h>
48 #include <mlsvc.h>
49 
50 static DWORD netlogon_logon_private(netr_client_t *, smb_userinfo_t *);
51 static DWORD netr_server_samlogon(mlsvc_handle_t *, netr_info_t *, char *,
52     netr_client_t *, smb_userinfo_t *);
53 static void netr_invalidate_chain(void);
54 static void netr_interactive_samlogon(netr_info_t *, netr_client_t *,
55     struct netr_logon_info1 *);
56 static void netr_network_samlogon(ndr_heap_t *, netr_info_t *,
57     netr_client_t *, struct netr_logon_info2 *);
58 static void netr_setup_identity(ndr_heap_t *, netr_client_t *,
59     netr_logon_id_t *);
60 
61 /*
62  * Shared with netr_auth.c
63  */
64 extern netr_info_t netr_global_info;
65 
66 static mutex_t netlogon_logon_mutex;
67 
68 /*
69  * netlogon_logon
70  *
71  * This is the entry point for authenticating a remote logon. The
72  * parameters here all refer to the remote user and workstation, i.e.
73  * the domain is the user's account domain, not our primary domain.
74  * In order to make it easy to track which domain is being used at
75  * each stage, and to reduce the number of things being pushed on the
76  * stack, the client information is bundled up in the clnt structure.
77  *
78  * If the user is successfully authenticated, an access token will be
79  * built and NT_STATUS_SUCCESS will be returned. Otherwise a non-zero
80  * NT status will be returned, in which case the token contents will
81  * be invalid.
82  */
83 DWORD
84 netlogon_logon(netr_client_t *clnt, smb_userinfo_t *user_info)
85 {
86 	DWORD status;
87 
88 	(void) mutex_lock(&netlogon_logon_mutex);
89 
90 	status = netlogon_logon_private(clnt, user_info);
91 
92 	(void) mutex_unlock(&netlogon_logon_mutex);
93 	return (status);
94 }
95 
96 static DWORD
97 netlogon_logon_private(netr_client_t *clnt, smb_userinfo_t *user_info)
98 {
99 	char resource_domain[SMB_PI_MAX_DOMAIN];
100 	char server[NETBIOS_NAME_SZ * 2];
101 	mlsvc_handle_t netr_handle;
102 	smb_domain_t di;
103 	DWORD status;
104 	int retries = 0, server_changed = 0;
105 
106 	(void) smb_getdomainname(resource_domain, SMB_PI_MAX_DOMAIN);
107 
108 	if (!smb_domain_getinfo(&di))
109 		return (NT_STATUS_CANT_ACCESS_DOMAIN_INFO);
110 
111 	if ((mlsvc_echo(di.d_dc)) < 0) {
112 		/*
113 		 * We had a session to the DC but it's not responding.
114 		 * So drop the credential chain.
115 		 */
116 		netr_invalidate_chain();
117 		return (NT_STATUS_CANT_ACCESS_DOMAIN_INFO);
118 	}
119 
120 	do {
121 		status = netr_open(di.d_dc, di.d_nbdomain, &netr_handle);
122 		if (status != 0)
123 			return (status);
124 
125 		if (di.d_dc && (*netr_global_info.server != '\0')) {
126 			(void) snprintf(server, sizeof (server),
127 			    "\\\\%s", di.d_dc);
128 			server_changed = strncasecmp(netr_global_info.server,
129 			    server, strlen(server));
130 		}
131 
132 		if (server_changed ||
133 		    (netr_global_info.flags & NETR_FLG_VALID) == 0 ||
134 		    !smb_match_netlogon_seqnum()) {
135 			status = netlogon_auth(di.d_dc, &netr_handle,
136 			    NETR_FLG_NULL);
137 
138 			if (status != 0) {
139 				(void) netr_close(&netr_handle);
140 				return (NT_STATUS_LOGON_FAILURE);
141 			}
142 
143 			netr_global_info.flags |= NETR_FLG_VALID;
144 		}
145 
146 		status = netr_server_samlogon(&netr_handle,
147 		    &netr_global_info, di.d_dc, clnt, user_info);
148 
149 		(void) netr_close(&netr_handle);
150 	} while (status == NT_STATUS_INSUFFICIENT_LOGON_INFO && retries++ < 3);
151 
152 	if (retries >= 3)
153 		status = NT_STATUS_LOGON_FAILURE;
154 
155 	return (status);
156 }
157 
158 static DWORD
159 netr_setup_userinfo(struct netr_validation_info3 *info3,
160     smb_userinfo_t *user_info, netr_client_t *clnt, netr_info_t *netr_info)
161 {
162 	smb_sid_attrs_t *other_grps;
163 	char *username, *domain;
164 	int i, nbytes;
165 	unsigned char rc4key[SMBAUTH_SESSION_KEY_SZ];
166 
167 	user_info->sid_name_use = SidTypeUser;
168 	user_info->rid = info3->UserId;
169 	user_info->primary_group_rid = info3->PrimaryGroupId;
170 	user_info->domain_sid = smb_sid_dup((smb_sid_t *)info3->LogonDomainId);
171 
172 	if (user_info->domain_sid == NULL)
173 		return (NT_STATUS_NO_MEMORY);
174 
175 	user_info->user_sid = smb_sid_splice(user_info->domain_sid,
176 	    user_info->rid);
177 	if (user_info->user_sid == NULL)
178 		return (NT_STATUS_NO_MEMORY);
179 
180 	user_info->pgrp_sid = smb_sid_splice(user_info->domain_sid,
181 	    user_info->primary_group_rid);
182 	if (user_info->pgrp_sid == NULL)
183 		return (NT_STATUS_NO_MEMORY);
184 
185 	username = (info3->EffectiveName.str)
186 	    ? (char *)info3->EffectiveName.str : clnt->username;
187 	domain = (info3->LogonDomainName.str)
188 	    ? (char *)info3->LogonDomainName.str : clnt->domain;
189 
190 	if (username)
191 		user_info->name = strdup(username);
192 	if (domain)
193 		user_info->domain_name = strdup(domain);
194 
195 	if (user_info->name == NULL || user_info->domain_name == NULL)
196 		return (NT_STATUS_NO_MEMORY);
197 
198 	nbytes = info3->GroupCount * sizeof (smb_rid_attrs_t);
199 	if (nbytes) {
200 		if ((user_info->groups = malloc(nbytes)) != NULL) {
201 			user_info->n_groups = info3->GroupCount;
202 			(void) memcpy(user_info->groups,
203 			    info3->GroupIds, nbytes);
204 		} else {
205 			return (NT_STATUS_NO_MEMORY);
206 		}
207 	}
208 	nbytes = info3->SidCount * sizeof (smb_sid_attrs_t);
209 	if (nbytes) {
210 		if ((other_grps = malloc(nbytes)) != NULL) {
211 			user_info->other_grps = other_grps;
212 			for (i = 0; i < info3->SidCount; i++) {
213 				other_grps[i].attrs =
214 				    info3->ExtraSids[i].attributes;
215 
216 				other_grps[i].sid = smb_sid_dup(
217 				    (smb_sid_t *)info3->ExtraSids[i].sid);
218 
219 				if (other_grps[i].sid == NULL)
220 					break;
221 			}
222 			user_info->n_other_grps = i;
223 		} else {
224 			return (NT_STATUS_NO_MEMORY);
225 		}
226 	}
227 	/*
228 	 * The UserSessionKey in NetrSamLogon RPC is obfuscated using the
229 	 * session key obtained in the NETLOGON credential chain.
230 	 * An 8 byte session key is zero extended to 16 bytes. This 16 byte
231 	 * key is the key to the RC4 algorithm. The RC4 byte stream is
232 	 * exclusively ored with the 16 byte UserSessionKey to recover
233 	 * the the clear form.
234 	 */
235 	if ((user_info->session_key = malloc(SMBAUTH_SESSION_KEY_SZ)) == NULL)
236 		return (NT_STATUS_NO_MEMORY);
237 	bzero(rc4key, SMBAUTH_SESSION_KEY_SZ);
238 	bcopy(netr_info->session_key.key, rc4key, netr_info->session_key.len);
239 	bcopy(info3->UserSessionKey.data, user_info->session_key,
240 	    SMBAUTH_SESSION_KEY_SZ);
241 	rand_hash((unsigned char *)user_info->session_key,
242 	    SMBAUTH_SESSION_KEY_SZ, rc4key, SMBAUTH_SESSION_KEY_SZ);
243 	mlsvc_setadmin_user_info(user_info);
244 	return (NT_STATUS_SUCCESS);
245 }
246 
247 /*
248  * netr_server_samlogon
249  *
250  * NetrServerSamLogon RPC: interactive or network. It is assumed that
251  * we have already authenticated with the PDC. If everything works,
252  * we build a user info structure and return it, where the caller will
253  * probably build an access token.
254  *
255  * Returns an NT status. There are numerous possibilities here.
256  * For example:
257  *	NT_STATUS_INVALID_INFO_CLASS
258  *	NT_STATUS_INVALID_PARAMETER
259  *	NT_STATUS_ACCESS_DENIED
260  *	NT_STATUS_PASSWORD_MUST_CHANGE
261  *	NT_STATUS_NO_SUCH_USER
262  *	NT_STATUS_WRONG_PASSWORD
263  *	NT_STATUS_LOGON_FAILURE
264  *	NT_STATUS_ACCOUNT_RESTRICTION
265  *	NT_STATUS_INVALID_LOGON_HOURS
266  *	NT_STATUS_INVALID_WORKSTATION
267  *	NT_STATUS_INTERNAL_ERROR
268  *	NT_STATUS_PASSWORD_EXPIRED
269  *	NT_STATUS_ACCOUNT_DISABLED
270  */
271 DWORD
272 netr_server_samlogon(mlsvc_handle_t *netr_handle, netr_info_t *netr_info,
273     char *server, netr_client_t *clnt, smb_userinfo_t *user_info)
274 {
275 	struct netr_SamLogon arg;
276 	struct netr_authenticator auth;
277 	struct netr_authenticator ret_auth;
278 	struct netr_logon_info1 info1;
279 	struct netr_logon_info2 info2;
280 	struct netr_validation_info3 *info3;
281 	ndr_heap_t *heap;
282 	int opnum;
283 	int rc, len;
284 	DWORD status;
285 
286 	bzero(&arg, sizeof (struct netr_SamLogon));
287 	opnum = NETR_OPNUM_SamLogon;
288 
289 	/*
290 	 * Should we get the server and hostname from netr_info?
291 	 */
292 
293 	len = strlen(server) + 4;
294 	arg.servername = ndr_rpc_malloc(netr_handle, len);
295 	arg.hostname = ndr_rpc_malloc(netr_handle, NETBIOS_NAME_SZ);
296 	if (arg.servername == NULL || arg.hostname == NULL) {
297 		ndr_rpc_release(netr_handle);
298 		return (NT_STATUS_INTERNAL_ERROR);
299 	}
300 
301 	(void) snprintf((char *)arg.servername, len, "\\\\%s", server);
302 	if (smb_getnetbiosname((char *)arg.hostname, NETBIOS_NAME_SZ) != 0) {
303 		ndr_rpc_release(netr_handle);
304 		return (NT_STATUS_INTERNAL_ERROR);
305 	}
306 
307 	rc = netr_setup_authenticator(netr_info, &auth, &ret_auth);
308 	if (rc != SMBAUTH_SUCCESS) {
309 		ndr_rpc_release(netr_handle);
310 		return (NT_STATUS_INTERNAL_ERROR);
311 	}
312 
313 	arg.auth = &auth;
314 	arg.ret_auth = &ret_auth;
315 	arg.validation_level = NETR_VALIDATION_LEVEL3;
316 	arg.logon_info.logon_level = clnt->logon_level;
317 	arg.logon_info.switch_value = clnt->logon_level;
318 
319 	heap = ndr_rpc_get_heap(netr_handle);
320 
321 	switch (clnt->logon_level) {
322 	case NETR_INTERACTIVE_LOGON:
323 		netr_setup_identity(heap, clnt, &info1.identity);
324 		netr_interactive_samlogon(netr_info, clnt, &info1);
325 		arg.logon_info.ru.info1 = &info1;
326 		break;
327 
328 	case NETR_NETWORK_LOGON:
329 		netr_setup_identity(heap, clnt, &info2.identity);
330 		netr_network_samlogon(heap, netr_info, clnt, &info2);
331 		arg.logon_info.ru.info2 = &info2;
332 		break;
333 
334 	default:
335 		ndr_rpc_release(netr_handle);
336 		return (NT_STATUS_INVALID_PARAMETER);
337 	}
338 
339 	rc = ndr_rpc_call(netr_handle, opnum, &arg);
340 	if (rc != 0) {
341 		bzero(netr_info, sizeof (netr_info_t));
342 		status = NT_STATUS_INVALID_PARAMETER;
343 	} else if (arg.status != 0) {
344 		status = NT_SC_VALUE(arg.status);
345 
346 		/*
347 		 * We need to validate the chain even though we have
348 		 * a non-zero status. If the status is ACCESS_DENIED
349 		 * this will trigger a new credential chain. However,
350 		 * a valid credential is returned with some status
351 		 * codes; for example, WRONG_PASSWORD.
352 		 */
353 		(void) netr_validate_chain(netr_info, arg.ret_auth);
354 	} else {
355 		status = netr_validate_chain(netr_info, arg.ret_auth);
356 		if (status == NT_STATUS_INSUFFICIENT_LOGON_INFO) {
357 			ndr_rpc_release(netr_handle);
358 			return (status);
359 		}
360 
361 		info3 = arg.ru.info3;
362 		status = netr_setup_userinfo(info3, user_info, clnt, netr_info);
363 	}
364 
365 	ndr_rpc_release(netr_handle);
366 	return (status);
367 }
368 
369 /*
370  * netr_interactive_samlogon
371  *
372  * Set things up for an interactive SamLogon. Copy the NT and LM
373  * passwords to the logon structure and hash them with the session
374  * key.
375  */
376 static void
377 netr_interactive_samlogon(netr_info_t *netr_info, netr_client_t *clnt,
378     struct netr_logon_info1 *info1)
379 {
380 	BYTE key[NETR_OWF_PASSWORD_SZ];
381 
382 	(void) memcpy(&info1->lm_owf_password,
383 	    clnt->lm_password.lm_password_val, sizeof (netr_owf_password_t));
384 
385 	(void) memcpy(&info1->nt_owf_password,
386 	    clnt->nt_password.nt_password_val, sizeof (netr_owf_password_t));
387 
388 	(void) memset(key, 0, NETR_OWF_PASSWORD_SZ);
389 	(void) memcpy(key, netr_info->session_key.key,
390 	    netr_info->session_key.len);
391 
392 	rand_hash((unsigned char *)&info1->lm_owf_password,
393 	    NETR_OWF_PASSWORD_SZ, key, NETR_OWF_PASSWORD_SZ);
394 
395 	rand_hash((unsigned char *)&info1->nt_owf_password,
396 	    NETR_OWF_PASSWORD_SZ, key, NETR_OWF_PASSWORD_SZ);
397 }
398 
399 /*
400  * netr_network_samlogon
401  *
402  * Set things up for a network SamLogon.  We provide a copy of the random
403  * challenge, that we sent to the client, to the domain controller.  This
404  * is the key that the client will have used to encrypt the NT and LM
405  * passwords.  Note that Windows 9x clients may not provide both passwords.
406  */
407 /*ARGSUSED*/
408 static void
409 netr_network_samlogon(ndr_heap_t *heap, netr_info_t *netr_info,
410     netr_client_t *clnt, struct netr_logon_info2 *info2)
411 {
412 	uint32_t len;
413 
414 	bcopy(clnt->challenge_key.challenge_key_val, info2->lm_challenge.data,
415 	    8);
416 
417 	if ((len = clnt->nt_password.nt_password_len) != 0) {
418 		ndr_heap_mkvcb(heap, clnt->nt_password.nt_password_val, len,
419 		    (ndr_vcbuf_t *)&info2->nt_response);
420 	} else {
421 		bzero(&info2->nt_response, sizeof (netr_vcbuf_t));
422 	}
423 
424 	if ((len = clnt->lm_password.lm_password_len) != 0) {
425 		ndr_heap_mkvcb(heap, clnt->lm_password.lm_password_val, len,
426 		    (ndr_vcbuf_t *)&info2->lm_response);
427 	} else {
428 		bzero(&info2->lm_response, sizeof (netr_vcbuf_t));
429 	}
430 }
431 
432 /*
433  * netr_setup_authenticator
434  *
435  * Set up the request and return authenticators. A new credential is
436  * generated from the session key, the current client credential and
437  * the current time, i.e.
438  *
439  *		NewCredential = Cred(SessionKey, OldCredential, time);
440  *
441  * The timestamp, which is used as a random seed, is stored in both
442  * the request and return authenticators.
443  *
444  * If any difficulties occur using the cryptographic framework, the
445  * function returns SMBAUTH_FAILURE.  Otherwise SMBAUTH_SUCCESS is
446  * returned.
447  */
448 int
449 netr_setup_authenticator(netr_info_t *netr_info,
450     struct netr_authenticator *auth, struct netr_authenticator *ret_auth)
451 {
452 	bzero(auth, sizeof (struct netr_authenticator));
453 
454 	netr_info->timestamp = time(0);
455 	auth->timestamp = netr_info->timestamp;
456 
457 	if (netr_gen_credentials(netr_info->session_key.key,
458 	    &netr_info->client_credential,
459 	    netr_info->timestamp,
460 	    (netr_cred_t *)&auth->credential) != SMBAUTH_SUCCESS)
461 		return (SMBAUTH_FAILURE);
462 
463 	if (ret_auth) {
464 		bzero(ret_auth, sizeof (struct netr_authenticator));
465 		ret_auth->timestamp = netr_info->timestamp;
466 	}
467 
468 	return (SMBAUTH_SUCCESS);
469 }
470 
471 /*
472  * Validate the returned credentials and update the credential chain.
473  * The server returns an updated client credential rather than a new
474  * server credential.  The server uses (timestamp + 1) when generating
475  * the credential.
476  *
477  * Generate the new seed for the credential chain. The new seed is
478  * formed by adding (timestamp + 1) to the current client credential.
479  * The only quirk is the DWORD style addition.
480  *
481  * Returns NT_STATUS_INSUFFICIENT_LOGON_INFO if auth->credential is a
482  * NULL pointer. The Authenticator field of the SamLogon response packet
483  * sent by the Samba 3 PDC always return NULL pointer if the received
484  * SamLogon request is not immediately followed by the ServerReqChallenge
485  * and ServerAuthenticate2 requests.
486  *
487  * Returns NT_STATUS_SUCCESS if the server returned a valid credential.
488  * Otherwise we retirm NT_STATUS_UNSUCCESSFUL.
489  */
490 DWORD
491 netr_validate_chain(netr_info_t *netr_info, struct netr_authenticator *auth)
492 {
493 	netr_cred_t cred;
494 	DWORD result = NT_STATUS_SUCCESS;
495 	DWORD *dwp;
496 
497 	++netr_info->timestamp;
498 
499 	if (netr_gen_credentials(netr_info->session_key.key,
500 	    &netr_info->client_credential,
501 	    netr_info->timestamp, &cred) != SMBAUTH_SUCCESS)
502 		return (NT_STATUS_INTERNAL_ERROR);
503 
504 	if (&auth->credential == 0) {
505 		/*
506 		 * If the validation fails, destroy the credential chain.
507 		 * This should trigger a new authentication chain.
508 		 */
509 		bzero(netr_info, sizeof (netr_info_t));
510 		return (NT_STATUS_INSUFFICIENT_LOGON_INFO);
511 	}
512 
513 	result = memcmp(&cred, &auth->credential, sizeof (netr_cred_t));
514 	if (result != 0) {
515 		/*
516 		 * If the validation fails, destroy the credential chain.
517 		 * This should trigger a new authentication chain.
518 		 */
519 		bzero(netr_info, sizeof (netr_info_t));
520 		result = NT_STATUS_UNSUCCESSFUL;
521 	} else {
522 		/*
523 		 * Otherwise generate the next step in the chain.
524 		 */
525 		/*LINTED E_BAD_PTR_CAST_ALIGN*/
526 		dwp = (DWORD *)&netr_info->client_credential;
527 		dwp[0] += netr_info->timestamp;
528 
529 		netr_info->flags |= NETR_FLG_VALID;
530 	}
531 
532 	return (result);
533 }
534 
535 /*
536  * netr_invalidate_chain
537  *
538  * Mark the credential chain as invalid so that it will be recreated
539  * on the next attempt.
540  */
541 static void
542 netr_invalidate_chain(void)
543 {
544 	netr_global_info.flags &= ~NETR_FLG_VALID;
545 }
546 
547 /*
548  * netr_setup_identity
549  *
550  * Set up the client identity information. All of this information is
551  * specifically related to the client user and workstation attempting
552  * to access this system. It may not be in our primary domain.
553  *
554  * I don't know what logon_id is, it seems to be a unique identifier.
555  * Increment it before each use.
556  */
557 static void
558 netr_setup_identity(ndr_heap_t *heap, netr_client_t *clnt,
559     netr_logon_id_t *identity)
560 {
561 	static mutex_t logon_id_mutex;
562 	static uint32_t logon_id;
563 
564 	(void) mutex_lock(&logon_id_mutex);
565 
566 	if (logon_id == 0)
567 		logon_id = 0xDCD0;
568 
569 	++logon_id;
570 	clnt->logon_id = logon_id;
571 
572 	(void) mutex_unlock(&logon_id_mutex);
573 
574 	identity->parameter_control = 0;
575 	identity->logon_id.LowPart = logon_id;
576 	identity->logon_id.HighPart = 0;
577 
578 	ndr_heap_mkvcs(heap, clnt->domain,
579 	    (ndr_vcstr_t *)&identity->domain_name);
580 
581 	ndr_heap_mkvcs(heap, clnt->username,
582 	    (ndr_vcstr_t *)&identity->username);
583 
584 	/*
585 	 * Some systems prefix the client workstation name with \\.
586 	 * It doesn't seem to make any difference whether it's there
587 	 * or not.
588 	 */
589 	ndr_heap_mkvcs(heap, clnt->workstation,
590 	    (ndr_vcstr_t *)&identity->workstation);
591 }
592