Beispiel #1
0
DWORD
mlsvc_netlogon(char *server, char *domain)
{
	mlsvc_handle_t netr_handle;
	DWORD status;

	status = netr_open(server, domain, &netr_handle);
	if (status != 0) {
		syslog(LOG_NOTICE, "Failed to connect to %s "
		    "for domain %s (%s)", server, domain,
		    xlate_nt_status(status));
		return (status);
	}

	status = netlogon_auth(server, &netr_handle, NETR_FLG_INIT);
	if (status != NT_STATUS_SUCCESS) {
		syslog(LOG_NOTICE, "Failed to establish NETLOGON "
		    "credential chain with DC: %s (%s)", server,
		    xlate_nt_status(status));
		syslog(LOG_NOTICE, "The machine account information on the "
		    "domain controller does not match the local storage.");
		syslog(LOG_NOTICE, "To correct this, use 'smbadm join'");
	}
	(void) netr_close(&netr_handle);

	return (status);
}
Beispiel #2
0
/*
 * 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[SMB_USERNAME_MAXLEN];

	smb_ipc_get_user(user, SMB_USERNAME_MAXLEN);

	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_CONTROL_INFO, &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);
}
Beispiel #3
0
/*
 * smbrdr_hdr_process
 *
 * Assuming 'srh->srh_mbuf' contains a response from a Windows client,
 * decodes the 32 bytes SMB header.
 *
 * Buffer overflow typically means that the server has more data than
 * it could fit in the response buffer.  The client can use subsequent
 * SmbReadX requests to obtain the remaining data (KB 193839).
 *
 * Returns:
 *
 *  NT_STATUS_INVALID_NETWORK_RESPONSE	error decoding the header
 *  NT_STATUS_REPLY_MESSAGE_MISMATCH	response doesn't match the request
 *  NT_STATUS_SUCCESS			successful
 *  smb_hdr->status.ntstatus		error returned by server
 */
static DWORD
smbrdr_hdr_process(smbrdr_handle_t *srh, smb_hdr_t *smb_hdr)
{
	int rc;

	rc = smb_decode_nt_hdr(&srh->srh_mbuf, smb_hdr);
	if (rc < SMB_HEADER_LEN) {
		smb_log(smbrdr_log_hdl, LOG_DEBUG,
		    "smbrdr_hdr_process[%d]: invalid header (%d)",
		    srh->srh_cmd, rc);
		return (NT_STATUS_INVALID_NETWORK_RESPONSE);
	}

	switch (NT_SC_VALUE(smb_hdr->status.ntstatus)) {
	case NT_STATUS_SUCCESS:
	case NT_STATUS_BUFFER_OVERFLOW:
		break;

	default:
		smb_log(smbrdr_log_hdl, LOG_DEBUG,
		    "smbrdr_hdr_process[%d]: request failed (%s)",
		    srh->srh_cmd, xlate_nt_status(smb_hdr->status.ntstatus));
		return (smb_hdr->status.ntstatus);
	}

	if (smb_hdr->command != srh->srh_cmd) {
		smb_log(smbrdr_log_hdl, LOG_DEBUG,
		    "smbrdr_hdr_process[%d]: reply mismatch (%d)",
		    srh->srh_cmd, smb_hdr->command);
		return (NT_STATUS_REPLY_MESSAGE_MISMATCH);
	}

	return (NT_STATUS_SUCCESS);
}
Beispiel #4
0
/*
 * smbadm_group_dump_members
 *
 * Dump group members details.
 */
static void
smbadm_group_dump_members(smb_gsid_t *members, int num)
{
	char		sidstr[SMB_SID_STRSZ];
	lsa_account_t	acct;
	int		i;

	if (num == 0) {
		(void) printf(gettext("\tNo members\n"));
		return;
	}

	(void) printf(gettext("\tMembers:\n"));
	for (i = 0; i < num; i++) {
		smb_sid_tostr(members[i].gs_sid, sidstr);

		if (smb_lookup_sid(sidstr, &acct) == 0) {
			if (acct.a_status == NT_STATUS_SUCCESS)
				smbadm_group_show_name(acct.a_domain,
				    acct.a_name);
			else
				(void) printf(gettext("\t\t%s [%s]\n"),
				    sidstr, xlate_nt_status(acct.a_status));
		} else {
			(void) printf(gettext("\t\t%s\n"), sidstr);
		}
	}
}
Beispiel #5
0
/*
 * Workgroups comprise a collection of standalone, independently administered
 * computers that use a common workgroup name.  This is a peer-to-peer model
 * with no formal membership mechanism.
 */
