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