/* * 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 (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved. * Copyright 2013 Nexenta Systems, Inc. All rights reserved. */ /* * Utility functions to support the RPC interface library. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include extern int netr_open(char *, char *, mlsvc_handle_t *); extern int netr_close(mlsvc_handle_t *); extern DWORD netlogon_auth(char *, mlsvc_handle_t *, DWORD); static DWORD mlsvc_join_rpc(smb_domainex_t *dxi, char *admin_user, char *admin_pw, char *machine_name, char *machine_pw); static DWORD mlsvc_join_noauth(smb_domainex_t *dxi, char *machine_name, char *machine_pw); DWORD mlsvc_netlogon(char *server, char *domain) { mlsvc_handle_t netr_handle; DWORD status; if (netr_open(server, domain, &netr_handle) == 0) { if ((status = netlogon_auth(server, &netr_handle, NETR_FLG_INIT)) != NT_STATUS_SUCCESS) syslog(LOG_NOTICE, "Failed to establish NETLOGON " "credential chain"); (void) netr_close(&netr_handle); } else { syslog(LOG_NOTICE, "Failed to connect to %s " "for domain %s", server, domain); status = NT_STATUS_CANT_ACCESS_DOMAIN_INFO; } return (status); } /* * Join the specified domain. The method varies depending on whether * we're using "secure join" (using an administrative account to join) * or "unsecure join" (using a pre-created machine account). In the * latter case, the machine account is created "by hand" before this * machine attempts to join, and we just change the password from the * (weak) default password for a new machine account to a random one. * * Note that the caller has already done "DC discovery" and passes the * domain info. in the first arg. * * Returns NT status codes. */ DWORD mlsvc_join(smb_domainex_t *dxi, char *admin_user, char *admin_pw) { char machine_name[SMB_SAMACCT_MAXLEN]; char machine_pw[NETR_MACHINE_ACCT_PASSWD_MAX]; unsigned char passwd_hash[SMBAUTH_HASH_SZ]; smb_domain_t *di = &dxi->d_primary; DWORD status; int rc; /* * Domain join support: AD (Kerberos+LDAP) or MS-RPC? * Leave the AD code path disabled until it can be * fixed up so that the SMB server is in complete * control of which AD server we talk to. See: * NX 12427 (Re-enable Kerberos+LDAP with...) */ boolean_t ads_enabled = smb_config_get_ads_enable(); if (smb_getsamaccount(machine_name, sizeof (machine_name)) != 0) return (NT_STATUS_INTERNAL_ERROR); (void) smb_gen_random_passwd(machine_pw, sizeof (machine_pw)); /* * A non-null user means we do "secure join". */ if (admin_user != NULL && admin_user[0] != '\0') { /* * Doing "secure join", so authenticate as the * specified user (with admin. rights). */ (void) smb_auth_ntlm_hash(admin_pw, passwd_hash); smb_ipc_set(admin_user, passwd_hash); /* * If enabled, try to join using AD Services. * The ADS code needs work. Not enabled yet. */ status = NT_STATUS_UNSUCCESSFUL; if (ads_enabled) { smb_adjoin_status_t err; err = smb_ads_join(di->di_fqname, admin_user, admin_pw, machine_pw); if (err != SMB_ADJOIN_SUCCESS) { smb_ads_join_errmsg(err); } else { status = NT_STATUS_SUCCESS; } } /* * If ADS was disabled or gave an error, * fall-back and try to join using RPC. */ if (status != NT_STATUS_SUCCESS) { status = mlsvc_join_rpc(dxi, admin_user, admin_pw, machine_name, machine_pw); } } else { /* * Doing "Unsecure join" (pre-created account) */ bzero(passwd_hash, sizeof (passwd_hash)); smb_ipc_set(MLSVC_ANON_USER, passwd_hash); status = mlsvc_join_noauth(dxi, machine_name, machine_pw); } if (status != NT_STATUS_SUCCESS) goto out; /* * Make sure we can authenticate using the * (new, or updated) machine account. */ (void) smb_auth_ntlm_hash(machine_pw, passwd_hash); smb_ipc_set(machine_name, passwd_hash); rc = smbrdr_logon(dxi->d_dc, di->di_nbname, machine_name); if (rc != 0) { syslog(LOG_NOTICE, "Authenticate with " "new/updated machine account: %s", strerror(rc)); status = NT_STATUS_CANT_ACCESS_DOMAIN_INFO; goto out; } /* * Store the new machine account password. */ rc = smb_setdomainprops(NULL, dxi->d_dc, machine_pw); if (rc != 0) { syslog(LOG_NOTICE, "Failed to save machine account password"); status = NT_STATUS_INTERNAL_DB_ERROR; goto out; } /* * Update idmap config */ if (smb_config_set_idmap_domain(di->di_fqname) != 0) syslog(LOG_NOTICE, "Failed to set idmap domain name"); if (smb_config_refresh_idmap() != 0) syslog(LOG_NOTICE, "Failed to refresh idmap service"); /* * Note: The caller (smbd) saves the "secmode" and * domain info (via smb_config_setdomaininfo) and * and does smb_ipc_commit (or rollback). */ status = 0; out: /* Avoid leaving cleartext passwords around. */ bzero(machine_pw, sizeof (machine_pw)); bzero(passwd_hash, sizeof (passwd_hash)); return (status); } static DWORD mlsvc_join_rpc(smb_domainex_t *dxi, char *admin_user, char *admin_pw, char *machine_name, char *machine_pw) { mlsvc_handle_t samr_handle; mlsvc_handle_t domain_handle; mlsvc_handle_t user_handle; smb_account_t ainfo; char *server = dxi->d_dc; smb_domain_t *di = &dxi->d_primary; DWORD account_flags; DWORD rid; DWORD status; int rc; /* Caller did smb_ipc_set() so we don't need the pw for now. */ _NOTE(ARGUNUSED(admin_pw)); rc = samr_open(server, di->di_nbname, admin_user, MAXIMUM_ALLOWED, &samr_handle); if (rc != 0) { syslog(LOG_NOTICE, "sam_connect to server %s failed", server); return (RPC_NT_SERVER_UNAVAILABLE); } /* have samr_handle */ status = samr_open_domain(&samr_handle, MAXIMUM_ALLOWED, (struct samr_sid *)di->di_binsid, &domain_handle); if (status != NT_STATUS_SUCCESS) goto out_samr_handle; /* have domain_handle */ account_flags = SAMR_AF_WORKSTATION_TRUST_ACCOUNT; status = samr_create_user(&domain_handle, machine_name, account_flags, &rid, &user_handle); if (status == NT_STATUS_USER_EXISTS) { status = samr_lookup_domain_names(&domain_handle, machine_name, &ainfo); if (status != NT_STATUS_SUCCESS) goto out_domain_handle; status = samr_open_user(&domain_handle, MAXIMUM_ALLOWED, ainfo.a_rid, &user_handle); } if (status != NT_STATUS_SUCCESS) { syslog(LOG_NOTICE, "Create or open machine account: %s", xlate_nt_status(status)); goto out_domain_handle; } /* * The account exists, and we have user_handle open * on that account. Set the password and flags. */ status = netr_set_user_password(&user_handle, machine_pw); if (status != NT_STATUS_SUCCESS) { syslog(LOG_NOTICE, "Set machine account password: %s", xlate_nt_status(status)); goto out_user_handle; } account_flags |= SAMR_AF_DONT_EXPIRE_PASSWD; status = netr_set_user_control(&user_handle, account_flags); if (status != NT_STATUS_SUCCESS) { syslog(LOG_NOTICE, "Set machine account control flags: %s", xlate_nt_status(status)); goto out_user_handle; } out_user_handle: (void) samr_close_handle(&user_handle); out_domain_handle: (void) samr_close_handle(&domain_handle); out_samr_handle: (void) samr_close_handle(&samr_handle); return (status); } /* * Doing "Unsecure join" (using a pre-created machine account). * All we need to do is change the password from the default * to a random string. * * Note: this is a work in progres. Nexenta issue 11960 * (allow joining an AD domain using a pre-created computer account) * It turns out that to change the machine account password, * we need to use a different RPC call, performed over the * NetLogon secure channel. (See netr_server_password_set2) */ static DWORD mlsvc_join_noauth(smb_domainex_t *dxi, char *machine_name, char *machine_pw) { char old_pw[SMB_SAMACCT_MAXLEN]; DWORD status; /* * Compose the current (default) password for the * pre-created machine account, which is just the * account name in lower case, truncated to 14 * characters. */ if (smb_gethostname(old_pw, sizeof (old_pw), SMB_CASE_LOWER) != 0) return (NT_STATUS_INTERNAL_ERROR); old_pw[14] = '\0'; status = netr_change_password(dxi->d_dc, machine_name, old_pw, machine_pw); if (status != NT_STATUS_SUCCESS) { syslog(LOG_NOTICE, "Change machine account password: %s", xlate_nt_status(status)); } return (status); } void mlsvc_disconnect(const char *server) { smbrdr_disconnect(server); }