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