static int
smbadm_join_workgroup(const char *workgroup)
{
	smb_joininfo_t jdi;
	uint32_t status;

	bzero(&jdi, sizeof (jdi));
	jdi.mode = SMB_SECMODE_WORKGRP;
	(void) strlcpy(jdi.domain_name, workgroup, sizeof (jdi.domain_name));
	(void) strtrim(jdi.domain_name, " \t\n");

	if (smb_name_validate_workgroup(jdi.domain_name) != ERROR_SUCCESS) {
		(void) fprintf(stderr, gettext("workgroup name is invalid\n"));
		smbadm_usage(B_FALSE);
	}

	if (!smbadm_join_prompt(jdi.domain_name))
		return (0);

	if ((status = smb_join(&jdi)) != NT_STATUS_SUCCESS) {
		(void) fprintf(stderr, gettext("failed to join %s: %s\n"),
		    jdi.domain_name, xlate_nt_status(status));
		return (1);
	}

	(void) printf(gettext("Successfully joined %s\n"), jdi.domain_name);
	smbadm_restart_service();
	return (0);
}
Beispiel #6
0
void
ndr_rpc_status(mlsvc_handle_t *handle, int opnum, DWORD status)
{
	ndr_service_t *svc;
	char *name = "NDR RPC";
	char *s = "unknown";

	switch (NT_SC_SEVERITY(status)) {
	case NT_STATUS_SEVERITY_SUCCESS:
		s = "success";
		break;
	case NT_STATUS_SEVERITY_INFORMATIONAL:
		s = "info";
		break;
	case NT_STATUS_SEVERITY_WARNING:
		s = "warning";
		break;
	case NT_STATUS_SEVERITY_ERROR:
		s = "error";
		break;
	}

	if (handle) {
		svc = handle->clnt->binding->service;
		name = svc->name;
	}

	smb_tracef("%s[0x%02x]: %s: %s (0x%08x)",
	    name, opnum, s, xlate_nt_status(status), status);
}
Beispiel #7
0
/*
 * 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_dci.dc_name, 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);
}
Beispiel #8
0
/*
 * samr_create_user
 *
 * Create a user in the domain specified by the domain handle. If this
 * call is successful, the server will return the RID for the user and
 * a user handle, which may be used to set or query the SAM.
 *
 * Observed status codes:
 *	NT_STATUS_INVALID_PARAMETER
 *	NT_STATUS_INVALID_ACCOUNT_NAME
 *	NT_STATUS_ACCESS_DENIED
 *	NT_STATUS_USER_EXISTS
 *
 * Returns 0 on success. Otherwise returns an NT status code.
 */
DWORD
samr_create_user(mlsvc_handle_t *domain_handle, char *username,
    DWORD account_flags, DWORD *rid, mlsvc_handle_t *user_handle)
{
	struct samr_CreateUser arg;
	ndr_heap_t *heap;
	int opnum;
	int rc;
	DWORD status = 0;

	if (ndr_is_null_handle(domain_handle) ||
	    username == NULL || rid == NULL) {
		return (NT_STATUS_INVALID_PARAMETER);
	}

	opnum = SAMR_OPNUM_CreateUser;

	bzero(&arg, sizeof (struct samr_CreateUser));
	(void) memcpy(&arg.handle, &domain_handle->handle,
	    sizeof (ndr_hdid_t));

	heap = ndr_rpc_get_heap(domain_handle);
	ndr_heap_mkvcs(heap, username, (ndr_vcstr_t *)&arg.username);

	arg.account_flags = account_flags;
	arg.desired_access = 0xE00500B0;

	rc = ndr_rpc_call(domain_handle, opnum, &arg);
	if (rc != 0) {
		status = NT_STATUS_INVALID_PARAMETER;
	} else if (arg.status != 0) {
		status = NT_SC_VALUE(arg.status);

		if (status != NT_STATUS_USER_EXISTS) {
			smb_tracef("SamrCreateUser[%s]: %s", username,
			    xlate_nt_status(status));
		}
	} else {
		ndr_inherit_handle(user_handle, domain_handle);

		(void) memcpy(&user_handle->handle, &arg.user_handle,
		    sizeof (ndr_hdid_t));

		*rid = arg.rid;

		if (ndr_is_null_handle(user_handle))
			status = NT_STATUS_INVALID_HANDLE;
		else
			status = 0;
	}

	ndr_rpc_release(domain_handle);
	return (status);
}
Beispiel #9
0
/*
 * This is the entry point for authenticating domain users.
 *
 * If we are not going to attempt to authenticate the user,
 * this function must return without updating the status.
 *
 * If the user is successfully authenticated, we build an
 * access token and the status will be NT_STATUS_SUCCESS.
 * Otherwise, the token contents are invalid.
 */
