xref: /onnv-gate/usr/src/lib/smbsrv/libmlsvc/common/netr_auth.c (revision 5521:cf62335046cd)
15331Samw /*
25331Samw  * CDDL HEADER START
35331Samw  *
45331Samw  * The contents of this file are subject to the terms of the
55331Samw  * Common Development and Distribution License (the "License").
65331Samw  * You may not use this file except in compliance with the License.
75331Samw  *
85331Samw  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
95331Samw  * or http://www.opensolaris.org/os/licensing.
105331Samw  * See the License for the specific language governing permissions
115331Samw  * and limitations under the License.
125331Samw  *
135331Samw  * When distributing Covered Code, include this CDDL HEADER in each
145331Samw  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
155331Samw  * If applicable, add the following below this CDDL HEADER, with the
165331Samw  * fields enclosed by brackets "[]" replaced with your own identifying
175331Samw  * information: Portions Copyright [yyyy] [name of copyright owner]
185331Samw  *
195331Samw  * CDDL HEADER END
205331Samw  */
215331Samw /*
225331Samw  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
235331Samw  * Use is subject to license terms.
245331Samw  */
255331Samw 
265331Samw #pragma ident	"%Z%%M%	%I%	%E% SMI"
275331Samw 
285331Samw /*
295331Samw  * NETR challenge/response client functions.
305331Samw  *
315331Samw  * NT_STATUS_INVALID_PARAMETER
325331Samw  * NT_STATUS_NO_TRUST_SAM_ACCOUNT
335331Samw  * NT_STATUS_ACCESS_DENIED
345331Samw  */
355331Samw 
365331Samw #include <stdio.h>
375331Samw #include <stdlib.h>
385331Samw #include <strings.h>
395331Samw #include <unistd.h>
405331Samw #include <ctype.h>
415331Samw 
425331Samw #include <smbsrv/libsmb.h>
43*5521Sas200622 #include <smbsrv/libsmbrdr.h>
445331Samw #include <smbsrv/mlsvc_util.h>
455331Samw #include <smbsrv/ndl/netlogon.ndl>
465331Samw #include <smbsrv/ntstatus.h>
475331Samw #include <smbsrv/smbinfo.h>
485331Samw #include <smbsrv/mlsvc.h>
495331Samw #include <smbsrv/netrauth.h>
505331Samw 
515331Samw int netr_setup_authenticator(netr_info_t *, struct netr_authenticator *,
525331Samw     struct netr_authenticator *);
535331Samw DWORD netr_validate_chain(netr_info_t *, struct netr_authenticator *);
545331Samw 
555331Samw static int netr_server_req_challenge(mlsvc_handle_t *, netr_info_t *);
565331Samw static int netr_server_authenticate2(mlsvc_handle_t *, netr_info_t *);
575331Samw static int netr_gen_password(BYTE *, BYTE *, BYTE *);
585331Samw 
595331Samw /*
605331Samw  * Shared with netr_logon.c
615331Samw  */
625331Samw netr_info_t netr_global_info;
635331Samw 
645331Samw /*
655331Samw  * netlogon_auth
665331Samw  *
675331Samw  * This is the core of the NETLOGON authentication protocol.
685331Samw  * Do the challenge response authentication.
695331Samw  *
705331Samw  * Prior to calling this function, an anonymous session to the NETLOGON
715331Samw  * pipe on a domain controller(server) should have already been opened.
725331Samw  */
735331Samw DWORD
745331Samw netlogon_auth(char *server, mlsvc_handle_t *netr_handle, DWORD flags)
755331Samw {
765331Samw 	netr_info_t *netr_info;
775331Samw 	int rc;
78*5521Sas200622 	DWORD leout_rc[2];
795331Samw 
805331Samw 	netr_info = &netr_global_info;
815331Samw 	bzero(netr_info, sizeof (netr_info_t));
825331Samw 
835331Samw 	netr_info->flags |= flags;
845331Samw 
855331Samw 	rc = smb_getnetbiosname(netr_info->hostname, MLSVC_DOMAIN_NAME_MAX);
865331Samw 	if (rc != 0)
875331Samw 		return (NT_STATUS_UNSUCCESSFUL);
885331Samw 
895331Samw 	(void) snprintf(netr_info->server, sizeof (netr_info->server),
905331Samw 	    "\\\\%s", server);
915331Samw 
92*5521Sas200622 	LE_OUT32(&leout_rc[0], random());
93*5521Sas200622 	LE_OUT32(&leout_rc[1], random());
94*5521Sas200622 	(void) memcpy(&netr_info->client_challenge, leout_rc,
955331Samw 	    sizeof (struct netr_credential));
965331Samw 
975331Samw 	if ((rc = netr_server_req_challenge(netr_handle, netr_info)) == 0) {
985331Samw 		rc = netr_server_authenticate2(netr_handle, netr_info);
995331Samw 		if (rc == 0)
1005331Samw 			netr_info->flags |= NETR_FLG_VALID;
1015331Samw 	}
1025331Samw 
1035331Samw 	return ((rc) ? NT_STATUS_UNSUCCESSFUL : NT_STATUS_SUCCESS);
1045331Samw }
1055331Samw 
1065331Samw /*
1075331Samw  * netr_open
1085331Samw  *
1095331Samw  * Open an anonymous session to the NETLOGON pipe on a domain
1105331Samw  * controller and bind to the NETR RPC interface. We store the
1115331Samw  * remote server's native OS type - we may need it due to
1125331Samw  * differences between versions of Windows.
1135331Samw  */
1145331Samw int
1155331Samw netr_open(char *server, char *domain, mlsvc_handle_t *netr_handle)
1165331Samw {
1175331Samw 	int fid;
1185331Samw 	int remote_os = 0;
1195331Samw 	int remote_lm = 0;
1205331Samw 	int server_pdc;
121*5521Sas200622 	char *user = smbrdr_ipc_get_user();
1225331Samw 
123*5521Sas200622 	if (mlsvc_logon(server, domain, user) != 0)
1245331Samw 		return (-1);
1255331Samw 
126*5521Sas200622 	fid = mlsvc_open_pipe(server, domain, user, "\\NETLOGON");
1275331Samw 	if (fid < 0)
1285331Samw 		return (-1);
1295331Samw 
1305331Samw 	if (mlsvc_rpc_bind(netr_handle, fid, "NETR") < 0) {
1315331Samw 		(void) mlsvc_close_pipe(fid);
1325331Samw 		return (-1);
1335331Samw 	}
1345331Samw 
1355331Samw 	(void) mlsvc_session_native_values(fid, &remote_os, &remote_lm,
1365331Samw 	    &server_pdc);
1375331Samw 	netr_handle->context->server_os = remote_os;
1385331Samw 	netr_handle->context->server_pdc = server_pdc;
1395331Samw 	return (0);
1405331Samw }
1415331Samw 
1425331Samw /*
1435331Samw  * netr_close
1445331Samw  *
1455331Samw  * Close a NETLOGON pipe and free the RPC context.
1465331Samw  */
1475331Samw int
1485331Samw netr_close(mlsvc_handle_t *netr_handle)
1495331Samw {
1505331Samw 	(void) mlsvc_close_pipe(netr_handle->context->fid);
1515331Samw 	free(netr_handle->context);
1525331Samw 	return (0);
1535331Samw }
1545331Samw 
1555331Samw /*
1565331Samw  * netr_server_req_challenge
1575331Samw  */
1585331Samw static int
1595331Samw netr_server_req_challenge(mlsvc_handle_t *netr_handle, netr_info_t *netr_info)
1605331Samw {
1615331Samw 	struct netr_ServerReqChallenge arg;
1625331Samw 	mlrpc_heapref_t heap;
1635331Samw 	int opnum;
1645331Samw 	int rc;
1655331Samw 
1665331Samw 	bzero(&arg, sizeof (struct netr_ServerReqChallenge));
1675331Samw 	opnum = NETR_OPNUM_ServerReqChallenge;
1685331Samw 
1695331Samw 	arg.servername = (unsigned char *)netr_info->server;
1705331Samw 	arg.hostname = (unsigned char *)netr_info->hostname;
1715331Samw 
1725331Samw 	(void) memcpy(&arg.client_challenge, &netr_info->client_challenge,
1735331Samw 	    sizeof (struct netr_credential));
1745331Samw 
1755331Samw 	(void) mlsvc_rpc_init(&heap);
1765331Samw 	rc = mlsvc_rpc_call(netr_handle->context, opnum, &arg, &heap);
1775331Samw 	if (rc == 0) {
1785331Samw 		if (arg.status != 0) {
1795331Samw 			mlsvc_rpc_report_status(opnum, arg.status);
1805331Samw 			rc = -1;
1815331Samw 		} else {
1825331Samw 			(void) memcpy(&netr_info->server_challenge,
1835331Samw 			    &arg.server_challenge,
1845331Samw 			    sizeof (struct netr_credential));
1855331Samw 		}
1865331Samw 	}
1875331Samw 
1885331Samw 	mlsvc_rpc_free(netr_handle->context, &heap);
1895331Samw 	return (rc);
1905331Samw }
1915331Samw 
1925331Samw /*
1935331Samw  * netr_server_authenticate2
1945331Samw  */
1955331Samw static int
1965331Samw netr_server_authenticate2(mlsvc_handle_t *netr_handle, netr_info_t *netr_info)
1975331Samw {
1985331Samw 	struct netr_ServerAuthenticate2 arg;
1995331Samw 	mlrpc_heapref_t heap;
2005331Samw 	int opnum;
2015331Samw 	int rc;
2025331Samw 	char account_name[MLSVC_DOMAIN_NAME_MAX * 2];
2035331Samw 
2045331Samw 	bzero(&arg, sizeof (struct netr_ServerAuthenticate2));
2055331Samw 	opnum = NETR_OPNUM_ServerAuthenticate2;
2065331Samw 
2075331Samw 	(void) snprintf(account_name, sizeof (account_name), "%s$",
2085331Samw 	    netr_info->hostname);
2095331Samw 
2105331Samw 	arg.servername = (unsigned char *)netr_info->server;
2115331Samw 	arg.account_name = (unsigned char *)account_name;
2125331Samw 	arg.account_type = NETR_WKSTA_TRUST_ACCOUNT_TYPE;
2135331Samw 	arg.hostname = (unsigned char *)netr_info->hostname;
2145331Samw 	arg.negotiate_flags = NETR_NEGOTIATE_FLAGS;
2155331Samw 
2165331Samw 	smb_tracef("server=[%s] account_name=[%s] hostname=[%s]\n",
2175331Samw 	    netr_info->server, account_name, netr_info->hostname);
2185331Samw 
2195331Samw 	if (netr_gen_session_key(netr_info) != SMBAUTH_SUCCESS)
2205331Samw 		return (-1);
2215331Samw 
2225331Samw 	if (netr_gen_credentials(netr_info->session_key,
2235331Samw 	    &netr_info->client_challenge,
2245331Samw 	    0,
2255331Samw 	    &netr_info->client_credential) != SMBAUTH_SUCCESS) {
2265331Samw 		return (-1);
2275331Samw 	}
2285331Samw 
2295331Samw 	if (netr_gen_credentials(netr_info->session_key,
2305331Samw 	    &netr_info->server_challenge,
2315331Samw 	    0,
2325331Samw 	    &netr_info->server_credential) != SMBAUTH_SUCCESS) {
2335331Samw 		return (-1);
2345331Samw 	}
2355331Samw 
2365331Samw 	(void) memcpy(&arg.client_credential, &netr_info->client_credential,
2375331Samw 	    sizeof (struct netr_credential));
2385331Samw 
2395331Samw 	(void) mlsvc_rpc_init(&heap);
2405331Samw 
2415331Samw 	rc = mlsvc_rpc_call(netr_handle->context, opnum, &arg, &heap);
2425331Samw 	if (rc == 0) {
2435331Samw 		if (arg.status != 0) {
2445331Samw 			mlsvc_rpc_report_status(opnum, arg.status);
2455331Samw 			rc = -1;
2465331Samw 		} else {
2475331Samw 			rc = memcmp(&netr_info->server_credential,
2485331Samw 			    &arg.server_credential,
2495331Samw 			    sizeof (struct netr_credential));
2505331Samw 		}
2515331Samw 	}
2525331Samw 
2535331Samw 	mlsvc_rpc_free(netr_handle->context, &heap);
2545331Samw 	return (rc);
2555331Samw }
2565331Samw 
2575331Samw /*
2585331Samw  * netr_gen_session_key
2595331Samw  *
2605331Samw  * Generate a session key from the client and server challenges. The
2615331Samw  * algorithm is a two stage hash. For the first hash, the input is
2625331Samw  * the combination of the client and server challenges, the key is
2635331Samw  * the first 8 bytes of the password. The initial password is formed
2645331Samw  * using the NT password hash on the local hostname in lower case.
2655331Samw  * The result is stored in a temporary buffer.
2665331Samw  *
2675331Samw  *		input:	challenge
2685331Samw  *		key:	passwd lower 8 bytes
2695331Samw  *		output:	intermediate result
2705331Samw  *
2715331Samw  * For the second hash, the input is the result of the first hash and
2725331Samw  * the key is the last 8 bytes of the password.
2735331Samw  *
2745331Samw  *		input:	result of first hash
2755331Samw  *		key:	passwd upper 8 bytes
2765331Samw  *		output:	session_key
2775331Samw  *
2785331Samw  * The final output should be the session key.
2795331Samw  *
2805331Samw  *		FYI: smb_auth_DES(output, key, input)
2815331Samw  *
2825331Samw  * If any difficulties occur using the cryptographic framework, the
2835331Samw  * function returns SMBAUTH_FAILURE.  Otherwise SMBAUTH_SUCCESS is
2845331Samw  * returned.
2855331Samw  */
2865331Samw int
2875331Samw netr_gen_session_key(netr_info_t *netr_info)
2885331Samw {
2895331Samw 	unsigned char md4hash[32];
2905331Samw 	unsigned char buffer[8];
2915331Samw 	DWORD data[2];
2925331Samw 	DWORD *client_challenge;
2935331Samw 	DWORD *server_challenge;
2945331Samw 	int rc;
2955331Samw 	char *machine_passwd;
296*5521Sas200622 	DWORD le_data[2];
2975331Samw 
2985331Samw 	client_challenge = (DWORD *)(uintptr_t)&netr_info->client_challenge;
2995331Samw 	server_challenge = (DWORD *)(uintptr_t)&netr_info->server_challenge;
3005331Samw 	bzero(md4hash, 32);
3015331Samw 
3025331Samw 	/*
3035331Samw 	 * We should check (netr_info->flags & NETR_FLG_INIT) and use
3045331Samw 	 * the appropriate password but it isn't working yet.  So we
3055331Samw 	 * always use the default one for now.
3065331Samw 	 */
3075331Samw 	smb_config_rdlock();
3085331Samw 	machine_passwd = smb_config_getstr(SMB_CI_MACHINE_PASSWD);
3095331Samw 
3105331Samw 	if (!machine_passwd || *machine_passwd == 0) {
3115331Samw 		smb_config_unlock();
3125331Samw 		return (-1);
3135331Samw 	}
3145331Samw 
3155331Samw 	bzero(netr_info->password, sizeof (netr_info->password));
3165331Samw 	(void) strlcpy((char *)netr_info->password, (char *)machine_passwd,
3175331Samw 	    sizeof (netr_info->password));
3185331Samw 
3195331Samw 	rc = smb_auth_ntlm_hash((char *)machine_passwd, md4hash);
3205331Samw 	smb_config_unlock();
3215331Samw 
3225331Samw 	if (rc != SMBAUTH_SUCCESS)
3235331Samw 		return (SMBAUTH_FAILURE);
3245331Samw 
3255331Samw 	data[0] = LE_IN32(&client_challenge[0]) + LE_IN32(&server_challenge[0]);
3265331Samw 	data[1] = LE_IN32(&client_challenge[1]) + LE_IN32(&server_challenge[1]);
327*5521Sas200622 	LE_OUT32(&le_data[0], data[0]);
328*5521Sas200622 	LE_OUT32(&le_data[1], data[1]);
3295331Samw 
330*5521Sas200622 	rc = smb_auth_DES(buffer, 8, md4hash, 8, (unsigned char *)le_data, 8);
3315331Samw 	if (rc != SMBAUTH_SUCCESS)
3325331Samw 		return (rc);
3335331Samw 
3345331Samw 	rc = smb_auth_DES(netr_info->session_key, 8, &md4hash[9], 8, buffer, 8);
3355331Samw 	return (rc);
3365331Samw }
3375331Samw 
3385331Samw /*
3395331Samw  * netr_gen_credentials
3405331Samw  *
3415331Samw  * Generate a set of credentials from a challenge and a session key.
3425331Samw  * The algorithm is a two stage hash. For the first hash, the
3435331Samw  * timestamp is added to the challenge and the result is stored in a
3445331Samw  * temporary buffer:
3455331Samw  *
3465331Samw  *		input:	challenge (including timestamp)
3475331Samw  *		key:	session_key
3485331Samw  *		output:	intermediate result
3495331Samw  *
3505331Samw  * For the second hash, the input is the result of the first hash and
3515331Samw  * a strange partial key is used:
3525331Samw  *
3535331Samw  *		input:	result of first hash
3545331Samw  *		key:	funny partial key
3555331Samw  *		output:	credentiails
3565331Samw  *
3575331Samw  * The final output should be an encrypted set of credentials.
3585331Samw  *
3595331Samw  *		FYI: smb_auth_DES(output, key, input)
3605331Samw  *
3615331Samw  * If any difficulties occur using the cryptographic framework, the
3625331Samw  * function returns SMBAUTH_FAILURE.  Otherwise SMBAUTH_SUCCESS is
3635331Samw  * returned.
3645331Samw  */
3655331Samw int
3665331Samw netr_gen_credentials(BYTE *session_key, netr_cred_t *challenge,
3675331Samw     DWORD timestamp, netr_cred_t *out_cred)
3685331Samw {
3695331Samw 	unsigned char buffer[8];
3705331Samw 	unsigned char partial_key[8];
3715331Samw 	DWORD data[2];
372*5521Sas200622 	DWORD le_data[2];
3735331Samw 	DWORD *p;
3745331Samw 	int rc;
3755331Samw 
3765331Samw 	p = (DWORD *)(uintptr_t)challenge;
377*5521Sas200622 	data[0] = LE_IN32(&p[0]) + timestamp;
378*5521Sas200622 	data[1] = LE_IN32(&p[1]);
379*5521Sas200622 
380*5521Sas200622 	LE_OUT32(&le_data[0], data[0]);
381*5521Sas200622 	LE_OUT32(&le_data[1], data[1]);
3825331Samw 
3835331Samw 	if (smb_auth_DES(buffer, 8, session_key, 8,
384*5521Sas200622 	    (unsigned char *)le_data, 8) != SMBAUTH_SUCCESS)
3855331Samw 		return (SMBAUTH_FAILURE);
3865331Samw 
3875331Samw 	bzero(partial_key, 8);
3885331Samw 	partial_key[0] = session_key[7];
3895331Samw 
3905331Samw 	rc = smb_auth_DES((unsigned char *)out_cred, 8, partial_key, 8,
3915331Samw 	    buffer, 8);
3925331Samw 	return (rc);
3935331Samw }
3945331Samw 
3955331Samw /*
3965331Samw  * netr_server_password_set
3975331Samw  *
3985331Samw  * Attempt to change the trust account password for this system.
3995331Samw  *
4005331Samw  * Note that this call may legitimately fail if the registry on the
4015331Samw  * domain controller has been setup to deny attempts to change the
4025331Samw  * trust account password. In this case we should just continue to
4035331Samw  * use the original password.
4045331Samw  *
4055331Samw  * Possible status values:
4065331Samw  *	NT_STATUS_ACCESS_DENIED
4075331Samw  */
4085331Samw int
4095331Samw netr_server_password_set(mlsvc_handle_t *netr_handle, netr_info_t *netr_info)
4105331Samw {
4115331Samw 	struct netr_PasswordSet  arg;
4125331Samw 	mlrpc_heapref_t heap;
4135331Samw 	int opnum;
4145331Samw 	int rc;
4155331Samw 	BYTE new_password[NETR_OWF_PASSWORD_SZ];
4165331Samw 	char account_name[MLSVC_DOMAIN_NAME_MAX * 2];
4175331Samw 
4185331Samw 	bzero(&arg, sizeof (struct netr_PasswordSet));
4195331Samw 	opnum = NETR_OPNUM_ServerPasswordSet;
4205331Samw 
4215331Samw 	(void) snprintf(account_name, sizeof (account_name), "%s$",
4225331Samw 	    netr_info->hostname);
4235331Samw 
4245331Samw 	arg.servername = (unsigned char *)netr_info->server;
4255331Samw 	arg.account_name = (unsigned char *)account_name;
4265331Samw 	arg.account_type = NETR_WKSTA_TRUST_ACCOUNT_TYPE;
4275331Samw 	arg.hostname = (unsigned char *)netr_info->hostname;
4285331Samw 
4295331Samw 	/*
4305331Samw 	 * Set up the client side authenticator.
4315331Samw 	 */
4325331Samw 	if (netr_setup_authenticator(netr_info, &arg.auth, 0) !=
4335331Samw 	    SMBAUTH_SUCCESS) {
4345331Samw 		return (-1);
4355331Samw 	}
4365331Samw 
4375331Samw 	/*
4385331Samw 	 * Generate a new password from the old password.
4395331Samw 	 */
4405331Samw 	if (netr_gen_password(netr_info->session_key,
4415331Samw 	    netr_info->password, new_password) == SMBAUTH_FAILURE) {
4425331Samw 		return (-1);
4435331Samw 	}
4445331Samw 
4455331Samw 	(void) memcpy(&arg.uas_new_password, &new_password,
4465331Samw 	    NETR_OWF_PASSWORD_SZ);
4475331Samw 
4485331Samw 	(void) mlsvc_rpc_init(&heap);
4495331Samw 	rc = mlsvc_rpc_call(netr_handle->context, opnum, &arg, &heap);
4505331Samw 	if ((rc != 0) || (arg.status != 0)) {
4515331Samw 		mlsvc_rpc_report_status(opnum, arg.status);
4525331Samw 		mlsvc_rpc_free(netr_handle->context, &heap);
4535331Samw 		return (-1);
4545331Samw 	}
4555331Samw 
4565331Samw 	/*
4575331Samw 	 * Check the returned credentials.  The server returns the new
4585331Samw 	 * client credential rather than the new server credentiali,
4595331Samw 	 * as documented elsewhere.
4605331Samw 	 *
4615331Samw 	 * Generate the new seed for the credential chain.  Increment
4625331Samw 	 * the timestamp and add it to the client challenge.  Then we
4635331Samw 	 * need to copy the challenge to the credential field in
4645331Samw 	 * preparation for the next cycle.
4655331Samw 	 */
4665331Samw 	if (netr_validate_chain(netr_info, &arg.auth) == 0) {
4675331Samw 		/*
4685331Samw 		 * Save the new password.
4695331Samw 		 */
4705331Samw 		(void) memcpy(netr_info->password, new_password,
4715331Samw 		    NETR_OWF_PASSWORD_SZ);
4725331Samw 	}
4735331Samw 
4745331Samw 	mlsvc_rpc_free(netr_handle->context, &heap);
4755331Samw 	return (0);
4765331Samw }
4775331Samw 
4785331Samw /*
4795331Samw  * netr_gen_password
4805331Samw  *
4815331Samw  * Generate a new pasword from the old password  and the session key.
4825331Samw  * The algorithm is a two stage hash. The session key is used in the
4835331Samw  * first hash but only part of the session key is used in the second
4845331Samw  * hash.
4855331Samw  *
4865331Samw  * If any difficulties occur using the cryptographic framework, the
4875331Samw  * function returns SMBAUTH_FAILURE.  Otherwise SMBAUTH_SUCCESS is
4885331Samw  * returned.
4895331Samw  */
4905331Samw static int
4915331Samw netr_gen_password(BYTE *session_key, BYTE *old_password, BYTE *new_password)
4925331Samw {
4935331Samw 	unsigned char partial_key[8];
4945331Samw 	int rv;
4955331Samw 
4965331Samw 	rv = smb_auth_DES(new_password, 8, session_key, 8, old_password, 8);
4975331Samw 	if (rv != SMBAUTH_SUCCESS)
4985331Samw 		return (rv);
4995331Samw 
5005331Samw 	bzero(partial_key, 8);
5015331Samw 	partial_key[0] = session_key[7];
5025331Samw 
5035331Samw 	rv = smb_auth_DES(&new_password[8], 8, partial_key, 8,
5045331Samw 	    &old_password[8], 8);
5055331Samw 	return (rv);
5065331Samw }
507