view usr/src/lib/smbsrv/libmlsvc/common/samlib.c @ 10504:ee04788f8605

6861127 Want an RPC function to get the session key 6865745 Consolidate request handlers for query file information requests 6864354 mdb dumps core in an smb dcmd 6841851 [Sparc] Can not make a mapped drive from Windows to any share on CIFS Server 6840783 Support IDMU userid and groupid data for joined domain 6845717 idmapd's scf_value2string unnecessarily complex, incorrect PSARC/2009/398 IDMU Support for idmap 6867994 Rework handling of set and query of file allocation size 6863385 MMC - Unable to use Event Viewer in Windows 2008 - "RPC server is unavailable" 6863390 MMC - Issues with the Services list in Windows 2008 6868501 Unable to map a CIFS share after bfu to cifs nightly build 6870248 Consolidate request handlers for set file information requests 6864074 idmap cannot map computer accounts 6874207 Incorrect access permission check when setting file size 6875658 Need locking around process-wide changes 6877595 eliminate smbd_fs_query() PSARC/2009/375 ABE share property for NFS and SMB 6802736 SMB share support for Access Based Enumeration 6875228 Issue with file copy from a CIFS share to the same share using Windows.
author Keyur Desai <Keyur.Desai@Sun.COM>
date Fri, 11 Sep 2009 17:22:48 -0600
parents 3569b6c7f56c
children fe0545fc3cdd
line wrap: on
line source

/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License (the "License").
 * You may not use this file except in compliance with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */
/*
 * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

/*
 * This module provides the high level interface to the SAM RPC
 * functions.
 */

#include <alloca.h>

#include <smbsrv/libsmb.h>
#include <smbsrv/libsmbrdr.h>
#include <smbsrv/libmlsvc.h>

#include <smbsrv/ntstatus.h>
#include <smbsrv/ntaccess.h>
#include <lsalib.h>
#include <samlib.h>

/*
 * Valid values for the OEM OWF password encryption.
 */
#define	SAM_PASSWORD_516	516
#define	SAM_KEYLEN		16

extern DWORD samr_set_user_info(mlsvc_handle_t *);
static struct samr_sid *sam_get_domain_sid(mlsvc_handle_t *, char *, char *);

/*
 * sam_create_trust_account
 *
 * Create a trust account for this system.
 *
 *	SAMR_AF_WORKSTATION_TRUST_ACCOUNT: servers and workstations.
 *	SAMR_AF_SERVER_TRUST_ACCOUNT: domain controllers.
 *
 * Returns NT status codes.
 */
DWORD
sam_create_trust_account(char *server, char *domain)
{
	char account_name[SMB_SAMACCT_MAXLEN];
	DWORD status;

	if (smb_getsamaccount(account_name, SMB_SAMACCT_MAXLEN) != 0)
		return (NT_STATUS_INTERNAL_ERROR);

	/*
	 * The trust account value here should match
	 * the value that will be used when the user
	 * information is set on this account.
	 */
	status = sam_create_account(server, domain, account_name,
	    SAMR_AF_WORKSTATION_TRUST_ACCOUNT);

	/*
	 * Based on network traces, a Windows 2000 client will
	 * always try to create the computer account first.
	 * If it existed, then check the user permission to join
	 * the domain.
	 */

	if (status == NT_STATUS_USER_EXISTS)
		status = sam_check_user(server, domain, account_name);

	return (status);
}


/*
 * sam_create_account
 *
 * Create the specified domain account in the SAM database on the
 * domain controller.
 *
 * Account flags:
 *		SAMR_AF_NORMAL_ACCOUNT
 *		SAMR_AF_WORKSTATION_TRUST_ACCOUNT
 *		SAMR_AF_SERVER_TRUST_ACCOUNT
 *
 * Returns NT status codes.
 */