void
smb_logon_domain(smb_logon_t *user_info, smb_token_t *token)
{
	uint32_t	status;
	int		i;

	if (user_info->lg_secmode != SMB_SECMODE_DOMAIN)
		return;

	if (user_info->lg_domain_type == SMB_DOMAIN_LOCAL)
		return;

	for (i = 0; i < NETLOGON_ATTEMPTS; ++i) {
		(void) mutex_lock(&netlogon_mutex);
		while (netlogon_busy && !netlogon_abort)
			(void) cond_wait(&netlogon_cv, &netlogon_mutex);

		if (netlogon_abort) {
			(void) mutex_unlock(&netlogon_mutex);
			user_info->lg_status = NT_STATUS_REQUEST_ABORTED;
			return;
		}

		netlogon_busy = B_TRUE;
		(void) mutex_unlock(&netlogon_mutex);

		status = netlogon_logon(user_info, token);

		(void) mutex_lock(&netlogon_mutex);
		netlogon_busy = B_FALSE;
		if (netlogon_abort)
			status = NT_STATUS_REQUEST_ABORTED;
		(void) cond_signal(&netlogon_cv);
		(void) mutex_unlock(&netlogon_mutex);

		if (status != NT_STATUS_CANT_ACCESS_DOMAIN_INFO)
			break;
	}

	if (status != NT_STATUS_SUCCESS)
		syslog(LOG_INFO, "logon[%s\\%s]: %s", user_info->lg_e_domain,
		    user_info->lg_e_username, xlate_nt_status(status));

	user_info->lg_status = status;
}
Beispiel #10
0
/*
 * smbrdr_rcv
 *
 * Receive a SMB response and decode the packet header.
 *
 * "Implementing CIFS" book, SMB requests always have an even sequence
 * number and replies always have an odd.
 *
 * With the original code, if the SMB Redirector skip the counter increment
 * in the event of any failure during SmbSessionSetupAndX, it causes the
 * domain controller to fail the next SMB request(odd sequence number)
 * with ACCESS_DENIED.
 *
 * Smbrdr module should use the same sequence number (i.e. ssc_seqnum of the
 * SMB Sign context) for generating the MAC signature for all incoming
 * responses per SmbTransact request. Otherwise, the validation will fail.
 * It is now fixed by decrementing the sequence number prior to validating
 * the subsequent responses for a single request.
 *
 * Returns:
 *
 *	status code returned by smbrdr_hdr_process()
 *	NT_STATUS_UNEXPECTED_NETWORK_ERROR	receive failed
 *	NT_STATUS_SUCCESS					successful
 */
DWORD
smbrdr_rcv(smbrdr_handle_t *srh, int is_first_rsp)
{
	smb_hdr_t smb_hdr;
	DWORD status;
	int rc;
	smb_sign_ctx_t *sign_ctx = &srh->srh_session->sign_ctx;

	rc = nb_rcv(srh->srh_session->sock, srh->srh_buf, SMBRDR_REQ_BUFSZ, 0);
	if (rc < 0) {
		smb_mac_inc_seqnum(sign_ctx);
		smb_log(smbrdr_log_hdl, LOG_ERR,
		    "smbrdr_rcv[%d]: receive failed (%d)", srh->srh_cmd, rc);
		return (NT_STATUS_UNEXPECTED_NETWORK_ERROR);
	}

	smb_msgbuf_init(&srh->srh_mbuf, srh->srh_buf, rc, srh->srh_mbflags);

	status = smbrdr_hdr_process(srh, &smb_hdr);
	if (status != NT_STATUS_SUCCESS) {
		smb_mac_inc_seqnum(sign_ctx);
		smb_log(smbrdr_log_hdl, LOG_ERR,
		    "smbrdr_rcv[%d]: failed (%s)", srh->srh_cmd,
		    xlate_nt_status(status));
		return (status);
	}

	if (!is_first_rsp)
		smb_mac_dec_seqnum(sign_ctx);

	if (!smbrdr_sign_chk(sign_ctx,
	    &srh->srh_mbuf, smb_hdr.extra.extra.security_sig)) {
		smb_log(smbrdr_log_hdl, LOG_ERR,
		    "smbrdr_rcv[%d]: bad signature", srh->srh_cmd);
		return (NT_STATUS_INVALID_NETWORK_RESPONSE);
	}

	return (NT_STATUS_SUCCESS);
}
/*
 * smbrdr_transact
 *
 * Send a SMB_COM_TRANSACTION request.
 */
