diff options
Diffstat (limited to 'usr/src/lib/smbsrv/libmlsvc/common')
| -rw-r--r-- | usr/src/lib/smbsrv/libmlsvc/common/lsar_clnt.c | 19 | ||||
| -rw-r--r-- | usr/src/lib/smbsrv/libmlsvc/common/mlsvc.h | 2 | ||||
| -rw-r--r-- | usr/src/lib/smbsrv/libmlsvc/common/mlsvc_client.c | 195 | ||||
| -rw-r--r-- | usr/src/lib/smbsrv/libmlsvc/common/mlsvc_domain.c | 25 | ||||
| -rw-r--r-- | usr/src/lib/smbsrv/libmlsvc/common/mlsvc_init.c | 5 | ||||
| -rw-r--r-- | usr/src/lib/smbsrv/libmlsvc/common/mlsvc_util.c | 298 | ||||
| -rw-r--r-- | usr/src/lib/smbsrv/libmlsvc/common/netr_auth.c | 16 | ||||
| -rw-r--r-- | usr/src/lib/smbsrv/libmlsvc/common/samlib.c | 425 | ||||
| -rw-r--r-- | usr/src/lib/smbsrv/libmlsvc/common/samlib.h | 48 | ||||
| -rw-r--r-- | usr/src/lib/smbsrv/libmlsvc/common/samr_clnt.c | 335 | ||||
| -rw-r--r-- | usr/src/lib/smbsrv/libmlsvc/common/samr_svc.c | 11 | ||||
| -rw-r--r-- | usr/src/lib/smbsrv/libmlsvc/common/smbrdr_glue.c | 34 |
12 files changed, 694 insertions, 719 deletions
diff --git a/usr/src/lib/smbsrv/libmlsvc/common/lsar_clnt.c b/usr/src/lib/smbsrv/libmlsvc/common/lsar_clnt.c index df29495ed1..043ee29549 100644 --- a/usr/src/lib/smbsrv/libmlsvc/common/lsar_clnt.c +++ b/usr/src/lib/smbsrv/libmlsvc/common/lsar_clnt.c @@ -21,6 +21,7 @@ /* * Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright 2012 Nexenta Systems, Inc. All rights reserved. */ /* @@ -88,7 +89,8 @@ static void lsar_set_trusted_domains(struct mslsa_EnumTrustedDomainBuf *, * * On success 0 is returned. Otherwise a -ve error code. */ -int lsar_open(char *server, char *domain, char *username, +int +lsar_open(char *server, char *domain, char *username, mlsvc_handle_t *domain_handle) { if (server == NULL || domain == NULL) @@ -109,11 +111,6 @@ int lsar_open(char *server, char *domain, char *username, * function via lsar_open to ensure that the appropriate connection is * in place. * - * I'm not sure if it makes a difference whether we use GENERIC_EXECUTE - * or STANDARD_RIGHTS_EXECUTE. For a long time I used the standard bit - * and then I added the generic bit while working on privileges because - * NT sets that bit. I don't think it matters. - * * Returns 0 on success. Otherwise non-zero to indicate a failure. */ int @@ -141,15 +138,7 @@ lsar_open_policy2(char *server, char *domain, char *username, (void) snprintf((char *)arg.servername, len, "\\\\%s", server); arg.attributes.length = sizeof (struct mslsa_object_attributes); - - if (ndr_rpc_server_os(lsa_handle) == NATIVE_OS_WIN2000) { - arg.desiredAccess = MAXIMUM_ALLOWED; - } else { - arg.desiredAccess = GENERIC_EXECUTE - | STANDARD_RIGHTS_EXECUTE - | POLICY_VIEW_LOCAL_INFORMATION - | POLICY_LOOKUP_NAMES; - } + arg.desiredAccess = MAXIMUM_ALLOWED; if ((rc = ndr_rpc_call(lsa_handle, opnum, &arg)) != 0) { ndr_rpc_unbind(lsa_handle); diff --git a/usr/src/lib/smbsrv/libmlsvc/common/mlsvc.h b/usr/src/lib/smbsrv/libmlsvc/common/mlsvc.h index 8a5d55785b..d693d8825c 100644 --- a/usr/src/lib/smbsrv/libmlsvc/common/mlsvc.h +++ b/usr/src/lib/smbsrv/libmlsvc/common/mlsvc.h @@ -20,6 +20,7 @@ */ /* * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright 2012 Nexenta Systems, Inc. All rights reserved. */ #ifndef _SMBSRV_MLSVC_H @@ -33,6 +34,7 @@ extern "C" { #endif int smb_dclocator_init(void); +void smbrdr_initialize(void); void dssetup_initialize(void); void srvsvc_initialize(void); void wkssvc_initialize(void); diff --git a/usr/src/lib/smbsrv/libmlsvc/common/mlsvc_client.c b/usr/src/lib/smbsrv/libmlsvc/common/mlsvc_client.c index bbeb99630d..0a52a43e65 100644 --- a/usr/src/lib/smbsrv/libmlsvc/common/mlsvc_client.c +++ b/usr/src/lib/smbsrv/libmlsvc/common/mlsvc_client.c @@ -20,8 +20,8 @@ */ /* - * Copyright 2011 Nexenta Systems, Inc. All rights reserved. * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright 2012 Nexenta Systems, Inc. All rights reserved. */ /* @@ -49,27 +49,6 @@ #include <libsmbrdr.h> #include <mlsvc.h> -/* - * Server info cache entry expiration in seconds. - */ -#define NDR_SVINFO_TIMEOUT 1800 - -typedef struct ndr_svinfo { - list_node_t svi_lnd; - time_t svi_tcached; - char svi_server[MAXNAMELEN]; - char svi_domain[MAXNAMELEN]; - srvsvc_server_info_t svi_svinfo; -} ndr_svinfo_t; - -typedef struct ndr_svlist { - list_t svl_list; - mutex_t svl_mtx; - boolean_t svl_init; -} ndr_svlist_t; - -static ndr_svlist_t ndr_svlist; - static int ndr_xa_init(ndr_client_t *, ndr_xa_t *); static int ndr_xa_exchange(ndr_client_t *, ndr_xa_t *); static int ndr_xa_read(ndr_client_t *, ndr_xa_t *); @@ -77,53 +56,6 @@ static void ndr_xa_preserve(ndr_client_t *, ndr_xa_t *); static void ndr_xa_destruct(ndr_client_t *, ndr_xa_t *); static void ndr_xa_release(ndr_client_t *); -static int ndr_svinfo_lookup(char *, char *, srvsvc_server_info_t *); -static boolean_t ndr_svinfo_match(const char *, const char *, const - ndr_svinfo_t *); -static boolean_t ndr_svinfo_expired(ndr_svinfo_t *); - -/* - * Initialize the RPC client interface: create the server info cache. - */ -void -ndr_rpc_init(void) -{ - (void) mutex_lock(&ndr_svlist.svl_mtx); - - if (!ndr_svlist.svl_init) { - list_create(&ndr_svlist.svl_list, sizeof (ndr_svinfo_t), - offsetof(ndr_svinfo_t, svi_lnd)); - ndr_svlist.svl_init = B_TRUE; - } - - (void) mutex_unlock(&ndr_svlist.svl_mtx); -} - -/* - * Terminate the RPC client interface: flush and destroy the server info - * cache. - */ -void -ndr_rpc_fini(void) -{ - ndr_svinfo_t *svi; - - (void) mutex_lock(&ndr_svlist.svl_mtx); - - if (ndr_svlist.svl_init) { - while ((svi = list_head(&ndr_svlist.svl_list)) != NULL) { - list_remove(&ndr_svlist.svl_list, svi); - free(svi->svi_svinfo.sv_name); - free(svi->svi_svinfo.sv_comment); - free(svi); - } - - list_destroy(&ndr_svlist.svl_list); - ndr_svlist.svl_init = B_FALSE; - } - - (void) mutex_unlock(&ndr_svlist.svl_mtx); -} /* * This call must be made to initialize an RPC client structure and bind @@ -158,10 +90,9 @@ ndr_rpc_bind(mlsvc_handle_t *handle, char *server, char *domain, /* * Set the default based on the assumption that most - * servers will be Windows 2000 or later. - * Don't lookup the svinfo if this is a SRVSVC request - * because the SRVSVC is used to get the server info. - * None of the SRVSVC calls depend on the server info. + * 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; @@ -170,8 +101,12 @@ ndr_rpc_bind(mlsvc_handle_t *handle, char *server, char *domain, svinfo.sv_type = SV_TYPE_DEFAULT; svinfo.sv_os = NATIVE_OS_WIN2000; - if (strcasecmp(service, "SRVSVC") != 0) - (void) ndr_svinfo_lookup(server, domain, &svinfo); + /* + * 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 @@ -180,9 +115,10 @@ ndr_rpc_bind(mlsvc_handle_t *handle, char *server, char *domain, * server, user, domain. */ if ((rc = smbrdr_ctx_new(&ctx, server, domain, username)) != 0) { - syslog(LOG_ERR, "ndr_rpc_bind: " - "smbrdr_ctx_new(S=%s, D=%s, U=%s), err=%d", - server, domain, username, rc); + syslog(LOG_ERR, "ndr_rpc_bind: smbrdr_ctx_new" + "(Srv=%s Dom=%s User=%s), %s (0x%x)", + server, domain, username, + xlate_nt_status(rc), rc); goto errout; } @@ -200,7 +136,7 @@ ndr_rpc_bind(mlsvc_handle_t *handle, char *server, char *domain, * Setup the RPC client handle. */ if ((clnt = malloc(sizeof (ndr_client_t))) == NULL) - return (-1); + goto errout; bzero(clnt, sizeof (ndr_client_t)); clnt->handle = &handle->handle; @@ -623,107 +559,6 @@ ndr_xa_release(ndr_client_t *clnt) } } -/* - * Lookup platform, type and version information about a server. - * If the cache doesn't already contain the data, contact the server and - * cache the response before returning the server info to the caller. - * - * We don't provide the name or comment for now, which avoids the need - * to deal with unnecessary memory management. - */ -static int -ndr_svinfo_lookup(char *server, char *domain, srvsvc_server_info_t *svinfo) -{ - static boolean_t timechecked = B_FALSE; - ndr_svinfo_t *svi; - - (void) mutex_lock(&ndr_svlist.svl_mtx); - if (!ndr_svlist.svl_init) - return (-1); - - svi = list_head(&ndr_svlist.svl_list); - while (svi != NULL) { - if (ndr_svinfo_expired(svi)) { - svi = list_head(&ndr_svlist.svl_list); - continue; - } - - if (ndr_svinfo_match(server, domain, svi)) { - bcopy(&svi->svi_svinfo, svinfo, - sizeof (srvsvc_server_info_t)); - svinfo->sv_name = NULL; - svinfo->sv_comment = NULL; - (void) mutex_unlock(&ndr_svlist.svl_mtx); - return (0); - } - - svi = list_next(&ndr_svlist.svl_list, svi); - } - - if ((svi = malloc(sizeof (ndr_svinfo_t))) == NULL) { - (void) mutex_unlock(&ndr_svlist.svl_mtx); - return (-1); - } - - if (srvsvc_net_server_getinfo(server, domain, &svi->svi_svinfo) < 0) { - (void) mutex_unlock(&ndr_svlist.svl_mtx); - free(svi); - return (-1); - } - - (void) time(&svi->svi_tcached); - (void) strlcpy(svi->svi_server, server, MAXNAMELEN); - (void) strlcpy(svi->svi_domain, domain, MAXNAMELEN); - list_insert_tail(&ndr_svlist.svl_list, svi); - bcopy(&svi->svi_svinfo, svinfo, sizeof (srvsvc_server_info_t)); - svinfo->sv_name = NULL; - svinfo->sv_comment = NULL; - - if (!timechecked) { - timechecked = B_TRUE; - ndr_srvsvc_timecheck(server, domain); - } - - (void) mutex_unlock(&ndr_svlist.svl_mtx); - return (0); -} - -static boolean_t -ndr_svinfo_match(const char *server, const char *domain, - const ndr_svinfo_t *svi) -{ - if ((smb_strcasecmp(server, svi->svi_server, 0) == 0) && - (smb_strcasecmp(domain, svi->svi_domain, 0) == 0)) { - return (B_TRUE); - } - - return (B_FALSE); -} - -/* - * If the server info in the cache has expired, discard it and return true. - * Otherwise return false. - * - * This is a private function to support ndr_svinfo_lookup() that assumes - * the list mutex is held. - */ -static boolean_t -ndr_svinfo_expired(ndr_svinfo_t *svi) -{ - time_t tnow; - - (void) time(&tnow); - - if (difftime(tnow, svi->svi_tcached) > NDR_SVINFO_TIMEOUT) { - list_remove(&ndr_svlist.svl_list, svi); - free(svi->svi_svinfo.sv_name); - free(svi->svi_svinfo.sv_comment); - free(svi); - return (B_TRUE); - } - - return (B_FALSE); -} /* * Compare the time here with the remote time on the server diff --git a/usr/src/lib/smbsrv/libmlsvc/common/mlsvc_domain.c b/usr/src/lib/smbsrv/libmlsvc/common/mlsvc_domain.c index 32f87fbbe6..00040f5482 100644 --- a/usr/src/lib/smbsrv/libmlsvc/common/mlsvc_domain.c +++ b/usr/src/lib/smbsrv/libmlsvc/common/mlsvc_domain.c @@ -21,7 +21,7 @@ /* * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved. - * Copyright 2011 Nexenta Systems, Inc. All rights reserved. + * Copyright 2012 Nexenta Systems, Inc. All rights reserved. */ #include <syslog.h> @@ -65,7 +65,7 @@ static pthread_t smb_dclocator_thr; static void *smb_ddiscover_service(void *); static void smb_ddiscover_main(char *, char *); -static boolean_t smb_ddiscover_dns(char *, char *, smb_domainex_t *); +static uint32_t smb_ddiscover_dns(char *, char *, smb_domainex_t *); static boolean_t smb_ddiscover_nbt(char *, char *, smb_domainex_t *); static boolean_t smb_ddiscover_domain_match(char *, char *, uint32_t); static uint32_t smb_ddiscover_qinfo(char *, char *, smb_domainex_t *); @@ -235,26 +235,27 @@ static void smb_ddiscover_main(char *domain, char *server) { smb_domainex_t dxi; - boolean_t discovered; + uint32_t status; bzero(&dxi, sizeof (smb_domainex_t)); if (smb_domain_start_update() != SMB_DOMAIN_SUCCESS) return; - if (SMB_IS_FQDN(domain)) - discovered = smb_ddiscover_dns(domain, server, &dxi); - else - discovered = smb_ddiscover_nbt(domain, server, &dxi); + status = smb_ddiscover_dns(domain, server, &dxi); + if (status != 0 && !SMB_IS_FQDN(domain)) { + if (smb_ddiscover_nbt(domain, server, &dxi)) + status = 0; + } - if (discovered) + if (status == 0) smb_domain_update(&dxi); smb_domain_end_update(); smb_domainex_free(&dxi); - if (discovered) + if (status == 0) smb_domain_save(); } @@ -262,16 +263,16 @@ smb_ddiscover_main(char *domain, char *server) * Discovers a DC for the specified domain via DNS. If a DC is found * primary and trusted domains information will be queried. */ -static boolean_t +static uint32_t smb_ddiscover_dns(char *domain, char *server, smb_domainex_t *dxi) { uint32_t status; if (!smb_ads_lookup_msdcs(domain, server, dxi->d_dc, MAXHOSTNAMELEN)) - return (B_FALSE); + return (NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND); status = smb_ddiscover_qinfo(domain, dxi->d_dc, dxi); - return (status == NT_STATUS_SUCCESS); + return (status); } /* diff --git a/usr/src/lib/smbsrv/libmlsvc/common/mlsvc_init.c b/usr/src/lib/smbsrv/libmlsvc/common/mlsvc_init.c index f1d11a6cfe..0fbe2a6890 100644 --- a/usr/src/lib/smbsrv/libmlsvc/common/mlsvc_init.c +++ b/usr/src/lib/smbsrv/libmlsvc/common/mlsvc_init.c @@ -20,7 +20,7 @@ */ /* * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved. - * Copyright 2011 Nexenta Systems, Inc. All rights reserved. + * Copyright 2012 Nexenta Systems, Inc. All rights reserved. */ #include <sys/errno.h> @@ -60,7 +60,7 @@ mlsvc_init(void) return (rc); smb_quota_init(); - ndr_rpc_init(); + smbrdr_initialize(); srvsvc_initialize(); wkssvc_initialize(); lsarpc_initialize(); @@ -89,7 +89,6 @@ mlsvc_fini(void) svcctl_finalize(); logr_finalize(); netdfs_finalize(); - ndr_rpc_fini(); smb_quota_fini(); } diff --git a/usr/src/lib/smbsrv/libmlsvc/common/mlsvc_util.c b/usr/src/lib/smbsrv/libmlsvc/common/mlsvc_util.c index e9ff66635b..45973ab820 100644 --- a/usr/src/lib/smbsrv/libmlsvc/common/mlsvc_util.c +++ b/usr/src/lib/smbsrv/libmlsvc/common/mlsvc_util.c @@ -19,8 +19,8 @@ * CDDL HEADER END */ /* - * Copyright 2011 Nexenta Systems, Inc. All rights reserved. * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright 2013 Nexenta Systems, Inc. All rights reserved. */ /* @@ -40,19 +40,26 @@ #include <smbsrv/libsmb.h> #include <smbsrv/libsmbns.h> #include <smbsrv/libmlsvc.h> +#include <smbsrv/ntaccess.h> #include <smbsrv/smbinfo.h> #include <libsmbrdr.h> #include <lsalib.h> #include <samlib.h> #include <smbsrv/netrauth.h> -/* Domain join support (using MS-RPC) */ -static boolean_t mlsvc_ntjoin_support = B_FALSE; - extern int netr_open(char *, char *, mlsvc_handle_t *); extern int netr_close(mlsvc_handle_t *); extern DWORD netlogon_auth(char *, mlsvc_handle_t *, DWORD); +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) { @@ -66,79 +73,268 @@ mlsvc_netlogon(char *server, char *domain) "credential chain"); (void) netr_close(&netr_handle); } else { - status = NT_STATUS_OPEN_FAILED; + syslog(LOG_NOTICE, "Failed to connect to %s " + "for domain %s", server, domain); + status = NT_STATUS_CANT_ACCESS_DOMAIN_INFO; } return (status); } /* - * Joins the specified domain by creating a machine account on - * the selected domain controller. + * 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. * - * Disconnect any existing connection with the domain controller. - * This will ensure that no stale connection will be used, it will - * also pickup any configuration changes in either side by trying - * to establish a new connection. + * Note that the caller has already done "DC discovery" and passes the + * domain info. in the first arg. * * Returns NT status codes. */ DWORD -mlsvc_join(smb_domainex_t *dxi, char *user, char *plain_text) +mlsvc_join(smb_domainex_t *dxi, char *admin_user, char *admin_pw) { - int erc; + char machine_name[SMB_SAMACCT_MAXLEN]; + char machine_pw[NETR_MACHINE_ACCT_PASSWD_MAX]; + unsigned char passwd_hash[SMBAUTH_HASH_SZ]; + smb_domain_t *di = &dxi->d_primary; DWORD status; - char machine_passwd[NETR_MACHINE_ACCT_PASSWD_MAX]; - smb_adjoin_status_t err; - smb_domain_t *domain; - - machine_passwd[0] = '\0'; + int rc; - domain = &dxi->d_primary; + /* + * Domain join support: AD (Kerberos+LDAP) or MS-RPC? + * Leave the AD code path disabled until it can be + * fixed up so that the SMB server is in complete + * control of which AD server we talk to. See: + * NX 12427 (Re-enable Kerberos+LDAP with...) + */ + boolean_t ads_enabled = smb_config_get_ads_enable(); - mlsvc_disconnect(dxi->d_dc); + if (smb_getsamaccount(machine_name, sizeof (machine_name)) != 0) + return (NT_STATUS_INTERNAL_ERROR); - erc = smbrdr_logon(dxi->d_dc, domain->di_nbname, user); + (void) smb_gen_random_passwd(machine_pw, sizeof (machine_pw)); - if (erc == AUTH_USER_GRANT) { - if (mlsvc_ntjoin_support == B_FALSE) { + /* + * A non-null user means we do "secure join". + */ + if (admin_user != NULL && admin_user[0] != '\0') { + /* + * Doing "secure join", so authenticate as the + * specified user (with admin. rights). + */ + (void) smb_auth_ntlm_hash(admin_pw, passwd_hash); + smb_ipc_set(admin_user, passwd_hash); - if ((err = smb_ads_join(domain->di_fqname, user, - plain_text, machine_passwd, - sizeof (machine_passwd))) == SMB_ADJOIN_SUCCESS) { - status = NT_STATUS_SUCCESS; - } else { + /* + * If enabled, try to join using AD Services. + * The ADS code needs work. Not enabled yet. + */ + status = NT_STATUS_UNSUCCESSFUL; + if (ads_enabled) { + smb_adjoin_status_t err; + err = smb_ads_join(di->di_fqname, + admin_user, admin_pw, machine_pw); + if (err != SMB_ADJOIN_SUCCESS) { smb_ads_join_errmsg(err); - status = NT_STATUS_UNSUCCESSFUL; - } - } else { - - status = sam_create_trust_account(dxi->d_dc, - domain->di_nbname); - if (status == NT_STATUS_SUCCESS) { - (void) smb_getnetbiosname(machine_passwd, - sizeof (machine_passwd)); - (void) smb_strlwr(machine_passwd); + } else { + status = NT_STATUS_SUCCESS; } } - if (status == NT_STATUS_SUCCESS) { - erc = smb_setdomainprops(NULL, dxi->d_dc, - machine_passwd); - if (erc != 0) { - syslog(LOG_NOTICE, - "Failed to update configuration"); - bzero(machine_passwd, sizeof (machine_passwd)); - return (NT_STATUS_UNSUCCESSFUL); - } - - status = mlsvc_netlogon(dxi->d_dc, domain->di_nbname); + /* + * If ADS was disabled or gave an error, + * fall-back and try to join using RPC. + */ + if (status != NT_STATUS_SUCCESS) { + status = mlsvc_join_rpc(dxi, + admin_user, admin_pw, + machine_name, machine_pw); } + } else { - status = NT_STATUS_LOGON_FAILURE; + /* + * Doing "Unsecure join" (pre-created account) + */ + bzero(passwd_hash, sizeof (passwd_hash)); + smb_ipc_set(MLSVC_ANON_USER, passwd_hash); + + status = mlsvc_join_noauth(dxi, machine_name, machine_pw); } - bzero(machine_passwd, sizeof (machine_passwd)); + 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_dc, di->di_nbname, machine_name); + if (rc != 0) { + syslog(LOG_NOTICE, "Authenticate with " + "new/updated machine account: %s", + strerror(rc)); + status = NT_STATUS_CANT_ACCESS_DOMAIN_INFO; + goto out; + } + + /* + * Store the new machine account password. + */ + rc = smb_setdomainprops(NULL, dxi->d_dc, machine_pw); + if (rc != 0) { + syslog(LOG_NOTICE, + "Failed to save machine account password"); + status = NT_STATUS_INTERNAL_DB_ERROR; + goto out; + } + + /* + * Update idmap config + */ + if (smb_config_set_idmap_domain(di->di_fqname) != 0) + syslog(LOG_NOTICE, "Failed to set idmap domain name"); + if (smb_config_refresh_idmap() != 0) + syslog(LOG_NOTICE, "Failed to refresh idmap service"); + + /* + * Note: The caller (smbd) saves the "secmode" and + * domain info (via smb_config_setdomaininfo) and + * and does smb_ipc_commit (or rollback). + */ + status = 0; + +out: + /* Avoid leaving cleartext passwords around. */ + bzero(machine_pw, sizeof (machine_pw)); + bzero(passwd_hash, sizeof (passwd_hash)); + + return (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_dc; + 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, + "Create or 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, + "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); +} + +/* + * 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_dc, 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); } diff --git a/usr/src/lib/smbsrv/libmlsvc/common/netr_auth.c b/usr/src/lib/smbsrv/libmlsvc/common/netr_auth.c index 96fc566706..4b0224261b 100644 --- a/usr/src/lib/smbsrv/libmlsvc/common/netr_auth.c +++ b/usr/src/lib/smbsrv/libmlsvc/common/netr_auth.c @@ -21,6 +21,7 @@ /* * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright 2012 Nexenta Systems, Inc. All rights reserved. */ /* @@ -104,6 +105,12 @@ netlogon_auth(char *server, mlsvc_handle_t *netr_handle, DWORD flags) if ((rc = netr_server_req_challenge(netr_handle, netr_info)) == 0) { rc = netr_server_authenticate2(netr_handle, netr_info); if (rc == 0) { + /* + * TODO: (later) When joining a domain using a + * pre-created machine account, should do: + * netr_server_password_set(&netr_handle, netr_info); + * Nexenta issue 11960 + */ smb_update_netlogon_seqnum(); netr_info->flags |= NETR_FLG_VALID; @@ -490,7 +497,7 @@ netr_server_password_set(mlsvc_handle_t *netr_handle, netr_info_t *netr_info) arg.servername = (unsigned char *)netr_info->server; arg.account_name = (unsigned char *)account_name; - arg.account_type = NETR_WKSTA_TRUST_ACCOUNT_TYPE; + arg.sec_chan_type = NETR_WKSTA_TRUST_ACCOUNT_TYPE; arg.hostname = (unsigned char *)netr_info->hostname; /* @@ -509,7 +516,7 @@ netr_server_password_set(mlsvc_handle_t *netr_handle, netr_info_t *netr_info) return (-1); } - (void) memcpy(&arg.uas_new_password, &new_password, + (void) memcpy(&arg.owf_password, &new_password, NETR_OWF_PASSWORD_SZ); if (ndr_rpc_call(netr_handle, opnum, &arg) != 0) @@ -569,3 +576,8 @@ netr_gen_password(BYTE *session_key, BYTE *old_password, BYTE *new_password) NETR_DESKEY_LEN, &old_password[8], 8); return (rv); } + +/* + * Todo: need netr_server_password_set2() + * used by "unsecure join". (NX 11960) + */ diff --git a/usr/src/lib/smbsrv/libmlsvc/common/samlib.c b/usr/src/lib/smbsrv/libmlsvc/common/samlib.c index 601283fa5e..1a2233ce39 100644 --- a/usr/src/lib/smbsrv/libmlsvc/common/samlib.c +++ b/usr/src/lib/smbsrv/libmlsvc/common/samlib.c @@ -21,6 +21,7 @@ /* * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright 2012 Nexenta Systems, Inc. All rights reserved. */ /* @@ -28,6 +29,10 @@ * functions. */ +#include <sys/types.h> +#include <sys/isa_defs.h> +#include <sys/byteorder.h> + #include <alloca.h> #include <smbsrv/libsmb.h> @@ -36,128 +41,32 @@ #include <lsalib.h> #include <samlib.h> +#ifdef _LITTLE_ENDIAN +/* little-endian values on little-endian */ +#define htolel(x) ((uint32_t)(x)) +#define letohl(x) ((uint32_t)(x)) +#else /* (BYTE_ORDER == LITTLE_ENDIAN) */ +/* little-endian values on big-endian (swap) */ +#define letohl(x) BSWAP_32(x) +#define htolel(x) BSWAP_32(x) +#endif /* (BYTE_ORDER == LITTLE_ENDIAN) */ + /* * Valid values for the OEM OWF password encryption. */ -#define SAM_PASSWORD_516 516 #define SAM_KEYLEN 16 -extern DWORD samr_set_user_info(mlsvc_handle_t *); -static struct samr_sid *sam_get_domain_sid(mlsvc_handle_t *, char *, char *); - -/* - * 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); -} +static void samr_fill_userpw(struct samr_user_password *, const char *); +static void samr_make_encrypted_password( + struct samr_encr_passwd *epw, + char *new_pw_clear, + uint8_t *crypt_key); /* - * 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. + * Todo: Implement "unjoin" domain, which would use the + * sam_remove_trust_account code below. */ -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); -} - /* * sam_remove_trust_account @@ -194,7 +103,7 @@ sam_delete_account(char *server, char *domain_name, char *account_name) mlsvc_handle_t domain_handle; mlsvc_handle_t user_handle; smb_account_t ainfo; - struct samr_sid *sid; + smb_sid_t *sid; DWORD access_mask; DWORD status; int rc; @@ -204,93 +113,44 @@ sam_delete_account(char *server, char *domain_name, char *account_name) rc = samr_open(server, domain_name, user, SAM_LOOKUP_INFORMATION, &samr_handle); - if (rc != 0) - return (NT_STATUS_OPEN_FAILED); - - sid = sam_get_domain_sid(&samr_handle, server, domain_name); - status = samr_open_domain(&samr_handle, SAM_LOOKUP_INFORMATION, sid, - &domain_handle); - free(sid); - if (status != NT_STATUS_SUCCESS) { - (void) samr_close_handle(&samr_handle); - return (status); - } + return (NT_STATUS_CANT_ACCESS_DOMAIN_INFO); - status = samr_lookup_domain_names(&domain_handle, account_name, &ainfo); - if (status == NT_STATUS_SUCCESS) { - access_mask = STANDARD_RIGHTS_EXECUTE | DELETE; - status = samr_open_user(&domain_handle, access_mask, - ainfo.a_rid, &user_handle); - if (status == NT_STATUS_SUCCESS) { - if (samr_delete_user(&user_handle) != 0) - (void) samr_close_handle(&user_handle); - } + sid = samr_lookup_domain(&samr_handle, domain_name); + if (sid == NULL) { + status = NT_STATUS_CANT_ACCESS_DOMAIN_INFO; + goto out_samr_hdl; } - (void) samr_close_handle(&domain_handle); - (void) samr_close_handle(&samr_handle); - return (status); -} - -/* - * sam_check_user - * - * Check to see if user have permission to access computer account. - * The user being checked is the specified user for joining the Solaris - * host to the domain. - */ -DWORD -sam_check_user(char *server, char *domain_name, char *account_name) -{ - mlsvc_handle_t samr_handle; - mlsvc_handle_t domain_handle; - mlsvc_handle_t user_handle; - smb_account_t ainfo; - struct samr_sid *sid; - DWORD access_mask; - 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_LOOKUP_INFORMATION, - &samr_handle); + status = samr_open_domain(&samr_handle, SAM_LOOKUP_INFORMATION, + (struct samr_sid *)sid, &domain_handle); + if (status != NT_STATUS_SUCCESS) + goto out_sid_ptr; - if (rc != 0) - return (NT_STATUS_OPEN_FAILED); + status = samr_lookup_domain_names(&domain_handle, account_name, &ainfo); + if (status != NT_STATUS_SUCCESS) + goto out_dom_hdl; - sid = sam_get_domain_sid(&samr_handle, server, domain_name); - status = samr_open_domain(&samr_handle, SAM_LOOKUP_INFORMATION, sid, - &domain_handle); - free(sid); - if (status != NT_STATUS_SUCCESS) { - (void) samr_close_handle(&samr_handle); - return (status); - } + access_mask = STANDARD_RIGHTS_EXECUTE | DELETE; + status = samr_open_user(&domain_handle, access_mask, + ainfo.a_rid, &user_handle); + if (status != NT_STATUS_SUCCESS) + goto out_dom_hdl; - status = samr_lookup_domain_names(&domain_handle, account_name, &ainfo); - if (status == NT_STATUS_SUCCESS) { - /* - * Win2000 client uses this access mask. The - * following SAMR user specific rights bits are - * set: set password, set attributes, and get - * attributes. - */ - - access_mask = 0xb0; - status = samr_open_user(&domain_handle, - access_mask, ainfo.a_rid, &user_handle); - if (status == NT_STATUS_SUCCESS) - (void) samr_close_handle(&user_handle); - } + status = samr_delete_user(&user_handle); + (void) samr_close_handle(&user_handle); +out_dom_hdl: (void) samr_close_handle(&domain_handle); +out_sid_ptr: + free(sid); +out_samr_hdl: (void) samr_close_handle(&samr_handle); + return (status); } + /* * sam_lookup_name * @@ -372,54 +232,169 @@ sam_get_local_domains(char *server, char *domain_name) } /* - * sam_oem_password + * Set the account control flags on some account for which we + * have already opened a SAM handle with appropriate rights, + * passed in here as sam_handle, along with the new flags. + */ +DWORD +netr_set_user_control( + mlsvc_handle_t *user_handle, + DWORD UserAccountControl) +{ + struct samr_SetUserInfo16 info; + + info.UserAccountControl = UserAccountControl; + return (samr_set_user_info(user_handle, 16, &info)); +} + +/* + * Set the password on some account, for which we have already + * opened a SAM handle with appropriate rights, passed in here + * as sam_handle, along with the new password as cleartext. * - * Generate an OEM password. + * This builds a struct SAMPR_USER_INTERNAL5_INFORMATION [MS-SAMR] + * containing the new password, encrypted with our session key. */ -int -sam_oem_password(oem_password_t *oem_password, unsigned char *new_password, - unsigned char *old_password) +DWORD +netr_set_user_password( + mlsvc_handle_t *user_handle, + char *new_pw_clear) +{ + unsigned char ssn_key[SMBAUTH_HASH_SZ]; + struct samr_SetUserInfo24 info; + + if (ndr_rpc_get_ssnkey(user_handle, ssn_key, SMBAUTH_HASH_SZ)) + return (NT_STATUS_INTERNAL_ERROR); + + (void) memset(&info, 0, sizeof (info)); + samr_make_encrypted_password(&info.encr_pw, new_pw_clear, ssn_key); + + /* Rather not leave the session key around. */ + (void) memset(ssn_key, 0, sizeof (ssn_key)); + + return (samr_set_user_info(user_handle, 24, &info)); +} + +/* + * 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) { - smb_wchar_t *unicode_password; - int length; + 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); -#ifdef PBSHORTCUT - assert(sizeof (oem_password_t) == SAM_PASSWORD_516); -#endif /* PBSHORTCUT */ + (void) smb_auth_DES(old_hash.data, SAMR_PWHASH_LEN, + new_nt_hash, 14, /* key */ + old_nt_hash, SAMR_PWHASH_LEN); - length = strlen((char const *)new_password); - unicode_password = alloca((length + 1) * sizeof (smb_wchar_t)); + /* + * Finally, ready to try the OtW call. + */ + rc = samr_change_password( + &handle, server, account, + &epw, &old_hash); - length = smb_auth_qnd_unicode((unsigned short *)unicode_password, - (char *)new_password, length); - oem_password->length = length; + /* 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)); - (void) memcpy(&oem_password->data[512 - length], - unicode_password, length); + ndr_rpc_unbind(&handle); + return (rc); +} - rand_hash((unsigned char *)oem_password, sizeof (oem_password_t), - old_password, SAM_KEYLEN); +/* + * Build an encrypted password, as used by samr_set_user_info + * and samr_change_password. Note: This builds the unencrypted + * form in one union arm, and encrypts it in the other union arm. + */ +void +samr_make_encrypted_password( + struct samr_encr_passwd *epw, + char *new_pw_clear, + uint8_t *crypt_key) +{ + union { + struct samr_user_password u; + struct samr_encr_passwd e; + } pwu; + + samr_fill_userpw(&pwu.u, new_pw_clear); - return (0); + (void) smb_auth_RC4(pwu.e.data, sizeof (pwu.e.data), + crypt_key, SAMR_PWHASH_LEN, + pwu.e.data, sizeof (pwu.e.data)); + + (void) memcpy(epw->data, pwu.e.data, sizeof (pwu.e.data)); + (void) memset(pwu.e.data, 0, sizeof (pwu.e.data)); } -static struct samr_sid * -sam_get_domain_sid(mlsvc_handle_t *samr_handle, char *server, char *domain_name) +/* + * This fills in a samr_user_password (a.k.a. SAMPR_USER_PASSWORD + * in the MS Net API) which has the new password "right justified" + * in the buffer, and any space on the left filled with random junk + * to improve the quality of the encryption that is subsequently + * applied to this buffer before it goes over the wire. + */ +static void +samr_fill_userpw(struct samr_user_password *upw, const char *new_pw) { - smb_sid_t *sid = NULL; - smb_domainex_t domain; - - if (ndr_rpc_server_os(samr_handle) == NATIVE_OS_WIN2000) { - if (!smb_domain_getinfo(&domain)) { - if (lsa_query_account_domain_info(server, domain_name, - &domain.d_primary) != NT_STATUS_SUCCESS) - return (NULL); - } - - sid = smb_sid_fromstr(domain.d_primary.di_sid); - } else { - sid = samr_lookup_domain(samr_handle, domain_name); - } + smb_wchar_t *pbuf; + uint32_t pwlen_bytes; + size_t pwlen_wchars; + + /* + * First fill the whole buffer with the random junk. + * (Slightly less random when debugging:) + */ +#ifdef DEBUG + (void) memset(upw->Buffer, '*', sizeof (upw->Buffer)); +#else + randomize((char *)upw->Buffer, sizeof (upw->Buffer)); +#endif + + /* + * Now overwrite the last pwlen characters of + * that buffer with the password, and set the + * length field so the receiving end knows where + * the junk ends and the real password starts. + */ + pwlen_wchars = smb_wcequiv_strlen(new_pw) / 2; + if (pwlen_wchars > SAMR_USER_PWLEN) + pwlen_wchars = SAMR_USER_PWLEN; + pwlen_bytes = pwlen_wchars * 2; + + pbuf = &upw->Buffer[SAMR_USER_PWLEN - pwlen_wchars]; + (void) smb_mbstowcs(pbuf, new_pw, pwlen_wchars); - return ((struct samr_sid *)sid); + /* Yes, this is in Bytes, not wchars. */ + upw->Length = htolel(pwlen_bytes); } diff --git a/usr/src/lib/smbsrv/libmlsvc/common/samlib.h b/usr/src/lib/smbsrv/libmlsvc/common/samlib.h index 3a3932e40f..7e8e1a458d 100644 --- a/usr/src/lib/smbsrv/libmlsvc/common/samlib.h +++ b/usr/src/lib/smbsrv/libmlsvc/common/samlib.h @@ -21,6 +21,7 @@ /* * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. + * Copyright 2012 Nexenta Systems, Inc. All rights reserved. */ #ifndef _SAMLIB_H @@ -71,9 +72,9 @@ DWORD sam_check_user(char *, char *, char *); /* * samr_open.c */ -int samr_open(char *, char *, char *, DWORD, mlsvc_handle_t *); -int samr_connect(char *, char *, char *, DWORD, mlsvc_handle_t *); -int samr_close_handle(mlsvc_handle_t *); +DWORD samr_open(char *, char *, char *, DWORD, mlsvc_handle_t *); +DWORD samr_connect(char *, char *, char *, DWORD, mlsvc_handle_t *); +void samr_close_handle(mlsvc_handle_t *); DWORD samr_open_domain(mlsvc_handle_t *, DWORD, struct samr_sid *, mlsvc_handle_t *); DWORD samr_open_user(mlsvc_handle_t *, DWORD, DWORD, mlsvc_handle_t *); @@ -112,7 +113,7 @@ union samr_user_info { } info9; struct info16 { - DWORD unknown; + DWORD acct_ctrl; } info16; }; @@ -120,16 +121,39 @@ union samr_user_info { smb_sid_t *samr_lookup_domain(mlsvc_handle_t *, char *); DWORD samr_enum_local_domains(mlsvc_handle_t *); uint32_t samr_lookup_domain_names(mlsvc_handle_t *, char *, smb_account_t *); -int samr_query_user_info(mlsvc_handle_t *, WORD, union samr_user_info *); +DWORD samr_query_user_info(mlsvc_handle_t *, WORD, union samr_user_info *); DWORD samr_get_user_pwinfo(mlsvc_handle_t *); -typedef struct oem_password { - BYTE data[512]; - DWORD length; -} oem_password_t; - - -int sam_oem_password(oem_password_t *, unsigned char *, unsigned char *); +DWORD +samr_change_password( + mlsvc_handle_t *handle, + char *server, + char *account, + struct samr_encr_passwd *newpw, + struct samr_encr_hash *oldpw); + +DWORD +samr_set_user_info( + mlsvc_handle_t *user_handle, + int info_level, + void *info_buf); + +DWORD +netr_set_user_control( + mlsvc_handle_t *user_handle, + DWORD UserAccountControl); + +DWORD +netr_set_user_password( + mlsvc_handle_t *user_handle, + char *new_pw_clear); + +DWORD +netr_change_password( + char *server, + char *account, + char *old_password, + char *new_password); #ifdef __cplusplus } diff --git a/usr/src/lib/smbsrv/libmlsvc/common/samr_clnt.c b/usr/src/lib/smbsrv/libmlsvc/common/samr_clnt.c index e19d1dfb03..fc2956f226 100644 --- a/usr/src/lib/smbsrv/libmlsvc/common/samr_clnt.c +++ b/usr/src/lib/smbsrv/libmlsvc/common/samr_clnt.c @@ -21,6 +21,7 @@ /* * Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright 2012 Nexenta Systems, Inc. All rights reserved. */ /* @@ -51,8 +52,6 @@ #include <smbsrv/smb_sid.h> #include <samlib.h> -/*LINTED E_STATIC_UNUSED*/ -static DWORD samr_connect1(char *, char *, char *, DWORD, mlsvc_handle_t *); static DWORD samr_connect2(char *, char *, char *, DWORD, mlsvc_handle_t *); static DWORD samr_connect4(char *, char *, char *, DWORD, mlsvc_handle_t *); static DWORD samr_connect5(char *, char *, char *, DWORD, mlsvc_handle_t *); @@ -62,9 +61,6 @@ typedef DWORD (*samr_connop_t)(char *, char *, char *, DWORD, static int samr_setup_user_info(WORD, struct samr_QueryUserInfo *, union samr_user_info *); -static void samr_set_user_unknowns(struct samr_SetUserInfo23 *); -static void samr_set_user_logon_hours(struct samr_SetUserInfo *); -static int samr_set_user_password(unsigned char *, BYTE *); /* * samr_open @@ -78,17 +74,16 @@ static int samr_set_user_password(unsigned char *, BYTE *); * * On success 0 is returned. Otherwise a -ve error code. */ -int +DWORD samr_open(char *server, char *domain, char *username, DWORD access_mask, mlsvc_handle_t *samr_handle) { smb_domainex_t di; - int rc; + DWORD status; if (server == NULL || domain == NULL) { if (!smb_domain_getinfo(&di)) - return (-1); - + return (NT_STATUS_INTERNAL_ERROR); server = di.d_dc; domain = di.d_primary.di_nbname; } @@ -96,8 +91,10 @@ samr_open(char *server, char *domain, char *username, DWORD access_mask, if (username == NULL) username = MLSVC_ANON_USER; - rc = samr_connect(server, domain, username, access_mask, samr_handle); - return (rc); + status = samr_connect(server, domain, username, access_mask, + samr_handle); + + return (status); } @@ -116,7 +113,7 @@ samr_open(char *server, char *domain, char *username, DWORD access_mask, * something other than an RPC protocol error. We don't use the original * connect call because all supported servers should support SamrConnect2. */ -int +DWORD samr_connect(char *server, char *domain, char *username, DWORD access_mask, mlsvc_handle_t *samr_handle) { @@ -130,60 +127,19 @@ samr_connect(char *server, char *domain, char *username, DWORD access_mask, DWORD status; int i; - if (ndr_rpc_bind(samr_handle, server, domain, username, "SAMR") < 0) - return (-1); + status = ndr_rpc_bind(samr_handle, server, domain, username, "SAMR"); + if (status) + return (status); for (i = 0; i < n_op; ++i) { status = (*samr_connop[i])(server, domain, username, access_mask, samr_handle); if (status == NT_STATUS_SUCCESS) - return (0); + return (status); } ndr_rpc_unbind(samr_handle); - return (-1); -} - -/* - * samr_connect1 - * - * Original SAMR connect call; probably used on Windows NT 3.51. - * Windows 95 uses this call with the srvmgr tools update. - * Servername appears to be a dword rather than a string. - * The first word contains '\' and the second word contains 0x001, - * (which is probably uninitialized junk: 0x0001005c. - */ -/*ARGSUSED*/ -static DWORD -samr_connect1(char *server, char *domain, char *username, DWORD access_mask, - mlsvc_handle_t *samr_handle) -{ - struct samr_Connect arg; - int opnum; - DWORD status; - - bzero(&arg, sizeof (struct samr_Connect)); - opnum = SAMR_OPNUM_Connect; - status = NT_STATUS_SUCCESS; - - arg.servername = ndr_rpc_malloc(samr_handle, sizeof (DWORD)); - *(arg.servername) = 0x0001005c; - arg.access_mask = access_mask; - - if (ndr_rpc_call(samr_handle, opnum, &arg) != 0) { - status = NT_STATUS_UNSUCCESSFUL; - } else if (arg.status != 0) { - status = NT_SC_VALUE(arg.status); - } else { - (void) memcpy(&samr_handle->handle, &arg.handle, - sizeof (ndr_hdid_t)); - - if (ndr_is_null_handle(samr_handle)) - status = NT_STATUS_INVALID_HANDLE; - } - - ndr_rpc_release(samr_handle); return (status); } @@ -293,23 +249,14 @@ samr_connect5(char *server, char *domain, char *username, DWORD access_mask, int len; int opnum; DWORD status; - smb_domainex_t dinfo; bzero(&arg, sizeof (struct samr_Connect5)); opnum = SAMR_OPNUM_Connect5; status = NT_STATUS_SUCCESS; - if (!smb_domain_getinfo(&dinfo)) - return (NT_STATUS_CANT_ACCESS_DOMAIN_INFO); - - len = strlen(server) + strlen(dinfo.d_primary.di_fqname) + 4; + len = strlen(server) + 4; arg.servername = ndr_rpc_malloc(samr_handle, len); - - if (*dinfo.d_primary.di_fqname != '\0') - (void) snprintf((char *)arg.servername, len, "\\\\%s.%s", - server, dinfo.d_primary.di_fqname); - else - (void) snprintf((char *)arg.servername, len, "\\\\%s", server); + (void) snprintf((char *)arg.servername, len, "\\\\%s", server); arg.access_mask = SAM_ENUM_LOCAL_DOMAIN; arg.unknown2_00000001 = 0x00000001; @@ -342,14 +289,14 @@ samr_connect5(char *server, char *domain, char *username, DWORD access_mask, * If the handle being closed is the top level connect handle, we unbind. * Then we zero out the handle to invalidate it. */ -int +void samr_close_handle(mlsvc_handle_t *samr_handle) { struct samr_CloseHandle arg; int opnum; if (ndr_is_null_handle(samr_handle)) - return (-1); + return; opnum = SAMR_OPNUM_CloseHandle; bzero(&arg, sizeof (struct samr_CloseHandle)); @@ -362,7 +309,6 @@ samr_close_handle(mlsvc_handle_t *samr_handle) ndr_rpc_unbind(samr_handle); bzero(samr_handle, sizeof (mlsvc_handle_t)); - return (0); } /* @@ -591,8 +537,8 @@ samr_create_user(mlsvc_handle_t *domain_handle, char *username, status = NT_SC_VALUE(arg.status); if (status != NT_STATUS_USER_EXISTS) { - smb_tracef("SamrCreateUser[%s]: %s", username, - xlate_nt_status(status)); + smb_tracef("SamrCreateUser[%s]: %s", + username, xlate_nt_status(status)); } } else { ndr_inherit_handle(user_handle, domain_handle); @@ -637,8 +583,7 @@ samr_lookup_domain(mlsvc_handle_t *samr_handle, char *domain_name) sizeof (samr_handle_t)); length = smb_wcequiv_strlen(domain_name); - if (ndr_rpc_server_os(samr_handle) == NATIVE_OS_WIN2000) - length += sizeof (smb_wchar_t); + length += sizeof (smb_wchar_t); arg.domain_name.length = length; arg.domain_name.allosize = length; @@ -727,8 +672,7 @@ samr_lookup_domain_names(mlsvc_handle_t *domain_handle, char *name, arg.total = 1; length = smb_wcequiv_strlen(name); - if (ndr_rpc_server_os(domain_handle) == NATIVE_OS_WIN2000) - length += sizeof (smb_wchar_t); + length += sizeof (smb_wchar_t); arg.name.length = length; arg.name.allosize = length; @@ -760,18 +704,17 @@ samr_lookup_domain_names(mlsvc_handle_t *domain_handle, char *name, * Query information on a specific user. The handle must be a valid * user handle obtained via samr_open_user. * - * Returns 0 on success, otherwise returns -ve error code. + * Returns 0 on success, otherwise returns NT status code. */ -int +DWORD samr_query_user_info(mlsvc_handle_t *user_handle, WORD switch_value, union samr_user_info *user_info) { struct samr_QueryUserInfo arg; int opnum; - int rc; if (ndr_is_null_handle(user_handle) || user_info == 0) - return (-1); + return (NT_STATUS_INTERNAL_ERROR); opnum = SAMR_OPNUM_QueryUserInfo; bzero(&arg, sizeof (struct samr_QueryUserInfo)); @@ -780,18 +723,13 @@ samr_query_user_info(mlsvc_handle_t *user_handle, WORD switch_value, sizeof (samr_handle_t)); arg.switch_value = switch_value; - if (ndr_rpc_call(user_handle, opnum, &arg) != 0) { - ndr_rpc_release(user_handle); - return (-1); - } + if (ndr_rpc_call(user_handle, opnum, &arg) != 0) + arg.status = RPC_NT_CALL_FAILED; - if (arg.status != 0) - rc = -1; - else - rc = samr_setup_user_info(switch_value, &arg, user_info); + if (arg.status == 0) + (void) samr_setup_user_info(switch_value, &arg, user_info); - ndr_rpc_release(user_handle); - return (rc); + return (arg.status); } /* @@ -846,6 +784,8 @@ samr_setup_user_info(WORD switch_value, return (0); case 16: + user_info->info16.acct_ctrl = + arg->ru.info16.UserAccountControl; return (0); default: @@ -945,6 +885,10 @@ samr_get_user_pwinfo(mlsvc_handle_t *user_handle) return (status); } +DECL_FIXUP_STRUCT(samr_SetUserInfo_u); +DECL_FIXUP_STRUCT(samr_SetUserInfo_s); +DECL_FIXUP_STRUCT(samr_SetUserInfo); + /* * samr_set_user_info * @@ -952,138 +896,129 @@ samr_get_user_pwinfo(mlsvc_handle_t *user_handle) * NT status codes observed so far: * NT_STATUS_WRONG_PASSWORD */ -/*ARGSUSED*/ DWORD -samr_set_user_info(mlsvc_handle_t *user_handle) +samr_set_user_info( + mlsvc_handle_t *user_handle, + int info_level, + void *info_buf) { - unsigned char ssn_key[SMBAUTH_SESSION_KEY_SZ]; struct samr_SetUserInfo arg; + uint16_t usize, tsize; int opnum; - DWORD status = 0; if (ndr_is_null_handle(user_handle)) - return (NT_STATUS_INVALID_PARAMETER); + return (NT_STATUS_INTERNAL_ERROR); - if (ndr_rpc_get_ssnkey(user_handle, ssn_key, sizeof (ssn_key))) - return (NT_STATUS_INVALID_PARAMETER); + /* + * Only support a few levels + * MS-SAMR: UserInternal4Information + */ + switch (info_level) { + case 16: /* samr_SetUserInfo16 */ + usize = sizeof (struct samr_SetUserInfo16); + break; + case 21: /* samr_SetUserInfo21 */ + usize = sizeof (struct samr_SetUserInfo21); + break; + case 23: /* samr_SetUserInfo23 */ + usize = sizeof (struct samr_SetUserInfo23); + break; + case 24: /* samr_SetUserInfo24 */ + usize = sizeof (struct samr_SetUserInfo24); + break; + default: + return (NT_STATUS_INVALID_LEVEL); + } + + /* + * OK, now this gets really ugly, because + * ndrgen doesn't do unions correctly. + */ + FIXUP_PDU_SIZE(samr_SetUserInfo_u, usize); + tsize = usize + (2 * sizeof (WORD)); + FIXUP_PDU_SIZE(samr_SetUserInfo_s, tsize); + tsize += sizeof (ndr_request_hdr_t) + sizeof (DWORD); + FIXUP_PDU_SIZE(samr_SetUserInfo, tsize); opnum = SAMR_OPNUM_SetUserInfo; - bzero(&arg, sizeof (struct samr_SetUserInfo)); + bzero(&arg, sizeof (arg)); (void) memcpy(&arg.user_handle, &user_handle->handle, sizeof (samr_handle_t)); + arg.info.info_level = info_level; + arg.info.switch_value = info_level; + (void) memcpy(&arg.info.ru, info_buf, usize); - arg.info.index = SAMR_SET_USER_INFO_23; - arg.info.switch_value = SAMR_SET_USER_INFO_23; - - samr_set_user_unknowns(&arg.info.ru.info23); - samr_set_user_logon_hours(&arg); - - if (samr_set_user_password(ssn_key, arg.info.ru.info23.password) < 0) - status = NT_STATUS_INTERNAL_ERROR; - - if (ndr_rpc_call(user_handle, opnum, &arg) != 0) { - status = NT_STATUS_INVALID_PARAMETER; - } else if (arg.status != 0) { + if (ndr_rpc_call(user_handle, opnum, &arg) != 0) + arg.status = RPC_NT_CALL_FAILED; + else if (arg.status != 0) ndr_rpc_status(user_handle, opnum, arg.status); - status = NT_SC_VALUE(arg.status); - } ndr_rpc_release(user_handle); - return (status); -} - -static void -samr_set_user_unknowns(struct samr_SetUserInfo23 *info) -{ - bzero(info, sizeof (struct samr_SetUserInfo23)); - - info->sd.length = 0; - info->sd.data = 0; - info->user_rid = 0; - info->group_rid = DOMAIN_GROUP_RID_USERS; - - /* - * The trust account value used here should probably - * match the one used to create the trust account. - */ - info->acct_info = SAMR_AF_WORKSTATION_TRUST_ACCOUNT; - info->flags = 0x09F827FA; + return (arg.status); } /* - * samr_set_user_logon_hours - * - * SamrSetUserInfo appears to contain some logon hours information, which - * looks like a varying, conformant array. The top level contains a value - * (units), which probably indicates the how to interpret the array. The - * array definition looks like it contains a maximum size, an initial - * offset and a bit length (units/8), followed by the bitmap. - * - * (info) - * +-------+ - * | units | - * +-------+ (hours) - * | hours |-->+-----------+ - * +-------+ | max_is | - * +-----------+ - * | first_is | - * +-----------+ - * | length_is | - * +------------------------+ - * | bitmap[length_is] | - * +---------+--------------+ - * - * 10080 minutes/week => 10080/8 = 1260 (0x04EC) bytes. - * 168 hours/week => 168/8 = 21 (0xA8) bytes. - * In the netmon examples seen so far, all bits are set to 1, i.e. - * an array containing 0xff. This is probably the default setting. - * - * ndrgen has a problem with complex [size_is] statements (length/8). - * So, for now, we fake it using two separate components. + * Client side wrapper for SamrUnicodeChangePasswordUser2 + * [MS-SAMR 3.1.5.10.3] */ -static void -samr_set_user_logon_hours(struct samr_SetUserInfo *sui) -{ - sui->logon_hours.size = SAMR_HOURS_MAX_SIZE; - sui->logon_hours.first = 0; - sui->logon_hours.length = SAMR_SET_USER_HOURS_SZ; - (void) memset(sui->logon_hours.bitmap, 0xFF, SAMR_SET_USER_HOURS_SZ); - - sui->info.ru.info23.logon_info.units = SAMR_HOURS_PER_WEEK; - sui->info.ru.info23.logon_info.hours = - (DWORD)(uintptr_t)sui->logon_hours.bitmap; -} -/* - * samr_set_user_password - * - * Set the initial password for the user. - * - * Returns 0 if everything goes well, -1 if there is trouble generating a - * key. - */ -static int -samr_set_user_password(unsigned char *nt_key, BYTE *oem_password) +DWORD +samr_change_password( + mlsvc_handle_t *handle, + char *server, + char *account, + struct samr_encr_passwd *newpw, + struct samr_encr_hash *oldpw) { - char hostname[NETBIOS_NAME_SZ]; + static struct samr_encr_passwd zero_newpw; + static struct samr_encr_hash zero_oldpw; + struct samr_ChangePasswordUser2 arg; + int opnum = SAMR_OPNUM_ChangePasswordUser2; + char *slashserver; + int len; - randomize((char *)oem_password, SAMR_SET_USER_DATA_SZ); + (void) memset(&arg, 0, sizeof (arg)); - /* - * The new password is going to be the NetBIOS name of the system - * in lower case. - */ - if (smb_getnetbiosname(hostname, sizeof (hostname)) != 0) - return (-1); + /* Need server name with slashes */ + len = 2 + strlen(server) + 1; + slashserver = ndr_rpc_malloc(handle, len); + if (slashserver == NULL) + return (NT_STATUS_NO_MEMORY); + (void) snprintf(slashserver, len, "\\\\%s", server); - (void) smb_strlwr(hostname); + arg.servername = ndr_rpc_malloc(handle, sizeof (samr_string_t)); + if (arg.servername == NULL) + return (NT_STATUS_NO_MEMORY); + len = smb_wcequiv_strlen(slashserver); + if (len < 1) + return (NT_STATUS_INVALID_PARAMETER); + len += 2; /* the WC null */ + arg.servername->length = len; + arg.servername->allosize = len; + arg.servername->str = (uint8_t *)slashserver; + + arg.username = ndr_rpc_malloc(handle, sizeof (samr_string_t)); + if (arg.username == NULL) + return (NT_STATUS_NO_MEMORY); + len = smb_wcequiv_strlen(account); + if (len < 1) + return (NT_STATUS_INVALID_PARAMETER); + len += 2; /* the WC null */ + arg.username->length = len; + arg.username->allosize = len; + arg.username->str = (uint8_t *)account; - /* - * Generate the OEM password from the hostname and the user session - * key(nt_key). - */ - /*LINTED E_BAD_PTR_CAST_ALIGN*/ - (void) sam_oem_password((oem_password_t *)oem_password, - (unsigned char *)hostname, nt_key); - return (0); + arg.nt_newpw = newpw; + arg.nt_oldpw = oldpw; + + arg.lm_newpw = &zero_newpw; + arg.lm_oldpw = &zero_oldpw; + + if (ndr_rpc_call(handle, opnum, &arg) != 0) + arg.status = RPC_NT_CALL_FAILED; + else if (arg.status != 0) + ndr_rpc_status(handle, opnum, arg.status); + + ndr_rpc_release(handle); + return (arg.status); } diff --git a/usr/src/lib/smbsrv/libmlsvc/common/samr_svc.c b/usr/src/lib/smbsrv/libmlsvc/common/samr_svc.c index a057e922b7..8e0753c0e3 100644 --- a/usr/src/lib/smbsrv/libmlsvc/common/samr_svc.c +++ b/usr/src/lib/smbsrv/libmlsvc/common/samr_svc.c @@ -21,6 +21,7 @@ /* * Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright 2012 Nexenta Systems, Inc. All rights reserved. */ /* @@ -1141,15 +1142,15 @@ samr_s_CreateUser(void *arg, ndr_xa_t *mxa) } /* - * samr_s_ChangeUserPasswd + * samr_s_ChangePasswordUser2 */ /*ARGSUSED*/ static int -samr_s_ChangeUserPasswd(void *arg, ndr_xa_t *mxa) +samr_s_ChangePasswordUser2(void *arg, ndr_xa_t *mxa) { - struct samr_ChangeUserPasswd *param = arg; + struct samr_ChangePasswordUser2 *param = arg; - bzero(param, sizeof (struct samr_ChangeUserPasswd)); + bzero(param, sizeof (*param)); param->status = NT_SC_ERROR(NT_STATUS_ACCESS_DENIED); return (NDR_DRC_OK); } @@ -1860,7 +1861,7 @@ static ndr_stub_table_t samr_stub_table[] = { { samr_s_Connect2, SAMR_OPNUM_Connect2 }, { samr_s_GetUserPwInfo, SAMR_OPNUM_GetUserPwInfo }, { samr_s_CreateUser, SAMR_OPNUM_CreateUser }, - { samr_s_ChangeUserPasswd, SAMR_OPNUM_ChangeUserPasswd }, + { samr_s_ChangePasswordUser2, SAMR_OPNUM_ChangePasswordUser2 }, { samr_s_GetDomainPwInfo, SAMR_OPNUM_GetDomainPwInfo }, { samr_s_SetUserInfo, SAMR_OPNUM_SetUserInfo }, { samr_s_Connect4, SAMR_OPNUM_Connect4 }, diff --git a/usr/src/lib/smbsrv/libmlsvc/common/smbrdr_glue.c b/usr/src/lib/smbsrv/libmlsvc/common/smbrdr_glue.c index 746acdf413..ab127b6150 100644 --- a/usr/src/lib/smbsrv/libmlsvc/common/smbrdr_glue.c +++ b/usr/src/lib/smbsrv/libmlsvc/common/smbrdr_glue.c @@ -20,8 +20,8 @@ */ /* - * Copyright 2011 Nexenta Systems, Inc. All rights reserved. * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright 2012 Nexenta Systems, Inc. All rights reserved. */ /* @@ -44,6 +44,12 @@ #include <assert.h> +void +smbrdr_initialize(void) +{ + (void) smb_lib_init(); +} + /* * mlsvc_disconnect * @@ -79,11 +85,9 @@ smbrdr_logon(char *srv, char *dom, char *user) int err; err = smbrdr_ctx_new(&ctx, srv, dom, user); - if (err) { - return (-1); - } - smb_ctx_free(ctx); - return (AUTH_USER_GRANT); + if (err == 0) + smb_ctx_free(ctx); + return (err); } void @@ -111,12 +115,8 @@ smbrdr_ctx_new(struct smb_ctx **ctx_p, char *server, assert(domain != NULL); assert(user != NULL); - /* Some callers pass this when they want a NULL session. */ - if (strcmp(user, "IPC$") == 0) - user = ""; - if ((err = smb_ctx_alloc(&ctx)) != 0) - return (err); + return (NT_STATUS_NO_MEMORY); /* * Set server, share, domain, user @@ -150,12 +150,18 @@ smbrdr_ctx_new(struct smb_ctx **ctx_p, char *server, * Do lookup, connect, session setup, tree connect. * Or find and reuse a session/tree, if one exists. */ - if ((err = smb_ctx_resolve(ctx)) != 0) + if ((err = smb_ctx_resolve(ctx)) != 0) { + err = NT_STATUS_BAD_NETWORK_PATH; goto errout; - if ((err = smb_ctx_get_ssn(ctx)) != 0) + } + if ((err = smb_ctx_get_ssn(ctx)) != 0) { + err = NT_STATUS_NETWORK_ACCESS_DENIED; goto errout; - if ((err = smb_ctx_get_tree(ctx)) != 0) + } + if ((err = smb_ctx_get_tree(ctx)) != 0) { + err = NT_STATUS_BAD_NETWORK_NAME; goto errout; + } /* Success! */ *ctx_p = ctx; |
