Home | History | Annotate | Download | only in common
      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 2009 Sun Microsystems, Inc.  All rights reserved.
     23  * Use is subject to license terms.
     24  */
     25 
     26 /*
     27  * This module provides the high level interface to the SAM RPC
     28  * functions.
     29  */
     30 
     31 #include <alloca.h>
     32 
     33 #include <smbsrv/libsmb.h>
     34 #include <smbsrv/libmlsvc.h>
     35 
     36 #include <smbsrv/ntstatus.h>
     37 #include <smbsrv/ntaccess.h>
     38 #include <lsalib.h>
     39 #include <samlib.h>
     40 
     41 /*
     42  * Valid values for the OEM OWF password encryption.
     43  */
     44 #define	SAM_PASSWORD_516	516
     45 #define	SAM_KEYLEN		16
     46 
     47 extern DWORD samr_set_user_info(mlsvc_handle_t *);
     48 static struct samr_sid *sam_get_domain_sid(mlsvc_handle_t *, char *, char *);
     49 
     50 /*
     51  * sam_create_trust_account
     52  *
     53  * Create a trust account for this system.
     54  *
     55  *	SAMR_AF_WORKSTATION_TRUST_ACCOUNT: servers and workstations.
     56  *	SAMR_AF_SERVER_TRUST_ACCOUNT: domain controllers.
     57  *
     58  * Returns NT status codes.
     59  */
     60 DWORD
     61 sam_create_trust_account(char *server, char *domain)
     62 {
     63 	char account_name[SMB_SAMACCT_MAXLEN];
     64 	DWORD status;
     65 
     66 	if (smb_getsamaccount(account_name, SMB_SAMACCT_MAXLEN) != 0)
     67 		return (NT_STATUS_INTERNAL_ERROR);
     68 
     69 	/*
     70 	 * The trust account value here should match
     71 	 * the value that will be used when the user
     72 	 * information is set on this account.
     73 	 */
     74 	status = sam_create_account(server, domain, account_name,
     75 	    SAMR_AF_WORKSTATION_TRUST_ACCOUNT);
     76 
     77 	/*
     78 	 * Based on network traces, a Windows 2000 client will
     79 	 * always try to create the computer account first.
     80 	 * If it existed, then check the user permission to join
     81 	 * the domain.
     82 	 */
     83 
     84 	if (status == NT_STATUS_USER_EXISTS)
     85 		status = sam_check_user(server, domain, account_name);
     86 
     87 	return (status);
     88 }
     89 
     90 
     91 /*
     92  * sam_create_account
     93  *
     94  * Create the specified domain account in the SAM database on the
     95  * domain controller.
     96  *
     97  * Account flags:
     98  *		SAMR_AF_NORMAL_ACCOUNT
     99  *		SAMR_AF_WORKSTATION_TRUST_ACCOUNT
    100  *		SAMR_AF_SERVER_TRUST_ACCOUNT
    101  *
    102  * Returns NT status codes.
    103  */
    104 DWORD
    105 sam_create_account(char *server, char *domain_name, char *account_name,
    106     DWORD account_flags)
    107 {
    108 	mlsvc_handle_t samr_handle;
    109 	mlsvc_handle_t domain_handle;
    110 	mlsvc_handle_t user_handle;
    111 	union samr_user_info sui;
    112 	struct samr_sid *sid;
    113 	DWORD rid;
    114 	DWORD status;
    115 	int rc;
    116 	char user[SMB_USERNAME_MAXLEN];
    117 
    118 	smb_ipc_get_user(user, SMB_USERNAME_MAXLEN);
    119 
    120 	rc = samr_open(server, domain_name, user, SAM_CONNECT_CREATE_ACCOUNT,
    121 	    &samr_handle);
    122 
    123 	if (rc != 0) {
    124 		status = NT_STATUS_OPEN_FAILED;
    125 		smb_tracef("SamCreateAccount[%s\\%s]: %s",
    126 		    domain_name, account_name, xlate_nt_status(status));
    127 		return (status);
    128 	}
    129 
    130 	sid = sam_get_domain_sid(&samr_handle, server, domain_name);
    131 
    132 	status = samr_open_domain(&samr_handle,
    133 	    SAM_DOMAIN_CREATE_ACCOUNT, sid, &domain_handle);
    134 
    135 	if (status == NT_STATUS_SUCCESS) {
    136 		status = samr_create_user(&domain_handle, account_name,
    137 		    account_flags, &rid, &user_handle);
    138 
    139 		if (status == NT_STATUS_SUCCESS) {
    140 			(void) samr_query_user_info(&user_handle,
    141 			    SAMR_QUERY_USER_CONTROL_INFO, &sui);
    142 
    143 			(void) samr_get_user_pwinfo(&user_handle);
    144 			(void) samr_set_user_info(&user_handle);
    145 			(void) samr_close_handle(&user_handle);
    146 		} else if (status != NT_STATUS_USER_EXISTS) {
    147 			smb_tracef("SamCreateAccount[%s]: %s",
    148 			    account_name, xlate_nt_status(status));
    149 		}
    150 
    151 		(void) samr_close_handle(&domain_handle);
    152 	} else {
    153 		smb_tracef("SamCreateAccount[%s]: open domain failed",
    154 		    account_name);
    155 		status = (NT_STATUS_CANT_ACCESS_DOMAIN_INFO);
    156 	}
    157 
    158 	(void) samr_close_handle(&samr_handle);
    159 	free(sid);
    160 	return (status);
    161 }
    162 
    163 
    164 /*
    165  * sam_remove_trust_account
    166  *
    167  * Attempt to remove the workstation trust account for this system.
    168  * Administrator access is required to perform this operation.
    169  *
    170  * Returns NT status codes.
    171  */
    172 DWORD
    173 sam_remove_trust_account(char *server, char *domain)
    174 {
    175 	char account_name[SMB_SAMACCT_MAXLEN];
    176 
    177 	if (smb_getsamaccount(account_name, SMB_SAMACCT_MAXLEN) != 0)
    178 		return (NT_STATUS_INTERNAL_ERROR);
    179 
    180 	return (sam_delete_account(server, domain, account_name));
    181 }
    182 
    183 
    184 /*
    185  * sam_delete_account
    186  *
    187  * Attempt to remove an account from the SAM database on the specified
    188  * server.
    189  *
    190  * Returns NT status codes.
    191  */
    192 DWORD
    193 sam_delete_account(char *server, char *domain_name, char *account_name)
    194 {
    195 	mlsvc_handle_t samr_handle;
    196 	mlsvc_handle_t domain_handle;
    197 	mlsvc_handle_t user_handle;
    198 	smb_account_t ainfo;
    199 	struct samr_sid *sid;
    200 	DWORD access_mask;
    201 	DWORD status;
    202 	int rc;
    203 	char user[SMB_USERNAME_MAXLEN];
    204 
    205 	smb_ipc_get_user(user, SMB_USERNAME_MAXLEN);
    206 
    207 	rc = samr_open(server, domain_name, user, SAM_LOOKUP_INFORMATION,
    208 	    &samr_handle);
    209 
    210 	if (rc != 0)
    211 		return (NT_STATUS_OPEN_FAILED);
    212 
    213 	sid = sam_get_domain_sid(&samr_handle, server, domain_name);
    214 	status = samr_open_domain(&samr_handle, SAM_LOOKUP_INFORMATION, sid,
    215 	    &domain_handle);
    216 	free(sid);
    217 	if (status != NT_STATUS_SUCCESS) {
    218 		(void) samr_close_handle(&samr_handle);
    219 		return (status);
    220 	}
    221 
    222 	status = samr_lookup_domain_names(&domain_handle, account_name, &ainfo);
    223 	if (status == NT_STATUS_SUCCESS) {
    224 		access_mask = STANDARD_RIGHTS_EXECUTE | DELETE;
    225 		status = samr_open_user(&domain_handle, access_mask,
    226 		    ainfo.a_rid, &user_handle);
    227 		if (status == NT_STATUS_SUCCESS) {
    228 			if (samr_delete_user(&user_handle) != 0)
    229 				(void) samr_close_handle(&user_handle);
    230 		}
    231 	}
    232 
    233 	(void) samr_close_handle(&domain_handle);
    234 	(void) samr_close_handle(&samr_handle);
    235 	return (status);
    236 }
    237 
    238 /*
    239  * sam_check_user
    240  *
    241  * Check to see if user have permission to access computer account.
    242  * The user being checked is the specified user for joining the Solaris
    243  * host to the domain.
    244  */
    245 DWORD
    246 sam_check_user(char *server, char *domain_name, char *account_name)
    247 {
    248 	mlsvc_handle_t samr_handle;
    249 	mlsvc_handle_t domain_handle;
    250 	mlsvc_handle_t user_handle;
    251 	smb_account_t ainfo;
    252 	struct samr_sid *sid;
    253 	DWORD access_mask;
    254 	DWORD status;
    255 	int rc;
    256 	char user[SMB_USERNAME_MAXLEN];
    257 
    258 	smb_ipc_get_user(user, SMB_USERNAME_MAXLEN);
    259 
    260 	rc = samr_open(server, domain_name, user, SAM_LOOKUP_INFORMATION,
    261 	    &samr_handle);
    262 
    263 	if (rc != 0)
    264 		return (NT_STATUS_OPEN_FAILED);
    265 
    266 	sid = sam_get_domain_sid(&samr_handle, server, domain_name);
    267 	status = samr_open_domain(&samr_handle, SAM_LOOKUP_INFORMATION, sid,
    268 	    &domain_handle);
    269 	free(sid);
    270 	if (status != NT_STATUS_SUCCESS) {
    271 		(void) samr_close_handle(&samr_handle);
    272 		return (status);
    273 	}
    274 
    275 	status = samr_lookup_domain_names(&domain_handle, account_name, &ainfo);
    276 	if (status == NT_STATUS_SUCCESS) {
    277 		/*
    278 		 * Win2000 client uses this access mask.  The
    279 		 * following SAMR user specific rights bits are
    280 		 * set: set password, set attributes, and get
    281 		 * attributes.
    282 		 */
    283 
    284 		access_mask = 0xb0;
    285 		status = samr_open_user(&domain_handle,
    286 		    access_mask, ainfo.a_rid, &user_handle);
    287 		if (status == NT_STATUS_SUCCESS)
    288 			(void) samr_close_handle(&user_handle);
    289 	}
    290 
    291 	(void) samr_close_handle(&domain_handle);
    292 	(void) samr_close_handle(&samr_handle);
    293 	return (status);
    294 }
    295 
    296 /*
    297  * sam_lookup_name
    298  *
    299  * Lookup an account name in the SAM database on the specified domain
    300  * controller. Provides the account RID on success.
    301  *
    302  * Returns NT status codes.
    303  */
    304 DWORD
    305 sam_lookup_name(char *server, char *domain_name, char *account_name,
    306     DWORD *rid_ret)
    307 {
    308 	mlsvc_handle_t samr_handle;
    309 	mlsvc_handle_t domain_handle;
    310 	smb_account_t ainfo;
    311 	struct samr_sid *domain_sid;
    312 	int rc;
    313 	DWORD status;
    314 	char user[SMB_USERNAME_MAXLEN];
    315 
    316 	smb_ipc_get_user(user, SMB_USERNAME_MAXLEN);
    317 
    318 	*rid_ret = 0;
    319 
    320 	rc = samr_open(server, domain_name, user, SAM_LOOKUP_INFORMATION,
    321 	    &samr_handle);
    322 
    323 	if (rc != 0)
    324 		return (NT_STATUS_OPEN_FAILED);
    325 
    326 	domain_sid = (struct samr_sid *)samr_lookup_domain(&samr_handle,
    327 	    domain_name);
    328 	if (domain_sid == NULL) {
    329 		(void) samr_close_handle(&samr_handle);
    330 		return (NT_STATUS_NO_SUCH_DOMAIN);
    331 	}
    332 
    333 	status = samr_open_domain(&samr_handle, SAM_LOOKUP_INFORMATION,
    334 	    domain_sid, &domain_handle);
    335 	if (status == NT_STATUS_SUCCESS) {
    336 		status = samr_lookup_domain_names(&domain_handle,
    337 		    account_name, &ainfo);
    338 		if (status == NT_STATUS_SUCCESS)
    339 			*rid_ret = ainfo.a_rid;
    340 
    341 		(void) samr_close_handle(&domain_handle);
    342 	}
    343 
    344 	(void) samr_close_handle(&samr_handle);
    345 	return (status);
    346 }
    347 
    348 /*
    349  * sam_get_local_domains
    350  *
    351  * Query a remote server to get the list of local domains that it
    352  * supports.
    353  *
    354  * Returns NT status codes.
    355  */
    356 DWORD
    357 sam_get_local_domains(char *server, char *domain_name)
    358 {
    359 	mlsvc_handle_t samr_handle;
    360 	DWORD status;
    361 	int rc;
    362 	char user[SMB_USERNAME_MAXLEN];
    363 
    364 	smb_ipc_get_user(user, SMB_USERNAME_MAXLEN);
    365 
    366 	rc = samr_open(server, domain_name, user, SAM_ENUM_LOCAL_DOMAIN,
    367 	    &samr_handle);
    368 	if (rc != 0)
    369 		return (NT_STATUS_OPEN_FAILED);
    370 
    371 	status = samr_enum_local_domains(&samr_handle);
    372 	(void) samr_close_handle(&samr_handle);
    373 	return (status);
    374 }
    375 
    376 /*
    377  * sam_oem_password
    378  *
    379  * Generate an OEM password.
    380  */
    381 int
    382 sam_oem_password(oem_password_t *oem_password, unsigned char *new_password,
    383     unsigned char *old_password)
    384 {
    385 	smb_wchar_t *unicode_password;
    386 	int length;
    387 
    388 #ifdef PBSHORTCUT
    389 	assert(sizeof (oem_password_t) == SAM_PASSWORD_516);
    390 #endif /* PBSHORTCUT */
    391 
    392 	length = strlen((char const *)new_password);
    393 	unicode_password = alloca((length + 1) * sizeof (smb_wchar_t));
    394 
    395 	length = smb_auth_qnd_unicode((unsigned short *)unicode_password,
    396 	    (char *)new_password, length);
    397 	oem_password->length = length;
    398 
    399 	(void) memcpy(&oem_password->data[512 - length],
    400 	    unicode_password, length);
    401 
    402 	rand_hash((unsigned char *)oem_password, sizeof (oem_password_t),
    403 	    old_password, SAM_KEYLEN);
    404 
    405 	return (0);
    406 }
    407 
    408 static struct samr_sid *
    409 sam_get_domain_sid(mlsvc_handle_t *samr_handle, char *server, char *domain_name)
    410 {
    411 	smb_sid_t *sid = NULL;
    412 	smb_domainex_t domain;
    413 
    414 	if (ndr_rpc_server_os(samr_handle) == NATIVE_OS_WIN2000) {
    415 		if (!smb_domain_getinfo(&domain)) {
    416 			if (lsa_query_account_domain_info(server, domain_name,
    417 			    &domain.d_primary) != NT_STATUS_SUCCESS)
    418 				return (NULL);
    419 		}
    420 
    421 		sid = smb_sid_fromstr(domain.d_primary.di_sid);
    422 	} else {
    423 		sid = samr_lookup_domain(samr_handle, domain_name);
    424 	}
    425 
    426 	return ((struct samr_sid *)sid);
    427 }
    428