int
smbrdr_transact(int fid, char *out_buf, int out_len, char *in_buf, int in_len)
{
	struct sdb_session *session;
	struct sdb_netuse *netuse;
	struct sdb_ofile *ofile;
	struct sdb_logon *logon;
	smb_transact_rsp_t rsp;
	smbrdr_handle_t srh;
	smb_msgbuf_t *mb;
	DWORD status;
	int rc;
	unsigned short rcv_dcnt;
	int cur_inlen;
	int first_rsp;

	if ((ofile = smbrdr_ofile_get(fid)) == 0)
		return (-1);

	netuse = ofile->netuse;
	session = netuse->session;
	logon = &session->logon;

	status = smbrdr_request_init(&srh, SMB_COM_TRANSACTION,
	    session, logon, netuse);

	if (status != NT_STATUS_SUCCESS) {
		smb_log(smbrdr_log_hdl, LOG_DEBUG, "smbrdr_transact: %s",
		    xlate_nt_status(status));
		smbrdr_ofile_put(ofile);
		return (-1);
	}

	mb = &srh.srh_mbuf;

	rc = prep_smb_transact(mb, ofile->fid, out_buf, out_len, in_len,
	    session->remote_caps & CAP_UNICODE);
	if (rc < 0) {
		smb_log(smbrdr_log_hdl, LOG_DEBUG,
		    "smbrdr_transact: prep failed");
		smbrdr_handle_free(&srh);
		smbrdr_ofile_put(ofile);
		return (rc);
	}

	smbrdr_lock_transport();

	status = smbrdr_send(&srh);
	if (status != NT_STATUS_SUCCESS) {
		smbrdr_unlock_transport();
		smbrdr_handle_free(&srh);
		smbrdr_ofile_put(ofile);
		smb_log(smbrdr_log_hdl, LOG_DEBUG,
		    "smbrdr_transact: send failed");
		return (-1);
	}

	rcv_dcnt = 0;
	cur_inlen = in_len;
	first_rsp = 1;

	do {
		if (smbrdr_rcv(&srh, first_rsp) != NT_STATUS_SUCCESS) {
			smb_log(smbrdr_log_hdl, LOG_DEBUG,
			    "smbrdr_transact: nb_rcv failed");
			rc = -1;
			break;
		}

		rc = decode_smb_transact(mb, in_buf, cur_inlen, &rsp);
		if (rc < 0 || rsp.TotalDataCount > in_len) {
			smb_log(smbrdr_log_hdl, LOG_DEBUG,
			    "smbrdr_transact: decode failed");
			rc = -1;
			break;
		}

		rcv_dcnt += rsp.DataCount;
		cur_inlen -= rsp.DataCount;
		first_rsp = 0;

	} while (rcv_dcnt < rsp.TotalDataCount);

	smbrdr_unlock_transport();
	smbrdr_handle_free(&srh);
	smbrdr_ofile_put(ofile);

	return ((rc < 0) ? rc : rcv_dcnt);
}
Beispiel #12
0
/*
 * smbrdr_exchange
 *
 * Send the SMB packet pointed by the given handle over
 * network. Receive the response and decode the packet header.
 *
 * From "Implementing CIFS" book, SMB requests always have an even sequence
 * number and replies always have an odd.
 *
 * With the original code, if the SMB Redirector skips the counter increment
 * in the event of any failure during SmbSessionSetupAndX, it causes the
 * domain controller to fail the next SMB request(odd sequence number)
 * with ACCESS_DENIED.
 *
 * Returns:
 *
 *	status code returned by smbrdr_hdr_process()
 *	NT_STATUS_INTERNAL_ERROR		crypto framework failure
 *	NT_STATUS_UNEXPECTED_NETWORK_ERROR	send/receive failed
 *	NT_STATUS_SUCCESS			successful
 */
