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