DWORD
sam_create_account(char *server, char *domain_name, char *account_name,
    DWORD account_flags)
{
	mlsvc_handle_t samr_handle;
	mlsvc_handle_t domain_handle;
	mlsvc_handle_t user_handle;
	union samr_user_info sui;
	struct samr_sid *sid;
	DWORD rid;
	DWORD status;
	int rc;
	char *user = smbrdr_ipc_get_user();

	rc = samr_open(server, domain_name, user, SAM_CONNECT_CREATE_ACCOUNT,
	    &samr_handle);

	if (rc != 0) {
		status = NT_STATUS_OPEN_FAILED;
		smb_tracef("SamCreateAccount[%s\\%s]: %s",
		    domain_name, account_name, xlate_nt_status(status));
		return (status);
	}

	sid = sam_get_domain_sid(&samr_handle, server, domain_name);

	status = samr_open_domain(&samr_handle,
	    SAM_DOMAIN_CREATE_ACCOUNT, sid, &domain_handle);

	if (status == NT_STATUS_SUCCESS) {
		status = samr_create_user(&domain_handle, account_name,
		    account_flags, &rid, &user_handle);

		if (status == NT_STATUS_SUCCESS) {
			(void) samr_query_user_info(&user_handle,
			    SAMR_QUERY_USER_UNKNOWN16, &sui);

			(void) samr_get_user_pwinfo(&user_handle);
			(void) samr_set_user_info(&user_handle);
			(void) samr_close_handle(&user_handle);
		} else if (status != NT_STATUS_USER_EXISTS) {
			smb_tracef("SamCreateAccount[%s]: %s",
			    account_name, xlate_nt_status(status));
		}

		(void) samr_close_handle(&domain_handle);
	} else {
		smb_tracef("SamCreateAccount[%s]: open domain failed",
		    account_name);
		status = (NT_STATUS_CANT_ACCESS_DOMAIN_INFO);
	}

	(void) samr_close_handle(&samr_handle);
	free(sid);
	return (status);
}


/*
 * sam_remove_trust_account
 *
 * Attempt to remove the workstation trust account for this system.
 * Administrator access is required to perform this operation.
 *
 * Returns NT status codes.
 */
DWORD
sam_remove_trust_account(char *server, char *domain)
{
	char account_name[SMB_SAMACCT_MAXLEN];

	if (smb_getsamaccount(account_name, SMB_SAMACCT_MAXLEN) != 0)
		return (NT_STATUS_INTERNAL_ERROR);

	return (sam_delete_account(server, domain, account_name));
}


/*
 * sam_delete_account
 *
 * Attempt to remove an account from the SAM database on the specified
 * server.
 *
 * Returns NT status codes.
 */
DWORD
sam_delete_account(char *server, char *domain_name, char *account_name)
{
	mlsvc_handle_t samr_handle;
	mlsvc_handle_t domain_handle;
	mlsvc_handle_t user_handle;
	smb_account_t ainfo;
	struct samr_sid *sid;
	DWORD access_mask;
	DWORD status;
	int rc;
	char *user = smbrdr_ipc_get_user();

	rc = samr_open(server, domain_name, user, SAM_LOOKUP_INFORMATION,
	    &samr_handle);

	if (rc != 0)
		return (NT_STATUS_OPEN_FAILED);

	sid = sam_get_domain_sid(&samr_handle, server, domain_name);
	status = samr_open_domain(&samr_handle, SAM_LOOKUP_INFORMATION, sid,
	    &domain_handle);
	free(sid);
	if (status != NT_STATUS_SUCCESS) {
		(void) samr_close_handle(&samr_handle);
		return (status);
	}

	status = samr_lookup_domain_names(&domain_handle, account_name, &ainfo);
	if (status == NT_STATUS_SUCCESS) {
		access_mask = STANDARD_RIGHTS_EXECUTE | DELETE;
		status = samr_open_user(&domain_handle, access_mask,
		    ainfo.a_rid, &user_handle);
		if (status == NT_STATUS_SUCCESS) {
			if (samr_delete_user(&user_handle) != 0)
				(void) samr_close_handle(&user_handle);
		}
	}

	(void) samr_close_handle(&domain_handle);
	(void) samr_close_handle(&samr_handle);
	return (status);
}

/*
 * sam_check_user
 *
 * Check to see if user have permission to access computer account.
 * The user being checked is the specified user for joining the Solaris
 * host to the domain.
 */
DWORD
sam_check_user(char *server, char *domain_name, char *account_name)
{
	mlsvc_handle_t samr_handle;
	mlsvc_handle_t domain_handle;
	mlsvc_handle_t user_handle;
	smb_account_t ainfo;
	struct samr_sid *sid;
	DWORD access_mask;
	DWORD status;
	int rc;
	char *user = smbrdr_ipc_get_user();

	rc = samr_open(server, domain_name, user, SAM_LOOKUP_INFORMATION,
	    &samr_handle);

	if (rc != 0)
		return (NT_STATUS_OPEN_FAILED);

	sid = sam_get_domain_sid(&samr_handle, server, domain_name);
	status = samr_open_domain(&samr_handle, SAM_LOOKUP_INFORMATION, sid,
	    &domain_handle);
	free(sid);
	if (status != NT_STATUS_SUCCESS) {
		(void) samr_close_handle(&samr_handle);
		return (status);
	}

	status = samr_lookup_domain_names(&domain_handle, account_name, &ainfo);
	if (status == NT_STATUS_SUCCESS) {
		/*
		 * Win2000 client uses this access mask.  The
		 * following SAMR user specific rights bits are
		 * set: set password, set attributes, and get
		 * attributes.
		 */

		access_mask = 0xb0;
		status = samr_open_user(&domain_handle,
		    access_mask, ainfo.a_rid, &user_handle);
		if (status == NT_STATUS_SUCCESS)
			(void) samr_close_handle(&user_handle);
	}

	(void) samr_close_handle(&domain_handle);
	(void) samr_close_handle(&samr_handle);
	return (status);
}