DWORD
smbrdr_exchange(smbrdr_handle_t *srh, smb_hdr_t *smb_hdr, long timeout)
{
	smb_sign_ctx_t *sign_ctx;
	smb_msgbuf_t *mb;
	DWORD status;
	int rc;

	smbrdr_lock_transport();

	mb = &srh->srh_mbuf;
	sign_ctx = &srh->srh_session->sign_ctx;

	if (smbrdr_sign(sign_ctx, mb) != SMBAUTH_SUCCESS) {
		smb_log(smbrdr_log_hdl, LOG_ERR,
		    "smbrdr_exchange[%d]: signing failed", srh->srh_cmd);
		smbrdr_unlock_transport();
		return (NT_STATUS_INTERNAL_ERROR);
	}

	rc = nb_exchange(srh->srh_session->sock,
	    srh->srh_buf, smb_msgbuf_used(mb),
	    srh->srh_buf, SMBRDR_REQ_BUFSZ, timeout);

	if (rc < 0) {
		smb_log(smbrdr_log_hdl, LOG_ERR,
		    "smbrdr_exchange[%d]: failed (%d)", srh->srh_cmd, rc);

		if (srh->srh_cmd != SMB_COM_ECHO) {
			/*
			 * Since SMB echo is used to check the session
			 * status then don't destroy the session if it's
			 * SMB echo.
			 */
			srh->srh_session->state = SDB_SSTATE_STALE;
		}
		smb_mac_inc_seqnum(sign_ctx);
		smbrdr_unlock_transport();
		return (NT_STATUS_UNEXPECTED_NETWORK_ERROR);
	}

	/* initialize for processing response */
	smb_msgbuf_init(mb, srh->srh_buf, rc, srh->srh_mbflags);

	status = smbrdr_hdr_process(srh, smb_hdr);
	if (status != NT_STATUS_SUCCESS) {
		smb_log(smbrdr_log_hdl, LOG_ERR,
		    "smbrdr_exchange[%d]: failed (%s)", srh->srh_cmd,
		    xlate_nt_status(status));
		smb_mac_inc_seqnum(sign_ctx);
		smbrdr_unlock_transport();
		return (status);
	}

	/* Signature validation */
	if (!smbrdr_sign_chk(sign_ctx, mb, smb_hdr->extra.extra.security_sig)) {
		smb_log(smbrdr_log_hdl, LOG_ERR,
		    "smbrdr_exchange[%d]: bad signature", srh->srh_cmd);
		smbrdr_unlock_transport();
		return (NT_STATUS_INVALID_NETWORK_RESPONSE);
	}

	smbrdr_unlock_transport();
	return (NT_STATUS_SUCCESS);
}
Beispiel #13
0
/*
 * Domains comprise a centrally administered group of computers and accounts
 * that share a common security and administration policy and database.
 * Computers must join a domain and become domain members, which requires
 * an administrator level account name.
 *
 * The '+' character is invalid within a username.  We allow the password
 * to be appended to the username using '+' as a scripting convenience.
 */
