xref: /titanic_50/usr/src/lib/smbsrv/libmlsvc/common/netr_auth.c (revision 09cb82ca24006b806e9f17e2135eef96364facfe)
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 challenge/response client functions.
30  *
31  * NT_STATUS_INVALID_PARAMETER
32  * NT_STATUS_NO_TRUST_SAM_ACCOUNT
33  * NT_STATUS_ACCESS_DENIED
34  */
35 
36 #include <stdio.h>
37 #include <stdlib.h>
38 #include <strings.h>
39 #include <unistd.h>
40 #include <ctype.h>
41 #include <security/cryptoki.h>
42 #include <security/pkcs11.h>
43 
44 #include <smbsrv/libsmb.h>
45 #include <smbsrv/libsmbrdr.h>
46 #include <smbsrv/libsmbns.h>
47 #include <smbsrv/mlsvc_util.h>
48 #include <smbsrv/ndl/netlogon.ndl>
49 #include <smbsrv/ntstatus.h>
50 #include <smbsrv/smbinfo.h>
51 #include <smbsrv/mlsvc.h>
52 #include <smbsrv/netrauth.h>
53 
54 #define	NETR_SESSKEY_ZEROBUF_SZ		4
55 #define	NETR_DESKEY_LEN			7
56 
57 int netr_setup_authenticator(netr_info_t *, struct netr_authenticator *,
58     struct netr_authenticator *);
59 DWORD netr_validate_chain(netr_info_t *, struct netr_authenticator *);
60 
61 static int netr_server_req_challenge(mlsvc_handle_t *, netr_info_t *);
62 static int netr_server_authenticate2(mlsvc_handle_t *, netr_info_t *);
63 static int netr_gen_password(BYTE *, BYTE *, BYTE *);
64 
65 /*
66  * Shared with netr_logon.c
67  */
68 netr_info_t netr_global_info;
69 
70 /*
71  * netlogon_auth
72  *
73  * This is the core of the NETLOGON authentication protocol.
74  * Do the challenge response authentication.
75  *
76  * Prior to calling this function, an anonymous session to the NETLOGON
77  * pipe on a domain controller(server) should have already been opened.
78  *
79  * Upon a successful NETLOGON credential chain establishment, the
80  * netlogon sequence number will be set to match the kpasswd sequence
81  * number.
82  *
83  */
84 DWORD
85 netlogon_auth(char *server, mlsvc_handle_t *netr_handle, DWORD flags)
86 {
87 	netr_info_t *netr_info;
88 	int rc;
89 	DWORD leout_rc[2];
90 
91 	netr_info = &netr_global_info;
92 	bzero(netr_info, sizeof (netr_info_t));
93 
94 	netr_info->flags |= flags;
95 
96 	rc = smb_getnetbiosname(netr_info->hostname, MLSVC_DOMAIN_NAME_MAX);
97 	if (rc != 0)
98 		return (NT_STATUS_UNSUCCESSFUL);
99 
100 	(void) snprintf(netr_info->server, sizeof (netr_info->server),
101 	    "\\\\%s", server);
102 
103 	LE_OUT32(&leout_rc[0], random());
104 	LE_OUT32(&leout_rc[1], random());
105 	(void) memcpy(&netr_info->client_challenge, leout_rc,
106 	    sizeof (struct netr_credential));
107 
108 	if ((rc = netr_server_req_challenge(netr_handle, netr_info)) == 0) {
109 		rc = netr_server_authenticate2(netr_handle, netr_info);
110 		if (rc == 0) {
111 			smb_update_netlogon_seqnum();
112 			netr_info->flags |= NETR_FLG_VALID;
113 
114 		}
115 	}
116 
117 	/*
118 	 * The NETLOGON credential chain establishment has unset
119 	 * both ServerPrincipalName and dNSHostName attributes of the
120 	 * workstation trust account. Those attributes will be updated
121 	 * here to avoid any Kerberos authentication errors from happening.
122 	 *
123 	 * Later, when NT4 domain controller is supported, we need to
124 	 * find a way to disable the following code.
125 	 */
126 	if (smb_ads_update_attrs() == -1)
127 		syslog(LOG_DEBUG, "netlogon_auth: ServerPrincipalName"
128 		    " and dNSHostName attributes might have been unset.");
129 
130 	return ((rc) ? NT_STATUS_UNSUCCESSFUL : NT_STATUS_SUCCESS);
131 }
132 
133 /*
134  * netr_open
135  *
136  * Open an anonymous session to the NETLOGON pipe on a domain
137  * controller and bind to the NETR RPC interface. We store the
138  * remote server's native OS type - we may need it due to
139  * differences between versions of Windows.
140  */
141 int
142 netr_open(char *server, char *domain, mlsvc_handle_t *netr_handle)
143 {
144 	int fid;
145 	int remote_os = 0;
146 	int remote_lm = 0;
147 	int server_pdc;
148 	char *user = smbrdr_ipc_get_user();
149 
150 	if (mlsvc_logon(server, domain, user) != 0)
151 		return (-1);
152 
153 	fid = mlsvc_open_pipe(server, domain, user, "\\NETLOGON");
154 	if (fid < 0)
155 		return (-1);
156 
157 	if (mlsvc_rpc_bind(netr_handle, fid, "NETR") < 0) {
158 		(void) mlsvc_close_pipe(fid);
159 		return (-1);
160 	}
161 
162 	(void) mlsvc_session_native_values(fid, &remote_os, &remote_lm,
163 	    &server_pdc);
164 	netr_handle->context->server_os = remote_os;
165 	netr_handle->context->server_pdc = server_pdc;
166 	return (0);
167 }
168 
169 /*
170  * netr_close
171  *
172  * Close a NETLOGON pipe and free the RPC context.
173  */
174 int
175 netr_close(mlsvc_handle_t *netr_handle)
176 {
177 	(void) mlsvc_close_pipe(netr_handle->context->fid);
178 	free(netr_handle->context);
179 	return (0);
180 }
181 
182 /*
183  * netr_server_req_challenge
184  */
185 static int
186 netr_server_req_challenge(mlsvc_handle_t *netr_handle, netr_info_t *netr_info)
187 {
188 	struct netr_ServerReqChallenge arg;
189 	mlrpc_heapref_t heap;
190 	int opnum;
191 	int rc;
192 
193 	bzero(&arg, sizeof (struct netr_ServerReqChallenge));
194 	opnum = NETR_OPNUM_ServerReqChallenge;
195 
196 	arg.servername = (unsigned char *)netr_info->server;
197 	arg.hostname = (unsigned char *)netr_info->hostname;
198 
199 	(void) memcpy(&arg.client_challenge, &netr_info->client_challenge,
200 	    sizeof (struct netr_credential));
201 
202 	(void) mlsvc_rpc_init(&heap);
203 	rc = mlsvc_rpc_call(netr_handle->context, opnum, &arg, &heap);
204 	if (rc == 0) {
205 		if (arg.status != 0) {
206 			mlsvc_rpc_report_status(opnum, arg.status);
207 			rc = -1;
208 		} else {
209 			(void) memcpy(&netr_info->server_challenge,
210 			    &arg.server_challenge,
211 			    sizeof (struct netr_credential));
212 		}
213 	}
214 
215 	mlsvc_rpc_free(netr_handle->context, &heap);
216 	return (rc);
217 }
218 
219 /*
220  * netr_server_authenticate2
221  */
222 static int
223 netr_server_authenticate2(mlsvc_handle_t *netr_handle, netr_info_t *netr_info)
224 {
225 	struct netr_ServerAuthenticate2 arg;
226 	mlrpc_heapref_t heap;
227 	int opnum;
228 	int rc;
229 	char account_name[MLSVC_DOMAIN_NAME_MAX * 2];
230 
231 	bzero(&arg, sizeof (struct netr_ServerAuthenticate2));
232 	opnum = NETR_OPNUM_ServerAuthenticate2;
233 
234 	(void) snprintf(account_name, sizeof (account_name), "%s$",
235 	    netr_info->hostname);
236 
237 	arg.servername = (unsigned char *)netr_info->server;
238 	arg.account_name = (unsigned char *)account_name;
239 	arg.account_type = NETR_WKSTA_TRUST_ACCOUNT_TYPE;
240 	arg.hostname = (unsigned char *)netr_info->hostname;
241 	arg.negotiate_flags = NETR_NEGOTIATE_FLAGS;
242 
243 	smb_tracef("server=[%s] account_name=[%s] hostname=[%s]\n",
244 	    netr_info->server, account_name, netr_info->hostname);
245 
246 	if (netr_gen_session_key(netr_info) != SMBAUTH_SUCCESS)
247 		return (-1);
248 
249 	if (netr_gen_credentials(netr_info->session_key,
250 	    &netr_info->client_challenge,
251 	    0,
252 	    &netr_info->client_credential) != SMBAUTH_SUCCESS) {
253 		return (-1);
254 	}
255 
256 	if (netr_gen_credentials(netr_info->session_key,
257 	    &netr_info->server_challenge,
258 	    0,
259 	    &netr_info->server_credential) != SMBAUTH_SUCCESS) {
260 		return (-1);
261 	}
262 
263 	(void) memcpy(&arg.client_credential, &netr_info->client_credential,
264 	    sizeof (struct netr_credential));
265 
266 	(void) mlsvc_rpc_init(&heap);
267 
268 	rc = mlsvc_rpc_call(netr_handle->context, opnum, &arg, &heap);
269 	if (rc == 0) {
270 		if (arg.status != 0) {
271 			mlsvc_rpc_report_status(opnum, arg.status);
272 			rc = -1;
273 		} else {
274 			rc = memcmp(&netr_info->server_credential,
275 			    &arg.server_credential,
276 			    sizeof (struct netr_credential));
277 		}
278 	}
279 
280 	mlsvc_rpc_free(netr_handle->context, &heap);
281 	return (rc);
282 }
283 
284 /*
285  * netr_gen_session_key
286  *
287  * Generate a session key from the client and server challenges. The
288  * algorithm is a two stage hash. For the first hash, the input is
289  * the combination of the client and server challenges, the key is
290  * the first 8 bytes of the password. The initial password is formed
291  * using the NT password hash on the local hostname in lower case.
292  * The result is stored in a temporary buffer.
293  *
294  *		input:	challenge
295  *		key:	passwd lower 8 bytes
296  *		output:	intermediate result
297  *
298  * For the second hash, the input is the result of the first hash and
299  * the key is the last 8 bytes of the password.
300  *
301  *		input:	result of first hash
302  *		key:	passwd upper 8 bytes
303  *		output:	session_key
304  *
305  * The final output should be the session key.
306  *
307  *		FYI: smb_auth_DES(output, key, input)
308  *
309  * If any difficulties occur using the cryptographic framework, the
310  * function returns SMBAUTH_FAILURE.  Otherwise SMBAUTH_SUCCESS is
311  * returned.
312  */
313 #ifdef NETR_NEGOTIATE_STRONG_KEY
314 int
315 netr_gen_session_key(netr_info_t *netr_info)
316 {
317 	unsigned char ntlmhash[SMBAUTH_HASH_SZ];
318 	int rc = SMBAUTH_FAILURE;
319 	CK_RV rv;
320 	CK_MECHANISM mechanism;
321 	CK_SESSION_HANDLE hSession;
322 	CK_ULONG diglen = MD_DIGEST_LEN;
323 	unsigned char md5digest[MD_DIGEST_LEN];
324 	unsigned char zerobuf[NETR_SESSKEY_ZEROBUF_SZ];
325 
326 	bzero(ntlmhash, SMBAUTH_HASH_SZ);
327 	/*
328 	 * We should check (netr_info->flags & NETR_FLG_INIT) and use
329 	 * the appropriate password but it isn't working yet.  So we
330 	 * always use the default one for now.
331 	 */
332 	bzero(netr_info->password, sizeof (netr_info->password));
333 	rc = smb_config_getstr(SMB_CI_MACHINE_PASSWD,
334 	    (char *)netr_info->password, sizeof (netr_info->password));
335 
336 	if ((rc != SMBD_SMF_OK) || *netr_info->password == '\0') {
337 		return (SMBAUTH_FAILURE);
338 	}
339 
340 	rc = smb_auth_ntlm_hash((char *)netr_info->password, ntlmhash);
341 	if (rc != SMBAUTH_SUCCESS)
342 		return (SMBAUTH_FAILURE);
343 
344 	bzero(zerobuf, NETR_SESSKEY_ZEROBUF_SZ);
345 
346 	mechanism.mechanism = CKM_MD5;
347 	mechanism.pParameter = 0;
348 	mechanism.ulParameterLen = 0;
349 
350 	rv = SUNW_C_GetMechSession(mechanism.mechanism, &hSession);
351 	if (rv != CKR_OK)
352 		return (SMBAUTH_FAILURE);
353 
354 	rv = C_DigestInit(hSession, &mechanism);
355 	if (rv != CKR_OK)
356 		goto cleanup;
357 
358 	rv = C_DigestUpdate(hSession, (CK_BYTE_PTR)zerobuf,
359 	    NETR_SESSKEY_ZEROBUF_SZ);
360 	if (rv != CKR_OK)
361 		goto cleanup;
362 
363 	rv = C_DigestUpdate(hSession,
364 	    (CK_BYTE_PTR)netr_info->client_challenge.data, NETR_CRED_DATA_SZ);
365 	if (rv != CKR_OK)
366 		goto cleanup;
367 
368 	rv = C_DigestUpdate(hSession,
369 	    (CK_BYTE_PTR)netr_info->server_challenge.data, NETR_CRED_DATA_SZ);
370 	if (rv != CKR_OK)
371 		goto cleanup;
372 
373 	rv = C_DigestFinal(hSession, (CK_BYTE_PTR)md5digest, &diglen);
374 	if (rv != CKR_OK)
375 		goto cleanup;
376 
377 	rc = smb_auth_hmac_md5(md5digest, diglen, ntlmhash, SMBAUTH_HASH_SZ,
378 	    netr_info->session_key);
379 
380 cleanup:
381 	(void) C_CloseSession(hSession);
382 	return (rc);
383 
384 }
385 #else
386 int
387 netr_gen_session_key(netr_info_t *netr_info)
388 {
389 	unsigned char md4hash[32];
390 	unsigned char buffer[8];
391 	DWORD data[2];
392 	DWORD *client_challenge;
393 	DWORD *server_challenge;
394 	int rc;
395 	DWORD le_data[2];
396 
397 	client_challenge = (DWORD *)(uintptr_t)&netr_info->client_challenge;
398 	server_challenge = (DWORD *)(uintptr_t)&netr_info->server_challenge;
399 	bzero(md4hash, 32);
400 
401 	/*
402 	 * We should check (netr_info->flags & NETR_FLG_INIT) and use
403 	 * the appropriate password but it isn't working yet.  So we
404 	 * always use the default one for now.
405 	 */
406 	bzero(netr_info->password, sizeof (netr_info->password));
407 	rc = smb_config_getstr(SMB_CI_MACHINE_PASSWD,
408 	    (char *)netr_info->password, sizeof (netr_info->password));
409 
410 	if ((rc != SMBD_SMF_OK) || *netr_info->password == '\0') {
411 		return (SMBAUTH_FAILURE);
412 	}
413 
414 	rc = smb_auth_ntlm_hash((char *)netr_info->password, md4hash);
415 
416 	if (rc != SMBAUTH_SUCCESS)
417 		return (SMBAUTH_FAILURE);
418 
419 	data[0] = LE_IN32(&client_challenge[0]) + LE_IN32(&server_challenge[0]);
420 	data[1] = LE_IN32(&client_challenge[1]) + LE_IN32(&server_challenge[1]);
421 	LE_OUT32(&le_data[0], data[0]);
422 	LE_OUT32(&le_data[1], data[1]);
423 
424 	rc = smb_auth_DES(buffer, 8, md4hash, 8, (unsigned char *)le_data, 8);
425 	if (rc != SMBAUTH_SUCCESS)
426 		return (rc);
427 
428 	rc = smb_auth_DES(netr_info->session_key, 8, &md4hash[9], 8, buffer, 8);
429 	return (rc);
430 }
431 #endif
432 
433 /*
434  * netr_gen_credentials
435  *
436  * Generate a set of credentials from a challenge and a session key.
437  * The algorithm is a two stage hash. For the first hash, the
438  * timestamp is added to the challenge and the result is stored in a
439  * temporary buffer:
440  *
441  *		input:	challenge (including timestamp)
442  *		key:	session_key
443  *		output:	intermediate result
444  *
445  * For the second hash, the input is the result of the first hash and
446  * a strange partial key is used:
447  *
448  *		input:	result of first hash
449  *		key:	funny partial key
450  *		output:	credentiails
451  *
452  * The final output should be an encrypted set of credentials.
453  *
454  *		FYI: smb_auth_DES(output, key, input)
455  *
456  * If any difficulties occur using the cryptographic framework, the
457  * function returns SMBAUTH_FAILURE.  Otherwise SMBAUTH_SUCCESS is
458  * returned.
459  */
460 int
461 netr_gen_credentials(BYTE *session_key, netr_cred_t *challenge,
462     DWORD timestamp, netr_cred_t *out_cred)
463 {
464 	unsigned char buffer[8];
465 	DWORD data[2];
466 	DWORD le_data[2];
467 	DWORD *p;
468 	int rc;
469 
470 	p = (DWORD *)(uintptr_t)challenge;
471 	data[0] = LE_IN32(&p[0]) + timestamp;
472 	data[1] = LE_IN32(&p[1]);
473 
474 	LE_OUT32(&le_data[0], data[0]);
475 	LE_OUT32(&le_data[1], data[1]);
476 
477 	if (smb_auth_DES(buffer, 8, session_key, NETR_DESKEY_LEN,
478 	    (unsigned char *)le_data, 8) != SMBAUTH_SUCCESS)
479 		return (SMBAUTH_FAILURE);
480 
481 	rc = smb_auth_DES(out_cred->data, 8, &session_key[NETR_DESKEY_LEN],
482 	    NETR_DESKEY_LEN, buffer, 8);
483 
484 	return (rc);
485 }
486 
487 /*
488  * netr_server_password_set
489  *
490  * Attempt to change the trust account password for this system.
491  *
492  * Note that this call may legitimately fail if the registry on the
493  * domain controller has been setup to deny attempts to change the
494  * trust account password. In this case we should just continue to
495  * use the original password.
496  *
497  * Possible status values:
498  *	NT_STATUS_ACCESS_DENIED
499  */
500 int
501 netr_server_password_set(mlsvc_handle_t *netr_handle, netr_info_t *netr_info)
502 {
503 	struct netr_PasswordSet  arg;
504 	mlrpc_heapref_t heap;
505 	int opnum;
506 	int rc;
507 	BYTE new_password[NETR_OWF_PASSWORD_SZ];
508 	char account_name[MLSVC_DOMAIN_NAME_MAX * 2];
509 
510 	bzero(&arg, sizeof (struct netr_PasswordSet));
511 	opnum = NETR_OPNUM_ServerPasswordSet;
512 
513 	(void) snprintf(account_name, sizeof (account_name), "%s$",
514 	    netr_info->hostname);
515 
516 	arg.servername = (unsigned char *)netr_info->server;
517 	arg.account_name = (unsigned char *)account_name;
518 	arg.account_type = NETR_WKSTA_TRUST_ACCOUNT_TYPE;
519 	arg.hostname = (unsigned char *)netr_info->hostname;
520 
521 	/*
522 	 * Set up the client side authenticator.
523 	 */
524 	if (netr_setup_authenticator(netr_info, &arg.auth, 0) !=
525 	    SMBAUTH_SUCCESS) {
526 		return (-1);
527 	}
528 
529 	/*
530 	 * Generate a new password from the old password.
531 	 */
532 	if (netr_gen_password(netr_info->session_key,
533 	    netr_info->password, new_password) == SMBAUTH_FAILURE) {
534 		return (-1);
535 	}
536 
537 	(void) memcpy(&arg.uas_new_password, &new_password,
538 	    NETR_OWF_PASSWORD_SZ);
539 
540 	(void) mlsvc_rpc_init(&heap);
541 	rc = mlsvc_rpc_call(netr_handle->context, opnum, &arg, &heap);
542 	if ((rc != 0) || (arg.status != 0)) {
543 		mlsvc_rpc_report_status(opnum, arg.status);
544 		mlsvc_rpc_free(netr_handle->context, &heap);
545 		return (-1);
546 	}
547 
548 	/*
549 	 * Check the returned credentials.  The server returns the new
550 	 * client credential rather than the new server credentiali,
551 	 * as documented elsewhere.
552 	 *
553 	 * Generate the new seed for the credential chain.  Increment
554 	 * the timestamp and add it to the client challenge.  Then we
555 	 * need to copy the challenge to the credential field in
556 	 * preparation for the next cycle.
557 	 */
558 	if (netr_validate_chain(netr_info, &arg.auth) == 0) {
559 		/*
560 		 * Save the new password.
561 		 */
562 		(void) memcpy(netr_info->password, new_password,
563 		    NETR_OWF_PASSWORD_SZ);
564 	}
565 
566 	mlsvc_rpc_free(netr_handle->context, &heap);
567 	return (0);
568 }
569 
570 /*
571  * netr_gen_password
572  *
573  * Generate a new pasword from the old password  and the session key.
574  * The algorithm is a two stage hash. The session key is used in the
575  * first hash but only part of the session key is used in the second
576  * hash.
577  *
578  * If any difficulties occur using the cryptographic framework, the
579  * function returns SMBAUTH_FAILURE.  Otherwise SMBAUTH_SUCCESS is
580  * returned.
581  */
582 static int
583 netr_gen_password(BYTE *session_key, BYTE *old_password, BYTE *new_password)
584 {
585 	int rv;
586 
587 	rv = smb_auth_DES(new_password, 8, session_key, NETR_DESKEY_LEN,
588 	    old_password, 8);
589 	if (rv != SMBAUTH_SUCCESS)
590 		return (rv);
591 
592 	rv = smb_auth_DES(&new_password[8], 8, &session_key[NETR_DESKEY_LEN],
593 	    NETR_DESKEY_LEN, &old_password[8], 8);
594 	return (rv);
595 }
596