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