xref: /illumos-gate/usr/src/lib/smbsrv/libmlsvc/common/netr_auth.c (revision 1d0ec46fafb49266ae79840a692bb48af60ade70)
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 /*
23  * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
24  * Copyright 2021 Tintri by DDN, Inc. All rights reserved.
25  * Copyright 2023 RackTop Systems, Inc.
26  */
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/libsmbns.h>
46 #include <smbsrv/libmlsvc.h>
47 #include <mlsvc.h>
48 #include <smbsrv/ndl/netlogon.ndl>
49 #include <smbsrv/smbinfo.h>
50 #include <smbsrv/netrauth.h>
51 
52 #define	NETR_SESSKEY_ZEROBUF_SZ		4
53 /* The DES algorithm uses a 56-bit encryption key. */
54 #define	NETR_DESKEY_LEN			7
55 
56 int netr_setup_authenticator(netr_info_t *, struct netr_authenticator *,
57     struct netr_authenticator *);
58 DWORD netr_validate_chain(netr_info_t *, struct netr_authenticator *);
59 
60 static int netr_server_req_challenge(mlsvc_handle_t *, netr_info_t *);
61 static int netr_server_authenticate2(mlsvc_handle_t *, netr_info_t *);
62 static int netr_gen_password(BYTE *, BYTE *, BYTE *);
63 
64 /*
65  * Shared with netr_logon.c
66  */
67 netr_info_t netr_global_info = {
68 	.use_secure_rpc = B_TRUE,
69 	.use_logon_ex = B_TRUE
70 };
71 extern ndr_auth_ctx_t netr_ssp_ctx;
72 
73 /*
74  * These flags control various parts of NetLogon RPC messages.
75  * The default is 0 - setting a bit disables some feature.
76  * They are set in smbd/netlogon_flags in svc:/network/smb/server.
77  * These are set when smbd starts. Changing them requires
78  * restarting smbd.
79  *
80  * These shouldn't be confused with either SamLogonEx's ExtraFlags,
81  * or NetrServerAuthenticate's negotiate_flags.
82  *
83  * DISABLE_SECURE_RPC causes Netlogon to use unauthenticated RPC.
84  * Note that the underlying transport is still authenticated and signed.
85  *
86  * DISABLE_RESP_VERIF instructs RPC authentication to ignore failures
87  * when verifying responses.
88  *
89  * DISABLE_SAMLOGONEX causes Netlogon to always use SamLogon, which
90  * makes use of Netlogon Authenticators.
91  *
92  * DISABLE_SEALING causes Netlogon to use Signing (integrity) instead of
93  * Sealing (privacy).
94  */
95 #define	NETR_CFG_DISABLE_SECURE_RPC	0x00000001
96 #define	NETR_CFG_DISABLE_RESP_VERIF	0x00000002
97 #define	NETR_CFG_DISABLE_SAMLOGONEX	0x00000004
98 #define	NETR_CFG_DISABLE_SEALING	0x00000008
99 
100 void
101 netlogon_init_global(uint32_t flags)
102 {
103 	netr_global_info.use_secure_rpc =
104 	    ((flags & NETR_CFG_DISABLE_SECURE_RPC) == 0);
105 	netr_ssp_ctx.auth_verify_resp =
106 	    ((flags & NETR_CFG_DISABLE_RESP_VERIF) == 0);
107 	netr_global_info.use_logon_ex =
108 	    ((flags & NETR_CFG_DISABLE_SAMLOGONEX) == 0);
109 	if ((flags & NETR_CFG_DISABLE_SEALING) != 0)
110 		netr_ssp_ctx.auth_level = NDR_C_AUTHN_LEVEL_PKT_INTEGRITY;
111 }
112 
113 /*
114  * AES-CFB8 has the odd property that 1/256 keys will encrypt
115  * a full block of 0s to all 0s. In order to mitigate this, Windows DCs
116  * now reject Challenges and Credentials where "none of the first 5 bytes
117  * are unique" (i.e. [MS-NRPC] 3.1.4.1 "Session-Key Negotiation" Step 7).
118  * This detects that condition so that we can avoid having our connection
119  * rejected unexpectedly.
120  *
121  * I've interpreted this condition as 'amongst the first 5 bytes,
122  * at least one must appear exactly once'.
123  *
124  * NOTE: Win2012r2 seems to only reject challenges whose first 5 bytes are 0.
125  */
126 boolean_t
127 passes_dc_mitigation(uint8_t *buf)
128 {
129 	int i, j;
130 
131 	for (i = 0; i < 5; i++) {
132 		for (j = 0; j < 5; j++) {
133 			if (i != j && buf[i] == buf[j])
134 				break;
135 		}
136 
137 		/* if this byte didn't match any other byte, this passes */
138 		if (j == 5)
139 			return (B_TRUE);
140 	}
141 
142 	/* None of the bytes were unique - the check fails */
143 	return (B_FALSE);
144 }
145 
146 /*
147  * netlogon_auth
148  *
149  * This is the core of the NETLOGON authentication protocol.
150  * Do the challenge response authentication.
151  *
152  * Prior to calling this function, an anonymous session to the NETLOGON
153  * pipe on a domain controller(server) should have already been opened.
154  *
155  * Upon a successful NETLOGON credential chain establishment, the
156  * netlogon sequence number will be set to match the kpasswd sequence
157  * number.
158  *
159  */
160 DWORD
161 netlogon_auth(char *server, char *domain, DWORD flags)
162 {
163 	mlsvc_handle_t netr_handle;
164 	netr_info_t *netr_info;
165 	int rc;
166 	DWORD leout_rc[2];
167 	boolean_t retry;
168 	DWORD status;
169 
170 	/*
171 	 * [MS-NRPC] 3.1.4.1 "Session-Key Negotiation"
172 	 * Negotiation happens on an 'unprotected RPC channel'
173 	 * (no RPC-level auth).
174 	 */
175 	status = netr_open(server, domain, &netr_handle);
176 
177 	if (status != 0) {
178 		syslog(LOG_ERR, "netlogon_auth remote open failed (%s)",
179 		    xlate_nt_status(status));
180 		return (status);
181 	}
182 
183 	netr_info = &netr_global_info;
184 	bzero(&netr_info->session_key, sizeof (netr_info->session_key));
185 	bzero(&netr_info->rpc_seal_key, sizeof (netr_info->rpc_seal_key));
186 	netr_info->flags = flags;
187 
188 	rc = smb_getnetbiosname(netr_info->hostname, NETBIOS_NAME_SZ);
189 	if (rc != 0)
190 		goto errout;
191 
192 	/* server is our DC.  Note: normally an FQDN. */
193 	(void) snprintf(netr_info->server, sizeof (netr_info->server),
194 	    "\\\\%s", server);
195 
196 	/*
197 	 * Domain (FQDN and NetBIOS) Name needed for Netlogon SSP-based
198 	 * Secure RPC.
199 	 */
200 	rc = smb_getdomainname(netr_info->nb_domain,
201 	    sizeof (netr_info->nb_domain));
202 	if (rc != 0)
203 		goto errout;
204 
205 	rc = smb_getfqdomainname(netr_info->fqdn_domain,
206 	    sizeof (netr_info->fqdn_domain));
207 	if (rc != 0)
208 		goto errout;
209 
210 	/*
211 	 * [MS-NRPC] 3.1.4.1 "Session-Key Negotiation" Step 7
212 	 * Windows DCs will reject negotiate attempts if none of the first
213 	 * 5 bytes of the Challenge are unique.
214 	 * Keep retrying until we've generated one that satisfies this.
215 	 */
216 	do {
217 		retry = B_FALSE;
218 		LE_OUT32(&leout_rc[0], arc4random());
219 		LE_OUT32(&leout_rc[1], arc4random());
220 		(void) memcpy(&netr_info->client_challenge, leout_rc,
221 		    sizeof (struct netr_credential));
222 
223 		if (!passes_dc_mitigation(netr_info->client_challenge.data))
224 			retry = B_TRUE;
225 	} while (retry);
226 
227 	if ((rc = netr_server_req_challenge(&netr_handle, netr_info)) == 0) {
228 		rc = netr_server_authenticate2(&netr_handle, netr_info);
229 		if (rc == 0) {
230 			/*
231 			 * TODO: (later)  When joining a domain using a
232 			 * pre-created machine account, should do:
233 			 * netr_server_password_set(&netr_handle, netr_info);
234 			 * Nexenta issue 11960
235 			 */
236 			smb_update_netlogon_seqnum();
237 			netr_info->flags |= NETR_FLG_VALID;
238 
239 		}
240 	}
241 
242 errout:
243 	(void) netr_close(&netr_handle);
244 
245 	return ((rc) ? NT_STATUS_UNSUCCESSFUL : NT_STATUS_SUCCESS);
246 }
247 
248 /*
249  * netr_open
250  *
251  * Open an anonymous session to the NETLOGON pipe on a domain controller
252  * and bind to the NETR RPC interface.
253  *
254  * We store the remote server information, which is used to drive Windows
255  * version specific behavior.
256  *
257  * Returns 0 or NT status
258  */
259 DWORD
260 netr_open(char *server, char *domain, mlsvc_handle_t *netr_handle)
261 {
262 	char user[SMB_USERNAME_MAXLEN];
263 	DWORD status;
264 
265 	smb_ipc_get_user(user, SMB_USERNAME_MAXLEN);
266 
267 	status = ndr_rpc_bind(netr_handle, server, domain, user, "NETR");
268 
269 	return (status);
270 }
271 
272 uint32_t auth_context_id = 1;
273 
274 DWORD
275 netr_open_secure(char *server, char *domain, mlsvc_handle_t *netr_handle)
276 {
277 	char user[SMB_USERNAME_MAXLEN];
278 	DWORD status;
279 
280 	smb_ipc_get_user(user, SMB_USERNAME_MAXLEN);
281 
282 	/*
283 	 * If the server doesn't support SECURE_RPC_FLAG, or we've disabled
284 	 * secure rpc (netr_global_info.use_secure_rpc), then SECURE_RPC_FLAG
285 	 * won't be in the set of negotiated flags. Don't use SecureRPC if
286 	 * that's the case.
287 	 */
288 	if ((netr_global_info.nego_flags & NETR_NEGO_SECURE_RPC_FLAG) != 0) {
289 		netr_ssp_ctx.auth_context_id = auth_context_id++;
290 		status = ndr_rpc_bind_secure(netr_handle, server, domain, user,
291 		    "NETR", &netr_ssp_ctx);
292 	} else {
293 		status = ndr_rpc_bind(netr_handle, server, domain, user,
294 		    "NETR");
295 	}
296 
297 	return (status);
298 }
299 
300 /*
301  * netr_close
302  *
303  * Close a NETLOGON pipe and free the RPC context.
304  */
305 int
306 netr_close(mlsvc_handle_t *netr_handle)
307 {
308 	ndr_rpc_unbind(netr_handle);
309 	return (0);
310 }
311 
312 /*
313  * netr_server_req_challenge
314  */
315 static int
316 netr_server_req_challenge(mlsvc_handle_t *netr_handle, netr_info_t *netr_info)
317 {
318 	struct netr_ServerReqChallenge arg;
319 	int opnum;
320 
321 	bzero(&arg, sizeof (struct netr_ServerReqChallenge));
322 	opnum = NETR_OPNUM_ServerReqChallenge;
323 
324 	arg.servername = (unsigned char *)netr_info->server;
325 	arg.hostname = (unsigned char *)netr_info->hostname;
326 
327 	(void) memcpy(&arg.client_challenge, &netr_info->client_challenge,
328 	    sizeof (struct netr_credential));
329 
330 	if (ndr_rpc_call(netr_handle, opnum, &arg) != 0)
331 		return (-1);
332 
333 	if (arg.status != 0) {
334 		ndr_rpc_status(netr_handle, opnum, arg.status);
335 		ndr_rpc_release(netr_handle);
336 		return (-1);
337 	}
338 
339 	(void) memcpy(&netr_info->server_challenge, &arg.server_challenge,
340 	    sizeof (struct netr_credential));
341 
342 	ndr_rpc_release(netr_handle);
343 	return (0);
344 }
345 
346 uint32_t netr_server_auth2_flags =
347     NETR_NEGO_BASE_FLAGS |
348     NETR_NEGO_STRONGKEY_FLAG |
349     NETR_NEGO_SECURE_RPC_FLAG;
350 
351 /*
352  * netr_server_authenticate2
353  */
354 static int
355 netr_server_authenticate2(mlsvc_handle_t *netr_handle, netr_info_t *netr_info)
356 {
357 	struct netr_ServerAuthenticate2 arg;
358 	/* sizeof netr_info->hostname, + 1 for the '$' */
359 	char account_name[(NETBIOS_NAME_SZ * 2) + 1];
360 	int opnum;
361 	int rc;
362 	int i;
363 
364 	bzero(&arg, sizeof (struct netr_ServerAuthenticate2));
365 	opnum = NETR_OPNUM_ServerAuthenticate2;
366 
367 	(void) snprintf(account_name, sizeof (account_name), "%s$",
368 	    netr_info->hostname);
369 
370 	smb_tracef("server=[%s] account_name=[%s] hostname=[%s]\n",
371 	    netr_info->server, account_name, netr_info->hostname);
372 
373 	arg.servername = (unsigned char *)netr_info->server;
374 	arg.account_name = (unsigned char *)account_name;
375 	arg.account_type = NETR_WKSTA_TRUST_ACCOUNT_TYPE;
376 	arg.hostname = (unsigned char *)netr_info->hostname;
377 	arg.negotiate_flags = netr_server_auth2_flags;
378 
379 	/*
380 	 * If we've disabled SecureRPC, remove it from our negotiate_flags
381 	 * so that the returned flags don't include it. We won't later use
382 	 * SecureRPC if the returned flags don't include the flag.
383 	 */
384 	if (!netr_global_info.use_secure_rpc)
385 		arg.negotiate_flags &= ~NETR_NEGO_SECURE_RPC_FLAG;
386 
387 	if (arg.negotiate_flags & NETR_NEGO_STRONGKEY_FLAG) {
388 		if (netr_gen_skey128(netr_info) != SMBAUTH_SUCCESS)
389 			return (-1);
390 	} else {
391 		if (netr_gen_skey64(netr_info) != SMBAUTH_SUCCESS)
392 			return (-1);
393 	}
394 
395 	/*
396 	 * Calculate the 'XorKey' used to derive certain Netlogon SSP keys.
397 	 * This is used in [MS-NRPC] 3.3.4.2 "The Netlogon Signature Token".
398 	 */
399 	for (i = 0; i < netr_info->session_key.len; i++)
400 		netr_info->rpc_seal_key.key[i] =
401 		    netr_info->session_key.key[i] ^ 0xf0;
402 	netr_info->rpc_seal_key.len = netr_info->session_key.len;
403 
404 	/*
405 	 * We can't 'fiddle' with anything here to prevent getting bitten by
406 	 * ClientStoredCredential-based mitigations.
407 	 *
408 	 * If we're using SamLogonEx, we won't use authenticators unless
409 	 * some other NetLogon command is implemented and used.
410 	 */
411 	if (netr_gen_credentials(netr_info->session_key.key,
412 	    &netr_info->client_challenge, 0,
413 	    &netr_info->client_credential, B_FALSE) != SMBAUTH_SUCCESS) {
414 		return (-1);
415 	}
416 
417 	if (netr_gen_credentials(netr_info->session_key.key,
418 	    &netr_info->server_challenge, 0,
419 	    &netr_info->server_credential, B_FALSE) != SMBAUTH_SUCCESS) {
420 		return (-1);
421 	}
422 
423 	(void) memcpy(&arg.client_credential, &netr_info->client_credential,
424 	    sizeof (struct netr_credential));
425 
426 	if (ndr_rpc_call(netr_handle, opnum, &arg) != 0)
427 		return (-1);
428 
429 	if (arg.status != 0) {
430 		ndr_rpc_status(netr_handle, opnum, arg.status);
431 		ndr_rpc_release(netr_handle);
432 		return (-1);
433 	}
434 
435 	/* The server returns the intersection of our flags and their flags. */
436 	netr_info->nego_flags = arg.negotiate_flags;
437 
438 	rc = memcmp(&netr_info->server_credential, &arg.server_credential,
439 	    sizeof (struct netr_credential));
440 
441 	ndr_rpc_release(netr_handle);
442 	return (rc);
443 }
444 
445 /*
446  * netr_gen_skey128
447  *
448  * Generate a 128-bit session key from the client and server challenges.
449  * See "Session-Key Computation" section of MS-NRPC document.
450  */
451 int
452 netr_gen_skey128(netr_info_t *netr_info)
453 {
454 	unsigned char ntlmhash[SMBAUTH_HASH_SZ];
455 	int rc = SMBAUTH_FAILURE;
456 	CK_RV rv;
457 	CK_MECHANISM mechanism;
458 	CK_SESSION_HANDLE hSession;
459 	CK_ULONG diglen = MD_DIGEST_LEN;
460 	unsigned char md5digest[MD_DIGEST_LEN];
461 	unsigned char zerobuf[NETR_SESSKEY_ZEROBUF_SZ];
462 
463 	bzero(ntlmhash, SMBAUTH_HASH_SZ);
464 	/*
465 	 * We should check (netr_info->flags & NETR_FLG_INIT) and use
466 	 * the appropriate password but it isn't working yet.  So we
467 	 * always use the default one for now.
468 	 */
469 	bzero(netr_info->password, sizeof (netr_info->password));
470 	rc = smb_config_getstr(SMB_CI_MACHINE_PASSWD,
471 	    (char *)netr_info->password, sizeof (netr_info->password));
472 
473 	if ((rc != SMBD_SMF_OK) || *netr_info->password == '\0') {
474 		return (SMBAUTH_FAILURE);
475 	}
476 
477 	rc = smb_auth_ntlm_hash((char *)netr_info->password, ntlmhash);
478 	if (rc != SMBAUTH_SUCCESS) {
479 		explicit_bzero(&netr_info->password,
480 		    sizeof (netr_info->password));
481 		return (SMBAUTH_FAILURE);
482 	}
483 
484 	bzero(zerobuf, NETR_SESSKEY_ZEROBUF_SZ);
485 
486 	mechanism.mechanism = CKM_MD5;
487 	mechanism.pParameter = 0;
488 	mechanism.ulParameterLen = 0;
489 
490 	rv = SUNW_C_GetMechSession(mechanism.mechanism, &hSession);
491 	if (rv != CKR_OK) {
492 		rc = SMBAUTH_FAILURE;
493 		goto errout;
494 	}
495 
496 	rv = C_DigestInit(hSession, &mechanism);
497 	if (rv != CKR_OK)
498 		goto cleanup;
499 
500 	rv = C_DigestUpdate(hSession, (CK_BYTE_PTR)zerobuf,
501 	    NETR_SESSKEY_ZEROBUF_SZ);
502 	if (rv != CKR_OK)
503 		goto cleanup;
504 
505 	rv = C_DigestUpdate(hSession,
506 	    (CK_BYTE_PTR)netr_info->client_challenge.data, NETR_CRED_DATA_SZ);
507 	if (rv != CKR_OK)
508 		goto cleanup;
509 
510 	rv = C_DigestUpdate(hSession,
511 	    (CK_BYTE_PTR)netr_info->server_challenge.data, NETR_CRED_DATA_SZ);
512 	if (rv != CKR_OK)
513 		goto cleanup;
514 
515 	rv = C_DigestFinal(hSession, (CK_BYTE_PTR)md5digest, &diglen);
516 	if (rv != CKR_OK)
517 		goto cleanup;
518 
519 	rc = smb_auth_hmac_md5(md5digest, diglen, ntlmhash, SMBAUTH_HASH_SZ,
520 	    netr_info->session_key.key);
521 
522 	netr_info->session_key.len = NETR_SESSKEY128_SZ;
523 cleanup:
524 	(void) C_CloseSession(hSession);
525 
526 errout:
527 	explicit_bzero(&netr_info->password, sizeof (netr_info->password));
528 	explicit_bzero(ntlmhash, sizeof (ntlmhash));
529 
530 	return (rc);
531 
532 }
533 /*
534  * netr_gen_skey64
535  *
536  * Generate a 64-bit session key from the client and server challenges.
537  * See "Session-Key Computation" section of MS-NRPC document.
538  *
539  * The algorithm is a two stage hash. For the first hash, the input is
540  * the combination of the client and server challenges, the key is
541  * the first 7 bytes of the password. The initial password is formed
542  * using the NT password hash on the local hostname in lower case.
543  * The result is stored in a temporary buffer.
544  *
545  *		input:	challenge
546  *		key:	passwd lower 7 bytes
547  *		output:	intermediate result
548  *
549  * For the second hash, the input is the result of the first hash and
550  * the key is the last 7 bytes of the password.
551  *
552  *		input:	result of first hash
553  *		key:	passwd upper 7 bytes
554  *		output:	session_key
555  *
556  * The final output should be the session key.
557  *
558  *		FYI: smb_auth_DES(output, key, input)
559  *
560  * If any difficulties occur using the cryptographic framework, the
561  * function returns SMBAUTH_FAILURE.  Otherwise SMBAUTH_SUCCESS is
562  * returned.
563  */
564 int
565 netr_gen_skey64(netr_info_t *netr_info)
566 {
567 	unsigned char md4hash[32];
568 	unsigned char buffer[8];
569 	DWORD data[2];
570 	DWORD *client_challenge;
571 	DWORD *server_challenge;
572 	int rc;
573 	DWORD le_data[2];
574 
575 	client_challenge = (DWORD *)(uintptr_t)&netr_info->client_challenge;
576 	server_challenge = (DWORD *)(uintptr_t)&netr_info->server_challenge;
577 	bzero(md4hash, 32);
578 
579 	/*
580 	 * We should check (netr_info->flags & NETR_FLG_INIT) and use
581 	 * the appropriate password but it isn't working yet.  So we
582 	 * always use the default one for now.
583 	 */
584 	bzero(netr_info->password, sizeof (netr_info->password));
585 	rc = smb_config_getstr(SMB_CI_MACHINE_PASSWD,
586 	    (char *)netr_info->password, sizeof (netr_info->password));
587 
588 	if ((rc != SMBD_SMF_OK) || *netr_info->password == '\0') {
589 		return (SMBAUTH_FAILURE);
590 	}
591 
592 	rc = smb_auth_ntlm_hash((char *)netr_info->password, md4hash);
593 
594 	if (rc != SMBAUTH_SUCCESS) {
595 		rc = SMBAUTH_FAILURE;
596 		goto out;
597 	}
598 
599 	data[0] = LE_IN32(&client_challenge[0]) + LE_IN32(&server_challenge[0]);
600 	data[1] = LE_IN32(&client_challenge[1]) + LE_IN32(&server_challenge[1]);
601 	LE_OUT32(&le_data[0], data[0]);
602 	LE_OUT32(&le_data[1], data[1]);
603 	rc = smb_auth_DES(buffer, 8, md4hash, NETR_DESKEY_LEN,
604 	    (unsigned char *)le_data, 8);
605 
606 	if (rc != SMBAUTH_SUCCESS)
607 		goto out;
608 
609 	netr_info->session_key.len = NETR_SESSKEY64_SZ;
610 	rc = smb_auth_DES(netr_info->session_key.key,
611 	    netr_info->session_key.len, &md4hash[9], NETR_DESKEY_LEN, buffer,
612 	    8);
613 
614 out:
615 	explicit_bzero(&netr_info->password, sizeof (netr_info->password));
616 	explicit_bzero(md4hash, sizeof (md4hash));
617 	explicit_bzero(buffer, sizeof (buffer));
618 	return (rc);
619 }
620 
621 /*
622  * netr_gen_credentials
623  *
624  * Generate a set of credentials from a challenge and a session key.
625  * The algorithm is a two stage hash. For the first hash, the
626  * timestamp is added to the challenge and the result is stored in a
627  * temporary buffer:
628  *
629  *		input:	challenge (including timestamp)
630  *		key:	session_key
631  *		output:	intermediate result
632  *
633  * For the second hash, the input is the result of the first hash and
634  * a strange partial key is used:
635  *
636  *		input:	result of first hash
637  *		key:	funny partial key
638  *		output:	credentiails
639  *
640  * The final output should be an encrypted set of credentials.
641  *
642  *		FYI: smb_auth_DES(output, key, input)
643  *
644  * If any difficulties occur using the cryptographic framework, the
645  * function returns SMBAUTH_FAILURE.  Otherwise SMBAUTH_SUCCESS is
646  * returned.
647  */
648 int
649 netr_gen_credentials(BYTE *session_key, netr_cred_t *challenge,
650     DWORD timestamp, netr_cred_t *out_cred, boolean_t retry)
651 {
652 	unsigned char buffer[8];
653 	DWORD data[2];
654 	DWORD le_data[2];
655 	DWORD *p;
656 	int rc;
657 
658 	p = (DWORD *)(uintptr_t)challenge;
659 	data[0] = LE_IN32(&p[0]) + timestamp;
660 	data[1] = LE_IN32(&p[1]);
661 
662 	LE_OUT32(&le_data[0], data[0]);
663 	LE_OUT32(&le_data[1], data[1]);
664 
665 	if (smb_auth_DES(buffer, 8, session_key, NETR_DESKEY_LEN,
666 	    (unsigned char *)le_data, 8) != SMBAUTH_SUCCESS)
667 		return (SMBAUTH_FAILURE);
668 
669 	rc = smb_auth_DES(out_cred->data, 8, &session_key[NETR_DESKEY_LEN],
670 	    NETR_DESKEY_LEN, buffer, 8);
671 
672 	/*
673 	 * [MS-NRPC] 3.1.4.6 "Calling Methods Requiring Session-Key
674 	 * Establishment" Step 6
675 	 *
676 	 * Windows DCs will reject authenticators if none of the first
677 	 * 5 bytes of the ClientStoredCredential are unique.
678 	 * Keep retrying until we've generated one that satisfies this,
679 	 * but only if the caller can handle retries.
680 	 */
681 	if (retry && !passes_dc_mitigation(out_cred->data))
682 		return (SMBAUTH_RETRY);
683 
684 	return (rc);
685 }
686 
687 /*
688  * netr_server_password_set
689  *
690  * Attempt to change the trust account password for this system.
691  *
692  * Note that this call may legitimately fail if the registry on the
693  * domain controller has been setup to deny attempts to change the
694  * trust account password. In this case we should just continue to
695  * use the original password.
696  *
697  * Possible status values:
698  *	NT_STATUS_ACCESS_DENIED
699  */
700 int
701 netr_server_password_set(mlsvc_handle_t *netr_handle, netr_info_t *netr_info)
702 {
703 	struct netr_PasswordSet  arg;
704 	int opnum;
705 	BYTE new_password[NETR_OWF_PASSWORD_SZ];
706 	char account_name[NETBIOS_NAME_SZ * 2];
707 
708 	bzero(&arg, sizeof (struct netr_PasswordSet));
709 	opnum = NETR_OPNUM_ServerPasswordSet;
710 
711 	(void) snprintf(account_name, sizeof (account_name), "%s$",
712 	    netr_info->hostname);
713 
714 	arg.servername = (unsigned char *)netr_info->server;
715 	arg.account_name = (unsigned char *)account_name;
716 	arg.sec_chan_type = NETR_WKSTA_TRUST_ACCOUNT_TYPE;
717 	arg.hostname = (unsigned char *)netr_info->hostname;
718 
719 	/*
720 	 * Set up the client side authenticator.
721 	 */
722 	if (netr_setup_authenticator(netr_info, &arg.auth, 0) !=
723 	    SMBAUTH_SUCCESS) {
724 		return (-1);
725 	}
726 
727 	/*
728 	 * Generate a new password from the old password.
729 	 */
730 	if (netr_gen_password(netr_info->session_key.key,
731 	    netr_info->password, new_password) == SMBAUTH_FAILURE) {
732 		return (-1);
733 	}
734 
735 	(void) memcpy(&arg.owf_password, &new_password,
736 	    NETR_OWF_PASSWORD_SZ);
737 
738 	if (ndr_rpc_call(netr_handle, opnum, &arg) != 0)
739 		return (-1);
740 
741 	if (arg.status != 0) {
742 		ndr_rpc_status(netr_handle, opnum, arg.status);
743 		ndr_rpc_release(netr_handle);
744 		return (-1);
745 	}
746 
747 	/*
748 	 * Check the returned credentials.  The server returns the new
749 	 * client credential rather than the new server credentiali,
750 	 * as documented elsewhere.
751 	 *
752 	 * Generate the new seed for the credential chain.  Increment
753 	 * the timestamp and add it to the client challenge.  Then we
754 	 * need to copy the challenge to the credential field in
755 	 * preparation for the next cycle.
756 	 */
757 	if (netr_validate_chain(netr_info, &arg.auth) == 0) {
758 		/*
759 		 * Save the new password.
760 		 */
761 		(void) memcpy(netr_info->password, new_password,
762 		    NETR_OWF_PASSWORD_SZ);
763 	}
764 
765 	ndr_rpc_release(netr_handle);
766 	return (0);
767 }
768 
769 /*
770  * netr_gen_password
771  *
772  * Generate a new pasword from the old password  and the session key.
773  * The algorithm is a two stage hash. The session key is used in the
774  * first hash but only part of the session key is used in the second
775  * hash.
776  *
777  * If any difficulties occur using the cryptographic framework, the
778  * function returns SMBAUTH_FAILURE.  Otherwise SMBAUTH_SUCCESS is
779  * returned.
780  */
781 static int
782 netr_gen_password(BYTE *session_key, BYTE *old_password, BYTE *new_password)
783 {
784 	int rv;
785 
786 	rv = smb_auth_DES(new_password, 8, session_key, NETR_DESKEY_LEN,
787 	    old_password, 8);
788 	if (rv != SMBAUTH_SUCCESS)
789 		return (rv);
790 
791 	rv = smb_auth_DES(&new_password[8], 8, &session_key[NETR_DESKEY_LEN],
792 	    NETR_DESKEY_LEN, &old_password[8], 8);
793 	return (rv);
794 }
795 
796 /*
797  * Todo: need netr_server_password_set2()
798  * used by "unsecure join". (NX 11960)
799  */
800