#include <stdio.h>
#include <stdarg.h>
#include <strings.h>
#include <unistd.h>
#include <netdb.h>
#include <stdlib.h>
#include <sys/time.h>
#include <sys/systm.h>
#include <note.h>
#include <syslog.h>
#include <smbsrv/libsmb.h>
#include <smbsrv/libsmbns.h>
#include <smbsrv/libmlsvc.h>
#include <smb/ntaccess.h>
#include <smbsrv/smbinfo.h>
#include <smbsrv/netrauth.h>
#include <libsmbrdr.h>
#include <lsalib.h>
#include <samlib.h>
#include <mlsvc.h>
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)
{
DWORD status;
status = smb_netlogon_check(server, domain);
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'");
}
return (status);
}
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];
char addrbuf[INET6_ADDRSTRLEN];
smb_domainex_t dxi;
smb_domain_t *di = &dxi.d_primary;
char *container;
char *username;
DWORD status;
int rc;
bzero(&dxi, sizeof (dxi));
if (smb_config_getbool(SMB_CI_DOMAIN_MEMB) == B_TRUE) {
syslog(LOG_INFO, "smbd: join when already joined");
res->join_err = SMB_ADJOIN_ERR_ALREADY_JOINED;
res->status = NT_STATUS_INVALID_SERVER_STATE;
return;
}
if (info->container_name[0] != '\0')
container = info->container_name;
else
container = NULL;
if (info->domain_username[0] != '\0')
username = info->domain_username;
else
username = NULL;
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));
(void) smb_config_setbool(SMB_CI_DOMAIN_MEMB, B_FALSE);
if (username != NULL) {
(void) smb_auth_ntlm_hash(info->domain_passwd, passwd_hash);
smb_ipc_set(username, passwd_hash);
syslog(LOG_INFO, "smbd: joining with user %s", username);
} else {
smb_ipc_set(MLSVC_ANON_USER, zero_hash);
syslog(LOG_INFO, "smbd: joining with anonymous");
}
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");
syslog(LOG_INFO, "smbd: set idmap domain %s", info->domain_name);
smb_ads_refresh(B_FALSE);
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;
}
(void) strlcpy(res->dc_name, dxi.d_dci.dc_name, MAXHOSTNAMELEN);
if (smb_inet_ntop(&dxi.d_dci.dc_addr,
addrbuf, sizeof (addrbuf)) == NULL)
strcpy(addrbuf, "?");
syslog(LOG_INFO, "smbd: found AD server %s (%s)",
dxi.d_dci.dc_name, addrbuf);
mlsvc_disconnect(dxi.d_dci.dc_name);
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;
}
syslog(LOG_INFO, "smbd_join: domain FQN=%s", di->di_fqname);
syslog(LOG_INFO, "smbd_join: domain NBN=%s", di->di_nbname);
syslog(LOG_INFO, "smbd_join: domain SID=%s", di->di_sid);
if (username != NULL) {
status = NT_STATUS_UNSUCCESSFUL;
if (ads_enabled) {
syslog(LOG_INFO, "use_ads=true (LDAP join)");
res->join_err = smb_ads_join(di->di_fqname, container,
info->domain_username, info->domain_passwd,
machine_pw);
if (res->join_err == SMB_ADS_SUCCESS) {
status = NT_STATUS_SUCCESS;
}
} else {
syslog(LOG_INFO, "use_ads=false (RPC join)");
status = mlsvc_join_rpc(&dxi,
info->domain_username,
info->domain_passwd,
machine_name, machine_pw);
}
} else {
status = mlsvc_join_noauth(&dxi, machine_name, machine_pw);
}
if (status != NT_STATUS_SUCCESS)
goto out;
(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;
}
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;
}
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) {
(void) smb_config_set_idmap_domain("");
(void) smb_config_refresh_idmap();
smb_ipc_rollback();
}
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;
_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);
}
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;
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;
}
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);
}
static DWORD
mlsvc_join_noauth(smb_domainex_t *dxi,
char *machine_name, char *machine_pw)
{
char old_pw[SMB_SAMACCT_MAXLEN];
DWORD status;
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);
}
void
mlsvc_disconnect(const char *server)
{
smbrdr_disconnect(server);
}
boolean_t
ndr_is_admin(ndr_xa_t *xa)
{
smb_netuserinfo_t *ctx = xa->pipe->np_user;
return (ctx->ui_flags & SMB_ATF_ADMIN);
}
boolean_t
ndr_is_poweruser(ndr_xa_t *xa)
{
smb_netuserinfo_t *ctx = xa->pipe->np_user;
return ((ctx->ui_flags & SMB_ATF_ADMIN) ||
(ctx->ui_flags & SMB_ATF_POWERUSER));
}
int32_t
ndr_native_os(ndr_xa_t *xa)
{
smb_netuserinfo_t *ctx = xa->pipe->np_user;
return (ctx->ui_native_os);
}