/*
 * sam_lookup_name
 *
 * Lookup an account name in the SAM database on the specified domain
 * controller. Provides the account RID on success.
 *
 * Returns NT status codes.
 */
DWORD
sam_lookup_name(char *server, char *domain_name, char *account_name,
    DWORD *rid_ret)
{
	mlsvc_handle_t samr_handle;
	mlsvc_handle_t domain_handle;
	smb_account_t ainfo;
	struct samr_sid *domain_sid;
	int rc;
	DWORD status;
	char *user = smbrdr_ipc_get_user();

	*rid_ret = 0;

	rc = samr_open(server, domain_name, user, SAM_LOOKUP_INFORMATION,
	    &samr_handle);

	if (rc != 0)
		return (NT_STATUS_OPEN_FAILED);

	domain_sid = (struct samr_sid *)samr_lookup_domain(&samr_handle,
	    domain_name);
	if (domain_sid == NULL) {
		(void) samr_close_handle(&samr_handle);
		return (NT_STATUS_NO_SUCH_DOMAIN);
	}

	status = samr_open_domain(&samr_handle, SAM_LOOKUP_INFORMATION,
	    domain_sid, &domain_handle);
	if (status == NT_STATUS_SUCCESS) {
		status = samr_lookup_domain_names(&domain_handle,
		    account_name, &ainfo);
		if (status == NT_STATUS_SUCCESS)
			*rid_ret = ainfo.a_rid;

		(void) samr_close_handle(&domain_handle);
	}

	(void) samr_close_handle(&samr_handle);
	return (status);
}

/*
 * sam_get_local_domains
 *
 * Query a remote server to get the list of local domains that it
 * supports.
 *
 * Returns NT status codes.
 */
DWORD
sam_get_local_domains(char *server, char *domain_name)
{
	mlsvc_handle_t samr_handle;
	DWORD status;
	int rc;
	char *user = smbrdr_ipc_get_user();

	rc = samr_open(server, domain_name, user, SAM_ENUM_LOCAL_DOMAIN,
	    &samr_handle);
	if (rc != 0)
		return (NT_STATUS_OPEN_FAILED);

	status = samr_enum_local_domains(&samr_handle);
	(void) samr_close_handle(&samr_handle);
	return (status);
}

/*
 * sam_oem_password
 *
 * Generate an OEM password.
 */
int
sam_oem_password(oem_password_t *oem_password, unsigned char *new_password,
    unsigned char *old_password)
{
	mts_wchar_t *unicode_password;
	int length;

#ifdef PBSHORTCUT
	assert(sizeof (oem_password_t) == SAM_PASSWORD_516);
#endif /* PBSHORTCUT */

	length = strlen((char const *)new_password);
	unicode_password = alloca((length + 1) * sizeof (mts_wchar_t));

	length = smb_auth_qnd_unicode((unsigned short *)unicode_password,
	    (char *)new_password, length);
	oem_password->length = length;

	(void) memcpy(&oem_password->data[512 - length],
	    unicode_password, length);

	rand_hash((unsigned char *)oem_password, sizeof (oem_password_t),
	    old_password, SAM_KEYLEN);

	return (0);
}

static struct samr_sid *
sam_get_domain_sid(mlsvc_handle_t *samr_handle, char *server, char *domain_name)
{
	struct samr_sid *sid = NULL;
	smb_domain_t domain;

	if (ndr_rpc_server_os(samr_handle) == NATIVE_OS_WIN2000) {
		if (!smb_domain_getinfo(&domain)) {
			if (lsa_query_account_domain_info(server, domain_name,
			    &domain.d_info) != NT_STATUS_SUCCESS)
				return (NULL);
		}

		sid = (struct samr_sid *)smb_sid_fromstr(domain.d_info.di_sid);
	} else {
		sid = (struct samr_sid *)samr_lookup_domain(samr_handle,
		    domain_name);
	}

	return (sid);
}