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