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); }
/* * 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); }
/* * 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); }
/* * 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); } } }
/* * 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); }
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); }
/* * 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); }
/* * 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); }
/* * 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; }
/* * 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); }
/* * 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); }
/* * 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); } }
/* * 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); }
/* * 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; }
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); }