static int
smbadm_join_domain(const char *domain, const char *username)
{
	smb_joininfo_t jdi;
	uint32_t status;
	char *prompt;
	char *p;
	int len;

	bzero(&jdi, sizeof (jdi));
	jdi.mode = SMB_SECMODE_DOMAIN;
	(void) strlcpy(jdi.domain_name, domain, sizeof (jdi.domain_name));
	(void) strtrim(jdi.domain_name, " \t\n");

	if (smb_name_validate_domain(jdi.domain_name) != ERROR_SUCCESS) {
		(void) fprintf(stderr, gettext("domain name is invalid\n"));
		smbadm_usage(B_FALSE);
	}

	if (!smbadm_join_prompt(jdi.domain_name))
		return (0);

	if ((p = strchr(username, '+')) != NULL) {
		++p;

		len = (int)(p - username);
		if (len > sizeof (jdi.domain_name))
			len = sizeof (jdi.domain_name);

		(void) strlcpy(jdi.domain_username, username, len);
		(void) strlcpy(jdi.domain_passwd, p,
		    sizeof (jdi.domain_passwd));
	} else {
		(void) strlcpy(jdi.domain_username, username,
		    sizeof (jdi.domain_username));
	}

	if (smb_name_validate_account(jdi.domain_username) != ERROR_SUCCESS) {
		(void) fprintf(stderr,
		    gettext("username contains invalid characters\n"));
		smbadm_usage(B_FALSE);
	}

	if (*jdi.domain_passwd == '\0') {
		prompt = gettext("Enter domain password: "******"missing password\n"));
			smbadm_usage(B_FALSE);
		}

		(void) strlcpy(jdi.domain_passwd, p,
		    sizeof (jdi.domain_passwd));
	}

	(void) printf(gettext("Joining %s ... this may take a minute ...\n"),
	    jdi.domain_name);

	status = smb_join(&jdi);

	switch (status) {
	case NT_STATUS_SUCCESS:
		(void) printf(gettext("Successfully joined %s\n"),
		    jdi.domain_name);
		bzero(&jdi, sizeof (jdi));
		smbadm_restart_service();
		return (0);

	case NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND:
		(void) fprintf(stderr,
		    gettext("failed to find any domain controllers for %s\n"),
		    jdi.domain_name);
		bzero(&jdi, sizeof (jdi));
		return (1);

	default:
		(void) fprintf(stderr, gettext("failed to join %s: %s\n"),
		    jdi.domain_name, xlate_nt_status(status));
		(void) fprintf(stderr, gettext("Please refer to the system log"
		    " for more information.\n"));
		bzero(&jdi, sizeof (jdi));
		return (1);
	}
}
Beispiel #14
0
/*
 * This call must be made to initialize an RPC client structure and bind
 * to the remote service before any RPCs can be exchanged with that service.
 *
 * The mlsvc_handle_t is a wrapper that is used to associate an RPC handle
 * with the client context for an instance of the interface.  The handle
 * is zeroed to ensure that it doesn't look like a valid handle -
 * handle content is provided by the remove service.
 *
 * The client points to this top-level handle so that we know when to
 * unbind and teardown the connection.  As each handle is initialized it
 * will inherit a reference to the client context.
 *
 * Returns 0 or an NT_STATUS:
 *	NT_STATUS_BAD_NETWORK_PATH	(get server addr)
 *	NT_STATUS_NETWORK_ACCESS_DENIED	(connect, auth)
 *	NT_STATUS_BAD_NETWORK_NAME	(tcon, open)
 *	NT_STATUS_ACCESS_DENIED		(open pipe)
 *	NT_STATUS_INVALID_PARAMETER	(rpc bind)
 *
 *	NT_STATUS_INTERNAL_ERROR	(bad args etc)
 *	NT_STATUS_NO_MEMORY
 */
