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