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