Example #1
0
/*
 * Change a password like NetUserChangePassword(),
 * but where we already know which AD server to use,
 * so we don't request the domain name or search for
 * an AD server for that domain here.
 */
DWORD
netr_change_password(
	char *server,
	char *account,
	char *old_pw_clear,
	char *new_pw_clear)
{
	struct samr_encr_passwd epw;
	struct samr_encr_hash old_hash;
	uint8_t old_nt_hash[SAMR_PWHASH_LEN];
	uint8_t new_nt_hash[SAMR_PWHASH_LEN];
	mlsvc_handle_t handle;
	DWORD rc;

	/*
	 * Create an RPC handle to this server, bound to SAMR.
	 */
	rc = ndr_rpc_bind(&handle, server, "", "", "SAMR");
	if (rc)
		return (RPC_NT_SERVER_UNAVAILABLE);

	/*
	 * Encrypt the new p/w (plus random filler) with the
	 * old password, and send the old p/w encrypted with
	 * the new p/w hash to prove we know the old p/w.
	 * Details:  [MS-SAMR 3.1.5.10.3]
	 */
	(void) smb_auth_ntlm_hash(old_pw_clear, old_nt_hash);
	(void) smb_auth_ntlm_hash(new_pw_clear, new_nt_hash);
	samr_make_encrypted_password(&epw, new_pw_clear, old_nt_hash);

	(void) smb_auth_DES(old_hash.data, SAMR_PWHASH_LEN,
	    new_nt_hash, 14, /* key */
	    old_nt_hash, SAMR_PWHASH_LEN);

	/*
	 * Finally, ready to try the OtW call.
	 */
	rc = samr_change_password(
	    &handle, server, account,
	    &epw, &old_hash);

	/* Avoid leaving cleartext (or equivalent) around. */
	(void) memset(old_nt_hash, 0, sizeof (old_nt_hash));
	(void) memset(new_nt_hash, 0, sizeof (new_nt_hash));

	ndr_rpc_unbind(&handle);
	return (rc);
}
Example #2
0
static int
smb_get_machine_passwd(uint8_t *buf, size_t buflen)
{
	char pwd[SMB_PASSWD_MAXLEN + 1];
	int rc;

	if (buflen < SMBAUTH_HASH_SZ)
		return (-1);

	rc = smb_config_getstr(SMB_CI_MACHINE_PASSWD, pwd, sizeof (pwd));
	if ((rc != SMBD_SMF_OK) || *pwd == '\0')
		return (-1);

	if (smb_auth_ntlm_hash(pwd, buf) != 0)
		return (-1);

	return (rc);
}
Example #3
0
/*
 * smb_pwd_chgpwent
 *
 * Updates the given smb_passwd_t structure with given password and
 * control information.
 */
static int
smb_pwd_chgpwent(smb_passwd_t *smbpw, const char *password, int control)
{
	if (control & SMB_PWC_DISABLE) {
		/* disable the user */
		smbpw->pw_flags |= SMB_PWF_DISABLE;
		(void) strcpy((char *)smbpw->pw_lmhash, SMB_PWD_DISABLE);
		(void) strcpy((char *)smbpw->pw_nthash, SMB_PWD_DISABLE);
		smbpw->pw_flags &= ~(SMB_PWF_LM | SMB_PWF_NT);
		return (SMB_PWE_SUCCESS);
	}

	if ((control & SMB_PWC_ENABLE) && (smbpw->pw_flags & SMB_PWF_DISABLE)) {
		/* enable the user if it's been disabled */
		*smbpw->pw_lmhash = '\0';
		*smbpw->pw_nthash = '\0';
		smbpw->pw_flags &= ~(SMB_PWF_LM | SMB_PWF_NT);
		return (SMB_PWE_SUCCESS);
	}

	/* No password update if account is disabled */
	if (smbpw->pw_flags & SMB_PWF_DISABLE)
		return (SMB_PWE_USER_DISABLE);

	/* This call was just to update the control flags */
	if (password == NULL)
		return (SMB_PWE_SUCCESS);

	if (control & SMB_PWC_NOLM) {
		/* LM hash should not be present */
		smbpw->pw_flags &= ~SMB_PWF_LM;
		*smbpw->pw_lmhash = '\0';
	} else {
		smbpw->pw_flags |= SMB_PWF_LM;
		(void) smb_auth_lm_hash(password, smbpw->pw_lmhash);
	}

	smbpw->pw_flags |= SMB_PWF_NT;
	(void) smb_auth_ntlm_hash(password, smbpw->pw_nthash);
	return (SMB_PWE_SUCCESS);
}
Example #4
0
/*
 * 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;
}