/* * 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); }
/* * Save the host credentials to be used for authenticated IPC. * The credentials are also saved to the original IPC info as * rollback data in case the join domain process fails later. */ void smb_ipc_commit(void) { (void) rw_wrlock(&smb_ipc_lock); (void) smb_getsamaccount(ipc_info.user, SMB_USERNAME_MAXLEN); (void) smb_get_machine_passwd(ipc_info.passwd, SMBAUTH_HASH_SZ); (void) memcpy(&ipc_orig_info, &ipc_info, sizeof (smb_ipc_t)); (void) rw_unlock(&smb_ipc_lock); }
/* * 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)); }
/* * Set up IPC connection credentials. */ void smb_ipc_init(void) { int rc; (void) rw_wrlock(&smb_ipc_lock); bzero(&ipc_info, sizeof (smb_ipc_t)); bzero(&ipc_orig_info, sizeof (smb_ipc_t)); (void) smb_getsamaccount(ipc_info.user, SMB_USERNAME_MAXLEN); rc = smb_get_machine_passwd(ipc_info.passwd, SMBAUTH_HASH_SZ); if (rc != 0) *ipc_info.passwd = 0; (void) rw_unlock(&smb_ipc_lock); }
/* * 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. * * Returns NT status codes. */ void mlsvc_join(smb_joininfo_t *info, smb_joinres_t *res) { static unsigned char zero_hash[SMBAUTH_HASH_SZ]; char machine_name[SMB_SAMACCT_MAXLEN]; char machine_pw[NETR_MACHINE_ACCT_PASSWD_MAX]; unsigned char passwd_hash[SMBAUTH_HASH_SZ]; smb_domainex_t dxi; smb_domain_t *di = &dxi.d_primary; DWORD status; int rc; bzero(&dxi, sizeof (dxi)); /* * Domain join support: AD (Kerberos+LDAP) or MS-RPC? */ boolean_t ads_enabled = smb_config_get_ads_enable(); if (smb_getsamaccount(machine_name, sizeof (machine_name)) != 0) { res->status = NT_STATUS_INVALID_COMPUTER_NAME; return; } (void) smb_gen_random_passwd(machine_pw, sizeof (machine_pw)); /* * Ensure that any previous membership of this domain has * been cleared from the environment before we start. This * will ensure that we don't attempt a NETLOGON_SAMLOGON * when attempting to find the PDC. */ (void) smb_config_setbool(SMB_CI_DOMAIN_MEMB, B_FALSE); if (info->domain_username[0] != '\0') { (void) smb_auth_ntlm_hash(info->domain_passwd, passwd_hash); smb_ipc_set(info->domain_username, passwd_hash); } else { smb_ipc_set(MLSVC_ANON_USER, zero_hash); } /* * Tentatively set the idmap domain to the one we're joining, * so that the DC locator in idmap knows what to look for. * Ditto the SMB server domain. */ if (smb_config_set_idmap_domain(info->domain_name) != 0) syslog(LOG_NOTICE, "Failed to set idmap domain name"); if (smb_config_refresh_idmap() != 0) syslog(LOG_NOTICE, "Failed to refresh idmap service"); /* Clear DNS local (ADS) lookup cache. */ smb_ads_refresh(B_FALSE); /* * Locate a DC for this domain. Intentionally bypass the * ddiscover service here because we're still joining. * This also allows better reporting of any failures. */ status = smb_ads_lookup_msdcs(info->domain_name, &dxi.d_dci); if (status != NT_STATUS_SUCCESS) { syslog(LOG_ERR, "smbd: failed to locate AD server for domain %s (%s)", info->domain_name, xlate_nt_status(status)); goto out; } /* * Found a DC. Report what we found along with the return status * so that admin will know which AD server we were talking to. */ (void) strlcpy(res->dc_name, dxi.d_dci.dc_name, MAXHOSTNAMELEN); syslog(LOG_INFO, "smbd: found AD server %s", dxi.d_dci.dc_name); /* * Domain discovery needs to authenticate with the AD server. * Disconnect any existing connection with the domain controller * to make sure we won't use any prior authentication context * our redirector might have. */ mlsvc_disconnect(dxi.d_dci.dc_name); /* * Get the domain policy info (domain SID etc). * Here too, bypass the smb_ddiscover_service. */ status = smb_ddiscover_main(info->domain_name, &dxi); if (status != NT_STATUS_SUCCESS) { syslog(LOG_ERR, "smbd: failed getting domain info for %s (%s)", info->domain_name, xlate_nt_status(status)); goto out; } /* * After a successful smbd_ddiscover_main() call * we should call smb_domain_save() to update the * data shown by smbadm list. Do that at the end, * only if all goes well with joining the domain. */ /* * Create or update our machine account on the DC. * A non-null user means we do "secure join". */ if (info->domain_username[0] != '\0') { /* * If enabled, try to join using AD Services. */ status = NT_STATUS_UNSUCCESSFUL; if (ads_enabled) { res->join_err = smb_ads_join(di->di_fqname, info->domain_username, info->domain_passwd, machine_pw); if (res->join_err == SMB_ADS_SUCCESS) { status = NT_STATUS_SUCCESS; } } else { syslog(LOG_DEBUG, "use_ads=false (do RPC join)"); /* * If ADS was disabled, join using RPC. */ status = mlsvc_join_rpc(&dxi, info->domain_username, info->domain_passwd, machine_name, machine_pw); } } else { /* * Doing "Unsecure join" (pre-created account) */ 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_dci.dc_name, di->di_nbname, machine_name); if (rc != 0) { syslog(LOG_NOTICE, "Authenticate with " "new/updated machine account: %s", strerror(rc)); res->join_err = SMB_ADJOIN_ERR_AUTH_NETLOGON; status = NT_STATUS_CANT_ACCESS_DOMAIN_INFO; goto out; } /* * Store the new machine account password, and * SMB_CI_DOMAIN_MEMB etc. */ rc = smb_setdomainprops(NULL, dxi.d_dci.dc_name, machine_pw); if (rc != 0) { syslog(LOG_NOTICE, "Failed to save machine account password"); res->join_err = SMB_ADJOIN_ERR_STORE_PROPS; status = NT_STATUS_INTERNAL_DB_ERROR; goto out; } /* * Update idmap config? * Already set the domain_name above. */ /* * Save the SMB server config. Sets: SMB_CI_DOMAIN_* * Should unify SMB vs idmap configs. */ smb_config_setdomaininfo(di->di_nbname, di->di_fqname, di->di_sid, di->di_u.di_dns.ddi_forest, di->di_u.di_dns.ddi_guid); smb_ipc_commit(); smb_domain_save(); status = 0; out: if (status != 0) { /* * Undo the tentative domain settings. */ (void) smb_config_set_idmap_domain(""); (void) smb_config_refresh_idmap(); smb_ipc_rollback(); } /* Avoid leaving cleartext passwords around. */ bzero(machine_pw, sizeof (machine_pw)); bzero(passwd_hash, sizeof (passwd_hash)); res->status = status; }