DWORD
ndr_rpc_bind(mlsvc_handle_t *handle, char *server, char *domain,
    char *username, const char *service)
{
	struct smb_ctx		*ctx = NULL;
	ndr_client_t		*clnt = NULL;
	ndr_service_t		*svc;
	srvsvc_server_info_t	svinfo;
	DWORD			status;
	int			fd = -1;
	int			rc;

	if (handle == NULL || server == NULL || server[0] == '\0' ||
	    domain == NULL || username == NULL)
		return (NT_STATUS_INTERNAL_ERROR);

	/* In case the service was not registered... */
	if ((svc = ndr_svc_lookup_name(service)) == NULL)
		return (NT_STATUS_INTERNAL_ERROR);

	/*
	 * Set the default based on the assumption that most
	 * servers will be Windows 2000 or later.  This used to
	 * try to get the actual server version, but that RPC
	 * is not necessarily allowed anymore, so don't bother.
	 */
	bzero(&svinfo, sizeof (srvsvc_server_info_t));
	svinfo.sv_platform_id = SV_PLATFORM_ID_NT;
	svinfo.sv_version_major = 5;
	svinfo.sv_version_minor = 0;
	svinfo.sv_type = SV_TYPE_DEFAULT;
	svinfo.sv_os = NATIVE_OS_WIN2000;

	/*
	 * Some callers pass this when they want a NULL session.
	 * Todo: have callers pass an empty string for that.
	 */
	if (strcmp(username, MLSVC_ANON_USER) == 0)
		username = "";

	/*
	 * Setup smbfs library handle, authenticate, connect to
	 * the IPC$ share.  This will reuse an existing connection
	 * if the driver already has one for this combination of
	 * server, user, domain.  It may return any of:
	 *	NT_STATUS_BAD_NETWORK_PATH	(get server addr)
	 *	NT_STATUS_NETWORK_ACCESS_DENIED	(connect, auth)
	 *	NT_STATUS_BAD_NETWORK_NAME	(tcon)
	 */
	status = smbrdr_ctx_new(&ctx, server, domain, username);
	if (status != NT_STATUS_SUCCESS) {
		syslog(LOG_ERR, "ndr_rpc_bind: smbrdr_ctx_new"
		    "(Srv=%s Dom=%s User=%s), %s (0x%x)",
		    server, domain, username,
		    xlate_nt_status(status), status);
		/* Tell the DC Locator this DC failed. */
		smb_ddiscover_bad_dc(server);
		goto errout;
	}

	/*
	 * Open the named pipe.
	 */
	fd = smb_fh_open(ctx, svc->endpoint, O_RDWR);
	if (fd < 0) {
		rc = errno;
		syslog(LOG_DEBUG, "ndr_rpc_bind: "
		    "smb_fh_open (%s) err=%d",
		    svc->endpoint, rc);
		switch (rc) {
		case EACCES:
			status = NT_STATUS_ACCESS_DENIED;
			break;
		default:
			status = NT_STATUS_BAD_NETWORK_NAME;
			break;
		}
		goto errout;
	}

	/*
	 * Setup the RPC client handle.
	 */
	if ((clnt = malloc(sizeof (ndr_client_t))) == NULL) {
		status = NT_STATUS_NO_MEMORY;
		goto errout;
	}
	bzero(clnt, sizeof (ndr_client_t));

	clnt->handle = &handle->handle;
	clnt->xa_init = ndr_xa_init;
	clnt->xa_exchange = ndr_xa_exchange;
	clnt->xa_read = ndr_xa_read;
	clnt->xa_preserve = ndr_xa_preserve;
	clnt->xa_destruct = ndr_xa_destruct;
	clnt->xa_release = ndr_xa_release;
	clnt->xa_private = ctx;
	clnt->xa_fd = fd;

	ndr_svc_binding_pool_init(&clnt->binding_list,
	    clnt->binding_pool, NDR_N_BINDING_POOL);

	if ((clnt->heap = ndr_heap_create()) == NULL) {
		status = NT_STATUS_NO_MEMORY;
		goto errout;
	}

	/*
	 * Fill in the caller's handle.
	 */
	bzero(&handle->handle, sizeof (ndr_hdid_t));
	handle->clnt = clnt;
	bcopy(&svinfo, &handle->svinfo, sizeof (srvsvc_server_info_t));

	/*
	 * Do the OtW RPC bind.
	 */
	rc = ndr_clnt_bind(clnt, service, &clnt->binding);
	switch (rc) {
	case NDR_DRC_FAULT_OUT_OF_MEMORY:
		status = NT_STATUS_NO_MEMORY;
		break;
	case NDR_DRC_FAULT_API_SERVICE_INVALID:	/* not registered */
		status = NT_STATUS_INTERNAL_ERROR;
		break;
	default:
		if (NDR_DRC_IS_FAULT(rc)) {
			status = NT_STATUS_INVALID_PARAMETER;
			break;
		}
		/* FALLTHROUGH */
	case NDR_DRC_OK:
		return (NT_STATUS_SUCCESS);
	}

	syslog(LOG_DEBUG, "ndr_rpc_bind: "
	    "ndr_clnt_bind, %s (0x%x)",
	    xlate_nt_status(status), status);

errout:
	handle->clnt = NULL;
	if (clnt != NULL) {
		ndr_heap_destroy(clnt->heap);
		free(clnt);
	}
	if (ctx != NULL) {
		if (fd != -1)
			(void) smb_fh_close(fd);
		smbrdr_ctx_free(ctx);
	}

	return (status);
}
Beispiel #15
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;
}
Beispiel #16
0
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_dci.dc_name;
	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,
		    "smbd: failed to 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,
		    "smbd: failed to 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);
}