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