summaryrefslogtreecommitdiffstats
path: root/source3/winbindd
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 17:20:00 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 17:20:00 +0000
commit8daa83a594a2e98f39d764422bfbdbc62c9efd44 (patch)
tree4099e8021376c7d8c05bdf8503093d80e9c7bad0 /source3/winbindd
parentInitial commit. (diff)
downloadsamba-8daa83a594a2e98f39d764422bfbdbc62c9efd44.tar.xz
samba-8daa83a594a2e98f39d764422bfbdbc62c9efd44.zip
Adding upstream version 2:4.20.0+dfsg.upstream/2%4.20.0+dfsg
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'source3/winbindd')
-rw-r--r--source3/winbindd/idmap.c632
-rw-r--r--source3/winbindd/idmap_ad.c1243
-rw-r--r--source3/winbindd/idmap_ad_nss.c418
-rw-r--r--source3/winbindd/idmap_autorid.c945
-rw-r--r--source3/winbindd/idmap_autorid_tdb.c1269
-rw-r--r--source3/winbindd/idmap_hash/idmap_hash.c504
-rw-r--r--source3/winbindd/idmap_hash/idmap_hash.h60
-rw-r--r--source3/winbindd/idmap_hash/mapfile.c182
-rw-r--r--source3/winbindd/idmap_ldap.c1140
-rw-r--r--source3/winbindd/idmap_nss.c446
-rw-r--r--source3/winbindd/idmap_passdb.c87
-rw-r--r--source3/winbindd/idmap_proto.h69
-rw-r--r--source3/winbindd/idmap_rfc2307.c848
-rw-r--r--source3/winbindd/idmap_rid.c182
-rw-r--r--source3/winbindd/idmap_rw.c109
-rw-r--r--source3/winbindd/idmap_rw.h56
-rw-r--r--source3/winbindd/idmap_script.c650
-rw-r--r--source3/winbindd/idmap_tdb.c434
-rw-r--r--source3/winbindd/idmap_tdb2.c612
-rw-r--r--source3/winbindd/idmap_tdb_common.c664
-rw-r--r--source3/winbindd/idmap_tdb_common.h137
-rw-r--r--source3/winbindd/idmap_util.c137
-rw-r--r--source3/winbindd/nss_info.c377
-rw-r--r--source3/winbindd/nss_info_template.c80
-rw-r--r--source3/winbindd/wb_alias_members.c137
-rw-r--r--source3/winbindd/wb_dsgetdcname.c255
-rw-r--r--source3/winbindd/wb_getgrsid.c403
-rw-r--r--source3/winbindd/wb_getpwsid.c156
-rw-r--r--source3/winbindd/wb_gettoken.c290
-rw-r--r--source3/winbindd/wb_group_members.c489
-rw-r--r--source3/winbindd/wb_lookupname.c123
-rw-r--r--source3/winbindd/wb_lookupsid.c114
-rw-r--r--source3/winbindd/wb_lookupsids.c699
-rw-r--r--source3/winbindd/wb_lookupuseraliases.c108
-rw-r--r--source3/winbindd/wb_lookupusergroups.c120
-rw-r--r--source3/winbindd/wb_next_grent.c169
-rw-r--r--source3/winbindd/wb_next_pwent.c162
-rw-r--r--source3/winbindd/wb_query_group_list.c94
-rw-r--r--source3/winbindd/wb_query_user_list.c146
-rw-r--r--source3/winbindd/wb_queryuser.c480
-rw-r--r--source3/winbindd/wb_seqnum.c78
-rw-r--r--source3/winbindd/wb_seqnums.c153
-rw-r--r--source3/winbindd/wb_sids2xids.c786
-rw-r--r--source3/winbindd/wb_xids2sids.c422
-rw-r--r--source3/winbindd/winbindd.c1742
-rw-r--r--source3/winbindd/winbindd.h370
-rw-r--r--source3/winbindd/winbindd_ads.c1604
-rw-r--r--source3/winbindd/winbindd_ads.h34
-rw-r--r--source3/winbindd/winbindd_allocate_gid.c121
-rw-r--r--source3/winbindd/winbindd_allocate_uid.c122
-rw-r--r--source3/winbindd/winbindd_cache.c4930
-rw-r--r--source3/winbindd/winbindd_ccache_access.c397
-rw-r--r--source3/winbindd/winbindd_change_machine_acct.c99
-rw-r--r--source3/winbindd/winbindd_check_machine_acct.c96
-rw-r--r--source3/winbindd/winbindd_cm.c3434
-rw-r--r--source3/winbindd/winbindd_cred_cache.c1061
-rw-r--r--source3/winbindd/winbindd_creds.c147
-rw-r--r--source3/winbindd/winbindd_domain.c36
-rw-r--r--source3/winbindd/winbindd_domain_info.c141
-rw-r--r--source3/winbindd/winbindd_dsgetdcname.c200
-rw-r--r--source3/winbindd/winbindd_dual.c2093
-rw-r--r--source3/winbindd/winbindd_dual_ndr.c615
-rw-r--r--source3/winbindd/winbindd_dual_srv.c2127
-rw-r--r--source3/winbindd/winbindd_endgrent.c54
-rw-r--r--source3/winbindd/winbindd_endpwent.c55
-rw-r--r--source3/winbindd/winbindd_getdcname.c95
-rw-r--r--source3/winbindd/winbindd_getgrent.c213
-rw-r--r--source3/winbindd/winbindd_getgrgid.c156
-rw-r--r--source3/winbindd/winbindd_getgrnam.c213
-rw-r--r--source3/winbindd/winbindd_getgroups.c283
-rw-r--r--source3/winbindd/winbindd_getpwent.c162
-rw-r--r--source3/winbindd/winbindd_getpwnam.c163
-rw-r--r--source3/winbindd/winbindd_getpwsid.c109
-rw-r--r--source3/winbindd/winbindd_getpwuid.c137
-rw-r--r--source3/winbindd/winbindd_getsidaliases.c160
-rw-r--r--source3/winbindd/winbindd_getuserdomgroups.c123
-rw-r--r--source3/winbindd/winbindd_getusersids.c128
-rw-r--r--source3/winbindd/winbindd_gpupdate.c184
-rw-r--r--source3/winbindd/winbindd_group.c156
-rw-r--r--source3/winbindd/winbindd_idmap.c436
-rw-r--r--source3/winbindd/winbindd_irpc.c891
-rw-r--r--source3/winbindd/winbindd_list_groups.c233
-rw-r--r--source3/winbindd/winbindd_list_users.c216
-rw-r--r--source3/winbindd/winbindd_locator.c56
-rw-r--r--source3/winbindd/winbindd_lookupname.c130
-rw-r--r--source3/winbindd/winbindd_lookuprids.c200
-rw-r--r--source3/winbindd/winbindd_lookupsid.c104
-rw-r--r--source3/winbindd/winbindd_lookupsids.c144
-rw-r--r--source3/winbindd/winbindd_misc.c513
-rw-r--r--source3/winbindd/winbindd_msrpc.c1124
-rw-r--r--source3/winbindd/winbindd_ndr.c162
-rw-r--r--source3/winbindd/winbindd_pam.c3616
-rw-r--r--source3/winbindd/winbindd_pam_auth.c291
-rw-r--r--source3/winbindd/winbindd_pam_auth_crap.c285
-rw-r--r--source3/winbindd/winbindd_pam_chauthtok.c204
-rw-r--r--source3/winbindd/winbindd_pam_chng_pswd_auth_crap.c171
-rw-r--r--source3/winbindd/winbindd_pam_logoff.c183
-rw-r--r--source3/winbindd/winbindd_ping_dc.c140
-rw-r--r--source3/winbindd/winbindd_proto.h1059
-rw-r--r--source3/winbindd/winbindd_reconnect.c354
-rw-r--r--source3/winbindd/winbindd_reconnect_ads.c362
-rw-r--r--source3/winbindd/winbindd_rpc.c855
-rw-r--r--source3/winbindd/winbindd_rpc.h95
-rw-r--r--source3/winbindd/winbindd_samr.c1424
-rw-r--r--source3/winbindd/winbindd_setgrent.c67
-rw-r--r--source3/winbindd/winbindd_setpwent.c67
-rw-r--r--source3/winbindd/winbindd_show_sequence.c167
-rw-r--r--source3/winbindd/winbindd_sids_to_xids.c164
-rw-r--r--source3/winbindd/winbindd_traceid.c147
-rw-r--r--source3/winbindd/winbindd_traceid.h29
-rw-r--r--source3/winbindd/winbindd_util.c2243
-rw-r--r--source3/winbindd/winbindd_wins_byip.c142
-rw-r--r--source3/winbindd/winbindd_wins_byname.c154
-rw-r--r--source3/winbindd/winbindd_xids_to_sids.c142
-rw-r--r--source3/winbindd/wscript_build291
115 files changed, 55286 insertions, 0 deletions
diff --git a/source3/winbindd/idmap.c b/source3/winbindd/idmap.c
new file mode 100644
index 0000000..53b860b
--- /dev/null
+++ b/source3/winbindd/idmap.c
@@ -0,0 +1,632 @@
+/*
+ Unix SMB/CIFS implementation.
+ ID Mapping
+ Copyright (C) Tim Potter 2000
+ Copyright (C) Jim McDonough <jmcd@us.ibm.com> 2003
+ Copyright (C) Simo Sorce 2003-2007
+ Copyright (C) Jeremy Allison 2006
+ Copyright (C) Michael Adam 2010
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "winbindd.h"
+#include "idmap.h"
+#include "lib/util_sid_passdb.h"
+#include "libcli/security/dom_sid.h"
+#include "passdb.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_IDMAP
+
+static_decl_idmap;
+
+/**
+ * Pointer to the backend methods. Modules register themselves here via
+ * smb_register_idmap.
+ */
+
+struct idmap_backend {
+ const char *name;
+ const struct idmap_methods *methods;
+ struct idmap_backend *prev, *next;
+};
+static struct idmap_backend *backends = NULL;
+
+/**
+ * Default idmap domain configured via "idmap backend".
+ */
+static struct idmap_domain *default_idmap_domain;
+
+/**
+ * Passdb idmap domain, not configurable. winbind must always give passdb a
+ * chance to map ids.
+ */
+static struct idmap_domain *passdb_idmap_domain;
+
+/**
+ * List of specially configured idmap domains. This list is filled on demand
+ * in the winbind idmap child when the parent winbind figures out via the
+ * special range parameter or via the domain SID that a special "idmap config
+ * domain" configuration is present.
+ */
+static struct idmap_domain **idmap_domains = NULL;
+static int num_domains = 0;
+
+static struct idmap_domain *idmap_init_named_domain(TALLOC_CTX *mem_ctx,
+ const char *domname);
+static struct idmap_domain *idmap_init_domain(TALLOC_CTX *mem_ctx,
+ const char *domainname,
+ const char *modulename,
+ bool check_range);
+
+struct lp_scan_idmap_domains_state {
+ bool (*fn)(const char *domname, void *private_data);
+ void *private_data;
+};
+
+static bool lp_scan_idmap_found_domain(
+ const char *string, regmatch_t matches[], void *private_data);
+
+bool lp_scan_idmap_domains(bool (*fn)(const char *domname,
+ void *private_data),
+ void *private_data)
+{
+ struct lp_scan_idmap_domains_state state = {
+ .fn = fn, .private_data = private_data };
+ int ret;
+
+ ret = lp_wi_scan_global_parametrics(
+ "idmapconfig\\(.*\\):backend", 2,
+ lp_scan_idmap_found_domain, &state);
+ if (ret != 0) {
+ DBG_WARNING("wi_scan_global_parametrics returned %d\n", ret);
+ return false;
+ }
+
+ return true;
+}
+
+static bool lp_scan_idmap_found_domain(
+ const char *string, regmatch_t matches[], void *private_data)
+{
+ bool ok;
+
+ if (matches[1].rm_so == -1) {
+ DBG_WARNING("Found match, but no name??\n");
+ return false;
+ }
+ if (matches[1].rm_eo <= matches[1].rm_so) {
+ DBG_WARNING("Invalid match\n");
+ return false;
+ }
+
+ {
+ struct lp_scan_idmap_domains_state *state = private_data;
+ regoff_t len = matches[1].rm_eo - matches[1].rm_so;
+ char domname[len+1];
+
+ memcpy(domname, string + matches[1].rm_so, len);
+ domname[len] = '\0';
+
+ DBG_DEBUG("Found idmap domain \"%s\"\n", domname);
+
+ ok = state->fn(domname, state->private_data);
+ }
+
+ return ok;
+}
+
+static bool idmap_found_domain_backend(const char *domname,
+ void *private_data);
+
+static bool idmap_init(void)
+{
+ static bool initialized;
+ bool ok;
+
+ if (initialized) {
+ return true;
+ }
+
+ DEBUG(10, ("idmap_init(): calling static_init_idmap\n"));
+
+ static_init_idmap(NULL);
+
+ initialized = true;
+
+ if (!pdb_is_responsible_for_everything_else()) {
+ default_idmap_domain = idmap_init_named_domain(NULL, "*");
+ if (default_idmap_domain == NULL) {
+ return false;
+ }
+ }
+
+ passdb_idmap_domain = idmap_init_domain(
+ NULL, get_global_sam_name(), "passdb", false);
+ if (passdb_idmap_domain == NULL) {
+ TALLOC_FREE(default_idmap_domain);
+ return false;
+ }
+
+ idmap_domains = talloc_array(NULL, struct idmap_domain *, 0);
+ if (idmap_domains == NULL) {
+ TALLOC_FREE(passdb_idmap_domain);
+ TALLOC_FREE(default_idmap_domain);
+ return false;
+ }
+
+ ok = lp_scan_idmap_domains(idmap_found_domain_backend, NULL);
+ if (!ok) {
+ DBG_WARNING("lp_scan_idmap_domains failed\n");
+ return false;
+ }
+
+ return true;
+}
+
+static int idmap_config_name(const char *domname, char *buf, size_t buflen)
+{
+ int len = snprintf(buf, buflen, "idmap config %s", domname);
+ SMB_ASSERT(len > 0);
+ return len + 1;
+}
+
+const char *idmap_config_const_string(const char *domname, const char *option,
+ const char *def)
+{
+ int len = idmap_config_name(domname, NULL, 0);
+ char config_option[len];
+ idmap_config_name(domname, config_option, sizeof(config_option));
+
+ return lp_parm_const_string(-1, config_option, option, def);
+}
+
+bool idmap_config_bool(const char *domname, const char *option, bool def)
+{
+ int len = idmap_config_name(domname, NULL, 0);
+ char config_option[len];
+ idmap_config_name(domname, config_option, sizeof(config_option));
+
+ return lp_parm_bool(-1, config_option, option, def);
+}
+
+int idmap_config_int(const char *domname, const char *option, int def)
+{
+ int len = idmap_config_name(domname, NULL, 0);
+ char config_option[len];
+ idmap_config_name(domname, config_option, sizeof(config_option));
+
+ return lp_parm_int(-1, config_option, option, def);
+}
+
+const char **idmap_config_string_list(const char *domname,
+ const char *option,
+ const char **def)
+{
+ int len = idmap_config_name(domname, NULL, 0);
+ char config_option[len];
+ idmap_config_name(domname, config_option, sizeof(config_option));
+
+ return lp_parm_string_list(-1, config_option, option, def);
+}
+
+bool domain_has_idmap_config(const char *domname)
+{
+ int i;
+ const char *range = NULL;
+ const char *backend = NULL;
+ bool ok;
+
+ ok = idmap_init();
+ if (!ok) {
+ return false;
+ }
+
+ for (i=0; i<num_domains; i++) {
+ if (strequal(idmap_domains[i]->name, domname)) {
+ return true;
+ }
+ }
+
+ /* fallback: also check loadparm */
+
+ range = idmap_config_const_string(domname, "range", NULL);
+ backend = idmap_config_const_string(domname, "backend", NULL);
+ if (range != NULL && backend != NULL) {
+ DEBUG(5, ("idmap configuration specified for domain '%s'\n",
+ domname));
+ return true;
+ }
+
+ return false;
+}
+
+static bool idmap_found_domain_backend(const char *domname,
+ void *private_data)
+{
+ struct idmap_domain *dom, **tmp;
+
+ DBG_DEBUG("Found idmap domain \"%s\"\n", domname);
+
+ if (strcmp(domname, "*") == 0) {
+ return false;
+ }
+
+ dom = idmap_init_named_domain(idmap_domains, domname);
+ if (dom == NULL) {
+ DBG_NOTICE("Could not init idmap domain %s\n", domname);
+ return false;
+ }
+
+ tmp = talloc_realloc(idmap_domains, idmap_domains,
+ struct idmap_domain *, num_domains + 1);
+ if (tmp == NULL) {
+ DBG_WARNING("talloc_realloc failed\n");
+ TALLOC_FREE(dom);
+ return false;
+ }
+ idmap_domains = tmp;
+ idmap_domains[num_domains] = dom;
+ num_domains += 1;
+
+ return false;
+}
+
+static const struct idmap_methods *get_methods(const char *name)
+{
+ struct idmap_backend *b;
+
+ for (b = backends; b; b = b->next) {
+ if (strequal(b->name, name)) {
+ return b->methods;
+ }
+ }
+
+ return NULL;
+}
+
+bool idmap_is_offline(void)
+{
+ return ( lp_winbind_offline_logon() &&
+ get_global_winbindd_state_offline() );
+}
+
+/**********************************************************************
+ Allow a module to register itself as a method.
+**********************************************************************/
+
+NTSTATUS smb_register_idmap(int version, const char *name,
+ const struct idmap_methods *methods)
+{
+ struct idmap_backend *entry;
+
+ if ((version != SMB_IDMAP_INTERFACE_VERSION)) {
+ DEBUG(0, ("Failed to register idmap module.\n"
+ "The module was compiled against "
+ "SMB_IDMAP_INTERFACE_VERSION %d,\n"
+ "current SMB_IDMAP_INTERFACE_VERSION is %d.\n"
+ "Please recompile against the current version "
+ "of samba!\n",
+ version, SMB_IDMAP_INTERFACE_VERSION));
+ return NT_STATUS_OBJECT_TYPE_MISMATCH;
+ }
+
+ if (!name || !name[0] || !methods) {
+ DEBUG(0,("Called with NULL pointer or empty name!\n"));
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ for (entry = backends; entry != NULL; entry = entry->next) {
+ if (strequal(entry->name, name)) {
+ DEBUG(5,("Idmap module %s already registered!\n",
+ name));
+ return NT_STATUS_OBJECT_NAME_COLLISION;
+ }
+ }
+
+ entry = talloc(NULL, struct idmap_backend);
+ if ( ! entry) {
+ DEBUG(0,("Out of memory!\n"));
+ TALLOC_FREE(entry);
+ return NT_STATUS_NO_MEMORY;
+ }
+ entry->name = talloc_strdup(entry, name);
+ if ( ! entry->name) {
+ DEBUG(0,("Out of memory!\n"));
+ TALLOC_FREE(entry);
+ return NT_STATUS_NO_MEMORY;
+ }
+ entry->methods = methods;
+
+ DLIST_ADD(backends, entry);
+ DEBUG(5, ("Successfully added idmap backend '%s'\n", name));
+ return NT_STATUS_OK;
+}
+
+/**
+ * Initialize a domain structure
+ * @param[in] mem_ctx memory context for the result
+ * @param[in] domainname which domain is this for
+ * @param[in] modulename which backend module
+ * @param[in] check_range whether range checking should be done
+ * @result The initialized structure
+ */
+static struct idmap_domain *idmap_init_domain(TALLOC_CTX *mem_ctx,
+ const char *domainname,
+ const char *modulename,
+ bool check_range)
+{
+ struct idmap_domain *result;
+ NTSTATUS status;
+ const char *range;
+ unsigned low_id = 0;
+ unsigned high_id = 0;
+
+ result = talloc_zero(mem_ctx, struct idmap_domain);
+ if (result == NULL) {
+ DEBUG(0, ("talloc failed\n"));
+ return NULL;
+ }
+
+ result->name = talloc_strdup(result, domainname);
+ if (result->name == NULL) {
+ DEBUG(0, ("talloc failed\n"));
+ goto fail;
+ }
+
+ /*
+ * Check whether the requested backend module exists and
+ * load the methods.
+ */
+
+ result->methods = get_methods(modulename);
+ if (result->methods == NULL) {
+ DEBUG(3, ("idmap backend %s not found\n", modulename));
+
+ status = smb_probe_module("idmap", modulename);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(3, ("Could not probe idmap module %s\n",
+ modulename));
+ goto fail;
+ }
+
+ result->methods = get_methods(modulename);
+ }
+ if (result->methods == NULL) {
+ DEBUG(1, ("idmap backend %s not found\n", modulename));
+ goto fail;
+ }
+
+ /*
+ * load ranges and read only information from the config
+ */
+
+ result->read_only = idmap_config_bool(result->name, "read only", false);
+ range = idmap_config_const_string(result->name, "range", NULL);
+
+ if (range == NULL) {
+ if (check_range) {
+ DEBUG(1, ("idmap range not specified for domain %s\n",
+ result->name));
+ goto fail;
+ }
+ } else if (sscanf(range, "%u - %u", &low_id, &high_id) != 2)
+ {
+ DEBUG(1, ("invalid range '%s' specified for domain "
+ "'%s'\n", range, result->name));
+ if (check_range) {
+ goto fail;
+ }
+ } else if (low_id > high_id) {
+ DEBUG(1, ("Error: invalid idmap range detected: %u - %u\n",
+ low_id, high_id));
+ if (check_range) {
+ goto fail;
+ }
+ }
+
+ result->low_id = low_id;
+ result->high_id = high_id;
+
+ status = result->methods->init(result);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(1, ("idmap initialization returned %s\n",
+ nt_errstr(status)));
+ goto fail;
+ }
+
+ return result;
+
+fail:
+ TALLOC_FREE(result);
+ return NULL;
+}
+
+/**
+ * Initialize a named domain structure
+ * @param[in] mem_ctx memory context for the result
+ * @param[in] domname the domain name
+ * @result The default domain structure
+ *
+ * This routine looks at the "idmap config <domname>" parameters to figure out
+ * the configuration.
+ */
+
+static struct idmap_domain *idmap_init_named_domain(TALLOC_CTX *mem_ctx,
+ const char *domname)
+{
+ struct idmap_domain *result = NULL;
+ const char *backend;
+ bool ok;
+
+ ok = idmap_init();
+ if (!ok) {
+ return NULL;
+ }
+
+ backend = idmap_config_const_string(domname, "backend", NULL);
+ if (backend == NULL) {
+ DEBUG(10, ("no idmap backend configured for domain '%s'\n",
+ domname));
+ goto fail;
+ }
+
+ result = idmap_init_domain(mem_ctx, domname, backend, true);
+ if (result == NULL) {
+ goto fail;
+ }
+
+ return result;
+
+fail:
+ TALLOC_FREE(result);
+ return NULL;
+}
+
+/**
+ * Find a domain struct according to a domain name
+ * @param[in] domname Domain name to get the config for
+ * @result The default domain structure that fits
+ *
+ * This is the central routine in the winbindd-idmap child to pick the correct
+ * domain for looking up IDs. If domname is NULL or empty, we use the default
+ * domain. If it contains something, we try to use idmap_init_named_domain()
+ * to fetch the correct backend.
+ *
+ * The choice about "domname" is being made by the winbind parent, look at the
+ * "have_idmap_config" of "struct winbindd_domain" which is set in
+ * add_trusted_domain.
+ */
+
+struct idmap_domain *idmap_find_domain(const char *domname)
+{
+ bool ok;
+ int i;
+
+ DEBUG(10, ("idmap_find_domain called for domain '%s'\n",
+ domname?domname:"NULL"));
+
+ ok = idmap_init();
+ if (!ok) {
+ return NULL;
+ }
+
+ if ((domname == NULL) || (domname[0] == '\0')) {
+ return default_idmap_domain;
+ }
+
+ for (i=0; i<num_domains; i++) {
+ if (strequal(idmap_domains[i]->name, domname)) {
+ return idmap_domains[i];
+ }
+ }
+
+ return default_idmap_domain;
+}
+
+struct idmap_domain *idmap_find_domain_with_sid(const char *domname,
+ const struct dom_sid *sid)
+{
+ bool ok;
+
+ ok = idmap_init();
+ if (!ok) {
+ return NULL;
+ }
+
+ if (sid_check_is_for_passdb(sid)) {
+ return passdb_idmap_domain;
+ }
+
+ return idmap_find_domain(domname);
+}
+
+void idmap_close(void)
+{
+ TALLOC_FREE(default_idmap_domain);
+ TALLOC_FREE(passdb_idmap_domain);
+ TALLOC_FREE(idmap_domains);
+ num_domains = 0;
+}
+
+/**************************************************************************
+ idmap allocator interface functions
+**************************************************************************/
+
+static NTSTATUS idmap_allocate_unixid(struct unixid *id)
+{
+ struct idmap_domain *dom;
+ NTSTATUS ret;
+
+ dom = idmap_find_domain(NULL);
+
+ if (dom == NULL) {
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ if (dom->methods->allocate_id == NULL) {
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+
+ ret = dom->methods->allocate_id(dom, id);
+
+ return ret;
+}
+
+
+NTSTATUS idmap_allocate_uid(struct unixid *id)
+{
+ id->type = ID_TYPE_UID;
+ return idmap_allocate_unixid(id);
+}
+
+NTSTATUS idmap_allocate_gid(struct unixid *id)
+{
+ id->type = ID_TYPE_GID;
+ return idmap_allocate_unixid(id);
+}
+
+NTSTATUS idmap_backend_unixids_to_sids(struct id_map **maps,
+ const char *domain_name,
+ struct dom_sid domain_sid)
+{
+ struct idmap_domain *dom = NULL;
+ NTSTATUS status;
+ bool ok;
+
+ ok = idmap_init();
+ if (!ok) {
+ return NT_STATUS_NONE_MAPPED;
+ }
+
+ if (strequal(domain_name, get_global_sam_name())) {
+ dom = passdb_idmap_domain;
+ }
+ if (dom == NULL) {
+ dom = idmap_find_domain(domain_name);
+ }
+ if (dom == NULL) {
+ return NT_STATUS_NONE_MAPPED;
+ }
+
+ dom->dom_sid = domain_sid;
+ status = dom->methods->unixids_to_sids(dom, maps);
+
+ DBG_DEBUG("unixid_to_sids for domain %s returned %s\n",
+ domain_name, nt_errstr(status));
+
+ return status;
+}
diff --git a/source3/winbindd/idmap_ad.c b/source3/winbindd/idmap_ad.c
new file mode 100644
index 0000000..5c9fe07
--- /dev/null
+++ b/source3/winbindd/idmap_ad.c
@@ -0,0 +1,1243 @@
+/*
+ * idmap_ad: map between Active Directory and RFC 2307 accounts
+ *
+ * Copyright (C) Volker Lendecke 2015
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "includes.h"
+#include "winbindd.h"
+#include "libsmb/namequery.h"
+#include "idmap.h"
+#include "tldap_gensec_bind.h"
+#include "tldap_util.h"
+#include "passdb.h"
+#include "lib/param/param.h"
+#include "auth/gensec/gensec.h"
+#include "librpc/gen_ndr/ndr_netlogon.h"
+#include "libads/ldap_schema_oids.h"
+#include "../libds/common/flags.h"
+#include "libcli/ldap/ldap_ndr.h"
+#include "libcli/security/dom_sid.h"
+#include "source3/libads/sitename_cache.h"
+#include "source3/libads/kerberos_proto.h"
+#include "source3/librpc/gen_ndr/ads.h"
+#include "source3/lib/global_contexts.h"
+#include <ldb.h>
+
+struct idmap_ad_schema_names;
+
+struct idmap_ad_context {
+ struct idmap_domain *dom;
+ struct tldap_context *ld;
+ struct idmap_ad_schema_names *schema;
+ const char *default_nc;
+
+ bool unix_primary_group;
+ bool unix_nss_info;
+
+ struct ldb_context *ldb;
+ struct ldb_dn **deny_ous;
+ struct ldb_dn **allow_ous;
+};
+
+static NTSTATUS idmap_ad_get_context(struct idmap_domain *dom,
+ struct idmap_ad_context **pctx);
+
+static char *get_schema_path(TALLOC_CTX *mem_ctx, struct tldap_context *ld)
+{
+ struct tldap_message *rootdse;
+
+ rootdse = tldap_rootdse(ld);
+ if (rootdse == NULL) {
+ return NULL;
+ }
+
+ return tldap_talloc_single_attribute(rootdse, "schemaNamingContext",
+ mem_ctx);
+}
+
+static char *get_default_nc(TALLOC_CTX *mem_ctx, struct tldap_context *ld)
+{
+ struct tldap_message *rootdse;
+
+ rootdse = tldap_rootdse(ld);
+ if (rootdse == NULL) {
+ return NULL;
+ }
+
+ return tldap_talloc_single_attribute(rootdse, "defaultNamingContext",
+ mem_ctx);
+}
+
+struct idmap_ad_schema_names {
+ char *name;
+ char *uid;
+ char *gid;
+ char *gecos;
+ char *dir;
+ char *shell;
+};
+
+static TLDAPRC get_attrnames_by_oids(struct tldap_context *ld,
+ TALLOC_CTX *mem_ctx,
+ const char *schema_path,
+ size_t num_oids,
+ const char **oids,
+ char **names)
+{
+ char *filter;
+ const char *attrs[] = { "lDAPDisplayName", "attributeId" };
+ size_t i;
+ TLDAPRC rc;
+ struct tldap_message **msgs;
+ size_t num_msgs;
+
+ filter = talloc_strdup(mem_ctx, "(|");
+
+ for (i=0; i<num_oids; i++) {
+ talloc_asprintf_addbuf(&filter, "(attributeId=%s)", oids[i]);
+ }
+ talloc_asprintf_addbuf(&filter, ")");
+
+ if (filter == NULL) {
+ return TLDAP_NO_MEMORY;
+ }
+
+ rc = tldap_search(ld, schema_path, TLDAP_SCOPE_SUB, filter,
+ attrs, ARRAY_SIZE(attrs), 0, NULL, 0, NULL, 0,
+ 0, 0, 0, mem_ctx, &msgs);;
+ TALLOC_FREE(filter);
+ if (!TLDAP_RC_IS_SUCCESS(rc)) {
+ return rc;
+ }
+
+ for (i=0; i<num_oids; i++) {
+ names[i] = NULL;
+ }
+
+ num_msgs = talloc_array_length(msgs);
+
+ for (i=0; i<num_msgs; i++) {
+ struct tldap_message *msg = msgs[i];
+ char *oid;
+ size_t j;
+
+ if (tldap_msg_type(msg) != TLDAP_RES_SEARCH_ENTRY) {
+ /* Could be a TLDAP_RES_SEARCH_REFERENCE */
+ continue;
+ }
+
+ oid = tldap_talloc_single_attribute(
+ msg, "attributeId", msg);
+ if (oid == NULL) {
+ continue;
+ }
+
+ for (j=0; j<num_oids; j++) {
+ if (strequal(oid, oids[j])) {
+ break;
+ }
+ }
+ TALLOC_FREE(oid);
+
+ if (j == num_oids) {
+ /* not found */
+ continue;
+ }
+
+ names[j] = tldap_talloc_single_attribute(
+ msg, "lDAPDisplayName", mem_ctx);
+ }
+
+ TALLOC_FREE(msgs);
+ for (i=0; i<num_oids; i++) {
+ if (names[i] == NULL) {
+ DBG_ERR("Failed to retrieve schema name for "
+ "oid [%s]. Schema mode is incorrect "
+ "for this domain.\n", oids[i]);
+ return TLDAP_FILTER_ERROR;
+ }
+ }
+
+ return TLDAP_SUCCESS;
+}
+
+static TLDAPRC get_posix_schema_names(struct tldap_context *ld,
+ const char *schema_mode,
+ TALLOC_CTX *mem_ctx,
+ struct idmap_ad_schema_names **pschema)
+{
+ char *schema_path;
+ struct idmap_ad_schema_names *schema;
+ char *names[6];
+ const char *oids_sfu[] = {
+ ADS_ATTR_SFU_UIDNUMBER_OID,
+ ADS_ATTR_SFU_GIDNUMBER_OID,
+ ADS_ATTR_SFU_HOMEDIR_OID,
+ ADS_ATTR_SFU_SHELL_OID,
+ ADS_ATTR_SFU_GECOS_OID,
+ ADS_ATTR_SFU_UID_OID
+ };
+ const char *oids_sfu20[] = {
+ ADS_ATTR_SFU20_UIDNUMBER_OID,
+ ADS_ATTR_SFU20_GIDNUMBER_OID,
+ ADS_ATTR_SFU20_HOMEDIR_OID,
+ ADS_ATTR_SFU20_SHELL_OID,
+ ADS_ATTR_SFU20_GECOS_OID,
+ ADS_ATTR_SFU20_UID_OID
+ };
+ const char *oids_rfc2307[] = {
+ ADS_ATTR_RFC2307_UIDNUMBER_OID,
+ ADS_ATTR_RFC2307_GIDNUMBER_OID,
+ ADS_ATTR_RFC2307_HOMEDIR_OID,
+ ADS_ATTR_RFC2307_SHELL_OID,
+ ADS_ATTR_RFC2307_GECOS_OID,
+ ADS_ATTR_RFC2307_UID_OID
+ };
+ const char **oids;
+
+ TLDAPRC rc;
+
+ schema = talloc(mem_ctx, struct idmap_ad_schema_names);
+ if (schema == NULL) {
+ return TLDAP_NO_MEMORY;
+ }
+
+ schema_path = get_schema_path(schema, ld);
+ if (schema_path == NULL) {
+ TALLOC_FREE(schema);
+ return TLDAP_NO_MEMORY;
+ }
+
+ oids = oids_rfc2307;
+
+ if ((schema_mode != NULL) && (schema_mode[0] != '\0')) {
+ if (strequal(schema_mode, "sfu")) {
+ oids = oids_sfu;
+ } else if (strequal(schema_mode, "sfu20")) {
+ oids = oids_sfu20;
+ } else if (strequal(schema_mode, "rfc2307" )) {
+ oids = oids_rfc2307;
+ } else {
+ DBG_WARNING("Unknown schema mode %s\n", schema_mode);
+ }
+ }
+
+ rc = get_attrnames_by_oids(ld, schema, schema_path, 6, oids, names);
+ TALLOC_FREE(schema_path);
+ if (!TLDAP_RC_IS_SUCCESS(rc)) {
+ TALLOC_FREE(schema);
+ return rc;
+ }
+
+ schema->uid = names[0];
+ schema->gid = names[1];
+ schema->dir = names[2];
+ schema->shell = names[3];
+ schema->gecos = names[4];
+ schema->name = names[5];
+
+ *pschema = schema;
+
+ return TLDAP_SUCCESS;
+}
+
+static void PRINTF_ATTRIBUTE(3, 0) idmap_ad_tldap_debug(
+ void *log_private,
+ enum tldap_debug_level level,
+ const char *fmt,
+ va_list ap)
+{
+ int samba_level = -1;
+
+ switch (level) {
+ case TLDAP_DEBUG_FATAL:
+ samba_level = DBGLVL_ERR;
+ break;
+ case TLDAP_DEBUG_ERROR:
+ samba_level = DBGLVL_ERR;
+ break;
+ case TLDAP_DEBUG_WARNING:
+ samba_level = DBGLVL_WARNING;
+ break;
+ case TLDAP_DEBUG_TRACE:
+ samba_level = DBGLVL_DEBUG;
+ break;
+ }
+
+ if (CHECK_DEBUGLVL(samba_level)) {
+ char *s = NULL;
+ int ret;
+
+ ret = vasprintf(&s, fmt, ap);
+ if (ret == -1) {
+ return;
+ }
+ DEBUG(samba_level, ("idmap_ad_tldap: %s", s));
+ free(s);
+ }
+}
+
+static uint32_t gensec_features_from_ldap_sasl_wrapping(void)
+{
+ int wrap_flags;
+ uint32_t gensec_features = 0;
+
+ wrap_flags = lp_client_ldap_sasl_wrapping();
+ if (wrap_flags == -1) {
+ wrap_flags = 0;
+ }
+
+ if (wrap_flags & ADS_AUTH_SASL_SEAL) {
+ gensec_features |= GENSEC_FEATURE_SEAL;
+ }
+ if (wrap_flags & ADS_AUTH_SASL_SIGN) {
+ gensec_features |= GENSEC_FEATURE_SIGN;
+ }
+
+ if (gensec_features != 0) {
+ gensec_features |= GENSEC_FEATURE_LDAP_STYLE;
+ }
+
+ return gensec_features;
+}
+
+static NTSTATUS idmap_ad_get_tldap_ctx(TALLOC_CTX *mem_ctx,
+ const char *domname,
+ struct tldap_context **pld)
+{
+ struct netr_DsRGetDCNameInfo *dcinfo;
+ struct sockaddr_storage dcaddr;
+ struct cli_credentials *creds;
+ struct loadparm_context *lp_ctx;
+ struct tldap_context *ld;
+ uint32_t gensec_features = gensec_features_from_ldap_sasl_wrapping();
+ char *sitename = NULL;
+ int fd;
+ NTSTATUS status;
+ bool ok;
+ TLDAPRC rc;
+
+ status = wb_dsgetdcname_gencache_get(mem_ctx, domname, &dcinfo);
+ if (!NT_STATUS_IS_OK(status)) {
+ DBG_DEBUG("Could not get dcinfo for %s: %s\n", domname,
+ nt_errstr(status));
+ return status;
+ }
+
+ if (dcinfo->dc_unc == NULL) {
+ TALLOC_FREE(dcinfo);
+ return NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND;
+ }
+ if (dcinfo->dc_unc[0] == '\\') {
+ dcinfo->dc_unc += 1;
+ }
+ if (dcinfo->dc_unc[0] == '\\') {
+ dcinfo->dc_unc += 1;
+ }
+
+ ok = resolve_name(dcinfo->dc_unc, &dcaddr, 0x20, true);
+ if (!ok) {
+ DBG_DEBUG("Could not resolve name %s\n", dcinfo->dc_unc);
+ TALLOC_FREE(dcinfo);
+ return NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND;
+ }
+
+ sitename = sitename_fetch(talloc_tos(), lp_realm());
+
+ /*
+ * create_local_private_krb5_conf_for_domain() can deal with
+ * sitename==NULL
+ */
+
+ ok = create_local_private_krb5_conf_for_domain(
+ lp_realm(), lp_workgroup(), sitename, &dcaddr);
+ TALLOC_FREE(sitename);
+ if (!ok) {
+ DBG_DEBUG("Could not create private krb5.conf\n");
+ TALLOC_FREE(dcinfo);
+ return NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND;
+ }
+
+ status = open_socket_out(&dcaddr, 389, 10000, &fd);
+ if (!NT_STATUS_IS_OK(status)) {
+ DBG_DEBUG("open_socket_out failed: %s\n", nt_errstr(status));
+ TALLOC_FREE(dcinfo);
+ return status;
+ }
+
+ ld = tldap_context_create(dcinfo, fd);
+ if (ld == NULL) {
+ DBG_DEBUG("tldap_context_create failed\n");
+ close(fd);
+ TALLOC_FREE(dcinfo);
+ return NT_STATUS_NO_MEMORY;
+ }
+ tldap_set_debug(ld, idmap_ad_tldap_debug, NULL);
+
+ /*
+ * Here we use or own machine account as
+ * we run as domain member.
+ */
+ status = pdb_get_trust_credentials(lp_workgroup(),
+ lp_realm(),
+ dcinfo,
+ &creds);
+ if (!NT_STATUS_IS_OK(status)) {
+ DBG_DEBUG("pdb_get_trust_credentials() failed - %s\n",
+ nt_errstr(status));
+ TALLOC_FREE(dcinfo);
+ return status;
+ }
+
+ lp_ctx = loadparm_init_s3(dcinfo, loadparm_s3_helpers());
+ if (lp_ctx == NULL) {
+ DBG_DEBUG("loadparm_init_s3 failed\n");
+ TALLOC_FREE(dcinfo);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ rc = tldap_gensec_bind(ld, creds, "ldap", dcinfo->dc_unc, NULL, lp_ctx,
+ gensec_features);
+ if (!TLDAP_RC_IS_SUCCESS(rc)) {
+ DBG_DEBUG("tldap_gensec_bind failed: %s\n",
+ tldap_errstr(dcinfo, ld, rc));
+ TALLOC_FREE(dcinfo);
+ return NT_STATUS_LDAP(TLDAP_RC_V(rc));
+ }
+
+ rc = tldap_fetch_rootdse(ld);
+ if (!TLDAP_RC_IS_SUCCESS(rc)) {
+ DBG_DEBUG("tldap_fetch_rootdse failed: %s\n",
+ tldap_errstr(dcinfo, ld, rc));
+ TALLOC_FREE(dcinfo);
+ return NT_STATUS_LDAP(TLDAP_RC_V(rc));
+ }
+
+ *pld = talloc_move(mem_ctx, &ld);
+ TALLOC_FREE(dcinfo);
+ return NT_STATUS_OK;
+}
+
+static int idmap_ad_context_destructor(struct idmap_ad_context *ctx)
+{
+ if ((ctx->dom != NULL) && (ctx->dom->private_data == ctx)) {
+ ctx->dom->private_data = NULL;
+ }
+ return 0;
+}
+
+static struct ldb_dn **str_list_to_dns(TALLOC_CTX *mem_ctx,
+ const char *dbgmsg,
+ struct ldb_context *ldb,
+ const char **strlist)
+{
+ size_t i, num_dns = str_list_length(strlist);
+ char *dbgstr = NULL;
+ struct ldb_dn **dns = NULL;
+
+ dns = talloc_array(mem_ctx, struct ldb_dn *, num_dns);
+ if (dns == NULL) {
+ TALLOC_FREE(dbgstr);
+ return NULL;
+ }
+
+ dbgstr = talloc_strdup(talloc_tos(), "");
+
+ for (i = 0; i < num_dns; i++) {
+ dns[i] = ldb_dn_new(dns, ldb, strlist[i]);
+ if (dns[i] == NULL) {
+ DBG_WARNING("ldb_dn_new(%s) failed\n", strlist[i]);
+ TALLOC_FREE(dns);
+ return NULL;
+ }
+ talloc_asprintf_addbuf(
+ &dbgstr,
+ "%s ",
+ ldb_dn_get_extended_linearized(dbgstr, dns[i], 1));
+ }
+
+ DBG_DEBUG("%s %s\n", dbgmsg, dbgstr);
+ TALLOC_FREE(dbgstr);
+
+ return dns;
+}
+
+static NTSTATUS idmap_ad_context_create(TALLOC_CTX *mem_ctx,
+ struct idmap_domain *dom,
+ const char *domname,
+ struct idmap_ad_context **pctx)
+{
+ struct idmap_ad_context *ctx;
+ const char *schema_mode;
+ const char **allow = NULL;
+ const char **deny = NULL;
+ NTSTATUS status;
+ TLDAPRC rc;
+
+ ctx = talloc_zero(mem_ctx, struct idmap_ad_context);
+ if (ctx == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ ctx->dom = dom;
+
+ talloc_set_destructor(ctx, idmap_ad_context_destructor);
+
+ status = idmap_ad_get_tldap_ctx(ctx, domname, &ctx->ld);
+ if (!NT_STATUS_IS_OK(status)) {
+ DBG_DEBUG("idmap_ad_get_tldap_ctx failed: %s\n",
+ nt_errstr(status));
+ TALLOC_FREE(ctx);
+ return status;
+ }
+
+ ctx->default_nc = get_default_nc(ctx, ctx->ld);
+ if (ctx->default_nc == NULL) {
+ DBG_DEBUG("No default nc\n");
+ TALLOC_FREE(ctx);
+ return status;
+ }
+
+ ctx->unix_primary_group = idmap_config_bool(
+ domname, "unix_primary_group", false);
+ ctx->unix_nss_info = idmap_config_bool(
+ domname, "unix_nss_info", false);
+
+ schema_mode = idmap_config_const_string(
+ domname, "schema_mode", "rfc2307");
+
+ rc = get_posix_schema_names(ctx->ld, schema_mode, ctx, &ctx->schema);
+ if (!TLDAP_RC_IS_SUCCESS(rc)) {
+ DBG_DEBUG("get_posix_schema_names failed: %s\n",
+ tldap_errstr(ctx, ctx->ld, rc));
+ TALLOC_FREE(ctx);
+ return NT_STATUS_LDAP(TLDAP_RC_V(rc));
+ }
+
+ deny = idmap_config_string_list(domname, "deny ous", NULL);
+ allow = idmap_config_string_list(domname, "allow ous", NULL);
+
+ if ((deny != NULL) || (allow != NULL)) {
+ int ret = ldb_global_init();
+ if (ret == -1) {
+ status = map_nt_error_from_unix(errno);
+ DBG_WARNING("ldb_global_init() failed: %s\n",
+ strerror(errno));
+ TALLOC_FREE(ctx);
+ return status;
+ }
+
+ ctx->ldb = ldb_init(ctx, global_event_context());
+ if (ctx->ldb == NULL) {
+ status = map_nt_error_from_unix(errno);
+ DBG_WARNING("ldb_init() failed: %s\n", strerror(errno));
+ TALLOC_FREE(ctx);
+ return status;
+ }
+ }
+
+ if (deny != NULL) {
+ ctx->deny_ous = str_list_to_dns(ctx, "Denying", ctx->ldb, deny);
+ if (ctx->deny_ous == NULL) {
+ DBG_DEBUG("str_list_to_dns failed\n");
+ TALLOC_FREE(ctx);
+ return NT_STATUS_NO_MEMORY;
+ }
+ }
+
+ if (allow != NULL) {
+ ctx->allow_ous =
+ str_list_to_dns(ctx, "Allowing", ctx->ldb, allow);
+ if (ctx->allow_ous == NULL) {
+ DBG_DEBUG("str_list_to_dns failed\n");
+ TALLOC_FREE(ctx);
+ return NT_STATUS_NO_MEMORY;
+ }
+ }
+
+ *pctx = ctx;
+ return NT_STATUS_OK;
+}
+
+static bool check_dn(struct ldb_dn **dns, struct ldb_dn *dn)
+{
+ size_t i, num_dns = talloc_array_length(dns);
+
+ for (i = 0; i < num_dns; i++) {
+ struct ldb_dn *base = dns[i];
+ int ret = ldb_dn_compare_base(base, dn);
+ if (ret == 0) {
+ return true;
+ }
+ }
+ return false;
+}
+
+static bool idmap_ad_dn_filter(struct idmap_domain *dom, const char *dnstr)
+{
+ struct idmap_ad_context *ctx = NULL;
+ struct ldb_dn *dn = NULL;
+ NTSTATUS status;
+ bool result = false;
+
+ status = idmap_ad_get_context(dom, &ctx);
+ if (!NT_STATUS_IS_OK(status)) {
+ DBG_DEBUG("idmap_ad_get_context failed: %s\n",
+ nt_errstr(status));
+ return false;
+ }
+
+ if ((ctx->allow_ous == NULL) && (ctx->deny_ous == NULL)) {
+ /*
+ * Nothing to check
+ */
+ return true;
+ }
+
+ dn = ldb_dn_new(talloc_tos(), ctx->ldb, dnstr);
+ if (dn == NULL) {
+ DBG_DEBUG("ldb_dn_new(%s) failed\n", dnstr);
+ return false;
+ }
+
+ if (ctx->deny_ous != NULL) {
+ bool denied = check_dn(ctx->deny_ous, dn);
+ if (denied) {
+ DBG_WARNING("Denied %s\n", dnstr);
+ TALLOC_FREE(dn);
+ return false;
+ }
+
+ if (ctx->allow_ous == NULL) {
+ /*
+ * Only a few denied OUs around, allow by
+ * default
+ */
+ result = true;
+ }
+ }
+
+ if (ctx->allow_ous != NULL) {
+ bool allowed = check_dn(ctx->allow_ous, dn);
+ if (allowed) {
+ return true;
+ }
+ DBG_WARNING("Did not allow %s\n", dnstr);
+ }
+
+ return result;
+}
+
+static NTSTATUS idmap_ad_query_user(struct idmap_domain *domain,
+ struct wbint_userinfo *info)
+{
+ struct idmap_ad_context *ctx;
+ TLDAPRC rc;
+ NTSTATUS status;
+ char *sidstr, *filter;
+ const char *attrs[4];
+ size_t i, num_msgs;
+ struct tldap_message **msgs;
+
+ status = idmap_ad_get_context(domain, &ctx);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ if (!(ctx->unix_primary_group || ctx->unix_nss_info)) {
+ return NT_STATUS_OK;
+ }
+
+ attrs[0] = ctx->schema->gid;
+ attrs[1] = ctx->schema->gecos;
+ attrs[2] = ctx->schema->dir;
+ attrs[3] = ctx->schema->shell;
+
+ sidstr = ldap_encode_ndr_dom_sid(talloc_tos(), &info->user_sid);
+ if (sidstr == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ filter = talloc_asprintf(talloc_tos(), "(objectsid=%s)", sidstr);
+ TALLOC_FREE(sidstr);
+ if (filter == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ DBG_DEBUG("Filter: [%s]\n", filter);
+
+ rc = tldap_search(ctx->ld, ctx->default_nc, TLDAP_SCOPE_SUB, filter,
+ attrs, ARRAY_SIZE(attrs), 0, NULL, 0, NULL, 0,
+ 0, 0, 0, talloc_tos(), &msgs);
+ if (!TLDAP_RC_IS_SUCCESS(rc)) {
+ return NT_STATUS_LDAP(TLDAP_RC_V(rc));
+ }
+
+ TALLOC_FREE(filter);
+
+ num_msgs = talloc_array_length(msgs);
+
+ for (i=0; i<num_msgs; i++) {
+ struct tldap_message *msg = msgs[i];
+ char *dn = NULL;
+ bool ok;
+
+ if (tldap_msg_type(msg) != TLDAP_RES_SEARCH_ENTRY) {
+ continue;
+ }
+ ok = tldap_entry_dn(msg, &dn);
+ if (!ok) {
+ continue;
+ }
+ ok = idmap_ad_dn_filter(domain, dn);
+ if (!ok) {
+ DBG_DEBUG("%s filtered out\n", dn);
+ continue;
+ }
+
+ if (ctx->unix_primary_group) {
+ uint32_t gid;
+
+ ok = tldap_pull_uint32(msg, ctx->schema->gid, &gid);
+ if (ok) {
+ DBG_DEBUG("Setting primary group "
+ "to %"PRIu32" from attr %s\n",
+ gid, ctx->schema->gid);
+ info->primary_gid = gid;
+ }
+ }
+
+ if (ctx->unix_nss_info) {
+ char *attr;
+
+ attr = tldap_talloc_single_attribute(
+ msg, ctx->schema->dir, talloc_tos());
+ if (attr != NULL) {
+ info->homedir = talloc_move(info, &attr);
+ }
+ TALLOC_FREE(attr);
+
+ attr = tldap_talloc_single_attribute(
+ msg, ctx->schema->shell, talloc_tos());
+ if (attr != NULL) {
+ info->shell = talloc_move(info, &attr);
+ }
+ TALLOC_FREE(attr);
+
+ attr = tldap_talloc_single_attribute(
+ msg, ctx->schema->gecos, talloc_tos());
+ if (attr != NULL) {
+ info->full_name = talloc_move(info, &attr);
+ }
+ TALLOC_FREE(attr);
+ }
+ }
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS idmap_ad_query_user_retry(struct idmap_domain *domain,
+ struct wbint_userinfo *info)
+{
+ const NTSTATUS status_server_down =
+ NT_STATUS_LDAP(TLDAP_RC_V(TLDAP_SERVER_DOWN));
+ NTSTATUS status;
+
+ status = idmap_ad_query_user(domain, info);
+
+ if (NT_STATUS_EQUAL(status, status_server_down)) {
+ TALLOC_FREE(domain->private_data);
+ status = idmap_ad_query_user(domain, info);
+ }
+
+ return status;
+}
+
+static NTSTATUS idmap_ad_initialize(struct idmap_domain *dom)
+{
+ dom->query_user = idmap_ad_query_user_retry;
+ dom->private_data = NULL;
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS idmap_ad_get_context(struct idmap_domain *dom,
+ struct idmap_ad_context **pctx)
+{
+ struct idmap_ad_context *ctx = NULL;
+ NTSTATUS status;
+
+ if (IS_AD_DC) {
+ /*
+ * Make sure we never try to use LDAP against
+ * a trusted domain as AD_DC.
+ *
+ * This shouldn't be called currently,
+ * but you never know what happens in future.
+ */
+ return NT_STATUS_REQUEST_NOT_ACCEPTED;
+ }
+
+ if (dom->private_data != NULL) {
+ *pctx = talloc_get_type_abort(dom->private_data,
+ struct idmap_ad_context);
+ return NT_STATUS_OK;
+ }
+
+ status = idmap_ad_context_create(dom, dom, dom->name, &ctx);
+ if (!NT_STATUS_IS_OK(status)) {
+ DBG_DEBUG("idmap_ad_context_create failed: %s\n",
+ nt_errstr(status));
+ return status;
+ }
+
+ dom->private_data = ctx;
+ *pctx = ctx;
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS idmap_ad_unixids_to_sids(struct idmap_domain *dom,
+ struct id_map **ids)
+{
+ struct idmap_ad_context *ctx;
+ TLDAPRC rc;
+ NTSTATUS status;
+ struct tldap_message **msgs;
+
+ size_t i, num_msgs;
+ char *u_filter, *g_filter, *filter;
+
+ const char *attrs[] = {
+ "sAMAccountType",
+ "objectSid",
+ NULL, /* attr_uidnumber */
+ NULL, /* attr_gidnumber */
+ };
+
+ status = idmap_ad_get_context(dom, &ctx);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ attrs[2] = ctx->schema->uid;
+ attrs[3] = ctx->schema->gid;
+
+ u_filter = talloc_strdup(talloc_tos(), "");
+ if (u_filter == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ g_filter = talloc_strdup(talloc_tos(), "");
+ if (g_filter == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ for (i=0; ids[i] != NULL; i++) {
+ struct id_map *id = ids[i];
+
+ id->status = ID_UNKNOWN;
+
+ switch (id->xid.type) {
+ case ID_TYPE_UID: {
+ u_filter = talloc_asprintf_append_buffer(
+ u_filter, "(%s=%ju)", ctx->schema->uid,
+ (uintmax_t)id->xid.id);
+ if (u_filter == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ break;
+ }
+
+ case ID_TYPE_GID: {
+ g_filter = talloc_asprintf_append_buffer(
+ g_filter, "(%s=%ju)", ctx->schema->gid,
+ (uintmax_t)id->xid.id);
+ if (g_filter == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ break;
+ }
+
+ default:
+ DBG_WARNING("Unknown id type: %u\n",
+ (unsigned)id->xid.type);
+ break;
+ }
+ }
+
+ filter = talloc_strdup(talloc_tos(), "(|");
+ if (filter == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ if (*u_filter != '\0') {
+ filter = talloc_asprintf_append_buffer(
+ filter,
+ "(&(|(sAMAccountType=%d)(sAMAccountType=%d)"
+ "(sAMAccountType=%d))(|%s))",
+ ATYPE_NORMAL_ACCOUNT, ATYPE_WORKSTATION_TRUST,
+ ATYPE_INTERDOMAIN_TRUST, u_filter);
+ if (filter == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ }
+ TALLOC_FREE(u_filter);
+
+ if (*g_filter != '\0') {
+ filter = talloc_asprintf_append_buffer(
+ filter,
+ "(&(|(sAMAccountType=%d)(sAMAccountType=%d))(|%s))",
+ ATYPE_SECURITY_GLOBAL_GROUP,
+ ATYPE_SECURITY_LOCAL_GROUP,
+ g_filter);
+ if (filter == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ }
+ TALLOC_FREE(g_filter);
+
+ filter = talloc_asprintf_append_buffer(filter, ")");
+ if (filter == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ DBG_DEBUG("Filter: [%s]\n", filter);
+
+ rc = tldap_search(ctx->ld, ctx->default_nc, TLDAP_SCOPE_SUB, filter,
+ attrs, ARRAY_SIZE(attrs), 0, NULL, 0, NULL, 0,
+ 0, 0, 0, talloc_tos(), &msgs);
+ if (!TLDAP_RC_IS_SUCCESS(rc)) {
+ return NT_STATUS_LDAP(TLDAP_RC_V(rc));
+ }
+
+ TALLOC_FREE(filter);
+
+ num_msgs = talloc_array_length(msgs);
+
+ for (i=0; i<num_msgs; i++) {
+ struct tldap_message *msg = msgs[i];
+ char *dn;
+ struct id_map *map;
+ struct dom_sid sid;
+ size_t j;
+ bool ok;
+ uint32_t atype, xid;
+ enum id_type type;
+ struct dom_sid_buf sidbuf;
+
+ if (tldap_msg_type(msg) != TLDAP_RES_SEARCH_ENTRY) {
+ continue;
+ }
+
+ ok = tldap_entry_dn(msg, &dn);
+ if (!ok) {
+ DBG_DEBUG("No dn found in msg %zu\n", i);
+ continue;
+ }
+
+ ok = idmap_ad_dn_filter(dom, dn);
+ if (!ok) {
+ DBG_DEBUG("%s filtered out\n", dn);
+ continue;
+ }
+
+ ok = tldap_pull_uint32(msg, "sAMAccountType", &atype);
+ if (!ok) {
+ DBG_DEBUG("No atype in object %s\n", dn);
+ continue;
+ }
+
+ switch (atype & 0xF0000000) {
+ case ATYPE_SECURITY_GLOBAL_GROUP:
+ case ATYPE_SECURITY_LOCAL_GROUP:
+ type = ID_TYPE_GID;
+ break;
+ case ATYPE_NORMAL_ACCOUNT:
+ case ATYPE_WORKSTATION_TRUST:
+ case ATYPE_INTERDOMAIN_TRUST:
+ type = ID_TYPE_UID;
+ break;
+ default:
+ DBG_WARNING("unrecognized SAM account type %08x\n",
+ atype);
+ continue;
+ }
+
+ ok = tldap_pull_uint32(msg, (type == ID_TYPE_UID) ?
+ ctx->schema->uid : ctx->schema->gid,
+ &xid);
+ if (!ok) {
+ DBG_WARNING("No unix id in object %s\n", dn);
+ continue;
+ }
+
+ ok = tldap_pull_binsid(msg, "objectSid", &sid);
+ if (!ok) {
+ DBG_DEBUG("No objectSid in object %s\n", dn);
+ continue;
+ }
+
+ map = NULL;
+ for (j=0; ids[j]; j++) {
+ if ((type == ids[j]->xid.type) &&
+ (xid == ids[j]->xid.id)) {
+ map = ids[j];
+ break;
+ }
+ }
+ if (map == NULL) {
+ DBG_DEBUG("Got unexpected sid %s from object %s\n",
+ dom_sid_str_buf(&sid, &sidbuf),
+ dn);
+ continue;
+ }
+
+ sid_copy(map->sid, &sid);
+ map->status = ID_MAPPED;
+
+ DBG_DEBUG("Mapped %s -> %ju (%d)\n",
+ dom_sid_str_buf(map->sid, &sidbuf),
+ (uintmax_t)map->xid.id, map->xid.type);
+ }
+
+ TALLOC_FREE(msgs);
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS idmap_ad_sids_to_unixids(struct idmap_domain *dom,
+ struct id_map **ids)
+{
+ struct idmap_ad_context *ctx;
+ TLDAPRC rc;
+ NTSTATUS status;
+ struct tldap_message **msgs;
+
+ char *filter;
+ size_t i, num_msgs;
+
+ const char *attrs[] = {
+ "sAMAccountType",
+ "objectSid",
+ NULL, /* attr_uidnumber */
+ NULL, /* attr_gidnumber */
+ };
+
+ status = idmap_ad_get_context(dom, &ctx);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ attrs[2] = ctx->schema->uid;
+ attrs[3] = ctx->schema->gid;
+
+ filter = talloc_asprintf(
+ talloc_tos(),
+ "(&(|(sAMAccountType=%d)(sAMAccountType=%d)(sAMAccountType=%d)"
+ "(sAMAccountType=%d)(sAMAccountType=%d))(|",
+ ATYPE_NORMAL_ACCOUNT, ATYPE_WORKSTATION_TRUST,
+ ATYPE_INTERDOMAIN_TRUST, ATYPE_SECURITY_GLOBAL_GROUP,
+ ATYPE_SECURITY_LOCAL_GROUP);
+ if (filter == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ for (i=0; ids[i]; i++) {
+ char *sidstr;
+
+ ids[i]->status = ID_UNKNOWN;
+
+ sidstr = ldap_encode_ndr_dom_sid(talloc_tos(), ids[i]->sid);
+ if (sidstr == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ filter = talloc_asprintf_append_buffer(
+ filter, "(objectSid=%s)", sidstr);
+ TALLOC_FREE(sidstr);
+ if (filter == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ }
+
+ filter = talloc_asprintf_append_buffer(filter, "))");
+ if (filter == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ DBG_DEBUG("Filter: [%s]\n", filter);
+
+ rc = tldap_search(ctx->ld, ctx->default_nc, TLDAP_SCOPE_SUB, filter,
+ attrs, ARRAY_SIZE(attrs), 0, NULL, 0, NULL, 0,
+ 0, 0, 0, talloc_tos(), &msgs);
+ if (!TLDAP_RC_IS_SUCCESS(rc)) {
+ return NT_STATUS_LDAP(TLDAP_RC_V(rc));
+ }
+
+ TALLOC_FREE(filter);
+
+ num_msgs = talloc_array_length(msgs);
+
+ for (i=0; i<num_msgs; i++) {
+ struct tldap_message *msg = msgs[i];
+ char *dn;
+ struct id_map *map;
+ struct dom_sid sid;
+ size_t j;
+ bool ok;
+ uint64_t account_type, xid;
+ enum id_type type;
+ struct dom_sid_buf buf;
+
+ if (tldap_msg_type(msg) != TLDAP_RES_SEARCH_ENTRY) {
+ continue;
+ }
+
+ ok = tldap_entry_dn(msg, &dn);
+ if (!ok) {
+ DBG_DEBUG("No dn found in msg %zu\n", i);
+ continue;
+ }
+
+ ok = idmap_ad_dn_filter(dom, dn);
+ if (!ok) {
+ DBG_DEBUG("%s filtered out\n", dn);
+ continue;
+ }
+
+ ok = tldap_pull_binsid(msg, "objectSid", &sid);
+ if (!ok) {
+ DBG_DEBUG("No objectSid in object %s\n", dn);
+ continue;
+ }
+
+ map = NULL;
+ for (j=0; ids[j]; j++) {
+ if (dom_sid_equal(&sid, ids[j]->sid)) {
+ map = ids[j];
+ break;
+ }
+ }
+ if (map == NULL) {
+ DBG_DEBUG("Got unexpected sid %s from object %s\n",
+ dom_sid_str_buf(&sid, &buf),
+ dn);
+ continue;
+ }
+
+ ok = tldap_pull_uint64(msg, "sAMAccountType", &account_type);
+ if (!ok) {
+ DBG_DEBUG("No sAMAccountType in %s\n", dn);
+ continue;
+ }
+
+ switch (account_type & 0xF0000000) {
+ case ATYPE_SECURITY_GLOBAL_GROUP:
+ case ATYPE_SECURITY_LOCAL_GROUP:
+ type = ID_TYPE_GID;
+ break;
+ case ATYPE_NORMAL_ACCOUNT:
+ case ATYPE_WORKSTATION_TRUST:
+ case ATYPE_INTERDOMAIN_TRUST:
+ type = ID_TYPE_UID;
+ break;
+ default:
+ DBG_WARNING("unrecognized SAM account type %"PRIu64"\n",
+ account_type);
+ continue;
+ }
+
+ ok = tldap_pull_uint64(msg,
+ type == ID_TYPE_UID ?
+ ctx->schema->uid : ctx->schema->gid,
+ &xid);
+ if (!ok) {
+ DBG_DEBUG("No xid in %s\n", dn);
+ continue;
+ }
+
+ /* mapped */
+ map->xid.type = type;
+ map->xid.id = xid;
+ map->status = ID_MAPPED;
+
+ DEBUG(10, ("Mapped %s -> %lu (%d)\n",
+ dom_sid_str_buf(map->sid, &buf),
+ (unsigned long)map->xid.id, map->xid.type));
+ }
+
+ TALLOC_FREE(msgs);
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS idmap_ad_unixids_to_sids_retry(struct idmap_domain *dom,
+ struct id_map **ids)
+{
+ const NTSTATUS status_server_down =
+ NT_STATUS_LDAP(TLDAP_RC_V(TLDAP_SERVER_DOWN));
+ NTSTATUS status;
+
+ status = idmap_ad_unixids_to_sids(dom, ids);
+
+ if (NT_STATUS_EQUAL(status, status_server_down)) {
+ TALLOC_FREE(dom->private_data);
+ status = idmap_ad_unixids_to_sids(dom, ids);
+ }
+
+ return status;
+}
+
+static NTSTATUS idmap_ad_sids_to_unixids_retry(struct idmap_domain *dom,
+ struct id_map **ids)
+{
+ const NTSTATUS status_server_down =
+ NT_STATUS_LDAP(TLDAP_RC_V(TLDAP_SERVER_DOWN));
+ NTSTATUS status;
+
+ status = idmap_ad_sids_to_unixids(dom, ids);
+
+ if (NT_STATUS_EQUAL(status, status_server_down)) {
+ TALLOC_FREE(dom->private_data);
+ status = idmap_ad_sids_to_unixids(dom, ids);
+ }
+
+ return status;
+}
+
+static const struct idmap_methods ad_methods = {
+ .init = idmap_ad_initialize,
+ .unixids_to_sids = idmap_ad_unixids_to_sids_retry,
+ .sids_to_unixids = idmap_ad_sids_to_unixids_retry,
+};
+
+static_decl_idmap;
+NTSTATUS idmap_ad_init(TALLOC_CTX *ctx)
+{
+ NTSTATUS status;
+
+ status = smb_register_idmap(SMB_IDMAP_INTERFACE_VERSION,
+ "ad", &ad_methods);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ status = idmap_ad_nss_init(ctx);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ return NT_STATUS_OK;
+}
diff --git a/source3/winbindd/idmap_ad_nss.c b/source3/winbindd/idmap_ad_nss.c
new file mode 100644
index 0000000..3120280
--- /dev/null
+++ b/source3/winbindd/idmap_ad_nss.c
@@ -0,0 +1,418 @@
+/*
+ * idmap_ad: map between Active Directory and RFC 2307 or "Services for Unix" (SFU) Accounts
+ *
+ * Unix SMB/CIFS implementation.
+ *
+ * Winbind ADS backend functions
+ *
+ * Copyright (C) Andrew Tridgell 2001
+ * Copyright (C) Andrew Bartlett <abartlet@samba.org> 2003
+ * Copyright (C) Gerald (Jerry) Carter 2004-2007
+ * Copyright (C) Luke Howard 2001-2004
+ * Copyright (C) Michael Adam 2008,2010
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "includes.h"
+#include "winbindd.h"
+#include "../libds/common/flags.h"
+#include "winbindd_ads.h"
+#include "libads/ldap_schema.h"
+#include "nss_info.h"
+#include "idmap.h"
+#include "../libcli/ldap/ldap_ndr.h"
+#include "../libcli/security/security.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_IDMAP
+
+#define CHECK_ALLOC_DONE(mem) do { \
+ if (!mem) { \
+ DEBUG(0, ("Out of memory!\n")); \
+ ret = NT_STATUS_NO_MEMORY; \
+ goto done; \
+ } \
+} while (0)
+
+struct idmap_ad_context {
+ ADS_STRUCT *ads;
+ struct posix_schema *ad_schema;
+ enum wb_posix_mapping ad_map_type; /* WB_POSIX_MAP_UNKNOWN */
+};
+
+/************************************************************************
+ ***********************************************************************/
+
+static ADS_STATUS ad_idmap_cached_connection(struct idmap_domain *dom)
+{
+ ADS_STATUS status;
+ struct idmap_ad_context * ctx;
+
+ DEBUG(10, ("ad_idmap_cached_connection: called for domain '%s'\n",
+ dom->name));
+
+ ctx = talloc_get_type(dom->private_data, struct idmap_ad_context);
+
+ status = ads_idmap_cached_connection(dom->name, ctx, &ctx->ads);
+ if (!ADS_ERR_OK(status)) {
+ return status;
+ }
+
+ /* if we have a valid ADS_STRUCT and the schema model is
+ defined, then we can return here. */
+
+ if ( ctx->ad_schema ) {
+ return ADS_SUCCESS;
+ }
+
+ /* Otherwise, set the schema model */
+
+ if ( (ctx->ad_map_type == WB_POSIX_MAP_SFU) ||
+ (ctx->ad_map_type == WB_POSIX_MAP_SFU20) ||
+ (ctx->ad_map_type == WB_POSIX_MAP_RFC2307) )
+ {
+ status = ads_check_posix_schema_mapping(
+ ctx, ctx->ads, ctx->ad_map_type, &ctx->ad_schema);
+ if ( !ADS_ERR_OK(status) ) {
+ DEBUG(2,("ad_idmap_cached_connection: Failed to obtain schema details!\n"));
+ }
+ }
+
+ return status;
+}
+
+/*
+ * nss_info_{sfu,sfu20,rfc2307}
+ */
+
+/************************************************************************
+ Initialize the {sfu,sfu20,rfc2307} state
+ ***********************************************************************/
+
+static const char *wb_posix_map_unknown_string = "WB_POSIX_MAP_UNKNOWN";
+static const char *wb_posix_map_template_string = "WB_POSIX_MAP_TEMPLATE";
+static const char *wb_posix_map_sfu_string = "WB_POSIX_MAP_SFU";
+static const char *wb_posix_map_sfu20_string = "WB_POSIX_MAP_SFU20";
+static const char *wb_posix_map_rfc2307_string = "WB_POSIX_MAP_RFC2307";
+static const char *wb_posix_map_unixinfo_string = "WB_POSIX_MAP_UNIXINFO";
+
+static const char *ad_map_type_string(enum wb_posix_mapping map_type)
+{
+ switch (map_type) {
+ case WB_POSIX_MAP_TEMPLATE:
+ return wb_posix_map_template_string;
+ case WB_POSIX_MAP_SFU:
+ return wb_posix_map_sfu_string;
+ case WB_POSIX_MAP_SFU20:
+ return wb_posix_map_sfu20_string;
+ case WB_POSIX_MAP_RFC2307:
+ return wb_posix_map_rfc2307_string;
+ case WB_POSIX_MAP_UNIXINFO:
+ return wb_posix_map_unixinfo_string;
+ default:
+ return wb_posix_map_unknown_string;
+ }
+}
+
+static NTSTATUS nss_ad_generic_init(struct nss_domain_entry *e,
+ enum wb_posix_mapping new_ad_map_type)
+{
+ struct idmap_domain *dom;
+ struct idmap_ad_context *ctx;
+
+ if (e->state != NULL) {
+ dom = talloc_get_type(e->state, struct idmap_domain);
+ } else {
+ dom = talloc_zero(e, struct idmap_domain);
+ if (dom == NULL) {
+ DEBUG(0, ("Out of memory!\n"));
+ return NT_STATUS_NO_MEMORY;
+ }
+ e->state = dom;
+ }
+
+ if (e->domain != NULL) {
+ dom->name = talloc_strdup(dom, e->domain);
+ if (dom->name == NULL) {
+ DEBUG(0, ("Out of memory!\n"));
+ return NT_STATUS_NO_MEMORY;
+ }
+ }
+
+ if (dom->private_data != NULL) {
+ ctx = talloc_get_type(dom->private_data,
+ struct idmap_ad_context);
+ } else {
+ ctx = talloc_zero(dom, struct idmap_ad_context);
+ if (ctx == NULL) {
+ DEBUG(0, ("Out of memory!\n"));
+ return NT_STATUS_NO_MEMORY;
+ }
+ ctx->ad_map_type = WB_POSIX_MAP_RFC2307;
+ dom->private_data = ctx;
+ }
+
+ if ((ctx->ad_map_type != WB_POSIX_MAP_UNKNOWN) &&
+ (ctx->ad_map_type != new_ad_map_type))
+ {
+ DEBUG(2, ("nss_ad_generic_init: "
+ "Warning: overriding previously set posix map type "
+ "%s for domain %s with map type %s.\n",
+ ad_map_type_string(ctx->ad_map_type),
+ dom->name,
+ ad_map_type_string(new_ad_map_type)));
+ }
+
+ ctx->ad_map_type = new_ad_map_type;
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS nss_sfu_init( struct nss_domain_entry *e )
+{
+ return nss_ad_generic_init(e, WB_POSIX_MAP_SFU);
+}
+
+static NTSTATUS nss_sfu20_init( struct nss_domain_entry *e )
+{
+ return nss_ad_generic_init(e, WB_POSIX_MAP_SFU20);
+}
+
+static NTSTATUS nss_rfc2307_init( struct nss_domain_entry *e )
+{
+ return nss_ad_generic_init(e, WB_POSIX_MAP_RFC2307);
+}
+
+/**********************************************************************
+ *********************************************************************/
+
+static NTSTATUS nss_ad_map_to_alias(TALLOC_CTX *mem_ctx,
+ struct nss_domain_entry *e,
+ const char *name,
+ char **alias)
+{
+ const char *attrs[] = {NULL, /* attr_uid */
+ NULL };
+ char *filter = NULL;
+ LDAPMessage *msg = NULL;
+ ADS_STATUS ads_status = ADS_ERROR_NT(NT_STATUS_UNSUCCESSFUL);
+ NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
+ struct idmap_domain *dom;
+ struct idmap_ad_context *ctx = NULL;
+
+ /* Check incoming parameters */
+
+ if ( !e || !e->domain || !name || !*alias) {
+ nt_status = NT_STATUS_INVALID_PARAMETER;
+ goto done;
+ }
+
+ /* Only do query if we are online */
+
+ if (idmap_is_offline()) {
+ nt_status = NT_STATUS_FILE_IS_OFFLINE;
+ goto done;
+ }
+
+ dom = talloc_get_type(e->state, struct idmap_domain);
+ ctx = talloc_get_type(dom->private_data, struct idmap_ad_context);
+
+ ads_status = ad_idmap_cached_connection(dom);
+ if (!ADS_ERR_OK(ads_status)) {
+ return NT_STATUS_OBJECT_NAME_NOT_FOUND;
+ }
+
+ if (!ctx->ad_schema) {
+ nt_status = NT_STATUS_OBJECT_PATH_NOT_FOUND;
+ goto done;
+ }
+
+ attrs[0] = ctx->ad_schema->posix_uid_attr;
+
+ filter = talloc_asprintf(mem_ctx,
+ "(sAMAccountName=%s)",
+ name);
+ if (!filter) {
+ nt_status = NT_STATUS_NO_MEMORY;
+ goto done;
+ }
+
+ ads_status = ads_search_retry(ctx->ads, &msg, filter, attrs);
+ if (!ADS_ERR_OK(ads_status)) {
+ nt_status = ads_ntstatus(ads_status);
+ goto done;
+ }
+
+ *alias = ads_pull_string(ctx->ads, mem_ctx, msg, ctx->ad_schema->posix_uid_attr);
+
+ if (!*alias) {
+ return NT_STATUS_OBJECT_NAME_NOT_FOUND;
+ }
+
+ nt_status = NT_STATUS_OK;
+
+done:
+ if (filter) {
+ talloc_destroy(filter);
+ }
+ if (msg) {
+ ads_msgfree(ctx->ads, msg);
+ }
+
+ return nt_status;
+}
+
+/**********************************************************************
+ *********************************************************************/
+
+static NTSTATUS nss_ad_map_from_alias( TALLOC_CTX *mem_ctx,
+ struct nss_domain_entry *e,
+ const char *alias,
+ char **name )
+{
+ const char *attrs[] = {"sAMAccountName",
+ NULL };
+ char *filter = NULL;
+ LDAPMessage *msg = NULL;
+ ADS_STATUS ads_status = ADS_ERROR_NT(NT_STATUS_UNSUCCESSFUL);
+ NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
+ char *username = NULL;
+ struct idmap_domain *dom;
+ struct idmap_ad_context *ctx = NULL;
+
+ /* Check incoming parameters */
+
+ if ( !alias || !name) {
+ nt_status = NT_STATUS_INVALID_PARAMETER;
+ goto done;
+ }
+
+ /* Only do query if we are online */
+
+ if (idmap_is_offline()) {
+ nt_status = NT_STATUS_FILE_IS_OFFLINE;
+ goto done;
+ }
+
+ dom = talloc_get_type(e->state, struct idmap_domain);
+ ctx = talloc_get_type(dom->private_data, struct idmap_ad_context);
+
+ ads_status = ad_idmap_cached_connection(dom);
+ if (!ADS_ERR_OK(ads_status)) {
+ return NT_STATUS_OBJECT_NAME_NOT_FOUND;
+ }
+
+ if (!ctx->ad_schema) {
+ nt_status = NT_STATUS_OBJECT_PATH_NOT_FOUND;
+ goto done;
+ }
+
+ filter = talloc_asprintf(mem_ctx,
+ "(%s=%s)",
+ ctx->ad_schema->posix_uid_attr,
+ alias);
+ if (!filter) {
+ nt_status = NT_STATUS_NO_MEMORY;
+ goto done;
+ }
+
+ ads_status = ads_search_retry(ctx->ads, &msg, filter, attrs);
+ if (!ADS_ERR_OK(ads_status)) {
+ nt_status = ads_ntstatus(ads_status);
+ goto done;
+ }
+
+ username = ads_pull_string(ctx->ads, mem_ctx, msg,
+ "sAMAccountName");
+ if (!username) {
+ nt_status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
+ goto done;
+ }
+
+ *name = talloc_asprintf(mem_ctx, "%s\\%s",
+ lp_workgroup(),
+ username);
+ if (!*name) {
+ nt_status = NT_STATUS_NO_MEMORY;
+ goto done;
+ }
+
+ nt_status = NT_STATUS_OK;
+
+done:
+ TALLOC_FREE(username);
+ TALLOC_FREE(filter);
+ if (msg) {
+ ads_msgfree(ctx->ads, msg);
+ }
+
+ return nt_status;
+}
+
+/************************************************************************
+ Function dispatch tables for the idmap and nss plugins
+ ***********************************************************************/
+
+/* The SFU and RFC2307 NSS plugins share everything but the init
+ function which sets the intended schema model to use */
+
+static const struct nss_info_methods nss_rfc2307_methods = {
+ .init = nss_rfc2307_init,
+ .map_to_alias = nss_ad_map_to_alias,
+ .map_from_alias = nss_ad_map_from_alias,
+};
+
+static const struct nss_info_methods nss_sfu_methods = {
+ .init = nss_sfu_init,
+ .map_to_alias = nss_ad_map_to_alias,
+ .map_from_alias = nss_ad_map_from_alias,
+};
+
+static const struct nss_info_methods nss_sfu20_methods = {
+ .init = nss_sfu20_init,
+ .map_to_alias = nss_ad_map_to_alias,
+ .map_from_alias = nss_ad_map_from_alias,
+};
+
+
+
+/************************************************************************
+ Initialize the plugins
+ ***********************************************************************/
+
+NTSTATUS idmap_ad_nss_init(TALLOC_CTX *mem_ctx)
+{
+ NTSTATUS status;
+
+ status = smb_register_idmap_nss(SMB_NSS_INFO_INTERFACE_VERSION,
+ "rfc2307", &nss_rfc2307_methods);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ status = smb_register_idmap_nss(SMB_NSS_INFO_INTERFACE_VERSION,
+ "sfu", &nss_sfu_methods);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ status = smb_register_idmap_nss(SMB_NSS_INFO_INTERFACE_VERSION,
+ "sfu20", &nss_sfu20_methods);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ return NT_STATUS_OK;
+}
diff --git a/source3/winbindd/idmap_autorid.c b/source3/winbindd/idmap_autorid.c
new file mode 100644
index 0000000..bf5947a
--- /dev/null
+++ b/source3/winbindd/idmap_autorid.c
@@ -0,0 +1,945 @@
+/*
+ * idmap_autorid: static map between Active Directory/NT RIDs
+ * and RFC 2307 accounts
+ *
+ * based on the idmap_rid module, but this module defines the ranges
+ * for the domains by automatically allocating a range for each domain
+ *
+ * Copyright (C) Christian Ambach, 2010-2012
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+/*
+ * This module allocates ranges for domains to be used in a
+ * algorithmic mode like idmap_rid. Multiple ranges are supported
+ * for a single domain: If a rid exceeds the range size, a matching
+ * range is allocated to hold the rid's id.
+ *
+ * Here are the formulas applied:
+ *
+ *
+ * For a sid of the form domain_sid-rid, we have
+ *
+ * rid = reduced_rid + domain_range_index * range_size
+ *
+ * with
+ * reduced_rid := rid % range_size
+ * domain_range_index := rid / range_size
+ *
+ * And reduced_rid fits into a range.
+ *
+ * In the database, we associate a range_number to
+ * the pair domain_sid,domain_range_index.
+ *
+ * Now the unix id for the given sid calculates as:
+ *
+ * id = reduced_rid + range_low_id
+ *
+ * with
+ *
+ * range_low_id = low_id + range_number * range_size
+ *
+ *
+ * The inverse calculation goes like this:
+ *
+ * Given a unix id, let
+ *
+ * normalized_id := id - low_id
+ * reduced_rid := normalized_id % range_size
+ * range_number = normalized_id / range_size
+ *
+ * Then we have
+ *
+ * id = reduced_rid + low_id + range_number * range_size
+ *
+ * From the database, get the domain_sid,domain_range_index pair
+ * belonging to the range_number (if there is already one).
+ *
+ * Then the rid for the unix id calculates as:
+ *
+ * rid = reduced_rid + domain_range_index * range_size
+ */
+
+#include "idmap_autorid_tdb.h"
+#include "winbindd.h"
+#include "idmap.h"
+#include "idmap_rw.h"
+#include "../libcli/security/dom_sid.h"
+#include "libsmb/samlogon_cache.h"
+#include "passdb/machine_sid.h"
+#include "lib/util/string_wrappers.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_IDMAP
+
+#define IDMAP_AUTORID_ALLOC_RESERVED 500
+
+/* handle to the tdb storing domain <-> range assignments */
+static struct db_context *autorid_db;
+
+static bool ignore_builtin = false;
+
+static NTSTATUS idmap_autorid_get_alloc_range(struct idmap_domain *dom,
+ struct autorid_range_config *range)
+{
+ NTSTATUS status;
+
+ ZERO_STRUCT(*range);
+
+ fstrcpy(range->domsid, ALLOC_RANGE);
+
+ status = idmap_autorid_get_domainrange(autorid_db,
+ range,
+ dom->read_only);
+
+ return status;
+}
+
+static NTSTATUS idmap_autorid_allocate_id(struct idmap_domain *dom,
+ struct unixid *xid) {
+
+ NTSTATUS ret;
+ struct autorid_range_config range;
+
+ if (dom->read_only) {
+ DEBUG(3, ("Backend is read-only, refusing "
+ "new allocation request\n"));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ /* fetch the range for the allocation pool */
+
+ ret = idmap_autorid_get_alloc_range(dom, &range);
+ if (!NT_STATUS_IS_OK(ret)) {
+ DEBUG(3, ("Could not determine range for allocation pool, "
+ "check previous messages for reason\n"));
+ return ret;
+ }
+
+ ret = idmap_tdb_common_get_new_id(dom, xid);
+
+ if (!NT_STATUS_IS_OK(ret)) {
+ DEBUG(1, ("Fatal error while allocating new ID!\n"));
+ return ret;
+ }
+
+ xid->id = xid->id + range.low_id;
+
+ DEBUG(10, ("Returned new %s %d from allocation range\n",
+ (xid->type==ID_TYPE_UID)?"uid":"gid", xid->id));
+
+ return ret;
+}
+
+/*
+ * map a xid to SID using the idmap_tdb like pool
+ */
+static NTSTATUS idmap_autorid_id_to_sid_alloc(struct idmap_domain *dom,
+ struct id_map *map)
+{
+ NTSTATUS ret;
+
+ /* look out for the mapping */
+ ret = idmap_tdb_common_unixid_to_sid(dom, map);
+
+ if (NT_STATUS_IS_OK(ret)) {
+ map->status = ID_MAPPED;
+ return ret;
+ }
+
+ map->status = ID_UNKNOWN;
+
+ DEBUG(10, ("no ID->SID mapping for %d could be found\n", map->xid.id));
+
+ return ret;
+}
+
+static NTSTATUS idmap_autorid_id_to_sid(struct autorid_global_config *cfg,
+ struct idmap_domain *dom,
+ struct id_map *map)
+{
+ uint32_t range_number;
+ uint32_t domain_range_index;
+ uint32_t normalized_id;
+ uint32_t reduced_rid;
+ uint32_t rid;
+ TDB_DATA data = tdb_null;
+ char *keystr;
+ struct dom_sid domsid;
+ NTSTATUS status;
+ bool ok;
+ const char *q = NULL;
+
+ /* can this be one of our ids? */
+ if (map->xid.id < cfg->minvalue) {
+ DEBUG(10, ("id %d is lower than minimum value, "
+ "ignoring mapping request\n", map->xid.id));
+ map->status = ID_UNKNOWN;
+ return NT_STATUS_OK;
+ }
+
+ if (map->xid.id > (cfg->minvalue + cfg->rangesize * cfg->maxranges)) {
+ DEBUG(10, ("id %d is outside of maximum id value, "
+ "ignoring mapping request\n", map->xid.id));
+ map->status = ID_UNKNOWN;
+ return NT_STATUS_OK;
+ }
+
+ /* determine the range of this uid */
+
+ normalized_id = map->xid.id - cfg->minvalue;
+ range_number = normalized_id / cfg->rangesize;
+
+ keystr = talloc_asprintf(talloc_tos(), "%u", range_number);
+ if (!keystr) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ status = dbwrap_fetch_bystring(autorid_db, talloc_tos(), keystr, &data);
+ TALLOC_FREE(keystr);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(4, ("id %d belongs to range %d which does not have "
+ "domain mapping, ignoring mapping request\n",
+ map->xid.id, range_number));
+ TALLOC_FREE(data.dptr);
+ map->status = ID_UNKNOWN;
+ return NT_STATUS_OK;
+ }
+
+ if ((data.dsize == 0) || (data.dptr[data.dsize-1] != '\0')) {
+ DBG_WARNING("Invalid range %"PRIu32"\n", range_number);
+ TALLOC_FREE(data.dptr);
+ map->status = ID_UNKNOWN;
+ return NT_STATUS_OK;
+ }
+
+ if (strncmp((const char *)data.dptr,
+ ALLOC_RANGE,
+ strlen(ALLOC_RANGE)) == 0) {
+ /*
+ * this is from the alloc range, check if there is a mapping
+ */
+ DEBUG(5, ("id %d belongs to allocation range, "
+ "checking for mapping\n",
+ map->xid.id));
+ TALLOC_FREE(data.dptr);
+ return idmap_autorid_id_to_sid_alloc(dom, map);
+ }
+
+ ok = dom_sid_parse_endp((const char *)data.dptr, &domsid, &q);
+ if (!ok) {
+ TALLOC_FREE(data.dptr);
+ map->status = ID_UNKNOWN;
+ return NT_STATUS_OK;
+ }
+
+ /*
+ * Allow for sid#range_index, just sid is range index 0
+ */
+
+ switch (*q) {
+ case '\0':
+ domain_range_index = 0;
+ break;
+ case '#':
+ if (sscanf(q+1, "%"SCNu32, &domain_range_index) == 1) {
+ break;
+ }
+ /* If we end up here, something weird is in the record. */
+
+ FALL_THROUGH;
+ default:
+ DBG_DEBUG("SID/domain range: %s\n",
+ (const char *)data.dptr);
+ TALLOC_FREE(data.dptr);
+ map->status = ID_UNKNOWN;
+ return NT_STATUS_OK;
+ }
+
+ TALLOC_FREE(data.dptr);
+
+ reduced_rid = normalized_id % cfg->rangesize;
+ rid = reduced_rid + domain_range_index * cfg->rangesize;
+
+ sid_compose(map->sid, &domsid, rid);
+
+ /* We **really** should have some way of validating
+ the SID exists and is the correct type here. But
+ that is a deficiency in the idmap_rid design. */
+
+ map->status = ID_MAPPED;
+ map->xid.type = ID_TYPE_BOTH;
+
+ return NT_STATUS_OK;
+}
+
+/**********************************
+ Single sid to id lookup function.
+**********************************/
+
+static NTSTATUS idmap_autorid_sid_to_id_rid(
+ uint32_t rangesize,
+ uint32_t low_id,
+ struct id_map *map)
+{
+ uint32_t rid;
+ uint32_t reduced_rid;
+
+ sid_peek_rid(map->sid, &rid);
+
+ reduced_rid = rid % rangesize;
+
+ map->xid.id = reduced_rid + low_id;
+ map->xid.type = ID_TYPE_BOTH;
+ map->status = ID_MAPPED;
+
+ return NT_STATUS_OK;
+}
+
+/**********************************
+ lookup a set of unix ids.
+**********************************/
+
+static NTSTATUS idmap_autorid_unixids_to_sids(struct idmap_domain *dom,
+ struct id_map **ids)
+{
+ struct idmap_tdb_common_context *commoncfg;
+ struct autorid_global_config *globalcfg;
+ NTSTATUS ret;
+ int i;
+ int num_tomap = 0;
+ int num_mapped = 0;
+
+ /* initialize the status to avoid surprise */
+ for (i = 0; ids[i]; i++) {
+ ids[i]->status = ID_UNKNOWN;
+ num_tomap++;
+ }
+
+ commoncfg =
+ talloc_get_type_abort(dom->private_data,
+ struct idmap_tdb_common_context);
+
+ globalcfg = talloc_get_type(commoncfg->private_data,
+ struct autorid_global_config);
+
+ for (i = 0; ids[i]; i++) {
+
+ ret = idmap_autorid_id_to_sid(globalcfg, dom, ids[i]);
+
+ if ((!NT_STATUS_IS_OK(ret)) &&
+ (!NT_STATUS_EQUAL(ret, NT_STATUS_NONE_MAPPED))) {
+ /* some fatal error occurred, log it */
+ DBG_NOTICE("Unexpected error resolving an ID "
+ "(%d): %s\n", ids[i]->xid.id,
+ nt_errstr(ret));
+ goto failure;
+ }
+
+ if (NT_STATUS_IS_OK(ret) && ids[i]->status == ID_MAPPED) {
+ num_mapped++;
+ }
+
+ }
+
+ if (num_tomap == num_mapped) {
+ return NT_STATUS_OK;
+ }
+ if (num_mapped == 0) {
+ return NT_STATUS_NONE_MAPPED;
+ }
+
+ return STATUS_SOME_UNMAPPED;
+
+
+ failure:
+ return ret;
+}
+
+static bool idmap_autorid_sid_is_special(struct dom_sid *sid)
+{
+ bool match;
+
+ match = sid_check_is_in_wellknown_domain(sid);
+ if (match) {
+ return true;
+ }
+
+ return false;
+}
+
+static NTSTATUS idmap_autorid_sid_to_id_special(struct idmap_domain *dom,
+ struct id_map *map)
+{
+ struct idmap_tdb_common_context *common =
+ talloc_get_type_abort(dom->private_data,
+ struct idmap_tdb_common_context);
+ uint32_t count;
+ struct autorid_range_config range;
+ NTSTATUS status;
+ uint32_t free_id;
+
+ status = idmap_autorid_get_alloc_range(dom, &range);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ /* Take the next free ID, counting from the top */
+ free_id = 0;
+ for (count = 0; count < IDMAP_AUTORID_ALLOC_RESERVED; count++) {
+ struct id_map test_map;
+ struct dom_sid sid;
+
+ test_map.sid = &sid;
+ test_map.xid.type = map->xid.type;
+ test_map.xid.id = range.high_id - count;
+ test_map.status = ID_UNKNOWN;
+
+ status = idmap_tdb_common_unixid_to_sid(dom, &test_map);
+ if (NT_STATUS_EQUAL(NT_STATUS_NONE_MAPPED, status)) {
+ free_id = test_map.xid.id;
+ break;
+ }
+
+ if (!NT_STATUS_IS_OK(status)) {
+ /* error - get out */
+ return status;
+ }
+
+ /* mapping exists - try next ID */
+ }
+
+ if (free_id == 0) {
+ return NT_STATUS_NONE_MAPPED;
+ }
+
+ map->status = ID_MAPPED;
+ map->xid.id = free_id;
+
+ status = common->rw_ops->set_mapping(dom, map);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(2, ("Error storing new mapping: %s\n",
+ nt_errstr(status)));
+ return status;
+ }
+
+ return NT_STATUS_OK;
+}
+
+struct idmap_autorid_sid_to_id_alloc_ctx {
+ struct idmap_domain *dom;
+ struct id_map *map;
+};
+
+static NTSTATUS idmap_autorid_sid_to_id_alloc_action(
+ struct db_context *db,
+ void *private_data)
+{
+ struct idmap_autorid_sid_to_id_alloc_ctx *ctx;
+
+ ctx = (struct idmap_autorid_sid_to_id_alloc_ctx *)private_data;
+
+ if (idmap_autorid_sid_is_special(ctx->map->sid)) {
+ struct dom_sid_buf buf;
+ NTSTATUS ret;
+
+ ret = idmap_autorid_sid_to_id_special(ctx->dom, ctx->map);
+ if (NT_STATUS_IS_OK(ret)) {
+ return NT_STATUS_OK;
+ }
+ if (!NT_STATUS_EQUAL(NT_STATUS_NONE_MAPPED, ret)) {
+ return ret;
+ }
+
+ DEBUG(10, ("Special sid %s not mapped. falling back to "
+ "regular allocation\n",
+ dom_sid_str_buf(ctx->map->sid, &buf)));
+ }
+
+ return idmap_tdb_common_new_mapping(ctx->dom, ctx->map);
+}
+
+/*
+ * map a SID to xid using the idmap_tdb like pool
+ */
+static NTSTATUS idmap_autorid_sid_to_id_alloc(
+ struct idmap_tdb_common_context *ctx,
+ struct idmap_domain *dom,
+ struct id_map *map)
+{
+ NTSTATUS ret;
+ struct idmap_autorid_sid_to_id_alloc_ctx alloc_ctx;
+ struct dom_sid_buf buf;
+
+ map->status = ID_UNKNOWN;
+
+ /* see if we already have a mapping */
+ ret = idmap_tdb_common_sid_to_unixid(dom, map);
+
+ if (NT_STATUS_IS_OK(ret)) {
+ map->status = ID_MAPPED;
+ return ret;
+ }
+
+ /* bad things happened */
+ if (!NT_STATUS_EQUAL(ret, NT_STATUS_NONE_MAPPED)) {
+ DEBUG(1, ("Looking up SID->ID mapping for %s failed: %s\n",
+ dom_sid_str_buf(map->sid, &buf),
+ nt_errstr(ret)));
+ return ret;
+ }
+
+ if (dom->read_only) {
+ DEBUG(3, ("Not allocating new mapping for %s, because backend "
+ "is read-only\n",
+ dom_sid_str_buf(map->sid, &buf)));
+ map->status = ID_UNMAPPED;
+ return NT_STATUS_NONE_MAPPED;
+ }
+
+ DEBUG(10, ("Creating new mapping in pool for %s\n",
+ dom_sid_str_buf(map->sid, &buf)));
+
+ alloc_ctx.dom = dom;
+ alloc_ctx.map = map;
+
+ ret = dbwrap_trans_do(ctx->db, idmap_autorid_sid_to_id_alloc_action,
+ &alloc_ctx);
+ if (!NT_STATUS_IS_OK(ret)) {
+ DEBUG(1, ("Failed to create a new mapping in alloc range: %s\n",
+ nt_errstr(ret)));
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+
+ map->status = ID_MAPPED;
+ return NT_STATUS_OK;
+}
+
+static bool idmap_autorid_domsid_is_for_alloc(struct dom_sid *sid)
+{
+ bool match;
+
+ match = sid_check_is_wellknown_domain(sid, NULL);
+ if (match) {
+ return true;
+ }
+
+ return false;
+}
+
+static NTSTATUS idmap_autorid_sid_to_id(struct idmap_tdb_common_context *common,
+ struct idmap_domain *dom,
+ struct id_map *map)
+{
+ struct autorid_global_config *global =
+ talloc_get_type_abort(common->private_data,
+ struct autorid_global_config);
+ struct autorid_range_config range;
+ uint32_t rid;
+ struct dom_sid domainsid;
+ struct dom_sid_buf buf;
+ NTSTATUS ret;
+
+ ZERO_STRUCT(range);
+ map->status = ID_UNKNOWN;
+
+ DEBUG(10, ("Trying to map %s\n", dom_sid_str_buf(map->sid, &buf)));
+
+ sid_copy(&domainsid, map->sid);
+ if (!sid_split_rid(&domainsid, &rid)) {
+ DEBUG(4, ("Could not determine domain SID from %s, "
+ "ignoring mapping request\n",
+ dom_sid_str_buf(map->sid, &buf)));
+ map->status = ID_UNMAPPED;
+ return NT_STATUS_NONE_MAPPED;
+ }
+
+ if (idmap_autorid_domsid_is_for_alloc(&domainsid)) {
+ DEBUG(10, ("SID %s is for ALLOC range.\n",
+ dom_sid_str_buf(map->sid, &buf)));
+
+ return idmap_autorid_sid_to_id_alloc(common, dom, map);
+ }
+
+ if (dom_sid_equal(&domainsid, &global_sid_Builtin) && ignore_builtin) {
+ DEBUG(10, ("Ignoring request for BUILTIN domain\n"));
+ map->status = ID_UNMAPPED;
+ return NT_STATUS_NONE_MAPPED;
+ }
+
+ sid_to_fstring(range.domsid, &domainsid);
+
+ range.domain_range_index = rid / (global->rangesize);
+
+ ret = idmap_autorid_getrange(autorid_db, range.domsid,
+ range.domain_range_index,
+ &range.rangenum, &range.low_id);
+ if (NT_STATUS_IS_OK(ret)) {
+ return idmap_autorid_sid_to_id_rid(
+ global->rangesize, range.low_id, map);
+ }
+
+ if (dom->read_only) {
+ DBG_DEBUG("read-only is enabled, did not allocate "
+ "new range for domain %s\n", range.domsid);
+ map->status = ID_UNMAPPED;
+ return NT_STATUS_NONE_MAPPED;
+ }
+
+ /*
+ * Check if we should allocate a domain range. We need to
+ * protect against unknown domains to not fill our ranges
+ * needlessly.
+ */
+
+ if (sid_check_is_builtin(&domainsid) ||
+ sid_check_is_our_sam(&domainsid)) {
+ goto allocate;
+ }
+
+ {
+ struct winbindd_domain *domain;
+
+ /*
+ * Deterministic check for domain members: We can be
+ * sure that the domain we are member of is worth to
+ * add a mapping for.
+ */
+
+ domain = find_our_domain();
+ if ((domain != NULL) &&
+ dom_sid_equal(&domain->sid, &domainsid)) {
+ goto allocate;
+ }
+ }
+
+ /*
+ * If we have already allocated range index 0, this domain is
+ * worth allocating for in higher ranges.
+ */
+ if (range.domain_range_index != 0) {
+ uint32_t zero_rangenum, zero_low_id;
+
+ ret = idmap_autorid_getrange(autorid_db, range.domsid, 0,
+ &zero_rangenum, &zero_low_id);
+ if (NT_STATUS_IS_OK(ret)) {
+ goto allocate;
+ }
+ }
+
+ /*
+ * If the caller already did a lookup sid and made sure the
+ * domain sid is valid, we can allocate a new range.
+ *
+ * Currently the winbindd parent already does a lookup sids
+ * first, but hopefully changes in future. If the
+ * caller knows the domain sid, ID_TYPE_BOTH should be
+ * passed instead of ID_TYPE_NOT_SPECIFIED.
+ */
+ if (map->xid.type != ID_TYPE_NOT_SPECIFIED) {
+ goto allocate;
+ }
+
+ /*
+ * Check of last resort: A domain is valid if a user from that
+ * domain has recently logged in. The samlogon_cache these
+ * days also stores the domain sid.
+ *
+ * We used to check the list of trusted domains we received
+ * from "our" dc, but this is not reliable enough.
+ */
+ if (netsamlogon_cache_have(&domainsid)) {
+ goto allocate;
+ }
+
+ /*
+ * Nobody knows this domain, so refuse to allocate a fresh
+ * range.
+ */
+
+ DBG_NOTICE("Allocating range for domain %s required type_hint\n", range.domsid);
+ map->status = ID_REQUIRE_TYPE;
+ return NT_STATUS_SOME_NOT_MAPPED;
+
+allocate:
+ ret = idmap_autorid_acquire_range(autorid_db, &range);
+ if (!NT_STATUS_IS_OK(ret)) {
+ DBG_NOTICE("Could not determine range for domain: %s, "
+ "check previous messages for reason\n",
+ nt_errstr(ret));
+ return ret;
+ }
+
+ return idmap_autorid_sid_to_id_rid(global->rangesize, range.low_id,
+ map);
+}
+
+/**********************************
+ lookup a set of sids.
+**********************************/
+
+static NTSTATUS idmap_autorid_sids_to_unixids(struct idmap_domain *dom,
+ struct id_map **ids)
+{
+ struct idmap_tdb_common_context *commoncfg;
+ NTSTATUS ret;
+ size_t i;
+ size_t num_tomap = 0;
+ size_t num_mapped = 0;
+ size_t num_required = 0;
+
+ /* initialize the status to avoid surprise */
+ for (i = 0; ids[i]; i++) {
+ ids[i]->status = ID_UNKNOWN;
+ num_tomap++;
+ }
+
+ commoncfg =
+ talloc_get_type_abort(dom->private_data,
+ struct idmap_tdb_common_context);
+
+ for (i = 0; ids[i]; i++) {
+ ret = idmap_autorid_sid_to_id(commoncfg, dom, ids[i]);
+ if (NT_STATUS_EQUAL(ret, NT_STATUS_SOME_NOT_MAPPED) &&
+ ids[i]->status == ID_REQUIRE_TYPE)
+ {
+ num_required++;
+ continue;
+ }
+ if ((!NT_STATUS_IS_OK(ret)) &&
+ (!NT_STATUS_EQUAL(ret, NT_STATUS_NONE_MAPPED))) {
+ struct dom_sid_buf buf;
+ /* some fatal error occurred, log it */
+ DEBUG(3, ("Unexpected error resolving a SID (%s)\n",
+ dom_sid_str_buf(ids[i]->sid, &buf)));
+ return ret;
+ }
+
+ if (NT_STATUS_IS_OK(ret) && ids[i]->status == ID_MAPPED) {
+ num_mapped++;
+ }
+ }
+
+ if (num_tomap == num_mapped) {
+ return NT_STATUS_OK;
+ } else if (num_required > 0) {
+ return STATUS_SOME_UNMAPPED;
+ } else if (num_mapped == 0) {
+ return NT_STATUS_NONE_MAPPED;
+ }
+
+ return STATUS_SOME_UNMAPPED;
+}
+
+static NTSTATUS idmap_autorid_preallocate_wellknown(struct idmap_domain *dom)
+{
+ const char *groups[] = { "S-1-1-0", "S-1-2-0", "S-1-2-1",
+ "S-1-3-0", "S-1-3-1", "S-1-3-2", "S-1-3-3", "S-1-3-4",
+ "S-1-5-1", "S-1-5-2", "S-1-5-3", "S-1-5-4", "S-1-5-6",
+ "S-1-5-7", "S-1-5-8", "S-1-5-9", "S-1-5-10", "S-1-5-11",
+ "S-1-5-12", "S-1-5-13", "S-1-5-14", "S-1-5-15",
+ "S-1-5-17", "S-1-5-18", "S-1-5-19", "S-1-5-20"
+ };
+
+ struct id_map **maps;
+ int i, num;
+ NTSTATUS status;
+
+ if (dom->read_only) {
+ return NT_STATUS_OK;
+ }
+
+ num = ARRAY_SIZE(groups);
+
+ maps = talloc_array(talloc_tos(), struct id_map*, num+1);
+ if (!maps) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ for (i = 0; i < num; i++) {
+ maps[i] = talloc(maps, struct id_map);
+ if (maps[i] == NULL) {
+ talloc_free(maps);
+ return NT_STATUS_NO_MEMORY;
+ }
+ maps[i]->xid.type = ID_TYPE_GID;
+ maps[i]->sid = dom_sid_parse_talloc(maps, groups[i]);
+ }
+
+ maps[num] = NULL;
+
+ status = idmap_autorid_sids_to_unixids(dom, maps);
+
+ DEBUG(10,("Preallocation run finished with status %s\n",
+ nt_errstr(status)));
+
+ talloc_free(maps);
+
+ return NT_STATUS_IS_OK(status)?NT_STATUS_OK:NT_STATUS_UNSUCCESSFUL;
+}
+
+static NTSTATUS idmap_autorid_initialize_action(struct db_context *db,
+ void *private_data)
+{
+ struct idmap_domain *dom;
+ struct idmap_tdb_common_context *common;
+ struct autorid_global_config *config;
+ NTSTATUS status;
+
+ dom = (struct idmap_domain *)private_data;
+ common = (struct idmap_tdb_common_context *)dom->private_data;
+ config = (struct autorid_global_config *)common->private_data;
+
+ status = idmap_autorid_init_hwms(db);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ status = idmap_autorid_saveconfig(db, config);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(1, ("Failed to store configuration data!\n"));
+ return status;
+ }
+
+ status = idmap_autorid_preallocate_wellknown(dom);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(1, ("Failed to preallocate wellknown sids: %s\n",
+ nt_errstr(status)));
+ return status;
+ }
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS idmap_autorid_initialize(struct idmap_domain *dom)
+{
+ struct idmap_tdb_common_context *commonconfig;
+ struct autorid_global_config *config;
+ NTSTATUS status;
+ char *db_path;
+
+ if (!strequal(dom->name, "*")) {
+ DEBUG(0, ("idmap_autorid_initialize: Error: autorid configured "
+ "for domain '%s'. But autorid can only be used for "
+ "the default idmap configuration.\n", dom->name));
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ commonconfig = talloc_zero(dom, struct idmap_tdb_common_context);
+ if (!commonconfig) {
+ DEBUG(0, ("Out of memory!\n"));
+ return NT_STATUS_NO_MEMORY;
+ }
+ dom->private_data = commonconfig;
+
+ commonconfig->rw_ops = talloc_zero(commonconfig, struct idmap_rw_ops);
+ if (commonconfig->rw_ops == NULL) {
+ DEBUG(0, ("Out of memory!\n"));
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ config = talloc_zero(commonconfig, struct autorid_global_config);
+ if (!config) {
+ DEBUG(0, ("Out of memory!\n"));
+ return NT_STATUS_NO_MEMORY;
+ }
+ commonconfig->private_data = config;
+
+ config->minvalue = dom->low_id;
+ config->rangesize = idmap_config_int("*", "rangesize", 100000);
+
+ config->maxranges = (dom->high_id - dom->low_id + 1) /
+ config->rangesize;
+
+ if (config->maxranges < 2) {
+ DBG_WARNING("Allowed idmap range is not a least double the "
+ "size of the rangesize. Please increase idmap "
+ "range.\n");
+ status = NT_STATUS_INVALID_PARAMETER;
+ goto error;
+ }
+
+ /* check if the high-low limit is a multiple of the rangesize */
+ if ((dom->high_id - dom->low_id + 1) % config->rangesize != 0) {
+ DEBUG(5, ("High uid-low uid difference of %d "
+ "is not a multiple of the rangesize %d, "
+ "limiting ranges to lower boundary number of %d\n",
+ (dom->high_id - dom->low_id + 1), config->rangesize,
+ config->maxranges));
+ }
+
+ DEBUG(5, ("%d domain ranges with a size of %d are available\n",
+ config->maxranges, config->rangesize));
+
+ ignore_builtin = idmap_config_bool("*", "ignore builtin", false);
+
+ /* fill the TDB common configuration */
+
+ commonconfig->max_id = config->rangesize - 1
+ - IDMAP_AUTORID_ALLOC_RESERVED;
+ commonconfig->hwmkey_uid = ALLOC_HWM_UID;
+ commonconfig->hwmkey_gid = ALLOC_HWM_GID;
+ commonconfig->rw_ops->get_new_id = idmap_autorid_allocate_id;
+ commonconfig->rw_ops->set_mapping = idmap_tdb_common_set_mapping;
+
+ db_path = state_path(talloc_tos(), "autorid.tdb");
+ if (db_path == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ goto error;
+ }
+
+ status = idmap_autorid_db_open(db_path,
+ NULL, /* TALLOC_CTX */
+ &autorid_db);
+ TALLOC_FREE(db_path);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto error;
+ }
+
+ commonconfig->db = autorid_db;
+
+ status = dbwrap_trans_do(autorid_db,
+ idmap_autorid_initialize_action,
+ dom);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(1, ("Failed to init the idmap database: %s\n",
+ nt_errstr(status)));
+ goto error;
+ }
+
+ goto done;
+
+error:
+ talloc_free(config);
+
+done:
+ return status;
+}
+
+static const struct idmap_methods autorid_methods = {
+ .init = idmap_autorid_initialize,
+ .unixids_to_sids = idmap_autorid_unixids_to_sids,
+ .sids_to_unixids = idmap_autorid_sids_to_unixids,
+ .allocate_id = idmap_autorid_allocate_id
+};
+
+static_decl_idmap;
+NTSTATUS idmap_autorid_init(TALLOC_CTX *ctx)
+{
+ return smb_register_idmap(SMB_IDMAP_INTERFACE_VERSION,
+ "autorid", &autorid_methods);
+}
diff --git a/source3/winbindd/idmap_autorid_tdb.c b/source3/winbindd/idmap_autorid_tdb.c
new file mode 100644
index 0000000..6c76764
--- /dev/null
+++ b/source3/winbindd/idmap_autorid_tdb.c
@@ -0,0 +1,1269 @@
+/*
+ * idmap_autorid_tdb: This file contains common code used by
+ * idmap_autorid and net idmap autorid utilities. The common
+ * code provides functions for performing various operations
+ * on autorid.tdb
+ *
+ * Copyright (C) Christian Ambach, 2010-2012
+ * Copyright (C) Atul Kulkarni, 2013
+ * Copyright (C) Michael Adam, 2012-2013
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "idmap_autorid_tdb.h"
+#include "../libcli/security/dom_sid.h"
+#include "lib/util/string_wrappers.h"
+
+/**
+ * Build the database keystring for getting a range
+ * belonging to a domain sid and a range index.
+ */
+static void idmap_autorid_build_keystr(const char *domsid,
+ uint32_t domain_range_index,
+ fstring keystr)
+{
+ if (domain_range_index > 0) {
+ fstr_sprintf(keystr, "%s#%"PRIu32,
+ domsid, domain_range_index);
+ } else {
+ fstrcpy(keystr, domsid);
+ }
+}
+
+static char *idmap_autorid_build_keystr_talloc(TALLOC_CTX *mem_ctx,
+ const char *domsid,
+ uint32_t domain_range_index)
+{
+ char *keystr;
+
+ if (domain_range_index > 0) {
+ keystr = talloc_asprintf(mem_ctx, "%s#%"PRIu32, domsid,
+ domain_range_index);
+ } else {
+ keystr = talloc_strdup(mem_ctx, domsid);
+ }
+
+ return keystr;
+}
+
+
+static bool idmap_autorid_validate_sid(const char *sid)
+{
+ struct dom_sid ignore;
+ if (sid == NULL) {
+ return false;
+ }
+
+ if (strcmp(sid, ALLOC_RANGE) == 0) {
+ return true;
+ }
+
+ return dom_sid_parse(sid, &ignore);
+}
+
+struct idmap_autorid_addrange_ctx {
+ struct autorid_range_config *range;
+ bool acquire;
+};
+
+static NTSTATUS idmap_autorid_addrange_action(struct db_context *db,
+ void *private_data)
+{
+ struct idmap_autorid_addrange_ctx *ctx;
+ uint32_t requested_rangenum, stored_rangenum;
+ struct autorid_range_config *range;
+ bool acquire;
+ NTSTATUS ret;
+ uint32_t hwm;
+ char *numstr;
+ struct autorid_global_config globalcfg = {0};
+ fstring keystr;
+ uint32_t increment;
+ TALLOC_CTX *mem_ctx = NULL;
+
+ ctx = (struct idmap_autorid_addrange_ctx *)private_data;
+ range = ctx->range;
+ acquire = ctx->acquire;
+ requested_rangenum = range->rangenum;
+
+ if (db == NULL) {
+ DEBUG(3, ("Invalid database argument: NULL\n"));
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ if (range == NULL) {
+ DEBUG(3, ("Invalid range argument: NULL\n"));
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ DEBUG(10, ("Adding new range for domain %s "
+ "(domain_range_index=%"PRIu32")\n",
+ range->domsid, range->domain_range_index));
+
+ if (!idmap_autorid_validate_sid(range->domsid)) {
+ DEBUG(3, ("Invalid SID: %s\n", range->domsid));
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ idmap_autorid_build_keystr(range->domsid, range->domain_range_index,
+ keystr);
+
+ ret = dbwrap_fetch_uint32_bystring(db, keystr, &stored_rangenum);
+
+ if (NT_STATUS_IS_OK(ret)) {
+ /* entry is already present*/
+ if (acquire) {
+ DEBUG(10, ("domain range already allocated - "
+ "Not adding!\n"));
+
+ ret = idmap_autorid_loadconfig(db, &globalcfg);
+ if (!NT_STATUS_IS_OK(ret)) {
+ DEBUG(1, ("Fatal error while fetching "
+ "configuration: %s\n",
+ nt_errstr(ret)));
+ goto error;
+ }
+
+ range->rangenum = stored_rangenum;
+ range->low_id = globalcfg.minvalue
+ + range->rangenum * globalcfg.rangesize;
+ range->high_id =
+ range->low_id + globalcfg.rangesize - 1;
+
+ return NT_STATUS_OK;
+ }
+
+ if (stored_rangenum != requested_rangenum) {
+ DEBUG(1, ("Error: requested rangenumber (%u) differs "
+ "from stored one (%u).\n",
+ requested_rangenum, stored_rangenum));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ DEBUG(10, ("Note: stored range agrees with requested "
+ "one - ok\n"));
+ return NT_STATUS_OK;
+ }
+
+ /* fetch the current HWM */
+ ret = dbwrap_fetch_uint32_bystring(db, HWM, &hwm);
+ if (!NT_STATUS_IS_OK(ret)) {
+ DEBUG(1, ("Fatal error while fetching current "
+ "HWM value: %s\n", nt_errstr(ret)));
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+
+ mem_ctx = talloc_stackframe();
+
+ ret = idmap_autorid_loadconfig(db, &globalcfg);
+ if (!NT_STATUS_IS_OK(ret)) {
+ DEBUG(1, ("Fatal error while fetching configuration: %s\n",
+ nt_errstr(ret)));
+ goto error;
+ }
+
+ if (acquire) {
+ /*
+ * automatically acquire the next range
+ */
+ requested_rangenum = hwm;
+ }
+
+ if (requested_rangenum >= globalcfg.maxranges) {
+ DBG_WARNING("Not enough ranges available: New range %u can't "
+ "be allocated. Consider increasing the range "
+ "[%u-%u] by %u.\n",
+ requested_rangenum,
+ globalcfg.minvalue,
+ globalcfg.minvalue +
+ (globalcfg.maxranges * globalcfg.rangesize),
+ globalcfg.rangesize);
+ ret = NT_STATUS_NO_MEMORY;
+ goto error;
+ }
+
+ /*
+ * Check that it is not yet taken.
+ * If the range is requested and < HWM, we need
+ * to check anyways, and otherwise, we also better
+ * check in order to prevent further corruption
+ * in case the db has been externally modified.
+ */
+
+ numstr = talloc_asprintf(mem_ctx, "%u", requested_rangenum);
+ if (!numstr) {
+ DEBUG(1, ("Talloc failed!\n"));
+ ret = NT_STATUS_NO_MEMORY;
+ goto error;
+ }
+
+ if (dbwrap_exists(db, string_term_tdb_data(numstr))) {
+ DEBUG(1, ("Requested range '%s' is already in use.\n", numstr));
+
+ if (requested_rangenum < hwm) {
+ ret = NT_STATUS_INVALID_PARAMETER;
+ } else {
+ ret = NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+
+ goto error;
+ }
+
+ if (requested_rangenum >= hwm) {
+ /*
+ * requested or automatic range >= HWM:
+ * increment the HWM.
+ */
+
+ /* HWM always contains current max range + 1 */
+ increment = requested_rangenum + 1 - hwm;
+
+ /* increase the HWM */
+ ret = dbwrap_change_uint32_atomic_bystring(db, HWM, &hwm,
+ increment);
+ if (!NT_STATUS_IS_OK(ret)) {
+ DEBUG(1, ("Fatal error while incrementing the HWM "
+ "value in the database: %s\n",
+ nt_errstr(ret)));
+ goto error;
+ }
+ }
+
+ /*
+ * store away the new mapping in both directions
+ */
+
+ ret = dbwrap_store_uint32_bystring(db, keystr, requested_rangenum);
+ if (!NT_STATUS_IS_OK(ret)) {
+ DEBUG(1, ("Fatal error while storing new "
+ "domain->range assignment: %s\n", nt_errstr(ret)));
+ goto error;
+ }
+
+ numstr = talloc_asprintf(mem_ctx, "%u", requested_rangenum);
+ if (!numstr) {
+ ret = NT_STATUS_NO_MEMORY;
+ goto error;
+ }
+
+ ret = dbwrap_store_bystring(db, numstr,
+ string_term_tdb_data(keystr), TDB_INSERT);
+
+ if (!NT_STATUS_IS_OK(ret)) {
+ DEBUG(1, ("Fatal error while storing new "
+ "domain->range assignment: %s\n", nt_errstr(ret)));
+ goto error;
+ }
+
+ DEBUG(5, ("%s new range #%d for domain %s "
+ "(domain_range_index=%"PRIu32")\n",
+ (acquire?"Acquired":"Stored"),
+ requested_rangenum, keystr,
+ range->domain_range_index));
+
+ range->rangenum = requested_rangenum;
+
+ range->low_id = globalcfg.minvalue
+ + range->rangenum * globalcfg.rangesize;
+ range->high_id = range->low_id + globalcfg.rangesize - 1;
+
+ ret = NT_STATUS_OK;
+
+error:
+ talloc_free(mem_ctx);
+ return ret;
+}
+
+static NTSTATUS idmap_autorid_addrange(struct db_context *db,
+ struct autorid_range_config *range,
+ bool acquire)
+{
+ NTSTATUS status;
+ struct idmap_autorid_addrange_ctx ctx;
+
+ ctx.acquire = acquire;
+ ctx.range = range;
+
+ status = dbwrap_trans_do(db, idmap_autorid_addrange_action, &ctx);
+ return status;
+}
+
+NTSTATUS idmap_autorid_setrange(struct db_context *db,
+ const char *domsid,
+ uint32_t domain_range_index,
+ uint32_t rangenum)
+{
+ NTSTATUS status;
+ struct autorid_range_config range;
+
+ ZERO_STRUCT(range);
+ fstrcpy(range.domsid, domsid);
+ range.domain_range_index = domain_range_index;
+ range.rangenum = rangenum;
+
+ status = idmap_autorid_addrange(db, &range, false);
+ return status;
+}
+
+NTSTATUS idmap_autorid_acquire_range(struct db_context *db,
+ struct autorid_range_config *range)
+{
+ return idmap_autorid_addrange(db, range, true);
+}
+
+static NTSTATUS idmap_autorid_getrange_int(struct db_context *db,
+ struct autorid_range_config *range)
+{
+ NTSTATUS status = NT_STATUS_INVALID_PARAMETER;
+ struct autorid_global_config globalcfg = {0};
+ fstring keystr;
+
+ if (db == NULL || range == NULL) {
+ DEBUG(3, ("Invalid arguments received\n"));
+ goto done;
+ }
+
+ if (!idmap_autorid_validate_sid(range->domsid)) {
+ DEBUG(3, ("Invalid SID: '%s'\n", range->domsid));
+ status = NT_STATUS_INVALID_PARAMETER;
+ goto done;
+ }
+
+ idmap_autorid_build_keystr(range->domsid, range->domain_range_index,
+ keystr);
+
+ DEBUG(10, ("reading domain range for key %s\n", keystr));
+ status = dbwrap_fetch_uint32_bystring(db, keystr, &(range->rangenum));
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(1, ("Failed to read database record for key '%s': %s\n",
+ keystr, nt_errstr(status)));
+ goto done;
+ }
+
+ status = idmap_autorid_loadconfig(db, &globalcfg);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(1, ("Failed to read global configuration\n"));
+ goto done;
+ }
+ range->low_id = globalcfg.minvalue
+ + range->rangenum * globalcfg.rangesize;
+ range->high_id = range->low_id + globalcfg.rangesize - 1;
+done:
+ return status;
+}
+
+NTSTATUS idmap_autorid_getrange(struct db_context *db,
+ const char *domsid,
+ uint32_t domain_range_index,
+ uint32_t *rangenum,
+ uint32_t *low_id)
+{
+ NTSTATUS status;
+ struct autorid_range_config range;
+
+ if (rangenum == NULL) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ ZERO_STRUCT(range);
+ fstrcpy(range.domsid, domsid);
+ range.domain_range_index = domain_range_index;
+
+ status = idmap_autorid_getrange_int(db, &range);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ *rangenum = range.rangenum;
+
+ if (low_id != NULL) {
+ *low_id = range.low_id;
+ }
+
+ return NT_STATUS_OK;
+}
+
+NTSTATUS idmap_autorid_get_domainrange(struct db_context *db,
+ struct autorid_range_config *range,
+ bool read_only)
+{
+ NTSTATUS ret;
+
+ ret = idmap_autorid_getrange_int(db, range);
+ if (!NT_STATUS_IS_OK(ret)) {
+ DEBUG(10, ("Failed to read range config for '%s': %s\n",
+ range->domsid, nt_errstr(ret)));
+ if (read_only) {
+ DEBUG(10, ("Not allocating new range for '%s' because "
+ "read-only is enabled.\n", range->domsid));
+ return NT_STATUS_NOT_FOUND;
+ }
+
+ ret = idmap_autorid_acquire_range(db, range);
+ }
+
+ DEBUG(10, ("Using range #%d for domain %s "
+ "(domain_range_index=%"PRIu32", low_id=%"PRIu32")\n",
+ range->rangenum, range->domsid, range->domain_range_index,
+ range->low_id));
+
+ return ret;
+}
+
+/* initialize the given HWM to 0 if it does not exist yet */
+static NTSTATUS idmap_autorid_init_hwm_action(struct db_context *db,
+ void *private_data)
+{
+ NTSTATUS status;
+ uint32_t hwmval;
+ const char *hwm;
+
+ hwm = (char *)private_data;
+
+ status = dbwrap_fetch_uint32_bystring(db, hwm, &hwmval);
+ if (NT_STATUS_IS_OK(status)) {
+ DEBUG(1, ("HWM (%s) already initialized in autorid database "
+ "(value %"PRIu32").\n", hwm, hwmval));
+ return NT_STATUS_OK;
+ }
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
+ DEBUG(0, ("Error fetching HWM (%s) from autorid "
+ "database: %s\n", hwm, nt_errstr(status)));
+ return status;
+ }
+
+ status = dbwrap_trans_store_uint32_bystring(db, hwm, 0);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0, ("Error storing HWM (%s) in autorid database: %s\n",
+ hwm, nt_errstr(status)));
+ return status;
+ }
+
+ return NT_STATUS_OK;
+}
+
+NTSTATUS idmap_autorid_init_hwm(struct db_context *db, const char *hwm)
+{
+ NTSTATUS status;
+ uint32_t hwmval;
+
+ status = dbwrap_fetch_uint32_bystring(db, hwm, &hwmval);
+ if (NT_STATUS_IS_OK(status)) {
+ DEBUG(1, ("HWM (%s) already initialized in autorid database "
+ "(value %"PRIu32").\n", hwm, hwmval));
+ return NT_STATUS_OK;
+ }
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
+ DEBUG(0, ("unable to fetch HWM (%s) from autorid "
+ "database: %s\n", hwm, nt_errstr(status)));
+ return status;
+ }
+
+ status = dbwrap_trans_do(db, idmap_autorid_init_hwm_action,
+ discard_const(hwm));
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0, ("Error initializing HWM (%s) in autorid database: "
+ "%s\n", hwm, nt_errstr(status)));
+ return NT_STATUS_INTERNAL_DB_ERROR;
+ }
+
+ DEBUG(1, ("Initialized HWM (%s) in autorid database.\n", hwm));
+
+ return NT_STATUS_OK;
+}
+
+/*
+ * Delete a domain#index <-> range mapping from the database.
+ * The mapping is specified by the sid and index.
+ * If force == true, invalid mapping records are deleted as far
+ * as possible, otherwise they are left untouched.
+ */
+
+struct idmap_autorid_delete_range_by_sid_ctx {
+ const char *domsid;
+ uint32_t domain_range_index;
+ bool force;
+};
+
+static NTSTATUS idmap_autorid_delete_range_by_sid_action(struct db_context *db,
+ void *private_data)
+{
+ struct idmap_autorid_delete_range_by_sid_ctx *ctx =
+ (struct idmap_autorid_delete_range_by_sid_ctx *)private_data;
+ const char *domsid;
+ uint32_t domain_range_index;
+ uint32_t rangenum;
+ char *keystr;
+ char *range_keystr;
+ TDB_DATA data;
+ NTSTATUS status;
+ TALLOC_CTX *frame = talloc_stackframe();
+ bool is_valid_range_mapping = true;
+ bool force;
+
+ domsid = ctx->domsid;
+ domain_range_index = ctx->domain_range_index;
+ force = ctx->force;
+
+ keystr = idmap_autorid_build_keystr_talloc(frame, domsid,
+ domain_range_index);
+ if (keystr == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ goto done;
+ }
+
+ status = dbwrap_fetch_uint32_bystring(db, keystr, &rangenum);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto done;
+ }
+
+ range_keystr = talloc_asprintf(frame, "%"PRIu32, rangenum);
+ if (range_keystr == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ goto done;
+ }
+
+ status = dbwrap_fetch_bystring(db, frame, range_keystr, &data);
+ if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
+ DEBUG(1, ("Incomplete mapping %s -> %s: no backward mapping\n",
+ keystr, range_keystr));
+ is_valid_range_mapping = false;
+ } else if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(1, ("Error fetching reverse mapping for %s -> %s: %s\n",
+ keystr, range_keystr, nt_errstr(status)));
+ goto done;
+ } else if (strncmp((const char *)data.dptr, keystr, strlen(keystr))
+ != 0)
+ {
+ DEBUG(1, ("Invalid mapping: %s -> %s -> %s\n",
+ keystr, range_keystr, (const char *)data.dptr));
+ is_valid_range_mapping = false;
+ }
+
+ if (!is_valid_range_mapping && !force) {
+ DEBUG(10, ("Not deleting invalid mapping, since not in force "
+ "mode.\n"));
+ status = NT_STATUS_FILE_INVALID;
+ goto done;
+ }
+
+ status = dbwrap_delete_bystring(db, keystr);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(1, ("Deletion of '%s' failed: %s\n",
+ keystr, nt_errstr(status)));
+ goto done;
+ }
+
+ if (!is_valid_range_mapping) {
+ goto done;
+ }
+
+ status = dbwrap_delete_bystring(db, range_keystr);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(1, ("Deletion of '%s' failed: %s\n",
+ range_keystr, nt_errstr(status)));
+ goto done;
+ }
+
+ DEBUG(10, ("Deleted range mapping %s <--> %s\n", keystr,
+ range_keystr));
+
+done:
+ TALLOC_FREE(frame);
+ return status;
+}
+
+NTSTATUS idmap_autorid_delete_range_by_sid(struct db_context *db,
+ const char *domsid,
+ uint32_t domain_range_index,
+ bool force)
+{
+ NTSTATUS status;
+ struct idmap_autorid_delete_range_by_sid_ctx ctx;
+
+ ctx.domain_range_index = domain_range_index;
+ ctx.domsid = domsid;
+ ctx.force = force;
+
+ status = dbwrap_trans_do(db, idmap_autorid_delete_range_by_sid_action,
+ &ctx);
+ return status;
+}
+
+/*
+ * Delete a domain#index <-> range mapping from the database.
+ * The mapping is specified by the range number.
+ * If force == true, invalid mapping records are deleted as far
+ * as possible, otherwise they are left untouched.
+ */
+struct idmap_autorid_delete_range_by_num_ctx {
+ uint32_t rangenum;
+ bool force;
+};
+
+static NTSTATUS idmap_autorid_delete_range_by_num_action(struct db_context *db,
+ void *private_data)
+{
+ struct idmap_autorid_delete_range_by_num_ctx *ctx =
+ (struct idmap_autorid_delete_range_by_num_ctx *)private_data;
+ uint32_t rangenum;
+ char *keystr = NULL;
+ char *range_keystr;
+ TDB_DATA val;
+ NTSTATUS status;
+ TALLOC_CTX *frame = talloc_stackframe();
+ bool is_valid_range_mapping = true;
+ bool force;
+
+ rangenum = ctx->rangenum;
+ force = ctx->force;
+
+ range_keystr = talloc_asprintf(frame, "%"PRIu32, rangenum);
+ if (range_keystr == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ goto done;
+ }
+
+ ZERO_STRUCT(val);
+
+ status = dbwrap_fetch_bystring(db, frame, range_keystr, &val);
+ if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
+ DEBUG(10, ("Did not find range '%s' in database.\n",
+ range_keystr));
+ goto done;
+ } else if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(5, ("Error fetching rang key: %s\n", nt_errstr(status)));
+ goto done;
+ }
+
+ if (val.dptr == NULL) {
+ DEBUG(1, ("Invalid mapping: %s -> empty value\n",
+ range_keystr));
+ is_valid_range_mapping = false;
+ } else {
+ uint32_t reverse_rangenum = 0;
+
+ keystr = (char *)val.dptr;
+
+ status = dbwrap_fetch_uint32_bystring(db, keystr,
+ &reverse_rangenum);
+ if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
+ DEBUG(1, ("Incomplete mapping %s -> %s: "
+ "no backward mapping\n",
+ range_keystr, keystr));
+ is_valid_range_mapping = false;
+ } else if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(1, ("Error fetching reverse mapping for "
+ "%s -> %s: %s\n",
+ range_keystr, keystr, nt_errstr(status)));
+ goto done;
+ } else if (rangenum != reverse_rangenum) {
+ is_valid_range_mapping = false;
+ }
+ }
+
+ if (!is_valid_range_mapping && !force) {
+ DEBUG(10, ("Not deleting invalid mapping, since not in force "
+ "mode.\n"));
+ status = NT_STATUS_FILE_INVALID;
+ goto done;
+ }
+
+ status = dbwrap_delete_bystring(db, range_keystr);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(1, ("Deletion of '%s' failed: %s\n",
+ range_keystr, nt_errstr(status)));
+ goto done;
+ }
+
+ if (!is_valid_range_mapping) {
+ goto done;
+ }
+
+ status = dbwrap_delete_bystring(db, keystr);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(1, ("Deletion of '%s' failed: %s\n",
+ keystr, nt_errstr(status)));
+ goto done;
+ }
+
+ DEBUG(10, ("Deleted range mapping %s <--> %s\n", range_keystr,
+ keystr));
+
+done:
+ talloc_free(frame);
+ return status;
+}
+
+NTSTATUS idmap_autorid_delete_range_by_num(struct db_context *db,
+ uint32_t rangenum,
+ bool force)
+{
+ NTSTATUS status;
+ struct idmap_autorid_delete_range_by_num_ctx ctx;
+
+ ctx.rangenum = rangenum;
+ ctx.force = force;
+
+ status = dbwrap_trans_do(db, idmap_autorid_delete_range_by_num_action,
+ &ctx);
+ return status;
+}
+
+/**
+ * Open and possibly create the database.
+ */
+NTSTATUS idmap_autorid_db_open(const char *path,
+ TALLOC_CTX *mem_ctx,
+ struct db_context **db)
+{
+ if (*db != NULL) {
+ /* its already open */
+ return NT_STATUS_OK;
+ }
+
+ /* Open idmap repository */
+ *db = db_open(mem_ctx, path, 0, TDB_DEFAULT, O_RDWR | O_CREAT, 0644,
+ DBWRAP_LOCK_ORDER_1, DBWRAP_FLAG_NONE);
+
+ if (*db == NULL) {
+ DEBUG(0, ("Unable to open idmap_autorid database '%s'\n", path));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ return NT_STATUS_OK;
+}
+
+/**
+ * Initialize the high watermark records in the database.
+ */
+NTSTATUS idmap_autorid_init_hwms(struct db_context *db)
+{
+ NTSTATUS status;
+
+ status = idmap_autorid_init_hwm(db, HWM);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ status = idmap_autorid_init_hwm(db, ALLOC_HWM_UID);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ status = idmap_autorid_init_hwm(db, ALLOC_HWM_GID);
+
+ return status;
+}
+
+NTSTATUS idmap_autorid_db_init(const char *path,
+ TALLOC_CTX *mem_ctx,
+ struct db_context **db)
+{
+ NTSTATUS status;
+
+ status = idmap_autorid_db_open(path, mem_ctx, db);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ status = idmap_autorid_init_hwms(*db);
+ return status;
+}
+
+
+
+struct idmap_autorid_fetch_config_state {
+ TALLOC_CTX *mem_ctx;
+ char *configstr;
+};
+
+static void idmap_autorid_config_parser(TDB_DATA key, TDB_DATA value,
+ void *private_data)
+{
+ struct idmap_autorid_fetch_config_state *state;
+
+ state = (struct idmap_autorid_fetch_config_state *)private_data;
+
+ /*
+ * strndup because we have non-nullterminated strings in the db
+ */
+ state->configstr = talloc_strndup(
+ state->mem_ctx, (const char *)value.dptr, value.dsize);
+}
+
+NTSTATUS idmap_autorid_getconfigstr(struct db_context *db, TALLOC_CTX *mem_ctx,
+ char **result)
+{
+ TDB_DATA key;
+ NTSTATUS status;
+ struct idmap_autorid_fetch_config_state state;
+
+ if (result == NULL) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ key = string_term_tdb_data(CONFIGKEY);
+
+ state.mem_ctx = mem_ctx;
+ state.configstr = NULL;
+
+ status = dbwrap_parse_record(db, key, idmap_autorid_config_parser,
+ &state);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(1, ("Error while retrieving config: %s\n",
+ nt_errstr(status)));
+ return status;
+ }
+
+ if (state.configstr == NULL) {
+ DEBUG(1, ("Error while retrieving config\n"));
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ DEBUG(5, ("found CONFIG: %s\n", state.configstr));
+
+ *result = state.configstr;
+ return NT_STATUS_OK;
+}
+
+bool idmap_autorid_parse_configstr(const char *configstr,
+ struct autorid_global_config *cfg)
+{
+ unsigned long minvalue, rangesize, maxranges;
+
+ if (sscanf(configstr,
+ "minvalue:%lu rangesize:%lu maxranges:%lu",
+ &minvalue, &rangesize, &maxranges) != 3) {
+ DEBUG(1,
+ ("Found invalid configuration data. "
+ "Creating new config\n"));
+ return false;
+ }
+
+ cfg->minvalue = minvalue;
+ cfg->rangesize = rangesize;
+ cfg->maxranges = maxranges;
+
+ return true;
+}
+
+NTSTATUS idmap_autorid_loadconfig(struct db_context *db,
+ struct autorid_global_config *result)
+{
+ struct autorid_global_config cfg = {0};
+ NTSTATUS status;
+ bool ok;
+ char *configstr = NULL;
+
+ if (result == NULL) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ status = idmap_autorid_getconfigstr(db, db, &configstr);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ ok = idmap_autorid_parse_configstr(configstr, &cfg);
+ TALLOC_FREE(configstr);
+ if (!ok) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ DEBUG(10, ("Loaded previously stored configuration "
+ "minvalue:%d rangesize:%d\n",
+ cfg.minvalue, cfg.rangesize));
+
+ *result = cfg;
+
+ return NT_STATUS_OK;
+}
+
+NTSTATUS idmap_autorid_saveconfig(struct db_context *db,
+ struct autorid_global_config *cfg)
+{
+
+ struct autorid_global_config storedconfig = {0};
+ NTSTATUS status = NT_STATUS_INVALID_PARAMETER;
+ TDB_DATA data;
+ char *cfgstr;
+ uint32_t hwm;
+ TALLOC_CTX *frame = talloc_stackframe();
+
+ DEBUG(10, ("New configuration provided for storing is "
+ "minvalue:%d rangesize:%d maxranges:%d\n",
+ cfg->minvalue, cfg->rangesize, cfg->maxranges));
+
+ if (cfg->rangesize < 2000) {
+ DEBUG(1, ("autorid rangesize must be at least 2000\n"));
+ goto done;
+ }
+
+ if (cfg->maxranges == 0) {
+ DEBUG(1, ("An autorid maxranges value of 0 is invalid. "
+ "Must have at least one range available.\n"));
+ goto done;
+ }
+
+ status = idmap_autorid_loadconfig(db, &storedconfig);
+ if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
+ DEBUG(5, ("No configuration found. Storing initial "
+ "configuration.\n"));
+ storedconfig = *cfg;
+ } else if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(1, ("Error loading configuration: %s\n",
+ nt_errstr(status)));
+ goto done;
+ }
+
+ /* did the minimum value or rangesize change? */
+ if ((storedconfig.minvalue != cfg->minvalue) ||
+ (storedconfig.rangesize != cfg->rangesize))
+ {
+ DEBUG(1, ("New configuration values for rangesize or "
+ "minimum uid value conflict with previously "
+ "used values! Not storing new config.\n"));
+ status = NT_STATUS_INVALID_PARAMETER;
+ goto done;
+ }
+
+ status = dbwrap_fetch_uint32_bystring(db, HWM, &hwm);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(1, ("Fatal error while fetching current "
+ "HWM value: %s\n", nt_errstr(status)));
+ status = NT_STATUS_INTERNAL_ERROR;
+ goto done;
+ }
+
+ /*
+ * has the highest uid value been reduced to setting that is not
+ * sufficient any more for already existing ranges?
+ */
+ if (hwm > cfg->maxranges) {
+ DEBUG(1, ("New upper uid limit is too low to cover "
+ "existing mappings! Not storing new config.\n"));
+ status = NT_STATUS_INVALID_PARAMETER;
+ goto done;
+ }
+
+ cfgstr =
+ talloc_asprintf(frame,
+ "minvalue:%u rangesize:%u maxranges:%u",
+ cfg->minvalue, cfg->rangesize, cfg->maxranges);
+
+ if (cfgstr == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ goto done;
+ }
+
+ data = string_tdb_data(cfgstr);
+
+ status = dbwrap_trans_store_bystring(db, CONFIGKEY, data, TDB_REPLACE);
+
+done:
+ TALLOC_FREE(frame);
+ return status;
+}
+
+NTSTATUS idmap_autorid_saveconfigstr(struct db_context *db,
+ const char *configstr)
+{
+ bool ok;
+ NTSTATUS status;
+ struct autorid_global_config cfg;
+
+ ok = idmap_autorid_parse_configstr(configstr, &cfg);
+ if (!ok) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ status = idmap_autorid_saveconfig(db, &cfg);
+ return status;
+}
+
+
+/*
+ * iteration: Work on all range mappings for a given domain
+ */
+
+struct domain_range_visitor_ctx {
+ const char *domsid;
+ NTSTATUS (*fn)(struct db_context *db,
+ const char *domsid,
+ uint32_t index,
+ uint32_t rangenum,
+ void *private_data);
+ void *private_data;
+ int count; /* number of records worked on */
+};
+
+static int idmap_autorid_visit_domain_range(struct db_record *rec,
+ void *private_data)
+{
+ struct domain_range_visitor_ctx *vi;
+ char *domsid;
+ char *sep;
+ uint32_t range_index = 0;
+ uint32_t rangenum = 0;
+ TDB_DATA key, value;
+ NTSTATUS status;
+ int ret = 0;
+ struct db_context *db;
+
+ vi = talloc_get_type_abort(private_data,
+ struct domain_range_visitor_ctx);
+
+ key = dbwrap_record_get_key(rec);
+
+ /*
+ * split string "<sid>[#<index>]" into sid string and index number
+ */
+
+ domsid = (char *)key.dptr;
+
+ DEBUG(10, ("idmap_autorid_visit_domain_range: visiting key '%s'\n",
+ domsid));
+
+ sep = strrchr(domsid, '#');
+ if (sep != NULL) {
+ char *index_str;
+ *sep = '\0';
+ index_str = sep+1;
+ if (sscanf(index_str, "%"SCNu32, &range_index) != 1) {
+ DEBUG(10, ("Found separator '#' but '%s' is not a "
+ "valid range index. Skipping record\n",
+ index_str));
+ goto done;
+ }
+ }
+
+ if (!idmap_autorid_validate_sid(domsid)) {
+ DEBUG(10, ("String '%s' is not a valid sid. "
+ "Skipping record.\n", domsid));
+ goto done;
+ }
+
+ if ((vi->domsid != NULL) && (strcmp(domsid, vi->domsid) != 0)) {
+ DEBUG(10, ("key sid '%s' does not match requested sid '%s'.\n",
+ domsid, vi->domsid));
+ goto done;
+ }
+
+ value = dbwrap_record_get_value(rec);
+
+ if (value.dsize != sizeof(uint32_t)) {
+ /* it might be a mapping of a well known sid */
+ DEBUG(10, ("value size %u != sizeof(uint32_t) for sid '%s', "
+ "skipping.\n", (unsigned)value.dsize, vi->domsid));
+ goto done;
+ }
+
+ rangenum = IVAL(value.dptr, 0);
+
+ db = dbwrap_record_get_db(rec);
+
+ status = vi->fn(db, domsid, range_index, rangenum, vi->private_data);
+ if (!NT_STATUS_IS_OK(status)) {
+ ret = -1;
+ goto done;
+ }
+
+ vi->count++;
+ ret = 0;
+
+done:
+ return ret;
+}
+
+static NTSTATUS idmap_autorid_iterate_domain_ranges_int(struct db_context *db,
+ const char *domsid,
+ NTSTATUS (*fn)(struct db_context *db,
+ const char *domsid,
+ uint32_t index,
+ uint32_t rangnum,
+ void *private_data),
+ void *private_data,
+ int *count,
+ NTSTATUS (*traverse)(struct db_context *db,
+ int (*f)(struct db_record *, void *),
+ void *private_data,
+ int *count))
+{
+ NTSTATUS status;
+ struct domain_range_visitor_ctx *vi;
+ TALLOC_CTX *frame = talloc_stackframe();
+
+ if (domsid == NULL) {
+ DEBUG(10, ("No sid provided, operating on all ranges\n"));
+ }
+
+ if (fn == NULL) {
+ DEBUG(1, ("Error: missing visitor callback\n"));
+ status = NT_STATUS_INVALID_PARAMETER;
+ goto done;
+ }
+
+ vi = talloc_zero(frame, struct domain_range_visitor_ctx);
+ if (vi == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ goto done;
+ }
+
+ vi->domsid = domsid;
+ vi->fn = fn;
+ vi->private_data = private_data;
+
+ status = traverse(db, idmap_autorid_visit_domain_range, vi, NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto done;
+ }
+
+ if (count != NULL) {
+ *count = vi->count;
+ }
+
+done:
+ talloc_free(frame);
+ return status;
+}
+
+NTSTATUS idmap_autorid_iterate_domain_ranges(struct db_context *db,
+ const char *domsid,
+ NTSTATUS (*fn)(struct db_context *db,
+ const char *domsid,
+ uint32_t index,
+ uint32_t rangenum,
+ void *private_data),
+ void *private_data,
+ int *count)
+{
+ NTSTATUS status;
+
+ status = idmap_autorid_iterate_domain_ranges_int(db,
+ domsid,
+ fn,
+ private_data,
+ count,
+ dbwrap_traverse);
+
+ return status;
+}
+
+
+NTSTATUS idmap_autorid_iterate_domain_ranges_read(struct db_context *db,
+ const char *domsid,
+ NTSTATUS (*fn)(struct db_context *db,
+ const char *domsid,
+ uint32_t index,
+ uint32_t rangenum,
+ void *count),
+ void *private_data,
+ int *count)
+{
+ NTSTATUS status;
+
+ status = idmap_autorid_iterate_domain_ranges_int(db,
+ domsid,
+ fn,
+ private_data,
+ count,
+ dbwrap_traverse_read);
+
+ return status;
+}
+
+
+/*
+ * Delete all ranges configured for a given domain
+ */
+
+struct delete_domain_ranges_visitor_ctx {
+ bool force;
+};
+
+static NTSTATUS idmap_autorid_delete_domain_ranges_visitor(
+ struct db_context *db,
+ const char *domsid,
+ uint32_t domain_range_index,
+ uint32_t rangenum,
+ void *private_data)
+{
+ struct delete_domain_ranges_visitor_ctx *ctx;
+ NTSTATUS status;
+
+ ctx = (struct delete_domain_ranges_visitor_ctx *)private_data;
+
+ status = idmap_autorid_delete_range_by_sid(
+ db, domsid, domain_range_index, ctx->force);
+ return status;
+}
+
+struct idmap_autorid_delete_domain_ranges_ctx {
+ const char *domsid;
+ bool force;
+ int count; /* output: count records operated on */
+};
+
+static NTSTATUS idmap_autorid_delete_domain_ranges_action(struct db_context *db,
+ void *private_data)
+{
+ struct idmap_autorid_delete_domain_ranges_ctx *ctx;
+ struct delete_domain_ranges_visitor_ctx visitor_ctx;
+ int count;
+ NTSTATUS status;
+
+ ctx = (struct idmap_autorid_delete_domain_ranges_ctx *)private_data;
+
+ ZERO_STRUCT(visitor_ctx);
+ visitor_ctx.force = ctx->force;
+
+ status = idmap_autorid_iterate_domain_ranges(db,
+ ctx->domsid,
+ idmap_autorid_delete_domain_ranges_visitor,
+ &visitor_ctx,
+ &count);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ ctx->count = count;
+
+ return NT_STATUS_OK;
+}
+
+NTSTATUS idmap_autorid_delete_domain_ranges(struct db_context *db,
+ const char *domsid,
+ bool force,
+ int *count)
+{
+ NTSTATUS status;
+ struct idmap_autorid_delete_domain_ranges_ctx ctx;
+
+ ZERO_STRUCT(ctx);
+ ctx.domsid = domsid;
+ ctx.force = force;
+
+ status = dbwrap_trans_do(db, idmap_autorid_delete_domain_ranges_action,
+ &ctx);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ *count = ctx.count;
+
+ return NT_STATUS_OK;
+}
diff --git a/source3/winbindd/idmap_hash/idmap_hash.c b/source3/winbindd/idmap_hash/idmap_hash.c
new file mode 100644
index 0000000..c2e835c
--- /dev/null
+++ b/source3/winbindd/idmap_hash/idmap_hash.c
@@ -0,0 +1,504 @@
+/*
+ * idmap_hash.c
+ *
+ * Copyright (C) Gerald Carter <jerry@samba.org> 2007 - 2008
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "includes.h"
+#include "winbindd/winbindd.h"
+#include "idmap.h"
+#include "idmap_hash.h"
+#include "ads.h"
+#include "nss_info.h"
+#include "../libcli/security/dom_sid.h"
+#include "libsmb/samlogon_cache.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_IDMAP
+
+struct sid_hash_table {
+ struct dom_sid *sid;
+};
+
+/*********************************************************************
+ Hash a domain SID (S-1-5-12-aaa-bbb-ccc) to a 12bit number
+ ********************************************************************/
+
+static uint32_t hash_domain_sid(const struct dom_sid *sid)
+{
+ uint32_t hash;
+
+ if (sid->num_auths != 4)
+ return 0;
+
+ /* XOR the last three subauths */
+
+ hash = ((sid->sub_auths[1] ^ sid->sub_auths[2]) ^ sid->sub_auths[3]);
+
+ /* Take all 32-bits into account when generating the 12-bit
+ hash value */
+ hash = (((hash & 0xFFF00000) >> 20)
+ + ((hash & 0x000FFF00) >> 8)
+ + (hash & 0x000000FF)) & 0x00000FFF;
+
+ /* return a 12-bit hash value */
+
+ return hash;
+}
+
+/*********************************************************************
+ Hash a Relative ID to a 19 bit number
+ ********************************************************************/
+
+static uint32_t hash_rid(uint32_t rid)
+{
+ /*
+ * 19 bits for the rid which allows us to support
+ * the first 50K users/groups in a domain
+ *
+ */
+
+ return (rid & 0x0007FFFF);
+}
+
+/*********************************************************************
+ ********************************************************************/
+
+static uint32_t combine_hashes(uint32_t h_domain,
+ uint32_t h_rid)
+{
+ uint32_t return_id = 0;
+
+ /*
+ * shift the hash_domain 19 bits to the left and OR with the
+ * hash_rid
+ *
+ * This will generate a 31 bit number out of
+ * 12 bit domain and 19 bit rid.
+ */
+
+ return_id = ((h_domain<<19) | h_rid);
+
+ return return_id;
+}
+
+/*********************************************************************
+ ********************************************************************/
+
+static void separate_hashes(uint32_t id,
+ uint32_t *h_domain,
+ uint32_t *h_rid)
+{
+ *h_rid = id & 0x0007FFFF;
+ *h_domain = (id & 0x7FF80000) >> 19;
+
+ return;
+}
+
+
+/*********************************************************************
+ ********************************************************************/
+
+static NTSTATUS idmap_hash_initialize(struct idmap_domain *dom)
+{
+ struct sid_hash_table *hashed_domains;
+ NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
+ struct winbindd_tdc_domain *dom_list = NULL;
+ size_t num_domains = 0;
+ size_t i;
+
+ DBG_ERR("The idmap_hash module is deprecated and should not be used. "
+ "Please migrate to a different plugin. This module will be "
+ "removed in a future version of Samba\n");
+
+ if (!strequal(dom->name, "*")) {
+ DBG_ERR("Error: idmap_hash configured for domain '%s'. "
+ "But the hash module can only be used for the default "
+ "idmap configuration.\n", dom->name);
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ if (!wcache_tdc_fetch_list(&dom_list, &num_domains)) {
+ nt_status = NT_STATUS_TRUSTED_DOMAIN_FAILURE;
+ BAIL_ON_NTSTATUS_ERROR(nt_status);
+ }
+
+ /* Create the hash table of domain SIDs */
+
+ hashed_domains = talloc_zero_array(dom, struct sid_hash_table, 4096);
+ BAIL_ON_PTR_NT_ERROR(hashed_domains, nt_status);
+
+ /* create the hash table of domain SIDs */
+
+ for (i=0; i<num_domains; i++) {
+ struct dom_sid_buf buf;
+ uint32_t hash;
+
+ if (is_null_sid(&dom_list[i].sid))
+ continue;
+
+ /*
+ * Check if the domain from the list is not already configured
+ * to use another idmap backend. Not checking this makes the
+ * idmap_hash module map IDs for *all* domains implicitly. This
+ * is quite dangerous in setups that use multiple idmap
+ * configurations.
+ */
+
+ if (domain_has_idmap_config(dom_list[i].domain_name)) {
+ continue;
+ }
+
+ if ((hash = hash_domain_sid(&dom_list[i].sid)) == 0)
+ continue;
+
+ DBG_INFO("Adding %s (%s) -> %d\n",
+ dom_list[i].domain_name,
+ dom_sid_str_buf(&dom_list[i].sid, &buf),
+ hash);
+
+ hashed_domains[hash].sid = talloc(hashed_domains, struct dom_sid);
+ sid_copy(hashed_domains[hash].sid, &dom_list[i].sid);
+ }
+
+ dom->private_data = hashed_domains;
+
+done:
+ return nt_status;
+}
+
+/*********************************************************************
+ ********************************************************************/
+
+static NTSTATUS idmap_hash_id_to_sid(struct sid_hash_table *hashed_domains,
+ struct idmap_domain *dom,
+ struct id_map *id)
+{
+ uint32_t h_domain = 0, h_rid = 0;
+
+ id->status = ID_UNMAPPED;
+
+ separate_hashes(id->xid.id, &h_domain, &h_rid);
+
+ /*
+ * If the domain hash doesn't find a SID in the table,
+ * skip it
+ */
+ if (hashed_domains[h_domain].sid == NULL) {
+ /* keep ID_UNMAPPED */
+ return NT_STATUS_OK;
+ }
+
+ id->xid.type = ID_TYPE_BOTH;
+ sid_compose(id->sid, hashed_domains[h_domain].sid, h_rid);
+ id->status = ID_MAPPED;
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS unixids_to_sids(struct idmap_domain *dom,
+ struct id_map **ids)
+{
+ struct sid_hash_table *hashed_domains = talloc_get_type_abort(
+ dom->private_data, struct sid_hash_table);
+ size_t i;
+ size_t num_tomap = 0;
+ size_t num_mapped = 0;
+
+ /* initialize the status to avoid surprise */
+ for (i = 0; ids[i]; i++) {
+ ids[i]->status = ID_UNKNOWN;
+ num_tomap++;
+ }
+
+ for (i=0; ids[i]; i++) {
+ NTSTATUS ret;
+
+ ret = idmap_hash_id_to_sid(hashed_domains, dom, ids[i]);
+ if (!NT_STATUS_IS_OK(ret)) {
+ /* some fatal error occurred, log it */
+ DBG_NOTICE("Unexpected error resolving an ID "
+ "(%d): %s\n", ids[i]->xid.id,
+ nt_errstr(ret));
+ return ret;
+ }
+
+ if (ids[i]->status == ID_MAPPED) {
+ num_mapped++;
+ }
+ }
+
+ if (num_tomap == num_mapped) {
+ return NT_STATUS_OK;
+ } else if (num_mapped == 0) {
+ return NT_STATUS_NONE_MAPPED;
+ }
+
+ return STATUS_SOME_UNMAPPED;
+}
+
+/*********************************************************************
+ ********************************************************************/
+
+static NTSTATUS idmap_hash_sid_to_id(struct sid_hash_table *hashed_domains,
+ struct idmap_domain *dom,
+ struct id_map *id)
+{
+ struct dom_sid sid;
+ uint32_t rid;
+ uint32_t h_domain, h_rid;
+
+ id->status = ID_UNMAPPED;
+
+ sid_copy(&sid, id->sid);
+ sid_split_rid(&sid, &rid);
+
+ h_domain = hash_domain_sid(&sid);
+ h_rid = hash_rid(rid);
+
+ /* Check that both hashes are non-zero*/
+ if (h_domain == 0) {
+ /* keep ID_UNMAPPED */
+ return NT_STATUS_OK;
+ }
+ if (h_rid == 0) {
+ /* keep ID_UNMAPPED */
+ return NT_STATUS_OK;
+ }
+
+ /*
+ * If the domain hash already exists find a SID in the table,
+ * just return the mapping.
+ */
+ if (hashed_domains[h_domain].sid != NULL) {
+ goto return_mapping;
+ }
+
+ /*
+ * Check of last resort: A domain is valid if a user from that
+ * domain has recently logged in. The samlogon_cache these
+ * days also stores the domain sid.
+ */
+ if (netsamlogon_cache_have(&sid)) {
+ /*
+ * The domain is valid, so we'll
+ * remember it in order to
+ * allow reverse mappings to work.
+ */
+ goto remember_domain;
+ }
+
+ if (id->xid.type == ID_TYPE_NOT_SPECIFIED) {
+ /*
+ * idmap_hash used to bounce back the requested type,
+ * which was ID_TYPE_UID, ID_TYPE_GID or
+ * ID_TYPE_NOT_SPECIFIED before as the winbindd parent
+ * always used a lookupsids. When the lookupsids
+ * failed because of an unknown domain, the idmap child
+ * weren't requested at all and the caller sees
+ * ID_TYPE_NOT_SPECIFIED.
+ *
+ * Now that the winbindd parent will pass ID_TYPE_BOTH
+ * in order to indicate that the domain exists.
+ * We should ask the parent to fallback to lookupsids
+ * if the domain is not known yet.
+ */
+ id->status = ID_REQUIRE_TYPE;
+ return NT_STATUS_OK;
+ }
+
+ /*
+ * Now we're sure the domain exist, remember
+ * the domain in order to return reverse mappings
+ * in future.
+ */
+remember_domain:
+ hashed_domains[h_domain].sid = dom_sid_dup(hashed_domains, &sid);
+ if (hashed_domains[h_domain].sid == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ /*
+ * idmap_hash used to bounce back the requested type,
+ * which was ID_TYPE_UID, ID_TYPE_GID or
+ * ID_TYPE_NOT_SPECIFIED before as the winbindd parent
+ * always used a lookupsids.
+ *
+ * This module should have supported ID_TYPE_BOTH since
+ * samba-4.1.0, similar to idmap_rid and idmap_autorid.
+ *
+ * Now that the winbindd parent will pass ID_TYPE_BOTH
+ * in order to indicate that the domain exists, it's
+ * better to always return ID_TYPE_BOTH instead of a
+ * random mix of ID_TYPE_UID, ID_TYPE_GID or
+ * ID_TYPE_BOTH.
+ */
+return_mapping:
+ id->xid.type = ID_TYPE_BOTH;
+ id->xid.id = combine_hashes(h_domain, h_rid);
+ id->status = ID_MAPPED;
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS sids_to_unixids(struct idmap_domain *dom,
+ struct id_map **ids)
+{
+ struct sid_hash_table *hashed_domains = talloc_get_type_abort(
+ dom->private_data, struct sid_hash_table);
+ size_t i;
+ size_t num_tomap = 0;
+ size_t num_mapped = 0;
+ size_t num_required = 0;
+
+ /* initialize the status to avoid surprise */
+ for (i = 0; ids[i]; i++) {
+ ids[i]->status = ID_UNKNOWN;
+ num_tomap++;
+ }
+
+ for (i=0; ids[i]; i++) {
+ NTSTATUS ret;
+
+ ret = idmap_hash_sid_to_id(hashed_domains, dom, ids[i]);
+ if (!NT_STATUS_IS_OK(ret)) {
+ struct dom_sid_buf buf;
+ /* some fatal error occurred, log it */
+ DBG_NOTICE("Unexpected error resolving a SID "
+ "(%s): %s\n",
+ dom_sid_str_buf(ids[i]->sid, &buf),
+ nt_errstr(ret));
+ return ret;
+ }
+
+ if (ids[i]->status == ID_MAPPED) {
+ num_mapped++;
+ }
+ if (ids[i]->status == ID_REQUIRE_TYPE) {
+ num_required++;
+ }
+ }
+
+ if (num_tomap == num_mapped) {
+ return NT_STATUS_OK;
+ } else if (num_required > 0) {
+ return STATUS_SOME_UNMAPPED;
+ } else if (num_mapped == 0) {
+ return NT_STATUS_NONE_MAPPED;
+ }
+
+ return STATUS_SOME_UNMAPPED;
+}
+
+/*********************************************************************
+ ********************************************************************/
+
+static NTSTATUS nss_hash_init(struct nss_domain_entry *e )
+{
+ return NT_STATUS_OK;
+}
+
+/**********************************************************************
+ *********************************************************************/
+
+static NTSTATUS nss_hash_map_to_alias(TALLOC_CTX *mem_ctx,
+ struct nss_domain_entry *e,
+ const char *name,
+ char **alias)
+{
+ NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
+ const char *value;
+
+ value = talloc_asprintf(mem_ctx, "%s\\%s", e->domain, name);
+ BAIL_ON_PTR_NT_ERROR(value, nt_status);
+
+ nt_status = mapfile_lookup_key(mem_ctx, value, alias);
+ BAIL_ON_NTSTATUS_ERROR(nt_status);
+
+done:
+ return nt_status;
+}
+
+/**********************************************************************
+ *********************************************************************/
+
+static NTSTATUS nss_hash_map_from_alias(TALLOC_CTX *mem_ctx,
+ struct nss_domain_entry *e,
+ const char *alias,
+ char **name)
+{
+ return mapfile_lookup_value(mem_ctx, alias, name);
+}
+
+/**********************************************************************
+ *********************************************************************/
+
+static NTSTATUS nss_hash_close(void)
+{
+ return NT_STATUS_OK;
+}
+
+/*********************************************************************
+ Dispatch Tables for IDMap and NssInfo Methods
+********************************************************************/
+
+static const struct idmap_methods hash_idmap_methods = {
+ .init = idmap_hash_initialize,
+ .unixids_to_sids = unixids_to_sids,
+ .sids_to_unixids = sids_to_unixids,
+};
+
+static const struct nss_info_methods hash_nss_methods = {
+ .init = nss_hash_init,
+ .map_to_alias = nss_hash_map_to_alias,
+ .map_from_alias = nss_hash_map_from_alias,
+ .close_fn = nss_hash_close
+};
+
+/**********************************************************************
+ Register with the idmap and idmap_nss subsystems. We have to protect
+ against the idmap and nss_info interfaces being in a half-registered
+ state.
+ **********************************************************************/
+
+static_decl_idmap;
+NTSTATUS idmap_hash_init(TALLOC_CTX *ctx)
+{
+ static NTSTATUS idmap_status = NT_STATUS_UNSUCCESSFUL;
+ static NTSTATUS nss_status = NT_STATUS_UNSUCCESSFUL;
+
+ if ( !NT_STATUS_IS_OK(idmap_status) ) {
+ idmap_status = smb_register_idmap(SMB_IDMAP_INTERFACE_VERSION,
+ "hash", &hash_idmap_methods);
+
+ if ( !NT_STATUS_IS_OK(idmap_status) ) {
+ DEBUG(0,("Failed to register hash idmap plugin.\n"));
+ return idmap_status;
+ }
+ }
+
+ if ( !NT_STATUS_IS_OK(nss_status) ) {
+ nss_status = smb_register_idmap_nss(SMB_NSS_INFO_INTERFACE_VERSION,
+ "hash", &hash_nss_methods);
+ if ( !NT_STATUS_IS_OK(nss_status) ) {
+ DEBUG(0,("Failed to register hash idmap nss plugin.\n"));
+ return nss_status;
+ }
+ }
+
+ return NT_STATUS_OK;
+}
diff --git a/source3/winbindd/idmap_hash/idmap_hash.h b/source3/winbindd/idmap_hash/idmap_hash.h
new file mode 100644
index 0000000..621520e
--- /dev/null
+++ b/source3/winbindd/idmap_hash/idmap_hash.h
@@ -0,0 +1,60 @@
+/*
+ * lwopen.h
+ *
+ * Copyright (C) Gerald Carter <jerry@samba.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef _LWOPEN_H
+#define _LWOPEN_H
+
+#define BAIL_ON_NTSTATUS_ERROR(x) \
+ do { \
+ if (!NT_STATUS_IS_OK(x)) { \
+ DEBUG(10,("Failed! (%s)\n", nt_errstr(x))); \
+ goto done; \
+ } \
+ } \
+ while (0); \
+
+#define BAIL_ON_PTR_NT_ERROR(p, x) \
+ do { \
+ if ((p) == NULL ) { \
+ DEBUG(10,("NULL pointer!\n")); \
+ x = NT_STATUS_NO_MEMORY; \
+ goto done; \
+ } else { \
+ x = NT_STATUS_OK; \
+ } \
+ } while (0);
+
+#define PRINT_NTSTATUS_ERROR(x, hdr, level) \
+ do { \
+ if (!NT_STATUS_IS_OK(x)) { \
+ DEBUG(level,("Likewise Open ("hdr"): %s\n", nt_errstr(x))); \
+ } \
+ } while(0);
+
+
+NTSTATUS mapfile_lookup_key(TALLOC_CTX *ctx,
+ const char *value,
+ char **key);
+
+NTSTATUS mapfile_lookup_value(TALLOC_CTX *ctx,
+ const char *key,
+ char **value);
+
+#endif /* _LWOPEN_H */
diff --git a/source3/winbindd/idmap_hash/mapfile.c b/source3/winbindd/idmap_hash/mapfile.c
new file mode 100644
index 0000000..82812a1
--- /dev/null
+++ b/source3/winbindd/idmap_hash/mapfile.c
@@ -0,0 +1,182 @@
+/*
+ * mapfile.c
+ *
+ * Copyright (C) Gerald Carter <jerry@samba.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "includes.h"
+#include "system/filesys.h"
+#include "winbindd/winbindd.h"
+#include "idmap.h"
+#include "idmap_hash.h"
+
+static FILE *lw_map_file = NULL;
+
+/*********************************************************************
+ ********************************************************************/
+
+static bool mapfile_open(void)
+{
+ const char *mapfile_name = NULL;
+
+ /* If we have an open handle, just reset it */
+
+ if (lw_map_file) {
+ return (fseek(lw_map_file, 0, SEEK_SET) == 0);
+ }
+
+ mapfile_name = lp_parm_const_string(-1, "idmap_hash", "name_map", NULL);
+ if (!mapfile_name) {
+ return false;
+ }
+
+ lw_map_file = fopen(mapfile_name, "r");
+ if (!lw_map_file) {
+ DEBUG(0,("can't open idmap_hash:name_map (%s). Error %s\n",
+ mapfile_name, strerror(errno) ));
+ return false;
+ }
+
+ return true;
+}
+
+/*********************************************************************
+ ********************************************************************/
+
+static bool mapfile_read_line(fstring key, fstring value)
+{
+ char buffer[1024];
+ char *p;
+ int len;
+
+ if (!lw_map_file)
+ return false;
+
+ p = fgets(buffer, sizeof(buffer)-1, lw_map_file);
+ if (p == NULL) {
+ return false;
+ }
+
+ /* Strip newlines and carriage returns */
+
+ len = strlen_m(buffer);
+ if (len == 0) {
+ return false;
+ }
+ len -= 1;
+
+ while ((buffer[len] == '\n') || (buffer[len] == '\r')) {
+ buffer[len--] = '\0';
+ }
+
+
+ if ((p = strchr_m(buffer, '=')) == NULL ) {
+ DEBUG(0,("idmap_hash: Bad line in name_map (%s)\n", buffer));
+ return false;
+ }
+
+ *p = '\0';
+ p++;
+
+ strlcpy(key, buffer, sizeof(fstring));
+ strlcpy(value, p, sizeof(fstring));
+
+ /* Eat whitespace */
+
+ if (!trim_char(key, ' ', ' '))
+ return false;
+
+ if (!trim_char(value, ' ', ' '))
+ return false;
+
+ return true;
+}
+
+/*********************************************************************
+ ********************************************************************/
+
+static bool mapfile_close(void)
+{
+ int ret = 0;
+ if (lw_map_file) {
+ ret = fclose(lw_map_file);
+ lw_map_file = NULL;
+ }
+
+ return (ret == 0);
+}
+
+
+/*********************************************************************
+ ********************************************************************/
+
+NTSTATUS mapfile_lookup_key(TALLOC_CTX *ctx, const char *value, char **key)
+{
+ fstring r_key, r_value;
+ NTSTATUS ret = NT_STATUS_NOT_FOUND;
+
+ if (!mapfile_open())
+ return NT_STATUS_OBJECT_PATH_NOT_FOUND;
+
+ while (mapfile_read_line(r_key, r_value))
+ {
+ if (strequal(r_value, value)) {
+ ret = NT_STATUS_OK;
+
+ /* We're done once finishing this block */
+ *key = talloc_strdup(ctx, r_key);
+ if (!*key) {
+ ret = NT_STATUS_NO_MEMORY;
+ }
+ break;
+ }
+ }
+
+ mapfile_close();
+
+ return ret;
+}
+
+/*********************************************************************
+ ********************************************************************/
+
+NTSTATUS mapfile_lookup_value(TALLOC_CTX *ctx, const char *key, char **value)
+{
+ fstring r_key, r_value;
+ NTSTATUS ret = NT_STATUS_NOT_FOUND;
+
+ if (!mapfile_open())
+ return NT_STATUS_OBJECT_PATH_NOT_FOUND;
+
+ while (mapfile_read_line(r_key, r_value))
+ {
+ if (strequal(r_key, key)) {
+ ret = NT_STATUS_OK;
+
+ /* We're done once finishing this block */
+ *value = talloc_strdup(ctx, r_value);
+ if (!*key) {
+ ret = NT_STATUS_NO_MEMORY;
+ }
+ break;
+ }
+ }
+
+ mapfile_close();
+
+ return ret;
+}
diff --git a/source3/winbindd/idmap_ldap.c b/source3/winbindd/idmap_ldap.c
new file mode 100644
index 0000000..0b0d82b
--- /dev/null
+++ b/source3/winbindd/idmap_ldap.c
@@ -0,0 +1,1140 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ idmap LDAP backend
+
+ Copyright (C) Tim Potter 2000
+ Copyright (C) Jim McDonough <jmcd@us.ibm.com> 2003
+ Copyright (C) Gerald Carter 2003
+ Copyright (C) Simo Sorce 2003-2007
+ Copyright (C) Michael Adam 2010
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "winbindd.h"
+#include "secrets.h"
+#include "idmap.h"
+#include "idmap_rw.h"
+#include "../libcli/security/security.h"
+#include "lib/util/smb_strtox.h"
+#include "lib/global_contexts.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_IDMAP
+
+#include <lber.h>
+#include <ldap.h>
+
+#include "smbldap.h"
+#include "passdb/pdb_ldap_schema.h"
+
+struct idmap_ldap_context {
+ struct smbldap_state *smbldap_state;
+ char *url;
+ char *suffix;
+ char *user_dn;
+ bool anon;
+ struct idmap_rw_ops *rw_ops;
+};
+
+#define CHECK_ALLOC_DONE(mem) do { \
+ if (!mem) { \
+ DEBUG(0, ("Out of memory!\n")); \
+ ret = NT_STATUS_NO_MEMORY; \
+ goto done; \
+ } } while (0)
+
+/**********************************************************************
+ IDMAP ALLOC TDB BACKEND
+**********************************************************************/
+
+/*********************************************************************
+ ********************************************************************/
+
+static NTSTATUS get_credentials( TALLOC_CTX *mem_ctx,
+ struct smbldap_state *ldap_state,
+ struct idmap_domain *dom,
+ char **dn )
+{
+ NTSTATUS ret = NT_STATUS_UNSUCCESSFUL;
+ char *secret = NULL;
+ const char *tmp = NULL;
+ char *user_dn = NULL;
+ bool anon = False;
+
+ /* assume anonymous if we don't have a specified user */
+
+ tmp = idmap_config_const_string(dom->name, "ldap_user_dn", NULL);
+
+ if ( tmp ) {
+ secret = idmap_fetch_secret("ldap", dom->name, tmp);
+ if (!secret) {
+ DEBUG(0, ("get_credentials: Unable to fetch "
+ "auth credentials for %s in %s\n",
+ tmp, (dom==NULL)?"ALLOC":dom->name));
+ ret = NT_STATUS_ACCESS_DENIED;
+ goto done;
+ }
+ *dn = talloc_strdup(mem_ctx, tmp);
+ CHECK_ALLOC_DONE(*dn);
+ } else {
+ if (!fetch_ldap_pw(&user_dn, &secret)) {
+ DEBUG(2, ("get_credentials: Failed to lookup ldap "
+ "bind creds. Using anonymous connection.\n"));
+ anon = True;
+ *dn = NULL;
+ } else {
+ *dn = talloc_strdup(mem_ctx, user_dn);
+ SAFE_FREE( user_dn );
+ CHECK_ALLOC_DONE(*dn);
+ }
+ }
+
+ smbldap_set_creds(ldap_state, anon, *dn, secret);
+ ret = NT_STATUS_OK;
+
+done:
+ BURN_FREE_STR(secret);
+
+ return ret;
+}
+
+
+/**********************************************************************
+ Verify the sambaUnixIdPool entry in the directory.
+**********************************************************************/
+
+static NTSTATUS verify_idpool(struct idmap_domain *dom)
+{
+ NTSTATUS ret;
+ TALLOC_CTX *mem_ctx;
+ LDAPMessage *result = NULL;
+ LDAPMod **mods = NULL;
+ const char **attr_list;
+ char *filter;
+ int count;
+ int rc;
+ struct idmap_ldap_context *ctx;
+
+ ctx = talloc_get_type(dom->private_data, struct idmap_ldap_context);
+
+ mem_ctx = talloc_new(ctx);
+ if (mem_ctx == NULL) {
+ DEBUG(0, ("Out of memory!\n"));
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ filter = talloc_asprintf(mem_ctx, "(objectclass=%s)", LDAP_OBJ_IDPOOL);
+ CHECK_ALLOC_DONE(filter);
+
+ attr_list = get_attr_list(mem_ctx, idpool_attr_list);
+ CHECK_ALLOC_DONE(attr_list);
+
+ rc = smbldap_search(ctx->smbldap_state,
+ ctx->suffix,
+ LDAP_SCOPE_SUBTREE,
+ filter,
+ attr_list,
+ 0,
+ &result);
+
+ if (rc != LDAP_SUCCESS) {
+ DEBUG(1, ("Unable to verify the idpool, "
+ "cannot continue initialization!\n"));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ count = ldap_count_entries(smbldap_get_ldap(ctx->smbldap_state),
+ result);
+
+ ldap_msgfree(result);
+
+ if ( count > 1 ) {
+ DEBUG(0,("Multiple entries returned from %s (base == %s)\n",
+ filter, ctx->suffix));
+ ret = NT_STATUS_UNSUCCESSFUL;
+ goto done;
+ }
+ else if (count == 0) {
+ char *uid_str, *gid_str;
+
+ uid_str = talloc_asprintf(mem_ctx, "%lu",
+ (unsigned long)dom->low_id);
+ gid_str = talloc_asprintf(mem_ctx, "%lu",
+ (unsigned long)dom->low_id);
+
+ smbldap_set_mod(&mods, LDAP_MOD_ADD,
+ "objectClass", LDAP_OBJ_IDPOOL);
+ smbldap_set_mod(&mods, LDAP_MOD_ADD,
+ get_attr_key2string(idpool_attr_list,
+ LDAP_ATTR_UIDNUMBER),
+ uid_str);
+ smbldap_set_mod(&mods, LDAP_MOD_ADD,
+ get_attr_key2string(idpool_attr_list,
+ LDAP_ATTR_GIDNUMBER),
+ gid_str);
+ if (mods) {
+ rc = smbldap_modify(ctx->smbldap_state,
+ ctx->suffix,
+ mods);
+ ldap_mods_free(mods, True);
+ } else {
+ ret = NT_STATUS_UNSUCCESSFUL;
+ goto done;
+ }
+ }
+
+ ret = (rc == LDAP_SUCCESS)?NT_STATUS_OK:NT_STATUS_UNSUCCESSFUL;
+done:
+ talloc_free(mem_ctx);
+ return ret;
+}
+
+/********************************
+ Allocate a new uid or gid
+********************************/
+
+static NTSTATUS idmap_ldap_allocate_id_internal(struct idmap_domain *dom,
+ struct unixid *xid)
+{
+ TALLOC_CTX *mem_ctx;
+ NTSTATUS ret = NT_STATUS_UNSUCCESSFUL;
+ int rc = LDAP_SERVER_DOWN;
+ int count = 0;
+ LDAPMessage *result = NULL;
+ LDAPMessage *entry = NULL;
+ LDAPMod **mods = NULL;
+ char *id_str;
+ char *new_id_str;
+ char *filter = NULL;
+ const char *dn = NULL;
+ const char **attr_list;
+ const char *type;
+ struct idmap_ldap_context *ctx;
+ int error = 0;
+
+ /* Only do query if we are online */
+ if (idmap_is_offline()) {
+ return NT_STATUS_FILE_IS_OFFLINE;
+ }
+
+ ctx = talloc_get_type(dom->private_data, struct idmap_ldap_context);
+
+ mem_ctx = talloc_new(ctx);
+ if (!mem_ctx) {
+ DEBUG(0, ("Out of memory!\n"));
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ /* get type */
+ switch (xid->type) {
+
+ case ID_TYPE_UID:
+ type = get_attr_key2string(idpool_attr_list,
+ LDAP_ATTR_UIDNUMBER);
+ break;
+
+ case ID_TYPE_GID:
+ type = get_attr_key2string(idpool_attr_list,
+ LDAP_ATTR_GIDNUMBER);
+ break;
+
+ case ID_TYPE_BOTH:
+ /*
+ * This is not supported here yet and
+ * already handled in idmap_rw_new_mapping()
+ */
+ FALL_THROUGH;
+ case ID_TYPE_NOT_SPECIFIED:
+ /*
+ * This is handled in idmap_rw_new_mapping()
+ */
+ FALL_THROUGH;
+ default:
+ DEBUG(2, ("Invalid ID type (0x%x)\n", xid->type));
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ filter = talloc_asprintf(mem_ctx, "(objectClass=%s)", LDAP_OBJ_IDPOOL);
+ CHECK_ALLOC_DONE(filter);
+
+ attr_list = get_attr_list(mem_ctx, idpool_attr_list);
+ CHECK_ALLOC_DONE(attr_list);
+
+ DEBUG(10, ("Search of the id pool (filter: %s)\n", filter));
+
+ rc = smbldap_search(ctx->smbldap_state,
+ ctx->suffix,
+ LDAP_SCOPE_SUBTREE, filter,
+ attr_list, 0, &result);
+
+ if (rc != LDAP_SUCCESS) {
+ DEBUG(0,("%s object not found\n", LDAP_OBJ_IDPOOL));
+ goto done;
+ }
+
+ smbldap_talloc_autofree_ldapmsg(mem_ctx, result);
+
+ count = ldap_count_entries(smbldap_get_ldap(ctx->smbldap_state),
+ result);
+ if (count != 1) {
+ DEBUG(0,("Single %s object not found\n", LDAP_OBJ_IDPOOL));
+ goto done;
+ }
+
+ entry = ldap_first_entry(smbldap_get_ldap(ctx->smbldap_state), result);
+
+ dn = smbldap_talloc_dn(mem_ctx,
+ smbldap_get_ldap(ctx->smbldap_state),
+ entry);
+ if ( ! dn) {
+ goto done;
+ }
+
+ id_str = smbldap_talloc_single_attribute(
+ smbldap_get_ldap(ctx->smbldap_state),
+ entry, type, mem_ctx);
+ if (id_str == NULL) {
+ DEBUG(0,("%s attribute not found\n", type));
+ ret = NT_STATUS_UNSUCCESSFUL;
+ goto done;
+ }
+
+ xid->id = smb_strtoul(id_str, NULL, 10, &error, SMB_STR_STANDARD);
+ if (error != 0) {
+ ret = NT_STATUS_UNSUCCESSFUL;
+ goto done;
+ }
+
+ /* make sure we still have room to grow */
+
+ switch (xid->type) {
+ case ID_TYPE_UID:
+ if (xid->id > dom->high_id) {
+ DEBUG(0,("Cannot allocate uid above %lu!\n",
+ (unsigned long)dom->high_id));
+ goto done;
+ }
+ break;
+
+ case ID_TYPE_GID:
+ if (xid->id > dom->high_id) {
+ DEBUG(0,("Cannot allocate gid above %lu!\n",
+ (unsigned long)dom->high_id));
+ goto done;
+ }
+ break;
+
+ default:
+ /* impossible */
+ goto done;
+ }
+
+ new_id_str = talloc_asprintf(mem_ctx, "%lu", (unsigned long)xid->id + 1);
+ if ( ! new_id_str) {
+ DEBUG(0,("Out of memory\n"));
+ ret = NT_STATUS_NO_MEMORY;
+ goto done;
+ }
+
+ smbldap_set_mod(&mods, LDAP_MOD_DELETE, type, id_str);
+ smbldap_set_mod(&mods, LDAP_MOD_ADD, type, new_id_str);
+
+ if (mods == NULL) {
+ DEBUG(0,("smbldap_set_mod() failed.\n"));
+ goto done;
+ }
+
+ DEBUG(10, ("Try to atomically increment the id (%s -> %s)\n",
+ id_str, new_id_str));
+
+ rc = smbldap_modify(ctx->smbldap_state, dn, mods);
+
+ ldap_mods_free(mods, True);
+
+ if (rc != LDAP_SUCCESS) {
+ DEBUG(1,("Failed to allocate new %s. "
+ "smbldap_modify() failed.\n", type));
+ goto done;
+ }
+
+ ret = NT_STATUS_OK;
+
+done:
+ talloc_free(mem_ctx);
+ return ret;
+}
+
+/**
+ * Allocate a new unix-ID.
+ * For now this is for the default idmap domain only.
+ * Should be extended later on.
+ */
+static NTSTATUS idmap_ldap_allocate_id(struct idmap_domain *dom,
+ struct unixid *id)
+{
+ NTSTATUS ret;
+
+ if (!strequal(dom->name, "*")) {
+ DEBUG(3, ("idmap_ldap_allocate_id: "
+ "Refusing allocation of a new unixid for domain'%s'. "
+ "This is only supported for the default "
+ "domain \"*\".\n",
+ dom->name));
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+
+ ret = idmap_ldap_allocate_id_internal(dom, id);
+
+ return ret;
+}
+
+
+/**********************************************************************
+ IDMAP MAPPING LDAP BACKEND
+**********************************************************************/
+
+static int idmap_ldap_close_destructor(struct idmap_ldap_context *ctx)
+{
+ smbldap_free_struct(&ctx->smbldap_state);
+ DEBUG(5,("The connection to the LDAP server was closed\n"));
+ /* maybe free the results here --metze */
+
+ return 0;
+}
+
+/********************************
+ Initialise idmap database.
+********************************/
+
+static NTSTATUS idmap_ldap_set_mapping(struct idmap_domain *dom,
+ const struct id_map *map);
+
+static NTSTATUS idmap_ldap_db_init(struct idmap_domain *dom)
+{
+ NTSTATUS ret;
+ struct idmap_ldap_context *ctx = NULL;
+ const char *tmp = NULL;
+
+ /* Only do init if we are online */
+ if (idmap_is_offline()) {
+ return NT_STATUS_FILE_IS_OFFLINE;
+ }
+
+ ctx = talloc_zero(dom, struct idmap_ldap_context);
+ if ( ! ctx) {
+ DEBUG(0, ("Out of memory!\n"));
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ tmp = idmap_config_const_string(dom->name, "ldap_url", NULL);
+
+ if ( ! tmp) {
+ DEBUG(1, ("ERROR: missing idmap ldap url\n"));
+ ret = NT_STATUS_UNSUCCESSFUL;
+ goto done;
+ }
+
+ ctx->url = talloc_strdup(ctx, tmp);
+
+ trim_char(ctx->url, '\"', '\"');
+
+ tmp = idmap_config_const_string(dom->name, "ldap_base_dn", NULL);
+ if ( ! tmp || ! *tmp) {
+ tmp = lp_ldap_idmap_suffix(talloc_tos());
+ if ( ! tmp) {
+ DEBUG(1, ("ERROR: missing idmap ldap suffix\n"));
+ ret = NT_STATUS_UNSUCCESSFUL;
+ goto done;
+ }
+ }
+
+ ctx->suffix = talloc_strdup(ctx, tmp);
+ CHECK_ALLOC_DONE(ctx->suffix);
+
+ ctx->rw_ops = talloc_zero(ctx, struct idmap_rw_ops);
+ CHECK_ALLOC_DONE(ctx->rw_ops);
+
+ ctx->rw_ops->get_new_id = idmap_ldap_allocate_id_internal;
+ ctx->rw_ops->set_mapping = idmap_ldap_set_mapping;
+
+ /* get_credentials deals with setting up creds */
+
+ ret = smbldap_init(ctx, global_event_context(), ctx->url,
+ false, NULL, NULL, &ctx->smbldap_state);
+ if (!NT_STATUS_IS_OK(ret)) {
+ DEBUG(1, ("ERROR: smbldap_init (%s) failed!\n", ctx->url));
+ goto done;
+ }
+
+ ret = get_credentials( ctx, ctx->smbldap_state,
+ dom, &ctx->user_dn );
+ if ( !NT_STATUS_IS_OK(ret) ) {
+ DEBUG(1,("idmap_ldap_db_init: Failed to get connection "
+ "credentials (%s)\n", nt_errstr(ret)));
+ goto done;
+ }
+
+ /*
+ * Set the destructor on the context, so that resources are
+ * properly freed when the context is released.
+ */
+ talloc_set_destructor(ctx, idmap_ldap_close_destructor);
+
+ dom->private_data = ctx;
+
+ ret = verify_idpool(dom);
+ if (!NT_STATUS_IS_OK(ret)) {
+ DEBUG(1, ("idmap_ldap_db_init: failed to verify ID pool (%s)\n",
+ nt_errstr(ret)));
+ goto done;
+ }
+
+ return NT_STATUS_OK;
+
+/*failed */
+done:
+ talloc_free(ctx);
+ return ret;
+}
+
+/**
+ * set a mapping.
+ */
+
+/* TODO: change this: This function cannot be called to modify a mapping,
+ * only set a new one */
+
+static NTSTATUS idmap_ldap_set_mapping(struct idmap_domain *dom,
+ const struct id_map *map)
+{
+ NTSTATUS ret;
+ TALLOC_CTX *memctx;
+ struct idmap_ldap_context *ctx;
+ LDAPMessage *entry = NULL;
+ LDAPMod **mods = NULL;
+ const char *type;
+ char *id_str;
+ struct dom_sid_buf sid;
+ char *dn;
+ int rc = -1;
+
+ /* Only do query if we are online */
+ if (idmap_is_offline()) {
+ return NT_STATUS_FILE_IS_OFFLINE;
+ }
+
+ ctx = talloc_get_type(dom->private_data, struct idmap_ldap_context);
+
+ switch(map->xid.type) {
+ case ID_TYPE_UID:
+ type = get_attr_key2string(sidmap_attr_list,
+ LDAP_ATTR_UIDNUMBER);
+ break;
+
+ case ID_TYPE_GID:
+ type = get_attr_key2string(sidmap_attr_list,
+ LDAP_ATTR_GIDNUMBER);
+ break;
+
+ default:
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ memctx = talloc_new(ctx);
+ if ( ! memctx) {
+ DEBUG(0, ("Out of memory!\n"));
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ id_str = talloc_asprintf(memctx, "%lu", (unsigned long)map->xid.id);
+ CHECK_ALLOC_DONE(id_str);
+
+ dn = talloc_asprintf(memctx, "%s=%s,%s",
+ get_attr_key2string(sidmap_attr_list, LDAP_ATTR_SID),
+ dom_sid_str_buf(map->sid, &sid),
+ ctx->suffix);
+ CHECK_ALLOC_DONE(dn);
+
+ smbldap_set_mod(&mods, LDAP_MOD_ADD,
+ "objectClass", LDAP_OBJ_IDMAP_ENTRY);
+
+ smbldap_make_mod(smbldap_get_ldap(ctx->smbldap_state),
+ entry, &mods, type, id_str);
+
+ smbldap_make_mod(smbldap_get_ldap(ctx->smbldap_state), entry, &mods,
+ get_attr_key2string(sidmap_attr_list, LDAP_ATTR_SID),
+ sid.buf);
+
+ if ( ! mods) {
+ DEBUG(2, ("ERROR: No mods?\n"));
+ ret = NT_STATUS_UNSUCCESSFUL;
+ goto done;
+ }
+
+ /* TODO: remove conflicting mappings! */
+
+ smbldap_set_mod(&mods, LDAP_MOD_ADD, "objectClass", LDAP_OBJ_SID_ENTRY);
+
+ DEBUG(10, ("Set DN %s (%s -> %s)\n", dn, sid.buf, id_str));
+
+ rc = smbldap_add(ctx->smbldap_state, dn, mods);
+ ldap_mods_free(mods, True);
+
+ if (rc != LDAP_SUCCESS) {
+ char *ld_error = NULL;
+ ldap_get_option(smbldap_get_ldap(ctx->smbldap_state),
+ LDAP_OPT_ERROR_STRING, &ld_error);
+ DEBUG(0,("ldap_set_mapping_internals: Failed to add %s to %lu "
+ "mapping [%s]\n", sid.buf,
+ (unsigned long)map->xid.id, type));
+ DEBUG(0, ("ldap_set_mapping_internals: Error was: %s (%s)\n",
+ ld_error ? ld_error : "(NULL)", ldap_err2string (rc)));
+ if (ld_error) {
+ ldap_memfree(ld_error);
+ }
+ ret = NT_STATUS_UNSUCCESSFUL;
+ goto done;
+ }
+
+ DEBUG(10,("ldap_set_mapping: Successfully created mapping from %s to "
+ "%lu [%s]\n", sid.buf, (unsigned long)map->xid.id, type));
+
+ ret = NT_STATUS_OK;
+
+done:
+ talloc_free(memctx);
+ return ret;
+}
+
+/**
+ * Create a new mapping for an unmapped SID, also allocating a new ID.
+ * If possible, this should be run inside a transaction to make the
+ * action atomic.
+ */
+static NTSTATUS idmap_ldap_new_mapping(struct idmap_domain *dom, struct id_map *map)
+{
+ NTSTATUS ret;
+ struct idmap_ldap_context *ctx;
+
+ ctx = talloc_get_type(dom->private_data, struct idmap_ldap_context);
+
+ ret = idmap_rw_new_mapping(dom, ctx->rw_ops, map);
+
+ return ret;
+}
+
+/**********************************
+ lookup a set of unix ids.
+**********************************/
+
+static NTSTATUS idmap_ldap_unixids_to_sids(struct idmap_domain *dom,
+ struct id_map **ids)
+{
+ NTSTATUS ret;
+ TALLOC_CTX *memctx;
+ struct idmap_ldap_context *ctx;
+ LDAPMessage *result = NULL;
+ LDAPMessage *entry = NULL;
+ const char *uidNumber;
+ const char *gidNumber;
+ const char **attr_list;
+ char *filter = NULL;
+ bool multi = False;
+ int idx = 0;
+ int bidx = 0;
+ int count;
+ int rc;
+ int i;
+ int error = 0;
+
+ /* Only do query if we are online */
+ if (idmap_is_offline()) {
+ return NT_STATUS_FILE_IS_OFFLINE;
+ }
+
+ ctx = talloc_get_type(dom->private_data, struct idmap_ldap_context);
+
+ memctx = talloc_new(ctx);
+ if ( ! memctx) {
+ DEBUG(0, ("Out of memory!\n"));
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ uidNumber = get_attr_key2string(idpool_attr_list, LDAP_ATTR_UIDNUMBER);
+ gidNumber = get_attr_key2string(idpool_attr_list, LDAP_ATTR_GIDNUMBER);
+
+ attr_list = get_attr_list(memctx, sidmap_attr_list);
+
+ if ( ! ids[1]) {
+ /* if we are requested just one mapping use the simple filter */
+
+ filter = talloc_asprintf(memctx, "(&(objectClass=%s)(%s=%lu))",
+ LDAP_OBJ_IDMAP_ENTRY,
+ (ids[0]->xid.type==ID_TYPE_UID)?uidNumber:gidNumber,
+ (unsigned long)ids[0]->xid.id);
+ CHECK_ALLOC_DONE(filter);
+ DEBUG(10, ("Filter: [%s]\n", filter));
+ } else {
+ /* multiple mappings */
+ multi = True;
+ }
+
+ for (i = 0; ids[i]; i++) {
+ ids[i]->status = ID_UNKNOWN;
+ }
+
+again:
+ if (multi) {
+
+ talloc_free(filter);
+ filter = talloc_asprintf(memctx,
+ "(&(objectClass=%s)(|",
+ LDAP_OBJ_IDMAP_ENTRY);
+ CHECK_ALLOC_DONE(filter);
+
+ bidx = idx;
+ for (i = 0; (i < IDMAP_LDAP_MAX_IDS) && ids[idx]; i++, idx++) {
+ filter = talloc_asprintf_append_buffer(filter, "(%s=%lu)",
+ (ids[idx]->xid.type==ID_TYPE_UID)?uidNumber:gidNumber,
+ (unsigned long)ids[idx]->xid.id);
+ CHECK_ALLOC_DONE(filter);
+ }
+ filter = talloc_asprintf_append_buffer(filter, "))");
+ CHECK_ALLOC_DONE(filter);
+ DEBUG(10, ("Filter: [%s]\n", filter));
+ } else {
+ bidx = 0;
+ idx = 1;
+ }
+
+ rc = smbldap_search(ctx->smbldap_state, ctx->suffix, LDAP_SCOPE_SUBTREE,
+ filter, attr_list, 0, &result);
+
+ if (rc != LDAP_SUCCESS) {
+ DEBUG(3,("Failure looking up ids (%s)\n", ldap_err2string(rc)));
+ ret = NT_STATUS_UNSUCCESSFUL;
+ goto done;
+ }
+
+ count = ldap_count_entries(smbldap_get_ldap(ctx->smbldap_state),
+ result);
+
+ if (count == 0) {
+ DEBUG(10, ("NO SIDs found\n"));
+ }
+
+ for (i = 0; i < count; i++) {
+ char *sidstr = NULL;
+ char *tmp = NULL;
+ enum id_type type;
+ struct id_map *map;
+ uint32_t id;
+ struct dom_sid_buf buf;
+
+ if (i == 0) { /* first entry */
+ entry = ldap_first_entry(
+ smbldap_get_ldap(ctx->smbldap_state), result);
+ } else { /* following ones */
+ entry = ldap_next_entry(
+ smbldap_get_ldap(ctx->smbldap_state), entry);
+ }
+ if ( ! entry) {
+ DEBUG(2, ("ERROR: Unable to fetch ldap entries "
+ "from results\n"));
+ break;
+ }
+
+ /* first check if the SID is present */
+ sidstr = smbldap_talloc_single_attribute(
+ smbldap_get_ldap(ctx->smbldap_state),
+ entry, LDAP_ATTRIBUTE_SID, memctx);
+ if ( ! sidstr) { /* no sid, skip entry */
+ DEBUG(2, ("WARNING SID not found on entry\n"));
+ continue;
+ }
+
+ /* now try to see if it is a uid, if not try with a gid
+ * (gid is more common, but in case both uidNumber and
+ * gidNumber are returned the SID is mapped to the uid
+ *not the gid) */
+ type = ID_TYPE_UID;
+ tmp = smbldap_talloc_single_attribute(
+ smbldap_get_ldap(ctx->smbldap_state),
+ entry, uidNumber, memctx);
+ if ( ! tmp) {
+ type = ID_TYPE_GID;
+ tmp = smbldap_talloc_single_attribute(
+ smbldap_get_ldap(ctx->smbldap_state),
+ entry, gidNumber, memctx);
+ }
+ if ( ! tmp) { /* wow very strange entry, how did it match ? */
+ DEBUG(5, ("Improbable match on (%s), no uidNumber, "
+ "nor gidNumber returned\n", sidstr));
+ TALLOC_FREE(sidstr);
+ continue;
+ }
+
+ id = smb_strtoul(tmp, NULL, 10, &error, SMB_STR_STANDARD);
+ TALLOC_FREE(tmp);
+ if (error != 0) {
+ DEBUG(5, ("Requested id (%u) out of range (%u - %u). "
+ "Filtered!\n", id,
+ dom->low_id, dom->high_id));
+ TALLOC_FREE(sidstr);
+ continue;
+ }
+
+ if (!idmap_unix_id_is_in_range(id, dom)) {
+ DEBUG(5, ("Requested id (%u) out of range (%u - %u). "
+ "Filtered!\n", id,
+ dom->low_id, dom->high_id));
+ TALLOC_FREE(sidstr);
+ continue;
+ }
+
+ map = idmap_find_map_by_id(&ids[bidx], type, id);
+ if (!map) {
+ DEBUG(2, ("WARNING: couldn't match sid (%s) "
+ "with requested ids\n", sidstr));
+ TALLOC_FREE(sidstr);
+ continue;
+ }
+
+ if ( ! string_to_sid(map->sid, sidstr)) {
+ DEBUG(2, ("ERROR: Invalid SID on entry\n"));
+ TALLOC_FREE(sidstr);
+ continue;
+ }
+
+ if (map->status == ID_MAPPED) {
+ DEBUG(1, ("WARNING: duplicate %s mapping in LDAP. "
+ "overwriting mapping %u -> %s with %u -> %s\n",
+ (type == ID_TYPE_UID) ? "UID" : "GID",
+ id,
+ dom_sid_str_buf(map->sid, &buf),
+ id,
+ sidstr));
+ }
+
+ TALLOC_FREE(sidstr);
+
+ /* mapped */
+ map->status = ID_MAPPED;
+
+ DEBUG(10, ("Mapped %s -> %lu (%d)\n",
+ dom_sid_str_buf(map->sid, &buf),
+ (unsigned long)map->xid.id, map->xid.type));
+ }
+
+ /* free the ldap results */
+ if (result) {
+ ldap_msgfree(result);
+ result = NULL;
+ }
+
+ if (multi && ids[idx]) { /* still some values to map */
+ goto again;
+ }
+
+ ret = NT_STATUS_OK;
+
+ /* mark all unknown/expired ones as unmapped */
+ for (i = 0; ids[i]; i++) {
+ if (ids[i]->status != ID_MAPPED)
+ ids[i]->status = ID_UNMAPPED;
+ }
+
+done:
+ talloc_free(memctx);
+ return ret;
+}
+
+/**********************************
+ lookup a set of sids.
+**********************************/
+
+static NTSTATUS idmap_ldap_sids_to_unixids(struct idmap_domain *dom,
+ struct id_map **ids)
+{
+ LDAPMessage *entry = NULL;
+ NTSTATUS ret;
+ TALLOC_CTX *memctx;
+ struct idmap_ldap_context *ctx;
+ LDAPMessage *result = NULL;
+ const char *uidNumber;
+ const char *gidNumber;
+ const char **attr_list;
+ char *filter = NULL;
+ bool multi = False;
+ size_t num_required = 0;
+ int idx = 0;
+ int bidx = 0;
+ int count;
+ int rc;
+ int i;
+
+ /* Only do query if we are online */
+ if (idmap_is_offline()) {
+ return NT_STATUS_FILE_IS_OFFLINE;
+ }
+
+ ctx = talloc_get_type(dom->private_data, struct idmap_ldap_context);
+
+ memctx = talloc_new(ctx);
+ if ( ! memctx) {
+ DEBUG(0, ("Out of memory!\n"));
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ uidNumber = get_attr_key2string(idpool_attr_list, LDAP_ATTR_UIDNUMBER);
+ gidNumber = get_attr_key2string(idpool_attr_list, LDAP_ATTR_GIDNUMBER);
+
+ attr_list = get_attr_list(memctx, sidmap_attr_list);
+
+ if ( ! ids[1]) {
+ struct dom_sid_buf buf;
+ /* if we are requested just one mapping use the simple filter */
+
+ filter = talloc_asprintf(memctx, "(&(objectClass=%s)(%s=%s))",
+ LDAP_OBJ_IDMAP_ENTRY,
+ LDAP_ATTRIBUTE_SID,
+ dom_sid_str_buf(ids[0]->sid, &buf));
+ CHECK_ALLOC_DONE(filter);
+ DEBUG(10, ("Filter: [%s]\n", filter));
+ } else {
+ /* multiple mappings */
+ multi = True;
+ }
+
+ for (i = 0; ids[i]; i++) {
+ ids[i]->status = ID_UNKNOWN;
+ }
+
+again:
+ if (multi) {
+
+ TALLOC_FREE(filter);
+ filter = talloc_asprintf(memctx,
+ "(&(objectClass=%s)(|",
+ LDAP_OBJ_IDMAP_ENTRY);
+ CHECK_ALLOC_DONE(filter);
+
+ bidx = idx;
+ for (i = 0; (i < IDMAP_LDAP_MAX_IDS) && ids[idx]; i++, idx++) {
+ struct dom_sid_buf buf;
+ filter = talloc_asprintf_append_buffer(filter, "("LDAP_ATTRIBUTE_SID"=%s)",
+ dom_sid_str_buf(ids[idx]->sid, &buf));
+ CHECK_ALLOC_DONE(filter);
+ }
+ filter = talloc_asprintf_append_buffer(filter, "))");
+ CHECK_ALLOC_DONE(filter);
+ DEBUG(10, ("Filter: [%s]\n", filter));
+ } else {
+ bidx = 0;
+ idx = 1;
+ }
+
+ rc = smbldap_search(ctx->smbldap_state, ctx->suffix, LDAP_SCOPE_SUBTREE,
+ filter, attr_list, 0, &result);
+
+ if (rc != LDAP_SUCCESS) {
+ DEBUG(3,("Failure looking up sids (%s)\n",
+ ldap_err2string(rc)));
+ ret = NT_STATUS_UNSUCCESSFUL;
+ goto done;
+ }
+
+ count = ldap_count_entries(smbldap_get_ldap(ctx->smbldap_state),
+ result);
+
+ if (count == 0) {
+ DEBUG(10, ("NO SIDs found\n"));
+ }
+
+ for (i = 0; i < count; i++) {
+ char *sidstr = NULL;
+ char *tmp = NULL;
+ enum id_type type;
+ struct id_map *map;
+ struct dom_sid sid;
+ struct dom_sid_buf buf;
+ uint32_t id;
+ int error = 0;
+
+ if (i == 0) { /* first entry */
+ entry = ldap_first_entry(
+ smbldap_get_ldap(ctx->smbldap_state), result);
+ } else { /* following ones */
+ entry = ldap_next_entry(
+ smbldap_get_ldap(ctx->smbldap_state), entry);
+ }
+ if ( ! entry) {
+ DEBUG(2, ("ERROR: Unable to fetch ldap entries "
+ "from results\n"));
+ break;
+ }
+
+ /* first check if the SID is present */
+ sidstr = smbldap_talloc_single_attribute(
+ smbldap_get_ldap(ctx->smbldap_state),
+ entry, LDAP_ATTRIBUTE_SID, memctx);
+ if ( ! sidstr) { /* no sid ??, skip entry */
+ DEBUG(2, ("WARNING SID not found on entry\n"));
+ continue;
+ }
+
+ if ( ! string_to_sid(&sid, sidstr)) {
+ DEBUG(2, ("ERROR: Invalid SID on entry\n"));
+ TALLOC_FREE(sidstr);
+ continue;
+ }
+
+ map = idmap_find_map_by_sid(&ids[bidx], &sid);
+ if (!map) {
+ DEBUG(2, ("WARNING: couldn't find entry sid (%s) "
+ "in ids\n", sidstr));
+ TALLOC_FREE(sidstr);
+ continue;
+ }
+
+ /* now try to see if it is a uid, if not try with a gid
+ * (gid is more common, but in case both uidNumber and
+ * gidNumber are returned the SID is mapped to the uid
+ * not the gid) */
+ type = ID_TYPE_UID;
+ tmp = smbldap_talloc_single_attribute(
+ smbldap_get_ldap(ctx->smbldap_state),
+ entry, uidNumber, memctx);
+ if ( ! tmp) {
+ type = ID_TYPE_GID;
+ tmp = smbldap_talloc_single_attribute(
+ smbldap_get_ldap(ctx->smbldap_state),
+ entry, gidNumber, memctx);
+ }
+ if ( ! tmp) { /* no ids ?? */
+ DEBUG(5, ("no uidNumber, "
+ "nor gidNumber attributes found\n"));
+ TALLOC_FREE(sidstr);
+ continue;
+ }
+
+ id = smb_strtoul(tmp, NULL, 10, &error, SMB_STR_STANDARD);
+ TALLOC_FREE(tmp);
+ if (error != 0) {
+ DEBUG(5, ("Requested id (%u) out of range (%u - %u). "
+ "Filtered!\n", id,
+ dom->low_id, dom->high_id));
+ TALLOC_FREE(sidstr);
+ continue;
+ }
+
+ if (error != 0 || !idmap_unix_id_is_in_range(id, dom)) {
+ DEBUG(5, ("Requested id (%u) out of range (%u - %u). "
+ "Filtered!\n", id,
+ dom->low_id, dom->high_id));
+ TALLOC_FREE(sidstr);
+ continue;
+ }
+
+ if (map->status == ID_MAPPED) {
+ DEBUG(1, ("WARNING: duplicate %s mapping in LDAP. "
+ "overwriting mapping %s -> %u with %s -> %u\n",
+ (type == ID_TYPE_UID) ? "UID" : "GID",
+ sidstr, map->xid.id, sidstr, id));
+ }
+
+ TALLOC_FREE(sidstr);
+
+ /* mapped */
+ map->xid.type = type;
+ map->xid.id = id;
+ map->status = ID_MAPPED;
+
+ DEBUG(10, ("Mapped %s -> %lu (%d)\n",
+ dom_sid_str_buf(map->sid, &buf),
+ (unsigned long)map->xid.id,
+ map->xid.type));
+ }
+
+ /* free the ldap results */
+ if (result) {
+ ldap_msgfree(result);
+ result = NULL;
+ }
+
+ if (multi && ids[idx]) { /* still some values to map */
+ goto again;
+ }
+
+ /*
+ * try to create new mappings for unmapped sids
+ */
+ for (i = 0; ids[i]; i++) {
+ if (ids[i]->status != ID_MAPPED) {
+ ids[i]->status = ID_UNMAPPED;
+ if (ids[i]->sid != NULL) {
+ ret = idmap_ldap_new_mapping(dom, ids[i]);
+ DBG_DEBUG("idmap_ldap_new_mapping returned %s\n",
+ nt_errstr(ret));
+ if (NT_STATUS_EQUAL(ret, STATUS_SOME_UNMAPPED)) {
+ if (ids[i]->status == ID_REQUIRE_TYPE) {
+ num_required += 1;
+ continue;
+ }
+ }
+ if (!NT_STATUS_IS_OK(ret)) {
+ /*
+ * If we can't create
+ * a new mapping it's unlikely
+ * that it will work for the
+ * next entry.
+ */
+ goto done;
+ }
+ }
+ }
+ }
+
+ ret = NT_STATUS_OK;
+ if (num_required > 0) {
+ ret = STATUS_SOME_UNMAPPED;
+ }
+
+done:
+ talloc_free(memctx);
+ return ret;
+}
+
+/**********************************
+ Close the idmap ldap instance
+**********************************/
+
+static const struct idmap_methods idmap_ldap_methods = {
+
+ .init = idmap_ldap_db_init,
+ .unixids_to_sids = idmap_ldap_unixids_to_sids,
+ .sids_to_unixids = idmap_ldap_sids_to_unixids,
+ .allocate_id = idmap_ldap_allocate_id,
+};
+
+NTSTATUS idmap_ldap_init(TALLOC_CTX *);
+NTSTATUS idmap_ldap_init(TALLOC_CTX *ctx)
+{
+ return smb_register_idmap(SMB_IDMAP_INTERFACE_VERSION, "ldap",
+ &idmap_ldap_methods);
+}
+
diff --git a/source3/winbindd/idmap_nss.c b/source3/winbindd/idmap_nss.c
new file mode 100644
index 0000000..0af2536
--- /dev/null
+++ b/source3/winbindd/idmap_nss.c
@@ -0,0 +1,446 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ idmap NSS backend
+
+ Copyright (C) Simo Sorce 2006
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "system/passwd.h"
+#include "winbindd.h"
+#include "nsswitch/winbind_client.h"
+#include "idmap.h"
+#include "lib/winbind_util.h"
+#include "libcli/security/dom_sid.h"
+#include "lib/global_contexts.h"
+#include "messages.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_IDMAP
+
+struct idmap_nss_context {
+ struct idmap_domain *dom;
+ bool use_upn;
+};
+
+static int idmap_nss_context_destructor(struct idmap_nss_context *ctx)
+{
+ if ((ctx->dom != NULL) && (ctx->dom->private_data == ctx)) {
+ ctx->dom->private_data = NULL;
+ }
+ return 0;
+}
+
+static NTSTATUS idmap_nss_context_create(TALLOC_CTX *mem_ctx,
+ struct idmap_domain *dom,
+ struct idmap_nss_context **pctx)
+{
+ struct idmap_nss_context *ctx = NULL;
+
+ ctx = talloc_zero(mem_ctx, struct idmap_nss_context);
+ if (ctx == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ ctx->dom = dom;
+
+ talloc_set_destructor(ctx, idmap_nss_context_destructor);
+
+ ctx->use_upn = idmap_config_bool(dom->name, "use_upn", false);
+
+ *pctx = ctx;
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS idmap_nss_get_context(struct idmap_domain *dom,
+ struct idmap_nss_context **pctx)
+{
+ struct idmap_nss_context *ctx = NULL;
+ NTSTATUS status;
+
+ if (dom->private_data != NULL) {
+ *pctx = talloc_get_type_abort(dom->private_data,
+ struct idmap_nss_context);
+ return NT_STATUS_OK;
+ }
+
+ status = idmap_nss_context_create(dom, dom, &ctx);
+ if (!NT_STATUS_IS_OK(status)) {
+ DBG_WARNING("idmap_nss_context_create failed: %s\n",
+ nt_errstr(status));
+ return status;
+ }
+
+ dom->private_data = ctx;
+ *pctx = ctx;
+ return NT_STATUS_OK;
+}
+
+static bool idmap_nss_msg_filter(struct messaging_rec *rec, void *private_data)
+{
+ struct idmap_domain *dom = talloc_get_type_abort(private_data,
+ struct idmap_domain);
+ struct idmap_nss_context *ctx = NULL;
+ NTSTATUS status;
+ bool ret;
+
+ if (rec->msg_type == MSG_SMB_CONF_UPDATED) {
+ ret = lp_load_global(get_dyn_CONFIGFILE());
+ if (!ret) {
+ DBG_WARNING("Failed to reload configuration\n");
+ return false;
+ }
+
+ status = idmap_nss_get_context(dom, &ctx);
+ if (NT_STATUS_IS_ERR(status)) {
+ DBG_WARNING("Failed to get idmap nss context: %s\n",
+ nt_errstr(status));
+ return false;
+ }
+
+ ctx->use_upn = idmap_config_bool(dom->name, "use_upn", false);
+ }
+
+ return false;
+}
+
+/*****************************
+ Initialise idmap database.
+*****************************/
+
+static NTSTATUS idmap_nss_int_init(struct idmap_domain *dom)
+{
+ struct idmap_nss_context *ctx = NULL;
+ NTSTATUS status;
+ struct messaging_context *msg_ctx = global_messaging_context();
+ struct tevent_req *req = NULL;
+
+ status = idmap_nss_context_create(dom, dom, &ctx);
+ if (NT_STATUS_IS_ERR(status)) {
+ return status;
+ }
+
+ dom->private_data = ctx;
+
+ req = messaging_filtered_read_send(
+ dom,
+ messaging_tevent_context(msg_ctx),
+ msg_ctx,
+ idmap_nss_msg_filter,
+ dom);
+ if (req == NULL) {
+ DBG_WARNING("messaging_filtered_read_send failed\n");
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ return status;
+}
+
+static NTSTATUS idmap_nss_lookup_name(const char *namespace,
+ const char *username,
+ struct dom_sid *sid,
+ enum lsa_SidType *type)
+{
+ bool ret;
+
+ /*
+ * By default calls to winbindd are disabled
+ * the following call will not recurse so this is safe
+ */
+ (void)winbind_on();
+ ret = winbind_lookup_name(namespace, username, sid, type);
+ (void)winbind_off();
+
+ if (!ret) {
+ DBG_NOTICE("Failed to lookup name [%s] in namespace [%s]\n",
+ username, namespace);
+ return NT_STATUS_NOT_FOUND;
+ }
+
+ return NT_STATUS_OK;
+}
+
+/**********************************
+ lookup a set of unix ids.
+**********************************/
+
+static NTSTATUS idmap_nss_unixids_to_sids(struct idmap_domain *dom, struct id_map **ids)
+{
+ struct idmap_nss_context *ctx = NULL;
+ NTSTATUS status;
+ int i;
+
+ status = idmap_nss_get_context(dom, &ctx);
+ if (NT_STATUS_IS_ERR(status)) {
+ DBG_WARNING("Failed to get idmap nss context: %s\n",
+ nt_errstr(status));
+ return status;
+ }
+
+ /* initialize the status to avoid surprise */
+ for (i = 0; ids[i]; i++) {
+ ids[i]->status = ID_UNKNOWN;
+ }
+
+ for (i = 0; ids[i]; i++) {
+ struct passwd *pw;
+ struct group *gr;
+ const char *name;
+ struct dom_sid sid;
+ enum lsa_SidType type;
+
+ switch (ids[i]->xid.type) {
+ case ID_TYPE_UID:
+ errno = 0;
+ pw = getpwuid((uid_t)ids[i]->xid.id);
+ if (!pw) {
+ DBG_DEBUG("getpwuid(%lu) failed: %s\n",
+ (unsigned long)ids[i]->xid.id,
+ errno != 0
+ ? strerror(errno)
+ : "not found");
+ ids[i]->status = ID_UNMAPPED;
+ continue;
+ }
+ name = pw->pw_name;
+ break;
+ case ID_TYPE_GID:
+ errno = 0;
+ gr = getgrgid((gid_t)ids[i]->xid.id);
+ if (!gr) {
+ DBG_DEBUG("getgrgid(%lu) failed: %s\n",
+ (unsigned long)ids[i]->xid.id,
+ errno != 0
+ ? strerror(errno)
+ : "not found");
+ ids[i]->status = ID_UNMAPPED;
+ continue;
+ }
+ name = gr->gr_name;
+ break;
+ default: /* ?? */
+ DBG_WARNING("Unexpected xid type %d\n",
+ ids[i]->xid.type);
+ ids[i]->status = ID_UNKNOWN;
+ continue;
+ }
+
+ /* Lookup name from PDC using lsa_lookup_names() */
+ if (ctx->use_upn) {
+ char *p = NULL;
+ const char *namespace = NULL;
+ const char *domname = NULL;
+ const char *domuser = NULL;
+
+ p = strstr(name, lp_winbind_separator());
+ if (p != NULL) {
+ *p = '\0';
+ domname = name;
+ namespace = domname;
+ domuser = p + 1;
+ } else {
+ p = strchr(name, '@');
+ if (p != NULL) {
+ *p = '\0';
+ namespace = p + 1;
+ domname = "";
+ domuser = name;
+ } else {
+ namespace = dom->name;
+ domuser = name;
+ }
+ }
+
+ DBG_DEBUG("Using namespace [%s] from UPN instead "
+ "of [%s] to lookup the name [%s]\n",
+ namespace, dom->name, domuser);
+
+ status = idmap_nss_lookup_name(namespace,
+ domuser,
+ &sid,
+ &type);
+ } else {
+ status = idmap_nss_lookup_name(dom->name,
+ name,
+ &sid,
+ &type);
+ }
+
+ if (NT_STATUS_IS_ERR(status)) {
+ /*
+ * TODO: how do we know if the name is really
+ * not mapped, or something just failed ?
+ */
+ ids[i]->status = ID_UNMAPPED;
+ continue;
+ }
+
+ switch (type) {
+ case SID_NAME_USER:
+ if (ids[i]->xid.type == ID_TYPE_UID) {
+ sid_copy(ids[i]->sid, &sid);
+ ids[i]->status = ID_MAPPED;
+ }
+ break;
+
+ case SID_NAME_DOM_GRP:
+ case SID_NAME_ALIAS:
+ case SID_NAME_WKN_GRP:
+ if (ids[i]->xid.type == ID_TYPE_GID) {
+ sid_copy(ids[i]->sid, &sid);
+ ids[i]->status = ID_MAPPED;
+ }
+ break;
+
+ default:
+ ids[i]->status = ID_UNKNOWN;
+ break;
+ }
+ }
+ return NT_STATUS_OK;
+}
+
+/**********************************
+ lookup a set of sids.
+**********************************/
+
+static NTSTATUS idmap_nss_sids_to_unixids(struct idmap_domain *dom, struct id_map **ids)
+{
+ struct idmap_nss_context *ctx = NULL;
+ NTSTATUS status;
+ int i;
+
+ status = idmap_nss_get_context(dom, &ctx);
+ if (NT_STATUS_IS_ERR(status)) {
+ DBG_WARNING("Failed to get idmap nss context: %s\n",
+ nt_errstr(status));
+ return status;
+ }
+
+ /* initialize the status to avoid surprise */
+ for (i = 0; ids[i]; i++) {
+ ids[i]->status = ID_UNKNOWN;
+ }
+
+ for (i = 0; ids[i]; i++) {
+ struct group *gr;
+ enum lsa_SidType type;
+ const char *_domain = NULL;
+ const char *_name = NULL;
+ char *domain = NULL;
+ char *name = NULL;
+ char *fqdn = NULL;
+ char *sname = NULL;
+ bool ret;
+
+ /* by default calls to winbindd are disabled
+ the following call will not recurse so this is safe */
+ (void)winbind_on();
+ ret = winbind_lookup_sid(talloc_tos(),
+ ids[i]->sid,
+ &_domain,
+ &_name,
+ &type);
+ (void)winbind_off();
+ if (!ret) {
+ /* TODO: how do we know if the name is really not mapped,
+ * or something just failed ? */
+ ids[i]->status = ID_UNMAPPED;
+ continue;
+ }
+
+ domain = discard_const_p(char, _domain);
+ name = discard_const_p(char, _name);
+
+ if (!strequal(domain, dom->name)) {
+ struct dom_sid_buf buf;
+ DBG_ERR("DOMAIN[%s] ignoring SID[%s] belongs to %s [%s\\%s]\n",
+ dom->name, dom_sid_str_buf(ids[i]->sid, &buf),
+ sid_type_lookup(type), domain, name);
+ ids[i]->status = ID_UNMAPPED;
+ continue;
+ }
+
+ if (ctx->use_upn) {
+ fqdn = talloc_asprintf(talloc_tos(),
+ "%s%s%s",
+ domain,
+ lp_winbind_separator(),
+ name);
+ if (fqdn == NULL) {
+ DBG_ERR("No memory\n");
+ ids[i]->status = ID_UNMAPPED;
+ continue;
+ }
+ DBG_DEBUG("Using UPN [%s] instead of plain name [%s]\n",
+ fqdn, name);
+ sname = fqdn;
+ } else {
+ sname = name;
+ }
+
+ switch (type) {
+ case SID_NAME_USER: {
+ struct passwd *pw;
+
+ /* this will find also all lower case name and use username level */
+ pw = Get_Pwnam_alloc(talloc_tos(), sname);
+ if (pw) {
+ ids[i]->xid.id = pw->pw_uid;
+ ids[i]->xid.type = ID_TYPE_UID;
+ ids[i]->status = ID_MAPPED;
+ }
+ TALLOC_FREE(pw);
+ break;
+ }
+
+ case SID_NAME_DOM_GRP:
+ case SID_NAME_ALIAS:
+ case SID_NAME_WKN_GRP:
+
+ gr = getgrnam(sname);
+ if (gr) {
+ ids[i]->xid.id = gr->gr_gid;
+ ids[i]->xid.type = ID_TYPE_GID;
+ ids[i]->status = ID_MAPPED;
+ }
+ break;
+
+ default:
+ ids[i]->status = ID_UNKNOWN;
+ break;
+ }
+ TALLOC_FREE(domain);
+ TALLOC_FREE(name);
+ TALLOC_FREE(fqdn);
+ }
+ return NT_STATUS_OK;
+}
+
+/**********************************
+ Close the idmap tdb instance
+**********************************/
+
+static const struct idmap_methods nss_methods = {
+ .init = idmap_nss_int_init,
+ .unixids_to_sids = idmap_nss_unixids_to_sids,
+ .sids_to_unixids = idmap_nss_sids_to_unixids,
+};
+
+NTSTATUS idmap_nss_init(TALLOC_CTX *mem_ctx)
+{
+ return smb_register_idmap(SMB_IDMAP_INTERFACE_VERSION, "nss", &nss_methods);
+}
diff --git a/source3/winbindd/idmap_passdb.c b/source3/winbindd/idmap_passdb.c
new file mode 100644
index 0000000..ab02119
--- /dev/null
+++ b/source3/winbindd/idmap_passdb.c
@@ -0,0 +1,87 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ idmap PASSDB backend
+
+ Copyright (C) Simo Sorce 2006
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "idmap.h"
+#include "passdb.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_IDMAP
+
+/*****************************
+ Initialise idmap database.
+*****************************/
+
+static NTSTATUS idmap_pdb_init(struct idmap_domain *dom)
+{
+ return NT_STATUS_OK;
+}
+
+/**********************************
+ lookup a set of unix ids.
+**********************************/
+
+static NTSTATUS idmap_pdb_unixids_to_sids(struct idmap_domain *dom, struct id_map **ids)
+{
+ int i;
+
+ for (i = 0; ids[i]; i++) {
+ /* unmapped by default */
+ ids[i]->status = ID_UNMAPPED;
+
+ if (pdb_id_to_sid(&ids[i]->xid, ids[i]->sid)) {
+ ids[i]->status = ID_MAPPED;
+ }
+ }
+
+ return NT_STATUS_OK;
+}
+
+/**********************************
+ lookup a set of sids.
+**********************************/
+
+static NTSTATUS idmap_pdb_sids_to_unixids(struct idmap_domain *dom, struct id_map **ids)
+{
+ int i;
+
+ for (i = 0; ids[i]; i++) {
+ if (pdb_sid_to_id(ids[i]->sid, &ids[i]->xid)) {
+ ids[i]->status = ID_MAPPED;
+ } else {
+ /* Query Failed */
+ ids[i]->status = ID_UNMAPPED;
+ }
+ }
+
+ return NT_STATUS_OK;
+}
+
+static const struct idmap_methods passdb_methods = {
+ .init = idmap_pdb_init,
+ .unixids_to_sids = idmap_pdb_unixids_to_sids,
+ .sids_to_unixids = idmap_pdb_sids_to_unixids,
+};
+
+NTSTATUS idmap_passdb_init(TALLOC_CTX *mem_ctx)
+{
+ return smb_register_idmap(SMB_IDMAP_INTERFACE_VERSION, "passdb", &passdb_methods);
+}
diff --git a/source3/winbindd/idmap_proto.h b/source3/winbindd/idmap_proto.h
new file mode 100644
index 0000000..adc0443
--- /dev/null
+++ b/source3/winbindd/idmap_proto.h
@@ -0,0 +1,69 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * ID Mapping
+ *
+ * Copyright (C) Tim Potter 2000
+ * Copyright (C) Jim McDonough <jmcd@us.ibm.com> 2003
+ * Copyright (C) Simo Sorce 2003-2007
+ * Copyright (C) Jeremy Allison 2006
+ * Copyright (C) Michael Adam 2009-2010
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef _WINBINDD_IDMAP_PROTO_H_
+#define _WINBINDD_IDMAP_PROTO_H_
+
+/* The following definitions come from winbindd/idmap.c */
+
+bool idmap_is_offline(void);
+NTSTATUS smb_register_idmap(int version, const char *name,
+ const struct idmap_methods *methods);
+void idmap_close(void);
+NTSTATUS idmap_allocate_uid(struct unixid *id);
+NTSTATUS idmap_allocate_gid(struct unixid *id);
+NTSTATUS idmap_backend_unixids_to_sids(struct id_map **maps,
+ const char *domain_name,
+ struct dom_sid domain_sid);
+struct idmap_domain *idmap_find_domain(const char *domname);
+
+/* The following definitions come from winbindd/idmap_nss.c */
+
+NTSTATUS idmap_nss_init(TALLOC_CTX *mem_ctx);
+
+/* The following definitions come from winbindd/idmap_passdb.c */
+
+NTSTATUS idmap_passdb_init(TALLOC_CTX *mem_ctx);
+
+/* The following definitions come from winbindd/idmap_tdb.c */
+
+NTSTATUS idmap_tdb_init(TALLOC_CTX *mem_ctx);
+
+/* The following definitions come from winbindd/idmap_util.c */
+
+bool idmap_unix_id_is_in_range(uint32_t id, struct idmap_domain *dom);
+struct id_map *idmap_find_map_by_id(struct id_map **maps, enum id_type type,
+ uint32_t id);
+struct id_map *idmap_find_map_by_sid(struct id_map **maps, struct dom_sid *sid);
+char *idmap_fetch_secret(const char *backend, const char *domain,
+ const char *identity);
+
+struct id_map **id_map_ptrs_init(TALLOC_CTX *mem_ctx, size_t num_ids);
+
+/* max number of ids requested per LDAP batch query */
+#define IDMAP_LDAP_MAX_IDS 30
+
+NTSTATUS idmap_ad_nss_init(TALLOC_CTX *mem_ctx);
+
+#endif /* _WINBINDD_IDMAP_PROTO_H_ */
diff --git a/source3/winbindd/idmap_rfc2307.c b/source3/winbindd/idmap_rfc2307.c
new file mode 100644
index 0000000..2b32238
--- /dev/null
+++ b/source3/winbindd/idmap_rfc2307.c
@@ -0,0 +1,848 @@
+/*
+ * Unix SMB/CIFS implementation.
+ *
+ * Id mapping using LDAP records as defined in RFC 2307
+ *
+ * The SID<->uid/gid mapping is performed in two steps: 1) Query the
+ * AD server for the name<->sid mapping. 2) Query an LDAP server
+ * according to RFC 2307 for the name<->uid/gid mapping.
+ *
+ * Copyright (C) Christof Schmitt 2012,2013
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "includes.h"
+#include "winbindd.h"
+#include "winbindd_ads.h"
+#include "idmap.h"
+#include "smbldap.h"
+#include "nsswitch/winbind_client.h"
+#include "lib/winbind_util.h"
+#include "libcli/security/dom_sid.h"
+#include "lib/global_contexts.h"
+
+/*
+ * Config and connection info per domain.
+ */
+struct idmap_rfc2307_context {
+ const char *bind_path_user;
+ const char *bind_path_group;
+ const char *ldap_domain;
+ bool user_cn;
+ const char *realm;
+
+ /*
+ * Pointer to ldap struct in ads or smbldap_state, has to be
+ * updated after connecting to server
+ */
+ LDAP *ldap;
+
+ /* Optional function to check connection to server */
+ NTSTATUS (*check_connection)(struct idmap_domain *dom);
+
+ /* Issue ldap query */
+ NTSTATUS (*search)(struct idmap_rfc2307_context *ctx,
+ const char *bind_path, const char *expr,
+ const char **attrs, LDAPMessage **res);
+
+ /* Access to LDAP in AD server */
+ ADS_STRUCT *ads;
+
+ /* Access to stand-alone LDAP server */
+ struct smbldap_state *smbldap_state;
+};
+
+/*
+ * backend functions for LDAP queries through ADS
+ */
+
+static NTSTATUS idmap_rfc2307_ads_check_connection(struct idmap_domain *dom)
+{
+ struct idmap_rfc2307_context *ctx;
+ const char *dom_name = dom->name;
+ ADS_STATUS status;
+
+ DEBUG(10, ("ad_idmap_cached_connection: called for domain '%s'\n",
+ dom->name));
+
+ ctx = talloc_get_type(dom->private_data, struct idmap_rfc2307_context);
+ dom_name = ctx->ldap_domain ? ctx->ldap_domain : dom->name;
+
+ status = ads_idmap_cached_connection(dom_name, ctx, &ctx->ads);
+ if (ADS_ERR_OK(status)) {
+ ctx->ldap = ctx->ads->ldap.ld;
+ } else {
+ DEBUG(1, ("Could not connect to domain %s: %s\n", dom->name,
+ ads_errstr(status)));
+ }
+
+ return ads_ntstatus(status);
+}
+
+static NTSTATUS idmap_rfc2307_ads_search(struct idmap_rfc2307_context *ctx,
+ const char *bind_path,
+ const char *expr,
+ const char **attrs,
+ LDAPMessage **result)
+{
+ ADS_STATUS status;
+
+ status = ads_do_search_retry(ctx->ads, bind_path,
+ LDAP_SCOPE_SUBTREE, expr, attrs, result);
+
+ if (!ADS_ERR_OK(status)) {
+ return ads_ntstatus(status);
+ }
+
+ ctx->ldap = ctx->ads->ldap.ld;
+ return ads_ntstatus(status);
+}
+
+static NTSTATUS idmap_rfc2307_init_ads(struct idmap_rfc2307_context *ctx,
+ const char *domain_name)
+{
+ const char *ldap_domain;
+
+ ctx->search = idmap_rfc2307_ads_search;
+ ctx->check_connection = idmap_rfc2307_ads_check_connection;
+
+ ldap_domain = idmap_config_const_string(domain_name, "ldap_domain",
+ NULL);
+ if (ldap_domain) {
+ ctx->ldap_domain = talloc_strdup(ctx, ldap_domain);
+ if (ctx->ldap_domain == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ }
+
+ return NT_STATUS_OK;
+}
+
+/*
+ * backend function for LDAP queries through stand-alone LDAP server
+ */
+
+static NTSTATUS idmap_rfc2307_ldap_search(struct idmap_rfc2307_context *ctx,
+ const char *bind_path,
+ const char *expr,
+ const char **attrs,
+ LDAPMessage **result)
+{
+ int ret;
+
+ ret = smbldap_search(ctx->smbldap_state, bind_path, LDAP_SCOPE_SUBTREE,
+ expr, attrs, 0, result);
+ ctx->ldap = smbldap_get_ldap(ctx->smbldap_state);
+
+ if (ret == LDAP_SUCCESS) {
+ return NT_STATUS_OK;
+ }
+
+ return NT_STATUS_LDAP(ret);
+}
+
+static bool idmap_rfc2307_get_uint32(LDAP *ldap, LDAPMessage *entry,
+ const char *field, uint32_t *value)
+{
+ bool b;
+ char str[20];
+
+ b = smbldap_get_single_attribute(ldap, entry, field, str, sizeof(str));
+
+ if (b) {
+ *value = atoi(str);
+ }
+
+ return b;
+}
+
+static NTSTATUS idmap_rfc2307_init_ldap(struct idmap_rfc2307_context *ctx,
+ const char *domain_name)
+{
+ NTSTATUS ret;
+ char *url;
+ char *secret = NULL;
+ const char *ldap_url, *user_dn;
+ TALLOC_CTX *mem_ctx = ctx;
+
+ ldap_url = idmap_config_const_string(domain_name, "ldap_url", NULL);
+ if (!ldap_url) {
+ DEBUG(1, ("ERROR: missing idmap ldap url\n"));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ url = talloc_strdup(talloc_tos(), ldap_url);
+
+ user_dn = idmap_config_const_string(domain_name, "ldap_user_dn", NULL);
+ if (user_dn) {
+ secret = idmap_fetch_secret("ldap", domain_name, user_dn);
+ if (!secret) {
+ ret = NT_STATUS_ACCESS_DENIED;
+ goto done;
+ }
+ }
+
+ /* assume anonymous if we don't have a specified user */
+ ret = smbldap_init(mem_ctx, global_event_context(), url,
+ (user_dn == NULL), user_dn, secret,
+ &ctx->smbldap_state);
+ BURN_FREE_STR(secret);
+ if (!NT_STATUS_IS_OK(ret)) {
+ DEBUG(1, ("ERROR: smbldap_init (%s) failed!\n", url));
+ goto done;
+ }
+
+ ctx->search = idmap_rfc2307_ldap_search;
+
+done:
+ talloc_free(url);
+ return ret;
+}
+
+/*
+ * common code for stand-alone LDAP and ADS
+ */
+
+static void idmap_rfc2307_map_sid_results(struct idmap_rfc2307_context *ctx,
+ TALLOC_CTX *mem_ctx,
+ struct id_map **ids,
+ LDAPMessage *result,
+ const char *dom_name,
+ const char **attrs, int type)
+{
+ int count, i;
+ LDAPMessage *entry;
+
+ count = ldap_count_entries(ctx->ldap, result);
+
+ for (i = 0; i < count; i++) {
+ char *name;
+ struct dom_sid sid;
+ enum lsa_SidType lsa_type;
+ struct id_map *map;
+ uint32_t id;
+ bool b;
+
+ if (i == 0) {
+ entry = ldap_first_entry(ctx->ldap, result);
+ } else {
+ entry = ldap_next_entry(ctx->ldap, entry);
+ }
+ if (!entry) {
+ DEBUG(2, ("Unable to fetch entry.\n"));
+ break;
+ }
+
+ name = smbldap_talloc_single_attribute(ctx->ldap, entry,
+ attrs[0], mem_ctx);
+ if (!name) {
+ DEBUG(1, ("Could not get user name\n"));
+ continue;
+ }
+
+ b = idmap_rfc2307_get_uint32(ctx->ldap, entry, attrs[1], &id);
+ if (!b) {
+ DEBUG(1, ("Could not pull id for record %s\n", name));
+ continue;
+ }
+
+ map = idmap_find_map_by_id(ids, type, id);
+ if (!map) {
+ DEBUG(1, ("Could not find id %d, name %s\n", id, name));
+ continue;
+ }
+
+ if (ctx->realm != NULL) {
+ /* Strip @realm from user or group name */
+ char *delim;
+
+ delim = strchr(name, '@');
+ if (delim) {
+ *delim = '\0';
+ }
+ }
+
+ /* by default calls to winbindd are disabled
+ the following call will not recurse so this is safe */
+ (void)winbind_on();
+ /* Lookup name from PDC using lsa_lookup_names() */
+ b = winbind_lookup_name(dom_name, name, &sid, &lsa_type);
+ (void)winbind_off();
+
+ if (!b) {
+ DEBUG(1, ("SID lookup failed for id %d, %s\n",
+ id, name));
+ continue;
+ }
+
+ if (type == ID_TYPE_UID && lsa_type != SID_NAME_USER) {
+ DEBUG(1, ("Wrong type %d for user name %s\n",
+ type, name));
+ continue;
+ }
+
+ if (type == ID_TYPE_GID && lsa_type != SID_NAME_DOM_GRP &&
+ lsa_type != SID_NAME_ALIAS &&
+ lsa_type != SID_NAME_WKN_GRP) {
+ DEBUG(1, ("Wrong type %d for group name %s\n",
+ type, name));
+ continue;
+ }
+
+ map->status = ID_MAPPED;
+ sid_copy(map->sid, &sid);
+ }
+}
+
+/*
+ * Map unixids to names and then to sids.
+ */
+static NTSTATUS idmap_rfc2307_unixids_to_sids(struct idmap_domain *dom,
+ struct id_map **ids)
+{
+ struct idmap_rfc2307_context *ctx;
+ char *fltr_usr = NULL, *fltr_grp = NULL;
+ TALLOC_CTX *mem_ctx;
+ int cnt_usr = 0, cnt_grp = 0, idx = 0, bidx = 0;
+ LDAPMessage *result = NULL;
+ NTSTATUS ret;
+
+ ctx = talloc_get_type(dom->private_data, struct idmap_rfc2307_context);
+ mem_ctx = talloc_new(ctx);
+ if (!mem_ctx) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ if (ctx->check_connection) {
+ ret = ctx->check_connection(dom);
+ if (!NT_STATUS_IS_OK(ret)) {
+ goto out;
+ }
+ }
+
+again:
+ bidx = idx;
+
+ if (!fltr_usr) {
+ /* prepare new user query, see getpwuid() in RFC2307 */
+ fltr_usr = talloc_asprintf(mem_ctx,
+ "(&(objectClass=posixAccount)(|");
+ }
+
+ if (!fltr_grp) {
+ /* prepare new group query, see getgrgid() in RFC2307 */
+ fltr_grp = talloc_asprintf(mem_ctx,
+ "(&(objectClass=posixGroup)(|");
+ }
+
+ if (!fltr_usr || !fltr_grp) {
+ ret = NT_STATUS_NO_MEMORY;
+ goto out;
+ }
+
+ while (cnt_usr < IDMAP_LDAP_MAX_IDS &&
+ cnt_grp < IDMAP_LDAP_MAX_IDS && ids[idx]) {
+
+ switch (ids[idx]->xid.type) {
+ case ID_TYPE_UID:
+ fltr_usr = talloc_asprintf_append_buffer(fltr_usr,
+ "(uidNumber=%d)", ids[idx]->xid.id);
+ cnt_usr++;
+ break;
+ case ID_TYPE_GID:
+ fltr_grp = talloc_asprintf_append_buffer(fltr_grp,
+ "(gidNumber=%d)", ids[idx]->xid.id);
+ cnt_grp++;
+ break;
+ default:
+ DEBUG(3, ("Error: unknown ID type %d\n",
+ ids[idx]->xid.type));
+ ret = NT_STATUS_UNSUCCESSFUL;
+ goto out;
+ }
+
+ if (!fltr_usr || !fltr_grp) {
+ ret = NT_STATUS_NO_MEMORY;
+ goto out;
+ }
+
+ idx++;
+ }
+
+ if (cnt_usr == IDMAP_LDAP_MAX_IDS || (cnt_usr != 0 && !ids[idx])) {
+ const char *attrs[] = { NULL, /* uid or cn */
+ "uidNumber",
+ NULL };
+
+ fltr_usr = talloc_strdup_append(fltr_usr, "))");
+ if (!fltr_usr) {
+ ret = NT_STATUS_NO_MEMORY;
+ goto out;
+ }
+
+ attrs[0] = ctx->user_cn ? "cn" : "uid";
+ ret = ctx->search(ctx, ctx->bind_path_user, fltr_usr, attrs,
+ &result);
+ if (!NT_STATUS_IS_OK(ret)) {
+ goto out;
+ }
+
+ idmap_rfc2307_map_sid_results(ctx, mem_ctx, &ids[bidx], result,
+ dom->name, attrs, ID_TYPE_UID);
+ cnt_usr = 0;
+ TALLOC_FREE(fltr_usr);
+ }
+
+ if (cnt_grp == IDMAP_LDAP_MAX_IDS || (cnt_grp != 0 && !ids[idx])) {
+ const char *attrs[] = { "cn", "gidNumber", NULL };
+
+ fltr_grp = talloc_strdup_append(fltr_grp, "))");
+ if (!fltr_grp) {
+ ret = NT_STATUS_NO_MEMORY;
+ goto out;
+ }
+ ret = ctx->search(ctx, ctx->bind_path_group, fltr_grp, attrs,
+ &result);
+ if (!NT_STATUS_IS_OK(ret)) {
+ goto out;
+ }
+
+ idmap_rfc2307_map_sid_results(ctx, mem_ctx, &ids[bidx], result,
+ dom->name, attrs, ID_TYPE_GID);
+ cnt_grp = 0;
+ TALLOC_FREE(fltr_grp);
+ }
+
+ if (ids[idx]) {
+ goto again;
+ }
+
+ ret = NT_STATUS_OK;
+
+out:
+ talloc_free(mem_ctx);
+ return ret;
+}
+
+struct idmap_rfc2307_map {
+ struct id_map *map;
+ const char *name;
+ enum id_type type;
+};
+
+/*
+ * Lookup names for SIDS and store the data in the local mapping
+ * array.
+ */
+static NTSTATUS idmap_rfc_2307_sids_to_names(TALLOC_CTX *mem_ctx,
+ struct id_map **ids,
+ struct idmap_rfc2307_map *maps,
+ struct idmap_rfc2307_context *ctx)
+{
+ int i;
+
+ for (i = 0; ids[i]; i++) {
+ const char *domain, *name;
+ enum lsa_SidType lsa_type;
+ struct id_map *id = ids[i];
+ struct idmap_rfc2307_map *map = &maps[i];
+ struct dom_sid_buf buf;
+ bool b;
+
+ /* by default calls to winbindd are disabled
+ the following call will not recurse so this is safe */
+ (void)winbind_on();
+ b = winbind_lookup_sid(mem_ctx, ids[i]->sid, &domain, &name,
+ &lsa_type);
+ (void)winbind_off();
+
+ if (!b) {
+ DEBUG(1, ("Lookup sid %s failed.\n",
+ dom_sid_str_buf(ids[i]->sid, &buf)));
+ continue;
+ }
+
+ switch(lsa_type) {
+ case SID_NAME_USER:
+ id->xid.type = map->type = ID_TYPE_UID;
+ if (ctx->user_cn && ctx->realm != NULL) {
+ name = talloc_asprintf(mem_ctx, "%s@%s",
+ name, ctx->realm);
+ }
+ id->xid.type = map->type = ID_TYPE_UID;
+ break;
+
+ case SID_NAME_DOM_GRP:
+ case SID_NAME_ALIAS:
+ case SID_NAME_WKN_GRP:
+ if (ctx->realm != NULL) {
+ name = talloc_asprintf(mem_ctx, "%s@%s",
+ name, ctx->realm);
+ }
+ id->xid.type = map->type = ID_TYPE_GID;
+ break;
+
+ default:
+ DEBUG(1, ("Unknown lsa type %d for sid %s\n",
+ lsa_type,
+ dom_sid_str_buf(id->sid, &buf)));
+ id->status = ID_UNMAPPED;
+ continue;
+ }
+
+ map->map = id;
+ id->status = ID_UNKNOWN;
+ map->name = strupper_talloc(mem_ctx, name);
+
+ if (!map->name) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ }
+
+ return NT_STATUS_OK;
+}
+
+/*
+ * Find id_map entry by looking up the name in the internal
+ * mapping array.
+ */
+static struct id_map* idmap_rfc2307_find_map(struct idmap_rfc2307_map *maps,
+ enum id_type type,
+ const char *name)
+{
+ int i;
+
+ DEBUG(10, ("Looking for name %s, type %d\n", name, type));
+
+ for (i = 0; maps[i].map != NULL; i++) {
+ DEBUG(10, ("Entry %d: name %s, type %d\n",
+ i, maps[i].name, maps[i].type));
+ if (type == maps[i].type && strcmp(name, maps[i].name) == 0) {
+ return maps[i].map;
+ }
+ }
+
+ return NULL;
+}
+
+static void idmap_rfc2307_map_xid_results(struct idmap_rfc2307_context *ctx,
+ TALLOC_CTX *mem_ctx,
+ struct idmap_rfc2307_map *maps,
+ LDAPMessage *result,
+ struct idmap_domain *dom,
+ const char **attrs, enum id_type type)
+{
+ int count, i;
+ LDAPMessage *entry;
+
+ count = ldap_count_entries(ctx->ldap, result);
+
+ for (i = 0; i < count; i++) {
+ uint32_t id;
+ char *name;
+ bool b;
+ struct id_map *id_map;
+
+ if (i == 0) {
+ entry = ldap_first_entry(ctx->ldap, result);
+ } else {
+ entry = ldap_next_entry(ctx->ldap, entry);
+ }
+ if (!entry) {
+ DEBUG(2, ("Unable to fetch entry.\n"));
+ break;
+ }
+
+ name = smbldap_talloc_single_attribute(ctx->ldap, entry,
+ attrs[0], mem_ctx);
+ if (!name) {
+ DEBUG(1, ("Could not get user name\n"));
+ continue;
+ }
+
+ b = idmap_rfc2307_get_uint32(ctx->ldap, entry, attrs[1], &id);
+ if (!b) {
+ DEBUG(5, ("Could not pull id for record %s\n", name));
+ continue;
+ }
+
+ if (!idmap_unix_id_is_in_range(id, dom)) {
+ DEBUG(5, ("Requested id (%u) out of range (%u - %u).\n",
+ id, dom->low_id, dom->high_id));
+ continue;
+ }
+
+ if (!strupper_m(name)) {
+ DEBUG(5, ("Could not convert %s to uppercase\n", name));
+ continue;
+ }
+ id_map = idmap_rfc2307_find_map(maps, type, name);
+ if (!id_map) {
+ DEBUG(0, ("Could not find mapping entry for name %s\n",
+ name));
+ continue;
+ }
+
+ id_map->xid.id = id;
+ id_map->status = ID_MAPPED;
+ }
+}
+
+/*
+ * Map sids to names and then to unixids.
+ */
+static NTSTATUS idmap_rfc2307_sids_to_unixids(struct idmap_domain *dom,
+ struct id_map **ids)
+{
+ struct idmap_rfc2307_context *ctx;
+ TALLOC_CTX *mem_ctx;
+ struct idmap_rfc2307_map *int_maps;
+ int cnt_usr = 0, cnt_grp = 0, idx = 0;
+ char *fltr_usr = NULL, *fltr_grp = NULL;
+ NTSTATUS ret;
+ int i;
+
+ ctx = talloc_get_type(dom->private_data, struct idmap_rfc2307_context);
+ mem_ctx = talloc_new(talloc_tos());
+ if (!mem_ctx) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ if (ctx->check_connection) {
+ ret = ctx->check_connection(dom);
+ if (!NT_STATUS_IS_OK(ret)) {
+ goto out;
+ }
+ }
+
+ for (i = 0; ids[i]; i++);
+ int_maps = talloc_zero_array(mem_ctx, struct idmap_rfc2307_map, i);
+ if (!int_maps) {
+ ret = NT_STATUS_NO_MEMORY;
+ goto out;
+ }
+
+ ret = idmap_rfc_2307_sids_to_names(mem_ctx, ids, int_maps, ctx);
+ if (!NT_STATUS_IS_OK(ret)) {
+ goto out;
+ }
+
+again:
+ if (!fltr_usr) {
+ /* prepare new user query, see getpwuid() in RFC2307 */
+ fltr_usr = talloc_asprintf(mem_ctx,
+ "(&(objectClass=posixAccount)(|");
+ }
+
+ if (!fltr_grp) {
+ /* prepare new group query, see getgrgid() in RFC2307 */
+ fltr_grp = talloc_asprintf(mem_ctx,
+ "(&(objectClass=posixGroup)(|");
+ }
+
+ if (!fltr_usr || !fltr_grp) {
+ ret = NT_STATUS_NO_MEMORY;
+ goto out;
+ }
+
+ while (cnt_usr < IDMAP_LDAP_MAX_IDS &&
+ cnt_grp < IDMAP_LDAP_MAX_IDS && ids[idx]) {
+ struct id_map *id = ids[idx];
+ struct idmap_rfc2307_map *map = &int_maps[idx];
+
+ switch(id->xid.type) {
+ case ID_TYPE_UID:
+ fltr_usr = talloc_asprintf_append_buffer(fltr_usr,
+ "(%s=%s)", (ctx->user_cn ? "cn" : "uid"),
+ map->name);
+ cnt_usr++;
+ break;
+
+ case ID_TYPE_GID:
+ fltr_grp = talloc_asprintf_append_buffer(fltr_grp,
+ "(cn=%s)", map->name);
+ cnt_grp++;
+ break;
+
+ default:
+ break;
+ }
+
+ if (!fltr_usr || !fltr_grp) {
+ ret = NT_STATUS_NO_MEMORY;
+ goto out;
+ }
+
+ idx++;
+ }
+
+ if (cnt_usr == IDMAP_LDAP_MAX_IDS || (cnt_usr != 0 && !ids[idx])) {
+ const char *attrs[] = { NULL, /* uid or cn */
+ "uidNumber",
+ NULL };
+ LDAPMessage *result;
+
+ fltr_usr = talloc_strdup_append(fltr_usr, "))");
+ if (!fltr_usr) {
+ ret = NT_STATUS_NO_MEMORY;
+ goto out;
+ }
+
+ attrs[0] = ctx->user_cn ? "cn" : "uid";
+ ret = ctx->search(ctx, ctx->bind_path_user, fltr_usr, attrs,
+ &result);
+ if (!NT_STATUS_IS_OK(ret)) {
+ goto out;
+ }
+
+ idmap_rfc2307_map_xid_results(ctx, mem_ctx, int_maps,
+ result, dom, attrs, ID_TYPE_UID);
+
+ cnt_usr = 0;
+ TALLOC_FREE(fltr_usr);
+ }
+
+ if (cnt_grp == IDMAP_LDAP_MAX_IDS || (cnt_grp != 0 && !ids[idx])) {
+ const char *attrs[] = {"cn", "gidNumber", NULL };
+ LDAPMessage *result;
+
+ fltr_grp = talloc_strdup_append(fltr_grp, "))");
+ if (!fltr_grp) {
+ ret = NT_STATUS_NO_MEMORY;
+ goto out;
+ }
+
+ ret = ctx->search(ctx, ctx->bind_path_group, fltr_grp, attrs,
+ &result);
+ if (!NT_STATUS_IS_OK(ret)) {
+ goto out;
+ }
+
+ idmap_rfc2307_map_xid_results(ctx, mem_ctx, int_maps, result,
+ dom, attrs, ID_TYPE_GID);
+ cnt_grp = 0;
+ TALLOC_FREE(fltr_grp);
+ }
+
+ if (ids[idx]) {
+ goto again;
+ }
+
+ ret = NT_STATUS_OK;
+
+out:
+ talloc_free(mem_ctx);
+ return ret;
+}
+
+static int idmap_rfc2307_context_destructor(struct idmap_rfc2307_context *ctx)
+{
+ TALLOC_FREE(ctx->ads);
+
+ if (ctx->smbldap_state != NULL) {
+ smbldap_free_struct(&ctx->smbldap_state);
+ }
+
+ return 0;
+}
+
+static NTSTATUS idmap_rfc2307_initialize(struct idmap_domain *domain)
+{
+ struct idmap_rfc2307_context *ctx;
+ const char *bind_path_user, *bind_path_group, *ldap_server, *realm;
+ NTSTATUS status;
+
+ ctx = talloc_zero(domain, struct idmap_rfc2307_context);
+ if (ctx == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ talloc_set_destructor(ctx, idmap_rfc2307_context_destructor);
+
+ bind_path_user = idmap_config_const_string(
+ domain->name, "bind_path_user", NULL);
+ if (bind_path_user == NULL) {
+ status = NT_STATUS_INVALID_PARAMETER;
+ goto err;
+ }
+ ctx->bind_path_user = talloc_strdup(ctx, bind_path_user);
+ if (ctx->bind_path_user == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ goto err;
+ }
+
+ bind_path_group = idmap_config_const_string(
+ domain->name, "bind_path_group", NULL);
+ if (bind_path_group == NULL) {
+ status = NT_STATUS_INVALID_PARAMETER;
+ goto err;
+ }
+ ctx->bind_path_group = talloc_strdup(ctx, bind_path_group);
+ if (ctx->bind_path_group == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ goto err;
+ }
+
+ ldap_server = idmap_config_const_string(
+ domain->name, "ldap_server", NULL);
+ if (!ldap_server) {
+ status = NT_STATUS_INVALID_PARAMETER;
+ goto err;
+ }
+
+ if (strcmp(ldap_server, "stand-alone") == 0) {
+ status = idmap_rfc2307_init_ldap(ctx, domain->name);
+
+ } else if (strcmp(ldap_server, "ad") == 0) {
+ status = idmap_rfc2307_init_ads(ctx, domain->name);
+
+ } else {
+ status = NT_STATUS_INVALID_PARAMETER;
+ }
+
+ if (!NT_STATUS_IS_OK(status)) {
+ goto err;
+ }
+
+ realm = idmap_config_const_string(domain->name, "realm", NULL);
+ if (realm) {
+ ctx->realm = talloc_strdup(ctx, realm);
+ if (ctx->realm == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ goto err;
+ }
+ }
+
+ ctx->user_cn = idmap_config_bool(domain->name, "user_cn", false);
+
+ domain->private_data = ctx;
+ return NT_STATUS_OK;
+
+err:
+ talloc_free(ctx);
+ return status;
+}
+
+static const struct idmap_methods rfc2307_methods = {
+ .init = idmap_rfc2307_initialize,
+ .unixids_to_sids = idmap_rfc2307_unixids_to_sids,
+ .sids_to_unixids = idmap_rfc2307_sids_to_unixids,
+};
+
+static_decl_idmap;
+NTSTATUS idmap_rfc2307_init(TALLOC_CTX *ctx)
+{
+ return smb_register_idmap(SMB_IDMAP_INTERFACE_VERSION, "rfc2307",
+ &rfc2307_methods);
+}
diff --git a/source3/winbindd/idmap_rid.c b/source3/winbindd/idmap_rid.c
new file mode 100644
index 0000000..4fecf22
--- /dev/null
+++ b/source3/winbindd/idmap_rid.c
@@ -0,0 +1,182 @@
+/*
+ * idmap_rid: static map between Active Directory/NT RIDs and RFC 2307 accounts
+ * Copyright (C) Guenther Deschner, 2004
+ * Copyright (C) Sumit Bose, 2004
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "includes.h"
+#include "winbindd.h"
+#include "idmap.h"
+#include "../libcli/security/dom_sid.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_IDMAP
+
+struct idmap_rid_context {
+ uint32_t base_rid;
+};
+
+/******************************************************************************
+ compat params can't be used because of the completely different way
+ we support multiple domains in the new idmap
+ *****************************************************************************/
+
+static NTSTATUS idmap_rid_initialize(struct idmap_domain *dom)
+{
+ struct idmap_rid_context *ctx;
+
+ ctx = talloc_zero(dom, struct idmap_rid_context);
+ if (ctx == NULL) {
+ DEBUG(0, ("Out of memory!\n"));
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ ctx->base_rid = idmap_config_int(dom->name, "base_rid", 0);
+
+ dom->private_data = ctx;
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS idmap_rid_id_to_sid(struct idmap_domain *dom, struct id_map *map)
+{
+ struct idmap_rid_context *ctx;
+
+ ctx = talloc_get_type(dom->private_data, struct idmap_rid_context);
+
+ /* apply filters before checking */
+ if (!idmap_unix_id_is_in_range(map->xid.id, dom)) {
+ DEBUG(5, ("Requested id (%u) out of range (%u - %u). Filtered!\n",
+ map->xid.id, dom->low_id, dom->high_id));
+ return NT_STATUS_NONE_MAPPED;
+ }
+
+ if (is_null_sid(&dom->dom_sid)) {
+ DBG_INFO("idmap domain '%s' without SID\n", dom->name);
+ return NT_STATUS_NONE_MAPPED;
+ }
+
+ sid_compose(map->sid, &dom->dom_sid,
+ map->xid.id - dom->low_id + ctx->base_rid);
+
+ map->status = ID_MAPPED;
+ map->xid.type = ID_TYPE_BOTH;
+
+ return NT_STATUS_OK;
+}
+
+/**********************************
+ Single sid to id lookup function.
+**********************************/
+
+static NTSTATUS idmap_rid_sid_to_id(struct idmap_domain *dom, struct id_map *map)
+{
+ uint32_t rid;
+ struct idmap_rid_context *ctx;
+
+ ctx = talloc_get_type(dom->private_data, struct idmap_rid_context);
+
+ sid_peek_rid(map->sid, &rid);
+ map->xid.id = rid - ctx->base_rid + dom->low_id;
+ map->xid.type = ID_TYPE_BOTH;
+
+ /* apply filters before returning result */
+
+ if (!idmap_unix_id_is_in_range(map->xid.id, dom)) {
+ DEBUG(5, ("Requested id (%u) out of range (%u - %u). Filtered!\n",
+ map->xid.id, dom->low_id, dom->high_id));
+ map->status = ID_UNMAPPED;
+ return NT_STATUS_NONE_MAPPED;
+ }
+
+ map->status = ID_MAPPED;
+
+ return NT_STATUS_OK;
+}
+
+/**********************************
+ lookup a set of unix ids.
+**********************************/
+
+static NTSTATUS idmap_rid_unixids_to_sids(struct idmap_domain *dom, struct id_map **ids)
+{
+ NTSTATUS ret;
+ int i;
+
+ /* initialize the status to avoid surprise */
+ for (i = 0; ids[i]; i++) {
+ ids[i]->status = ID_UNKNOWN;
+ }
+
+ for (i = 0; ids[i]; i++) {
+
+ ret = idmap_rid_id_to_sid(dom, ids[i]);
+
+ if (( ! NT_STATUS_IS_OK(ret)) &&
+ ( ! NT_STATUS_EQUAL(ret, NT_STATUS_NONE_MAPPED))) {
+ /* some fatal error occurred, log it */
+ DBG_NOTICE("Unexpected error resolving an ID "
+ "(%d): %s\n", ids[i]->xid.id,
+ nt_errstr(ret));
+ }
+ }
+
+ return NT_STATUS_OK;
+}
+
+/**********************************
+ lookup a set of sids.
+**********************************/
+
+static NTSTATUS idmap_rid_sids_to_unixids(struct idmap_domain *dom, struct id_map **ids)
+{
+ NTSTATUS ret;
+ int i;
+
+ /* initialize the status to avoid surprise */
+ for (i = 0; ids[i]; i++) {
+ ids[i]->status = ID_UNKNOWN;
+ }
+
+ for (i = 0; ids[i]; i++) {
+
+ ret = idmap_rid_sid_to_id(dom, ids[i]);
+
+ if (( ! NT_STATUS_IS_OK(ret)) &&
+ ( ! NT_STATUS_EQUAL(ret, NT_STATUS_NONE_MAPPED))) {
+ struct dom_sid_buf buf;
+ /* some fatal error occurred, log it */
+ DEBUG(3, ("Unexpected error resolving a SID (%s)\n",
+ dom_sid_str_buf(ids[i]->sid, &buf)));
+ }
+ }
+
+ return NT_STATUS_OK;
+}
+
+static const struct idmap_methods rid_methods = {
+ .init = idmap_rid_initialize,
+ .unixids_to_sids = idmap_rid_unixids_to_sids,
+ .sids_to_unixids = idmap_rid_sids_to_unixids,
+};
+
+static_decl_idmap;
+NTSTATUS idmap_rid_init(TALLOC_CTX *ctx)
+{
+ return smb_register_idmap(SMB_IDMAP_INTERFACE_VERSION, "rid", &rid_methods);
+}
+
diff --git a/source3/winbindd/idmap_rw.c b/source3/winbindd/idmap_rw.c
new file mode 100644
index 0000000..71bfc14
--- /dev/null
+++ b/source3/winbindd/idmap_rw.c
@@ -0,0 +1,109 @@
+/*
+ * Unix SMB/CIFS implementation.
+ *
+ * ID mapping: abstract r/w new-mapping mechanism
+ *
+ * Copyright (C) Michael Adam 2010
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "includes.h"
+#include "winbindd.h"
+#include "idmap.h"
+#include "idmap_rw.h"
+#include "libcli/security/dom_sid.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_IDMAP
+
+NTSTATUS idmap_rw_new_mapping(struct idmap_domain *dom,
+ struct idmap_rw_ops *ops,
+ struct id_map *map)
+{
+ struct dom_sid_buf buf;
+ NTSTATUS status;
+
+ if (map == NULL) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ if (map->sid == NULL) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ switch (map->xid.type) {
+ case ID_TYPE_NOT_SPECIFIED:
+ /*
+ * We need to know if we need a user or group mapping.
+ * Ask the winbindd parent to provide a valid type hint.
+ */
+ DBG_INFO("%s ID_TYPE_NOT_SPECIFIED => ID_REQUIRE_TYPE\n",
+ dom_sid_str_buf(map->sid, &buf));
+ map->status = ID_REQUIRE_TYPE;
+ return NT_STATUS_SOME_NOT_MAPPED;
+
+ case ID_TYPE_BOTH:
+ /*
+ * For now we still require
+ * an explicit type as hint
+ * and don't support ID_TYPE_BOTH
+ */
+ DBG_INFO("%s ID_TYPE_BOTH => ID_REQUIRE_TYPE\n",
+ dom_sid_str_buf(map->sid, &buf));
+ map->status = ID_REQUIRE_TYPE;
+ return NT_STATUS_SOME_NOT_MAPPED;
+
+ case ID_TYPE_UID:
+ break;
+
+ case ID_TYPE_GID:
+ break;
+
+ default:
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ status = ops->get_new_id(dom, &map->xid);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(3, ("Could not allocate id: %s\n", nt_errstr(status)));
+ return status;
+ }
+
+ DEBUG(10, ("Setting mapping: %s <-> %s %lu\n",
+ dom_sid_str_buf(map->sid, &buf),
+ (map->xid.type == ID_TYPE_UID) ? "UID" : "GID",
+ (unsigned long)map->xid.id));
+
+ map->status = ID_MAPPED;
+ status = ops->set_mapping(dom, map);
+
+ if (NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_COLLISION)) {
+ struct id_map *ids[2];
+ DEBUG(5, ("Mapping for %s exists - retrying to map sid\n",
+ dom_sid_str_buf(map->sid, &buf)));
+ ids[0] = map;
+ ids[1] = NULL;
+ status = dom->methods->sids_to_unixids(dom, ids);
+ }
+
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(3, ("Could not store the new mapping: %s\n",
+ nt_errstr(status)));
+ return status;
+ }
+
+ return NT_STATUS_OK;
+}
diff --git a/source3/winbindd/idmap_rw.h b/source3/winbindd/idmap_rw.h
new file mode 100644
index 0000000..72389e7
--- /dev/null
+++ b/source3/winbindd/idmap_rw.h
@@ -0,0 +1,56 @@
+/*
+ * Unix SMB/CIFS implementation.
+ *
+ * ID mapping: abstract r/w new-mapping mechanism
+ *
+ * Copyright (C) Michael Adam 2010
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/*
+ * This module implements the abstract logic for creating a new
+ * SID<->Unix-ID mapping. It can be used by idmap backends that
+ * need to create mappings for unmapped SIDs upon request.
+ */
+
+#ifndef _IDMAP_RW_H_
+#define _IDMAP_RW_H_
+
+#include "includes.h"
+#include "idmap.h"
+
+struct idmap_rw_ops {
+ NTSTATUS (*get_new_id)(struct idmap_domain *dom, struct unixid *id);
+ NTSTATUS (*set_mapping)(struct idmap_domain *dom,
+ const struct id_map *map);
+};
+
+/**
+ * This is the abstract mechanism of creating a new mapping
+ * for a given SID. It is meant to be called called from an
+ * allocating backend from within idmap_<backend>_sids_to_unixids().
+ * It expects map->sid and map->xid.type to be set.
+ * Upon success, the new mapping is stored by the backend and
+ * map contains the new mapped xid.
+ *
+ * The caller has to take care of the necessary steps to
+ * guarantee atomicity of the operation, e.g. wrapping
+ * this call in a transaction if available.
+ */
+NTSTATUS idmap_rw_new_mapping(struct idmap_domain *dom,
+ struct idmap_rw_ops *ops,
+ struct id_map *map);
+
+#endif /* _IDMAP_RW_H_ */
diff --git a/source3/winbindd/idmap_script.c b/source3/winbindd/idmap_script.c
new file mode 100644
index 0000000..bb89175
--- /dev/null
+++ b/source3/winbindd/idmap_script.c
@@ -0,0 +1,650 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ idmap script backend, used for Samba setups where you need to map SIDs to
+ specific UIDs/GIDs.
+
+ Copyright (C) Richard Sharpe 2014.
+
+ This is heavily based upon idmap_tdb2.c, which is:
+
+ Copyright (C) Tim Potter 2000
+ Copyright (C) Jim McDonough <jmcd@us.ibm.com> 2003
+ Copyright (C) Jeremy Allison 2006
+ Copyright (C) Simo Sorce 2003-2006
+ Copyright (C) Michael Adam 2009-2010
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+#include "system/filesys.h"
+#include "winbindd.h"
+#include "idmap.h"
+#include "idmap_rw.h"
+#include "../libcli/security/dom_sid.h"
+#include "lib/util_file.h"
+#include "lib/util/tevent_unix.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_IDMAP
+
+struct idmap_script_context {
+ const char *script; /* script to provide idmaps */
+};
+
+/*
+ run a script to perform a mapping
+
+ The script should accept the following command lines:
+
+ SIDTOID S-1-xxxx -> XID:<id> | ERR:<str>
+ SIDTOID S-1-xxxx -> UID:<id> | ERR:<str>
+ SIDTOID S-1-xxxx -> GID:<id> | ERR:<str>
+ IDTOSID XID xxxx -> SID:<sid> | ERR:<str>
+ IDTOSID UID xxxx -> SID:<sid> | ERR:<str>
+ IDTOSID GID xxxx -> SID:<sid> | ERR:<str>
+
+ where XID means both a UID and a GID. This is the case for ID_TYPE_BOTH.
+
+ TODO: Needs more validation ... like that we got a UID when we asked for one.
+ */
+
+struct idmap_script_xid2sid_state {
+ char **argl;
+ size_t idx;
+ uint8_t *out;
+};
+
+static void idmap_script_xid2sid_done(struct tevent_req *subreq);
+
+static struct tevent_req *idmap_script_xid2sid_send(
+ TALLOC_CTX *mem_ctx, struct tevent_context *ev,
+ struct unixid xid, const char *script, size_t idx)
+{
+ struct tevent_req *req, *subreq;
+ struct idmap_script_xid2sid_state *state;
+ char key;
+
+ req = tevent_req_create(mem_ctx, &state,
+ struct idmap_script_xid2sid_state);
+ if (req == NULL) {
+ return NULL;
+ }
+ state->idx = idx;
+
+ switch (xid.type) {
+ case ID_TYPE_UID:
+ key = 'U';
+ break;
+ case ID_TYPE_GID:
+ key = 'G';
+ break;
+ case ID_TYPE_BOTH:
+ key = 'X';
+ break;
+ default:
+ DBG_WARNING("INVALID unix ID type: 0x02%x\n", xid.type);
+ tevent_req_error(req, EINVAL);
+ return tevent_req_post(req, ev);
+ }
+
+ state->argl = str_list_make_empty(state);
+ str_list_add_printf(&state->argl, "%s", script);
+ str_list_add_printf(&state->argl, "IDTOSID");
+ str_list_add_printf(&state->argl, "%cID", key);
+ str_list_add_printf(&state->argl, "%lu", (unsigned long)xid.id);
+ if (tevent_req_nomem(state->argl, req)) {
+ return tevent_req_post(req, ev);
+ }
+
+ subreq = file_ploadv_send(state, ev, state->argl, 1024);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, idmap_script_xid2sid_done, req);
+ return req;
+}
+
+static void idmap_script_xid2sid_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct idmap_script_xid2sid_state *state = tevent_req_data(
+ req, struct idmap_script_xid2sid_state);
+ int ret;
+
+ ret = file_ploadv_recv(subreq, state, &state->out);
+ TALLOC_FREE(subreq);
+ if (tevent_req_error(req, ret)) {
+ return;
+ }
+ tevent_req_done(req);
+}
+
+static int idmap_script_xid2sid_recv(struct tevent_req *req, size_t *idx,
+ enum id_mapping *status,
+ struct dom_sid *sid)
+{
+ struct idmap_script_xid2sid_state *state = tevent_req_data(
+ req, struct idmap_script_xid2sid_state);
+ char *out = (char *)state->out;
+ size_t out_size = talloc_get_size(out);
+ int err;
+
+ if (tevent_req_is_unix_error(req, &err)) {
+ return err;
+ }
+
+ if (out_size == 0) {
+ goto unmapped;
+ }
+ if (state->out[out_size-1] != '\0') {
+ goto unmapped;
+ }
+
+ *idx = state->idx;
+
+ if ((strncmp(out, "SID:S-", 6) != 0) ||
+ !dom_sid_parse(out+4, sid)) {
+ DBG_WARNING("Bad sid from script: %s\n", out);
+ goto unmapped;
+ }
+
+ *status = ID_MAPPED;
+ return 0;
+
+unmapped:
+ *sid = (struct dom_sid) {0};
+ *status = ID_UNMAPPED;
+ return 0;
+}
+
+struct idmap_script_xids2sids_state {
+ struct id_map **ids;
+ size_t num_ids;
+ size_t num_done;
+};
+
+static void idmap_script_xids2sids_done(struct tevent_req *subreq);
+
+static struct tevent_req *idmap_script_xids2sids_send(
+ TALLOC_CTX *mem_ctx, struct tevent_context *ev,
+ struct id_map **ids, size_t num_ids, const char *script)
+{
+ struct tevent_req *req;
+ struct idmap_script_xids2sids_state *state;
+ size_t i;
+
+ req = tevent_req_create(mem_ctx, &state,
+ struct idmap_script_xids2sids_state);
+ if (req == NULL) {
+ return NULL;
+ }
+ state->ids = ids;
+ state->num_ids = num_ids;
+
+ if (state->num_ids == 0) {
+ tevent_req_done(req);
+ return tevent_req_post(req, ev);
+ }
+
+ for (i=0; i<num_ids; i++) {
+ struct tevent_req *subreq;
+
+ subreq = idmap_script_xid2sid_send(
+ state, ev, ids[i]->xid, script, i);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, idmap_script_xids2sids_done,
+ req);
+ }
+
+ return req;
+}
+
+static void idmap_script_xids2sids_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct idmap_script_xids2sids_state *state = tevent_req_data(
+ req, struct idmap_script_xids2sids_state);
+ size_t idx = 0;
+ enum id_mapping status = ID_UNKNOWN;
+ struct dom_sid sid = {0};
+ int ret;
+
+ ret = idmap_script_xid2sid_recv(subreq, &idx, &status, &sid);
+ TALLOC_FREE(subreq);
+ if (tevent_req_error(req, ret)) {
+ return;
+ }
+
+ if (idx >= state->num_ids) {
+ tevent_req_error(req, EINVAL);
+ return;
+ }
+
+ state->ids[idx]->status = status;
+
+ state->ids[idx]->sid = dom_sid_dup(state->ids, &sid);
+ if (tevent_req_nomem(state->ids[idx]->sid, req)) {
+ return;
+ }
+
+ state->num_done += 1;
+
+ if (state->num_done >= state->num_ids) {
+ tevent_req_done(req);
+ }
+}
+
+static int idmap_script_xids2sids_recv(struct tevent_req *req)
+{
+ return tevent_req_simple_recv_unix(req);
+}
+
+static int idmap_script_xids2sids(struct id_map **ids, size_t num_ids,
+ const char *script)
+{
+ TALLOC_CTX *frame = talloc_stackframe();
+ struct tevent_context *ev;
+ struct tevent_req *req;
+ int ret = ENOMEM;
+
+ ev = samba_tevent_context_init(frame);
+ if (ev == NULL) {
+ goto fail;
+ }
+ req = idmap_script_xids2sids_send(frame, ev, ids, num_ids, script);
+ if (req == NULL) {
+ goto fail;
+ }
+ if (!tevent_req_poll(req, ev)) {
+ ret = errno;
+ goto fail;
+ }
+ ret = idmap_script_xids2sids_recv(req);
+fail:
+ TALLOC_FREE(frame);
+ return ret;
+}
+
+static NTSTATUS idmap_script_unixids_to_sids(struct idmap_domain *dom,
+ struct id_map **ids)
+{
+ struct idmap_script_context *ctx = talloc_get_type_abort(
+ dom->private_data, struct idmap_script_context);
+ int ret;
+ size_t i, num_ids, num_mapped;
+
+ DEBUG(10, ("%s called ...\n", __func__));
+ /* Init status to avoid surprise ... */
+ for (i = 0; ids[i]; i++) {
+ ids[i]->status = ID_UNKNOWN;
+ }
+ num_ids = i;
+
+ ret = idmap_script_xids2sids(ids, num_ids, ctx->script);
+ if (ret != 0) {
+ DBG_DEBUG("idmap_script_xids2sids returned %s\n",
+ strerror(ret));
+ return map_nt_error_from_unix(ret);
+ }
+
+ num_mapped = 0;
+
+ for (i = 0; ids[i]; i++) {
+ if (ids[i]->status == ID_MAPPED) {
+ num_mapped += 1;
+ }
+ }
+
+ if (num_mapped == 0) {
+ return NT_STATUS_NONE_MAPPED;
+ }
+ if (num_mapped < num_ids) {
+ return STATUS_SOME_UNMAPPED;
+ }
+ return NT_STATUS_OK;
+}
+
+struct idmap_script_sid2xid_state {
+ char **argl;
+ size_t idx;
+ uint8_t *out;
+};
+
+static void idmap_script_sid2xid_done(struct tevent_req *subreq);
+
+static struct tevent_req *idmap_script_sid2xid_send(
+ TALLOC_CTX *mem_ctx, struct tevent_context *ev,
+ const struct dom_sid *sid, const char *script, size_t idx)
+{
+ struct tevent_req *req, *subreq;
+ struct idmap_script_sid2xid_state *state;
+ struct dom_sid_buf sidbuf;
+
+ req = tevent_req_create(mem_ctx, &state,
+ struct idmap_script_sid2xid_state);
+ if (req == NULL) {
+ return NULL;
+ }
+ state->idx = idx;
+
+ state->argl = str_list_make_empty(state);
+ str_list_add_printf(&state->argl, "%s", script);
+ str_list_add_printf(&state->argl, "SIDTOID");
+ str_list_add_printf(
+ &state->argl, "%s", dom_sid_str_buf(sid, &sidbuf));
+ if (tevent_req_nomem(state->argl, req)) {
+ return tevent_req_post(req, ev);
+ }
+
+ subreq = file_ploadv_send(state, ev, state->argl, 1024);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, idmap_script_sid2xid_done, req);
+ return req;
+}
+
+static void idmap_script_sid2xid_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct idmap_script_sid2xid_state *state = tevent_req_data(
+ req, struct idmap_script_sid2xid_state);
+ int ret;
+
+ ret = file_ploadv_recv(subreq, state, &state->out);
+ TALLOC_FREE(subreq);
+ if (tevent_req_error(req, ret)) {
+ return;
+ }
+ tevent_req_done(req);
+}
+
+static int idmap_script_sid2xid_recv(struct tevent_req *req,
+ size_t *idx, enum id_mapping *status,
+ struct unixid *xid)
+{
+ struct idmap_script_sid2xid_state *state = tevent_req_data(
+ req, struct idmap_script_sid2xid_state);
+ char *out = (char *)state->out;
+ size_t out_size = talloc_get_size(out);
+ unsigned long v;
+ int err;
+
+ if (tevent_req_is_unix_error(req, &err)) {
+ return err;
+ }
+
+ if (out_size == 0) {
+ goto unmapped;
+ }
+ if (state->out[out_size-1] != '\0') {
+ goto unmapped;
+ }
+
+ *idx = state->idx;
+
+ if (sscanf(out, "XID:%lu\n", &v) == 1) {
+ *xid = (struct unixid) { .id = v, .type = ID_TYPE_BOTH };
+ } else if (sscanf(out, "UID:%lu\n", &v) == 1) {
+ *xid = (struct unixid) { .id = v, .type = ID_TYPE_UID };
+ } else if (sscanf(out, "GID:%lu\n", &v) == 1) {
+ *xid = (struct unixid) { .id = v, .type = ID_TYPE_GID };
+ } else {
+ goto unmapped;
+ }
+
+ *status = ID_MAPPED;
+ return 0;
+
+unmapped:
+ *xid = (struct unixid) { .id = UINT32_MAX,
+ .type = ID_TYPE_NOT_SPECIFIED };
+ *status = ID_UNMAPPED;
+ return 0;
+}
+
+struct idmap_script_sids2xids_state {
+ struct id_map **ids;
+ size_t num_ids;
+ size_t num_done;
+};
+
+static void idmap_script_sids2xids_done(struct tevent_req *subreq);
+
+static struct tevent_req *idmap_script_sids2xids_send(
+ TALLOC_CTX *mem_ctx, struct tevent_context *ev,
+ struct id_map **ids, size_t num_ids, const char *script)
+{
+ struct tevent_req *req;
+ struct idmap_script_sids2xids_state *state;
+ size_t i;
+
+ req = tevent_req_create(mem_ctx, &state,
+ struct idmap_script_sids2xids_state);
+ if (req == NULL) {
+ return NULL;
+ }
+ state->ids = ids;
+ state->num_ids = num_ids;
+
+ if (state->num_ids == 0) {
+ tevent_req_done(req);
+ return tevent_req_post(req, ev);
+ }
+
+ for (i=0; i<num_ids; i++) {
+ struct tevent_req *subreq;
+
+ subreq = idmap_script_sid2xid_send(
+ state, ev, ids[i]->sid, script, i);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, idmap_script_sids2xids_done,
+ req);
+ }
+
+ return req;
+}
+
+static void idmap_script_sids2xids_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct idmap_script_sids2xids_state *state = tevent_req_data(
+ req, struct idmap_script_sids2xids_state);
+ size_t idx = 0;
+ enum id_mapping status = ID_UNKNOWN;
+ struct unixid xid = { .id = UINT32_MAX,
+ .type = ID_TYPE_NOT_SPECIFIED };
+ int ret;
+
+ ret = idmap_script_sid2xid_recv(subreq, &idx, &status, &xid);
+ TALLOC_FREE(subreq);
+ if (tevent_req_error(req, ret)) {
+ return;
+ }
+
+ if (idx >= state->num_ids) {
+ tevent_req_error(req, EINVAL);
+ return;
+ }
+
+ state->ids[idx]->status = status;
+ state->ids[idx]->xid = xid;
+
+ state->num_done += 1;
+
+ if (state->num_done >= state->num_ids) {
+ tevent_req_done(req);
+ }
+}
+
+static int idmap_script_sids2xids_recv(struct tevent_req *req)
+{
+ return tevent_req_simple_recv_unix(req);
+}
+
+static int idmap_script_sids2xids(struct id_map **ids, size_t num_ids,
+ const char *script)
+{
+ TALLOC_CTX *frame = talloc_stackframe();
+ struct tevent_context *ev;
+ struct tevent_req *req;
+ int ret = ENOMEM;
+
+ ev = samba_tevent_context_init(frame);
+ if (ev == NULL) {
+ goto fail;
+ }
+ req = idmap_script_sids2xids_send(frame, ev, ids, num_ids, script);
+ if (req == NULL) {
+ goto fail;
+ }
+ if (!tevent_req_poll(req, ev)) {
+ ret = errno;
+ goto fail;
+ }
+ ret = idmap_script_sids2xids_recv(req);
+fail:
+ TALLOC_FREE(frame);
+ return ret;
+}
+
+static NTSTATUS idmap_script_sids_to_unixids(struct idmap_domain *dom,
+ struct id_map **ids)
+{
+ struct idmap_script_context *ctx = talloc_get_type_abort(
+ dom->private_data, struct idmap_script_context);
+ int ret;
+ size_t i, num_ids, num_mapped;
+
+ DEBUG(10, ("%s called ...\n", __func__));
+ /* Init status to avoid surprise ... */
+ for (i = 0; ids[i]; i++) {
+ ids[i]->status = ID_UNKNOWN;
+ }
+ num_ids = i;
+
+ ret = idmap_script_sids2xids(ids, num_ids, ctx->script);
+ if (ret != 0) {
+ DBG_DEBUG("idmap_script_sids2xids returned %s\n",
+ strerror(ret));
+ return map_nt_error_from_unix(ret);
+ }
+
+ num_mapped = 0;
+
+ for (i=0; i<num_ids; i++) {
+ struct id_map *map = ids[i];
+
+ if ((map->status == ID_MAPPED) &&
+ !idmap_unix_id_is_in_range(map->xid.id, dom)) {
+ DBG_INFO("Script returned id (%u) out of range "
+ "(%u - %u). Filtered!\n",
+ map->xid.id, dom->low_id, dom->high_id);
+ map->status = ID_UNMAPPED;
+ }
+
+ if (map->status == ID_MAPPED) {
+ num_mapped += 1;
+ }
+ }
+
+ if (num_mapped == 0) {
+ return NT_STATUS_NONE_MAPPED;
+ }
+ if (num_mapped < num_ids) {
+ return STATUS_SOME_UNMAPPED;
+ }
+ return NT_STATUS_OK;
+}
+
+/*
+ * Initialise idmap_script database.
+ */
+static NTSTATUS idmap_script_db_init(struct idmap_domain *dom)
+{
+ NTSTATUS ret;
+ struct idmap_script_context *ctx;
+ const char * idmap_script = NULL;
+ const char *ctx_script = NULL;
+
+ DEBUG(10, ("%s called ...\n", __func__));
+
+ ctx = talloc_zero(dom, struct idmap_script_context);
+ if (!ctx) {
+ DEBUG(0, ("Out of memory!\n"));
+ ret = NT_STATUS_NO_MEMORY;
+ goto failed;
+ }
+
+ ctx_script = idmap_config_const_string(dom->name, "script", NULL);
+
+ /* Do we even need to handle this? */
+ idmap_script = lp_parm_const_string(-1, "idmap", "script", NULL);
+ if (idmap_script != NULL) {
+ DEBUG(0, ("Warning: 'idmap:script' is deprecated. "
+ " Please use 'idmap config * : script' instead!\n"));
+ }
+
+ if (strequal(dom->name, "*") && ctx_script == NULL) {
+ /* fall back to idmap:script for backwards compatibility */
+ ctx_script = idmap_script;
+ }
+
+ if (ctx_script) {
+ DEBUG(1, ("using idmap script '%s'\n", ctx->script));
+ /*
+ * We must ensure this memory is owned by ctx.
+ * The ctx_script const pointer is a pointer into
+ * the config file data and may become invalid
+ * on config file reload. BUG: 13956
+ */
+ ctx->script = talloc_strdup(ctx, ctx_script);
+ if (ctx->script == NULL) {
+ ret = NT_STATUS_NO_MEMORY;
+ goto failed;
+ }
+ }
+
+ dom->private_data = ctx;
+ dom->read_only = true; /* We do not allocate!*/
+
+ return NT_STATUS_OK;
+
+failed:
+ talloc_free(ctx);
+ return ret;
+}
+
+static const struct idmap_methods db_methods = {
+ .init = idmap_script_db_init,
+ .unixids_to_sids = idmap_script_unixids_to_sids,
+ .sids_to_unixids = idmap_script_sids_to_unixids,
+};
+
+static_decl_idmap;
+NTSTATUS idmap_script_init(TALLOC_CTX *ctx)
+{
+ return smb_register_idmap(SMB_IDMAP_INTERFACE_VERSION, "script", &db_methods);
+}
diff --git a/source3/winbindd/idmap_tdb.c b/source3/winbindd/idmap_tdb.c
new file mode 100644
index 0000000..77714d9
--- /dev/null
+++ b/source3/winbindd/idmap_tdb.c
@@ -0,0 +1,434 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ idmap TDB backend
+
+ Copyright (C) Tim Potter 2000
+ Copyright (C) Jim McDonough <jmcd@us.ibm.com> 2003
+ Copyright (C) Jeremy Allison 2006
+ Copyright (C) Simo Sorce 2003-2006
+ Copyright (C) Michael Adam 2009-2010
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "system/filesys.h"
+#include "winbindd.h"
+#include "idmap.h"
+#include "idmap_rw.h"
+#include "idmap_tdb_common.h"
+#include "dbwrap/dbwrap.h"
+#include "dbwrap/dbwrap_open.h"
+#include "../libcli/security/security.h"
+#include "util_tdb.h"
+#include "lib/util/string_wrappers.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_IDMAP
+
+/* idmap version determines auto-conversion - this is the database
+ structure version specifier. */
+
+#define IDMAP_VERSION 2
+
+/* High water mark keys */
+#define HWM_GROUP "GROUP HWM"
+#define HWM_USER "USER HWM"
+
+struct convert_fn_state {
+ struct db_context *db;
+ bool failed;
+};
+
+/*****************************************************************************
+ For idmap conversion: convert one record to new format
+ Ancient versions (eg 2.2.3a) of winbindd_idmap.tdb mapped DOMAINNAME/rid
+ instead of the SID.
+*****************************************************************************/
+static int convert_fn(struct db_record *rec, void *private_data)
+{
+ struct winbindd_domain *domain;
+ char *p;
+ NTSTATUS status;
+ struct dom_sid sid;
+ uint32_t rid;
+ struct dom_sid_buf keystr;
+ fstring dom_name;
+ TDB_DATA key;
+ TDB_DATA key2;
+ TDB_DATA value;
+ struct convert_fn_state *s = (struct convert_fn_state *)private_data;
+
+ key = dbwrap_record_get_key(rec);
+
+ DEBUG(10,("Converting %s\n", (const char *)key.dptr));
+
+ p = strchr((const char *)key.dptr, '/');
+ if (!p)
+ return 0;
+
+ *p = 0;
+ fstrcpy(dom_name, (const char *)key.dptr);
+ *p++ = '/';
+
+ domain = find_domain_from_name(dom_name);
+ if (domain == NULL) {
+ /* We must delete the old record. */
+ DEBUG(0,("Unable to find domain %s\n", dom_name ));
+ DEBUG(0,("deleting record %s\n", (const char *)key.dptr ));
+
+ status = dbwrap_record_delete(rec);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0, ("Unable to delete record %s:%s\n",
+ (const char *)key.dptr,
+ nt_errstr(status)));
+ s->failed = true;
+ return -1;
+ }
+
+ return 0;
+ }
+
+ rid = atoi(p);
+
+ sid_compose(&sid, &domain->sid, rid);
+
+ key2 = string_term_tdb_data(dom_sid_str_buf(&sid, &keystr));
+
+ value = dbwrap_record_get_value(rec);
+
+ status = dbwrap_store(s->db, key2, value, TDB_INSERT);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0,("Unable to add record %s:%s\n",
+ (const char *)key2.dptr,
+ nt_errstr(status)));
+ s->failed = true;
+ return -1;
+ }
+
+ status = dbwrap_store(s->db, value, key2, TDB_REPLACE);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0,("Unable to update record %s:%s\n",
+ (const char *)value.dptr,
+ nt_errstr(status)));
+ s->failed = true;
+ return -1;
+ }
+
+ status = dbwrap_record_delete(rec);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0,("Unable to delete record %s:%s\n",
+ (const char *)key.dptr,
+ nt_errstr(status)));
+ s->failed = true;
+ return -1;
+ }
+
+ return 0;
+}
+
+/*****************************************************************************
+ Convert the idmap database from an older version.
+*****************************************************************************/
+
+static bool idmap_tdb_upgrade(struct idmap_domain *dom, struct db_context *db)
+{
+ int32_t vers;
+ struct convert_fn_state s;
+ NTSTATUS status;
+
+ status = dbwrap_fetch_int32_bystring(db, "IDMAP_VERSION", &vers);
+ if (!NT_STATUS_IS_OK(status)) {
+ vers = -1;
+ }
+
+ if (IREV(vers) == IDMAP_VERSION) {
+ /* Arrggghh ! Bytereversed - make order independent ! */
+ /*
+ * high and low records were created on a
+ * big endian machine and will need byte-reversing.
+ */
+
+ int32_t wm;
+
+ status = dbwrap_fetch_int32_bystring(db, HWM_USER, &wm);
+ if (!NT_STATUS_IS_OK(status)) {
+ wm = -1;
+ }
+
+ if (wm != -1) {
+ wm = IREV(wm);
+ } else {
+ wm = dom->low_id;
+ }
+
+ status = dbwrap_store_int32_bystring(db, HWM_USER, wm);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0, ("Unable to byteswap user hwm in idmap "
+ "database: %s\n", nt_errstr(status)));
+ return False;
+ }
+
+ status = dbwrap_fetch_int32_bystring(db, HWM_GROUP, &wm);
+ if (!NT_STATUS_IS_OK(status)) {
+ wm = -1;
+ }
+
+ if (wm != -1) {
+ wm = IREV(wm);
+ } else {
+ wm = dom->low_id;
+ }
+
+ status = dbwrap_store_int32_bystring(db, HWM_GROUP, wm);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0, ("Unable to byteswap group hwm in idmap "
+ "database: %s\n", nt_errstr(status)));
+ return False;
+ }
+ }
+
+ s.db = db;
+ s.failed = false;
+
+ /* the old format stored as DOMAIN/rid - now we store the SID direct */
+ status = dbwrap_traverse(db, convert_fn, &s, NULL);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0, ("Database traverse failed during conversion\n"));
+ return false;
+ }
+
+ if (s.failed) {
+ DEBUG(0, ("Problem during conversion\n"));
+ return False;
+ }
+
+ status = dbwrap_store_int32_bystring(db, "IDMAP_VERSION",
+ IDMAP_VERSION);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0, ("Unable to store idmap version in database: %s\n",
+ nt_errstr(status)));
+ return False;
+ }
+
+ return True;
+}
+
+static NTSTATUS idmap_tdb_init_hwm(struct idmap_domain *dom)
+{
+ uint32_t low_uid;
+ uint32_t low_gid;
+ bool update_uid = false;
+ bool update_gid = false;
+ struct idmap_tdb_common_context *ctx;
+ NTSTATUS status;
+
+ ctx = talloc_get_type(dom->private_data,
+ struct idmap_tdb_common_context);
+
+ status = dbwrap_fetch_uint32_bystring(ctx->db, HWM_USER, &low_uid);
+ if (!NT_STATUS_IS_OK(status) || low_uid < dom->low_id) {
+ update_uid = true;
+ }
+
+ status = dbwrap_fetch_uint32_bystring(ctx->db, HWM_GROUP, &low_gid);
+ if (!NT_STATUS_IS_OK(status) || low_gid < dom->low_id) {
+ update_gid = true;
+ }
+
+ if (!update_uid && !update_gid) {
+ return NT_STATUS_OK;
+ }
+
+ if (dbwrap_transaction_start(ctx->db) != 0) {
+ DEBUG(0, ("Unable to start upgrade transaction!\n"));
+ return NT_STATUS_INTERNAL_DB_ERROR;
+ }
+
+ if (update_uid) {
+ status = dbwrap_store_uint32_bystring(ctx->db, HWM_USER,
+ dom->low_id);
+ if (!NT_STATUS_IS_OK(status)) {
+ dbwrap_transaction_cancel(ctx->db);
+ DEBUG(0, ("Unable to initialise user hwm in idmap "
+ "database: %s\n", nt_errstr(status)));
+ return NT_STATUS_INTERNAL_DB_ERROR;
+ }
+ }
+
+ if (update_gid) {
+ status = dbwrap_store_uint32_bystring(ctx->db, HWM_GROUP,
+ dom->low_id);
+ if (!NT_STATUS_IS_OK(status)) {
+ dbwrap_transaction_cancel(ctx->db);
+ DEBUG(0, ("Unable to initialise group hwm in idmap "
+ "database: %s\n", nt_errstr(status)));
+ return NT_STATUS_INTERNAL_DB_ERROR;
+ }
+ }
+
+ if (dbwrap_transaction_commit(ctx->db) != 0) {
+ DEBUG(0, ("Unable to commit upgrade transaction!\n"));
+ return NT_STATUS_INTERNAL_DB_ERROR;
+ }
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS idmap_tdb_open_db(struct idmap_domain *dom)
+{
+ NTSTATUS ret;
+ TALLOC_CTX *mem_ctx;
+ char *tdbfile = NULL;
+ struct db_context *db = NULL;
+ int32_t version;
+ struct idmap_tdb_common_context *ctx;
+
+ ctx = talloc_get_type(dom->private_data,
+ struct idmap_tdb_common_context);
+
+ if (ctx->db) {
+ /* it is already open */
+ return NT_STATUS_OK;
+ }
+
+ /* use our own context here */
+ mem_ctx = talloc_stackframe();
+
+ /* use the old database if present */
+ tdbfile = state_path(talloc_tos(), "winbindd_idmap.tdb");
+ if (!tdbfile) {
+ DEBUG(0, ("Out of memory!\n"));
+ ret = NT_STATUS_NO_MEMORY;
+ goto done;
+ }
+
+ DEBUG(10,("Opening tdbfile %s\n", tdbfile ));
+
+ /* Open idmap repository */
+ db = db_open(mem_ctx, tdbfile, 0, TDB_DEFAULT, O_RDWR | O_CREAT, 0644,
+ DBWRAP_LOCK_ORDER_1, DBWRAP_FLAG_NONE);
+ if (!db) {
+ DEBUG(0, ("Unable to open idmap database\n"));
+ ret = NT_STATUS_UNSUCCESSFUL;
+ goto done;
+ }
+
+ /* check against earlier versions */
+ ret = dbwrap_fetch_int32_bystring(db, "IDMAP_VERSION", &version);
+ if (!NT_STATUS_IS_OK(ret)) {
+ version = -1;
+ }
+
+ if (version != IDMAP_VERSION) {
+ if (dbwrap_transaction_start(db) != 0) {
+ DEBUG(0, ("Unable to start upgrade transaction!\n"));
+ ret = NT_STATUS_INTERNAL_DB_ERROR;
+ goto done;
+ }
+
+ if (!idmap_tdb_upgrade(dom, db)) {
+ dbwrap_transaction_cancel(db);
+ DEBUG(0, ("Unable to open idmap database, it's in an old format, and upgrade failed!\n"));
+ ret = NT_STATUS_INTERNAL_DB_ERROR;
+ goto done;
+ }
+
+ if (dbwrap_transaction_commit(db) != 0) {
+ DEBUG(0, ("Unable to commit upgrade transaction!\n"));
+ ret = NT_STATUS_INTERNAL_DB_ERROR;
+ goto done;
+ }
+ }
+
+ ctx->db = talloc_move(ctx, &db);
+
+ ret = idmap_tdb_init_hwm(dom);
+
+done:
+ talloc_free(mem_ctx);
+ return ret;
+}
+
+/**********************************************************************
+ IDMAP MAPPING TDB BACKEND
+**********************************************************************/
+
+/*****************************
+ Initialise idmap database.
+*****************************/
+
+static NTSTATUS idmap_tdb_db_init(struct idmap_domain *dom)
+{
+ NTSTATUS ret;
+ struct idmap_tdb_common_context *ctx;
+
+ DEBUG(10, ("idmap_tdb_db_init called for domain '%s'\n", dom->name));
+
+ ctx = talloc_zero(dom, struct idmap_tdb_common_context);
+ if ( ! ctx) {
+ DEBUG(0, ("Out of memory!\n"));
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ /* load backend specific configuration here: */
+#if 0
+ if (strequal(dom->name, "*")) {
+ } else {
+ }
+#endif
+
+ ctx->rw_ops = talloc_zero(ctx, struct idmap_rw_ops);
+ if (ctx->rw_ops == NULL) {
+ DEBUG(0, ("Out of memory!\n"));
+ ret = NT_STATUS_NO_MEMORY;
+ goto failed;
+ }
+
+ ctx->max_id = dom->high_id;
+ ctx->hwmkey_uid = HWM_USER;
+ ctx->hwmkey_gid = HWM_GROUP;
+
+ ctx->rw_ops->get_new_id = idmap_tdb_common_get_new_id;
+ ctx->rw_ops->set_mapping = idmap_tdb_common_set_mapping;
+
+ dom->private_data = ctx;
+
+ ret = idmap_tdb_open_db(dom);
+ if ( ! NT_STATUS_IS_OK(ret)) {
+ goto failed;
+ }
+
+ return NT_STATUS_OK;
+
+failed:
+ talloc_free(ctx);
+ return ret;
+}
+
+static const struct idmap_methods db_methods = {
+ .init = idmap_tdb_db_init,
+ .unixids_to_sids = idmap_tdb_common_unixids_to_sids,
+ .sids_to_unixids = idmap_tdb_common_sids_to_unixids,
+ .allocate_id = idmap_tdb_common_get_new_id,
+};
+
+NTSTATUS idmap_tdb_init(TALLOC_CTX *mem_ctx)
+{
+ DEBUG(10, ("calling idmap_tdb_init\n"));
+
+ return smb_register_idmap(SMB_IDMAP_INTERFACE_VERSION, "tdb", &db_methods);
+}
diff --git a/source3/winbindd/idmap_tdb2.c b/source3/winbindd/idmap_tdb2.c
new file mode 100644
index 0000000..d843aee
--- /dev/null
+++ b/source3/winbindd/idmap_tdb2.c
@@ -0,0 +1,612 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ idmap TDB2 backend, used for clustered Samba setups.
+
+ This uses dbwrap to access tdb files. The location can be set
+ using tdb:idmap2.tdb =" in smb.conf
+
+ Copyright (C) Andrew Tridgell 2007
+
+ This is heavily based upon idmap_tdb.c, which is:
+
+ Copyright (C) Tim Potter 2000
+ Copyright (C) Jim McDonough <jmcd@us.ibm.com> 2003
+ Copyright (C) Jeremy Allison 2006
+ Copyright (C) Simo Sorce 2003-2006
+ Copyright (C) Michael Adam 2009-2010
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+#include "system/filesys.h"
+#include "winbindd.h"
+#include "idmap.h"
+#include "idmap_rw.h"
+#include "dbwrap/dbwrap.h"
+#include "dbwrap/dbwrap_open.h"
+#include "../libcli/security/dom_sid.h"
+#include "util_tdb.h"
+#include "idmap_tdb_common.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_IDMAP
+
+struct idmap_tdb2_context {
+ const char *script; /* script to provide idmaps */
+};
+
+/* High water mark keys */
+#define HWM_GROUP "GROUP HWM"
+#define HWM_USER "USER HWM"
+
+/*
+ * check and initialize high/low water marks in the db
+ */
+static NTSTATUS idmap_tdb2_init_hwm(struct idmap_domain *dom)
+{
+ NTSTATUS status;
+ uint32_t low_id;
+ struct idmap_tdb_common_context *ctx;
+
+ ctx = talloc_get_type(dom->private_data,
+ struct idmap_tdb_common_context);
+
+ /* Create high water marks for group and user id */
+
+ status = dbwrap_fetch_uint32_bystring(ctx->db, HWM_USER, &low_id);
+ if (!NT_STATUS_IS_OK(status) || (low_id < dom->low_id)) {
+ status = dbwrap_trans_store_uint32_bystring(ctx->db, HWM_USER,
+ dom->low_id);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0, ("Unable to initialise user hwm in idmap "
+ "database: %s\n", nt_errstr(status)));
+ return NT_STATUS_INTERNAL_DB_ERROR;
+ }
+ }
+
+ status = dbwrap_fetch_uint32_bystring(ctx->db, HWM_GROUP, &low_id);
+ if (!NT_STATUS_IS_OK(status) || (low_id < dom->low_id)) {
+ status = dbwrap_trans_store_uint32_bystring(ctx->db, HWM_GROUP,
+ dom->low_id);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0, ("Unable to initialise group hwm in idmap "
+ "database: %s\n", nt_errstr(status)));
+ return NT_STATUS_INTERNAL_DB_ERROR;
+ }
+ }
+
+ return NT_STATUS_OK;
+}
+
+
+/*
+ open the permanent tdb
+ */
+static NTSTATUS idmap_tdb2_open_db(struct idmap_domain *dom)
+{
+ char *db_path;
+ struct idmap_tdb_common_context *ctx;
+
+ ctx = talloc_get_type(dom->private_data,
+ struct idmap_tdb_common_context);
+
+ if (ctx->db) {
+ /* its already open */
+ return NT_STATUS_OK;
+ }
+
+ db_path = talloc_asprintf(NULL, "%s/idmap2.tdb", lp_private_dir());
+ NT_STATUS_HAVE_NO_MEMORY(db_path);
+
+ /* Open idmap repository */
+ ctx->db = db_open(ctx, db_path, 0, TDB_DEFAULT, O_RDWR|O_CREAT, 0644,
+ DBWRAP_LOCK_ORDER_1, DBWRAP_FLAG_NONE);
+ if (ctx->db == NULL) {
+ DEBUG(0, ("Unable to open idmap_tdb2 database '%s'\n",
+ db_path));
+ TALLOC_FREE(db_path);
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+ TALLOC_FREE(db_path);
+
+ return idmap_tdb2_init_hwm(dom);
+}
+
+/**
+ * store a mapping in the database.
+ */
+
+struct idmap_tdb2_set_mapping_context {
+ const char *ksidstr;
+ const char *kidstr;
+};
+
+static NTSTATUS idmap_tdb2_set_mapping_action(struct db_context *db,
+ void *private_data)
+{
+ TDB_DATA data;
+ NTSTATUS ret;
+ struct idmap_tdb2_set_mapping_context *state;
+ TALLOC_CTX *tmp_ctx = talloc_stackframe();
+
+ state = (struct idmap_tdb2_set_mapping_context *)private_data;
+
+ DEBUG(10, ("Storing %s <-> %s map\n", state->ksidstr, state->kidstr));
+
+ /* check whether sid mapping is already present in db */
+ ret = dbwrap_fetch_bystring(db, tmp_ctx, state->ksidstr, &data);
+ if (NT_STATUS_IS_OK(ret)) {
+ ret = NT_STATUS_OBJECT_NAME_COLLISION;
+ goto done;
+ }
+
+ ret = dbwrap_store_bystring(db, state->ksidstr,
+ string_term_tdb_data(state->kidstr),
+ TDB_INSERT);
+ if (!NT_STATUS_IS_OK(ret)) {
+ DEBUG(0, ("Error storing SID -> ID: %s\n", nt_errstr(ret)));
+ goto done;
+ }
+
+ ret = dbwrap_store_bystring(db, state->kidstr,
+ string_term_tdb_data(state->ksidstr),
+ TDB_INSERT);
+ if (!NT_STATUS_IS_OK(ret)) {
+ DEBUG(0, ("Error storing ID -> SID: %s\n", nt_errstr(ret)));
+ /* try to remove the previous stored SID -> ID map */
+ dbwrap_delete_bystring(db, state->ksidstr);
+ goto done;
+ }
+
+ DEBUG(10,("Stored %s <-> %s\n", state->ksidstr, state->kidstr));
+
+done:
+ talloc_free(tmp_ctx);
+ return ret;
+}
+
+static NTSTATUS idmap_tdb2_set_mapping(struct idmap_domain *dom, const struct id_map *map)
+{
+ struct idmap_tdb2_context *ctx;
+ NTSTATUS ret;
+ char *kidstr;
+ struct dom_sid_buf sid_str;
+ struct idmap_tdb_common_context *commonctx;
+ struct idmap_tdb2_set_mapping_context state;
+
+ if (!map || !map->sid) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ kidstr = NULL;
+
+ /* TODO: should we filter a set_mapping using low/high filters ? */
+
+ commonctx = talloc_get_type(dom->private_data,
+ struct idmap_tdb_common_context);
+
+ ctx = talloc_get_type(commonctx->private_data,
+ struct idmap_tdb2_context);
+
+ switch (map->xid.type) {
+
+ case ID_TYPE_UID:
+ kidstr = talloc_asprintf(ctx, "UID %lu", (unsigned long)map->xid.id);
+ break;
+
+ case ID_TYPE_GID:
+ kidstr = talloc_asprintf(ctx, "GID %lu", (unsigned long)map->xid.id);
+ break;
+
+ default:
+ DEBUG(2, ("INVALID unix ID type: 0x02%x\n", map->xid.type));
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ if (kidstr == NULL) {
+ DEBUG(0, ("ERROR: Out of memory!\n"));
+ ret = NT_STATUS_NO_MEMORY;
+ goto done;
+ }
+
+ state.ksidstr = dom_sid_str_buf(map->sid, &sid_str);
+ state.kidstr = kidstr;
+
+ ret = dbwrap_trans_do(commonctx->db, idmap_tdb2_set_mapping_action,
+ &state);
+
+done:
+ talloc_free(kidstr);
+ return ret;
+}
+
+/*
+ run a script to perform a mapping
+
+ The script should the following command lines:
+
+ SIDTOID S-1-xxxx
+ IDTOSID UID xxxx
+ IDTOSID GID xxxx
+
+ and should return one of the following as a single line of text
+ UID:xxxx
+ GID:xxxx
+ SID:xxxx
+ ERR:xxxx
+ */
+static NTSTATUS idmap_tdb2_script(struct idmap_tdb2_context *ctx,
+ struct id_map *map, const char *fmt, ...)
+ PRINTF_ATTRIBUTE(3,4);
+
+static NTSTATUS idmap_tdb2_script(struct idmap_tdb2_context *ctx, struct id_map *map,
+ const char *fmt, ...)
+{
+ va_list ap;
+ char *cmd;
+ FILE *p;
+ char line[64];
+ unsigned long v;
+
+ cmd = talloc_asprintf(ctx, "%s ", ctx->script);
+ NT_STATUS_HAVE_NO_MEMORY(cmd);
+
+ va_start(ap, fmt);
+ cmd = talloc_vasprintf_append(cmd, fmt, ap);
+ va_end(ap);
+ NT_STATUS_HAVE_NO_MEMORY(cmd);
+
+ p = popen(cmd, "r");
+ talloc_free(cmd);
+ if (p == NULL) {
+ return NT_STATUS_NONE_MAPPED;
+ }
+
+ if (fgets(line, sizeof(line)-1, p) == NULL) {
+ pclose(p);
+ return NT_STATUS_NONE_MAPPED;
+ }
+ pclose(p);
+
+ DEBUG(10,("idmap script gave: %s\n", line));
+
+ if (sscanf(line, "UID:%lu", &v) == 1) {
+ map->xid.id = v;
+ map->xid.type = ID_TYPE_UID;
+ } else if (sscanf(line, "GID:%lu", &v) == 1) {
+ map->xid.id = v;
+ map->xid.type = ID_TYPE_GID;
+ } else if (strncmp(line, "SID:S-", 6) == 0) {
+ if (!string_to_sid(map->sid, &line[4])) {
+ DEBUG(0,("Bad SID in '%s' from idmap script %s\n",
+ line, ctx->script));
+ return NT_STATUS_NONE_MAPPED;
+ }
+ } else {
+ DEBUG(0,("Bad reply '%s' from idmap script %s\n",
+ line, ctx->script));
+ return NT_STATUS_NONE_MAPPED;
+ }
+
+ return NT_STATUS_OK;
+}
+
+
+
+/*
+ Single id to sid lookup function.
+*/
+static NTSTATUS idmap_tdb2_id_to_sid(struct idmap_domain *dom, struct id_map *map)
+{
+ NTSTATUS ret;
+ TDB_DATA data;
+ char *keystr;
+ NTSTATUS status;
+ struct idmap_tdb_common_context *commonctx;
+ struct idmap_tdb2_context *ctx;
+
+
+ if (!dom || !map) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ status = idmap_tdb2_open_db(dom);
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ commonctx = talloc_get_type(dom->private_data,
+ struct idmap_tdb_common_context);
+
+ ctx = talloc_get_type(commonctx->private_data,
+ struct idmap_tdb2_context);
+
+ /* apply filters before checking */
+ if (!idmap_unix_id_is_in_range(map->xid.id, dom)) {
+ DEBUG(5, ("Requested id (%u) out of range (%u - %u). Filtered!\n",
+ map->xid.id, dom->low_id, dom->high_id));
+ return NT_STATUS_NONE_MAPPED;
+ }
+
+ switch (map->xid.type) {
+
+ case ID_TYPE_UID:
+ keystr = talloc_asprintf(ctx, "UID %lu", (unsigned long)map->xid.id);
+ break;
+
+ case ID_TYPE_GID:
+ keystr = talloc_asprintf(ctx, "GID %lu", (unsigned long)map->xid.id);
+ break;
+
+ default:
+ DEBUG(2, ("INVALID unix ID type: 0x02%x\n", map->xid.type));
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ if (keystr == NULL) {
+ DEBUG(0, ("Out of memory!\n"));
+ ret = NT_STATUS_NO_MEMORY;
+ goto done;
+ }
+
+ DEBUG(10,("Fetching record %s\n", keystr));
+
+ /* Check if the mapping exists */
+ status = dbwrap_fetch_bystring(commonctx->db, keystr, keystr, &data);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ struct dom_sid_buf sidstr;
+ struct idmap_tdb2_set_mapping_context store_state;
+
+ DEBUG(10,("Record %s not found\n", keystr));
+ if (ctx->script == NULL) {
+ ret = NT_STATUS_NONE_MAPPED;
+ goto done;
+ }
+
+ ret = idmap_tdb2_script(ctx, map, "IDTOSID %s", keystr);
+ if (!NT_STATUS_IS_OK(ret)) {
+ goto done;
+ }
+
+ store_state.ksidstr = dom_sid_str_buf(map->sid, &sidstr);
+ store_state.kidstr = keystr;
+
+ ret = dbwrap_trans_do(commonctx->db,
+ idmap_tdb2_set_mapping_action,
+ &store_state);
+ goto done;
+ }
+
+ if (!string_to_sid(map->sid, (const char *)data.dptr)) {
+ DEBUG(10,("INVALID SID (%s) in record %s\n",
+ (const char *)data.dptr, keystr));
+ ret = NT_STATUS_INTERNAL_DB_ERROR;
+ goto done;
+ }
+
+ DEBUG(10,("Found record %s -> %s\n", keystr, (const char *)data.dptr));
+ ret = NT_STATUS_OK;
+
+done:
+ talloc_free(keystr);
+ return ret;
+}
+
+
+/*
+ Single sid to id lookup function.
+*/
+static NTSTATUS idmap_tdb2_sid_to_id(struct idmap_domain *dom, struct id_map *map)
+{
+ NTSTATUS ret;
+ TDB_DATA data;
+ struct dom_sid_buf keystr;
+ unsigned long rec_id = 0;
+ struct idmap_tdb_common_context *commonctx;
+ struct idmap_tdb2_context *ctx;
+ TALLOC_CTX *tmp_ctx = talloc_stackframe();
+
+ ret = idmap_tdb2_open_db(dom);
+ NT_STATUS_NOT_OK_RETURN(ret);
+
+ commonctx = talloc_get_type(dom->private_data,
+ struct idmap_tdb_common_context);
+
+ ctx = talloc_get_type(commonctx->private_data,
+ struct idmap_tdb2_context);
+
+ dom_sid_str_buf(map->sid, &keystr);
+
+ DEBUG(10, ("Fetching record %s\n", keystr.buf));
+
+ /* Check if sid is present in database */
+ ret = dbwrap_fetch_bystring(commonctx->db, tmp_ctx, keystr.buf, &data);
+ if (!NT_STATUS_IS_OK(ret)) {
+ char *idstr;
+ struct idmap_tdb2_set_mapping_context store_state;
+
+ DBG_DEBUG("Record %s not found\n", keystr.buf);
+
+ if (ctx->script == NULL) {
+ ret = NT_STATUS_NONE_MAPPED;
+ goto done;
+ }
+
+ ret = idmap_tdb2_script(ctx, map, "SIDTOID %s", keystr.buf);
+ if (!NT_STATUS_IS_OK(ret)) {
+ goto done;
+ }
+
+ /* apply filters before returning result */
+ if (!idmap_unix_id_is_in_range(map->xid.id, dom)) {
+ DEBUG(5, ("Script returned id (%u) out of range "
+ "(%u - %u). Filtered!\n",
+ map->xid.id, dom->low_id, dom->high_id));
+ ret = NT_STATUS_NONE_MAPPED;
+ goto done;
+ }
+
+ idstr = talloc_asprintf(tmp_ctx, "%cID %lu",
+ map->xid.type == ID_TYPE_UID?'U':'G',
+ (unsigned long)map->xid.id);
+ if (idstr == NULL) {
+ ret = NT_STATUS_NO_MEMORY;
+ goto done;
+ }
+
+ store_state.ksidstr = keystr.buf;
+ store_state.kidstr = idstr;
+
+ ret = dbwrap_trans_do(commonctx->db,
+ idmap_tdb2_set_mapping_action,
+ &store_state);
+ goto done;
+ }
+
+ /* What type of record is this ? */
+ if (sscanf((const char *)data.dptr, "UID %lu", &rec_id) == 1) { /* Try a UID record. */
+ map->xid.id = rec_id;
+ map->xid.type = ID_TYPE_UID;
+ DBG_DEBUG("Found uid record %s -> %s \n",
+ keystr.buf,
+ (const char *)data.dptr );
+ ret = NT_STATUS_OK;
+
+ } else if (sscanf((const char *)data.dptr, "GID %lu", &rec_id) == 1) { /* Try a GID record. */
+ map->xid.id = rec_id;
+ map->xid.type = ID_TYPE_GID;
+ DBG_DEBUG("Found gid record %s -> %s \n",
+ keystr.buf,
+ (const char *)data.dptr );
+ ret = NT_STATUS_OK;
+
+ } else { /* Unknown record type ! */
+ DBG_WARNING("Found INVALID record %s -> %s\n",
+ keystr.buf,
+ (const char *)data.dptr);
+ ret = NT_STATUS_INTERNAL_DB_ERROR;
+ goto done;
+ }
+
+ /* apply filters before returning result */
+ if (!idmap_unix_id_is_in_range(map->xid.id, dom)) {
+ DEBUG(5, ("Requested id (%u) out of range (%u - %u). Filtered!\n",
+ map->xid.id, dom->low_id, dom->high_id));
+ ret = NT_STATUS_NONE_MAPPED;
+ }
+
+done:
+ talloc_free(tmp_ctx);
+ return ret;
+}
+
+/*
+ Initialise idmap database.
+*/
+static NTSTATUS idmap_tdb2_db_init(struct idmap_domain *dom)
+{
+ NTSTATUS ret;
+ struct idmap_tdb_common_context *commonctx;
+ struct idmap_tdb2_context *ctx;
+ const char * idmap_script = NULL;
+ const char *ctx_script = NULL;
+
+ commonctx = talloc_zero(dom, struct idmap_tdb_common_context);
+ if(!commonctx) {
+ DEBUG(0, ("Out of memory!\n"));
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ commonctx->rw_ops = talloc_zero(commonctx, struct idmap_rw_ops);
+ if (commonctx->rw_ops == NULL) {
+ DEBUG(0, ("Out of memory!\n"));
+ ret = NT_STATUS_NO_MEMORY;
+ goto failed;
+ }
+
+ ctx = talloc_zero(commonctx, struct idmap_tdb2_context);
+ if (!ctx) {
+ DEBUG(0, ("Out of memory!\n"));
+ ret = NT_STATUS_NO_MEMORY;
+ goto failed;
+ }
+
+ ctx_script = idmap_config_const_string(dom->name, "script", NULL);
+
+ idmap_script = lp_parm_const_string(-1, "idmap", "script", NULL);
+ if (idmap_script != NULL) {
+ DEBUG(0, ("Warning: 'idmap:script' is deprecated. "
+ " Please use 'idmap config * : script' instead!\n"));
+ }
+
+ if (strequal(dom->name, "*") && ctx_script == NULL) {
+ /* fall back to idmap:script for backwards compatibility */
+ ctx_script = idmap_script;
+ }
+
+ if (ctx_script) {
+ DEBUG(1, ("using idmap script '%s'\n", ctx_script));
+ /*
+ * We must ensure this memory is owned by ctx.
+ * The ctx_script const pointer is a pointer into
+ * the config file data and may become invalid
+ * on config file reload. BUG: 13956
+ */
+ ctx->script = talloc_strdup(ctx, ctx_script);
+ if (ctx->script == NULL) {
+ ret = NT_STATUS_NO_MEMORY;
+ goto failed;
+ }
+ }
+
+ commonctx->max_id = dom->high_id;
+ commonctx->hwmkey_uid = HWM_USER;
+ commonctx->hwmkey_gid = HWM_GROUP;
+
+ commonctx->sid_to_unixid_fn = idmap_tdb2_sid_to_id;
+ commonctx->unixid_to_sid_fn = idmap_tdb2_id_to_sid;
+
+ commonctx->rw_ops->get_new_id = idmap_tdb_common_get_new_id;
+ commonctx->rw_ops->set_mapping = idmap_tdb2_set_mapping;
+
+ commonctx->private_data = ctx;
+ dom->private_data = commonctx;
+
+ ret = idmap_tdb2_open_db(dom);
+ if (!NT_STATUS_IS_OK(ret)) {
+ goto failed;
+ }
+
+ return NT_STATUS_OK;
+
+failed:
+ talloc_free(commonctx);
+ return ret;
+}
+
+
+static const struct idmap_methods db_methods = {
+ .init = idmap_tdb2_db_init,
+ .unixids_to_sids = idmap_tdb_common_unixids_to_sids,
+ .sids_to_unixids = idmap_tdb_common_sids_to_unixids,
+ .allocate_id = idmap_tdb_common_get_new_id
+};
+
+static_decl_idmap;
+NTSTATUS idmap_tdb2_init(TALLOC_CTX *ctx)
+{
+ return smb_register_idmap(SMB_IDMAP_INTERFACE_VERSION, "tdb2", &db_methods);
+}
diff --git a/source3/winbindd/idmap_tdb_common.c b/source3/winbindd/idmap_tdb_common.c
new file mode 100644
index 0000000..0df8f2f
--- /dev/null
+++ b/source3/winbindd/idmap_tdb_common.c
@@ -0,0 +1,664 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ common functions for TDB based idmapping backends
+
+ Copyright (C) Christian Ambach 2012
+
+ These functions were initially copied over from idmap_tdb.c and idmap_tdb2.c
+ which are:
+
+ Copyright (C) Tim Potter 2000
+ Copyright (C) Jim McDonough <jmcd@us.ibm.com> 2003
+ Copyright (C) Jeremy Allison 2006
+ Copyright (C) Simo Sorce 2003-2006
+ Copyright (C) Michael Adam 2009-2010
+ Copyright (C) Andrew Tridgell 2007
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+#include "idmap_tdb_common.h"
+#include "dbwrap/dbwrap.h"
+#include "util_tdb.h"
+#include "idmap_rw.h"
+#include "../libcli/security/dom_sid.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_IDMAP
+
+struct idmap_tdb_common_allocate_id_context {
+ const char *hwmkey;
+ const char *hwmtype;
+ uint32_t high_hwm;
+ uint32_t hwm;
+};
+
+static NTSTATUS idmap_tdb_common_allocate_id_action(struct db_context *db,
+ void *private_data)
+{
+ NTSTATUS ret;
+ struct idmap_tdb_common_allocate_id_context *state = private_data;
+ uint32_t hwm;
+
+ ret = dbwrap_fetch_uint32_bystring(db, state->hwmkey, &hwm);
+ if (!NT_STATUS_IS_OK(ret)) {
+ ret = NT_STATUS_INTERNAL_DB_ERROR;
+ goto done;
+ }
+
+ /* check it is in the range */
+ if (hwm > state->high_hwm) {
+ DEBUG(1, ("Fatal Error: %s range full!! (max: %lu)\n",
+ state->hwmtype, (unsigned long)state->high_hwm));
+ ret = NT_STATUS_UNSUCCESSFUL;
+ goto done;
+ }
+
+ /* fetch a new id and increment it */
+ ret = dbwrap_change_uint32_atomic_bystring(db, state->hwmkey, &hwm, 1);
+ if (!NT_STATUS_IS_OK(ret)) {
+ DEBUG(1, ("Fatal error while fetching a new %s value\n!",
+ state->hwmtype));
+ goto done;
+ }
+
+ /* recheck it is in the range */
+ if (hwm > state->high_hwm) {
+ DEBUG(1, ("Fatal Error: %s range full!! (max: %lu)\n",
+ state->hwmtype, (unsigned long)state->high_hwm));
+ ret = NT_STATUS_UNSUCCESSFUL;
+ goto done;
+ }
+
+ ret = NT_STATUS_OK;
+ state->hwm = hwm;
+
+ done:
+ return ret;
+}
+
+static NTSTATUS idmap_tdb_common_allocate_id(struct idmap_domain *dom,
+ struct unixid *xid)
+{
+ const char *hwmkey;
+ const char *hwmtype;
+ uint32_t hwm = 0;
+ NTSTATUS status;
+ struct idmap_tdb_common_allocate_id_context state;
+ struct idmap_tdb_common_context *ctx;
+
+ ctx =
+ talloc_get_type_abort(dom->private_data,
+ struct idmap_tdb_common_context);
+
+ /* Get current high water mark */
+ switch (xid->type) {
+
+ case ID_TYPE_UID:
+ hwmkey = ctx->hwmkey_uid;
+ hwmtype = "UID";
+ break;
+
+ case ID_TYPE_GID:
+ hwmkey = ctx->hwmkey_gid;
+ hwmtype = "GID";
+ break;
+
+ case ID_TYPE_BOTH:
+ /*
+ * This is not supported here yet and
+ * already handled in idmap_rw_new_mapping()
+ */
+ FALL_THROUGH;
+ case ID_TYPE_NOT_SPECIFIED:
+ /*
+ * This is handled in idmap_rw_new_mapping()
+ */
+ FALL_THROUGH;
+ default:
+ DEBUG(2, ("Invalid ID type (0x%x)\n", xid->type));
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ state.hwm = hwm;
+ state.high_hwm = ctx->max_id;
+ state.hwmtype = hwmtype;
+ state.hwmkey = hwmkey;
+
+ status = dbwrap_trans_do(ctx->db, idmap_tdb_common_allocate_id_action,
+ &state);
+
+ if (NT_STATUS_IS_OK(status)) {
+ xid->id = state.hwm;
+ DEBUG(10, ("New %s = %d\n", hwmtype, state.hwm));
+ } else {
+ DEBUG(1, ("Error allocating a new %s\n", hwmtype));
+ }
+
+ return status;
+}
+
+/**
+ * Allocate a new unix-ID.
+ * For now this is for the default idmap domain only.
+ * Should be extended later on.
+ */
+NTSTATUS idmap_tdb_common_get_new_id(struct idmap_domain * dom,
+ struct unixid * id)
+{
+ NTSTATUS ret;
+
+ if (!strequal(dom->name, "*")) {
+ DEBUG(3, ("idmap_tdb_common_get_new_id: "
+ "Refusing allocation of a new unixid for domain'%s'. "
+ "Currently only supported for the default "
+ "domain \"*\".\n", dom->name));
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+
+ ret = idmap_tdb_common_allocate_id(dom, id);
+
+ return ret;
+}
+
+/**
+ * store a mapping in the database.
+ */
+
+struct idmap_tdb_common_set_mapping_context {
+ const char *ksidstr;
+ const char *kidstr;
+};
+
+static NTSTATUS idmap_tdb_common_set_mapping_action(struct db_context *db,
+ void *private_data)
+{
+ TDB_DATA data;
+ NTSTATUS ret;
+ struct idmap_tdb_common_set_mapping_context *state = private_data;
+ TALLOC_CTX *tmp_ctx = talloc_stackframe();
+
+ DEBUG(10, ("Storing %s <-> %s map\n", state->ksidstr, state->kidstr));
+
+ /* check whether sid mapping is already present in db */
+ ret = dbwrap_fetch_bystring(db, tmp_ctx, state->ksidstr, &data);
+ if (NT_STATUS_IS_OK(ret)) {
+ ret = NT_STATUS_OBJECT_NAME_COLLISION;
+ goto done;
+ }
+
+ ret = dbwrap_store_bystring(db, state->ksidstr,
+ string_term_tdb_data(state->kidstr),
+ TDB_INSERT);
+ if (!NT_STATUS_IS_OK(ret)) {
+ DEBUG(0, ("Error storing SID -> ID: %s\n", nt_errstr(ret)));
+ goto done;
+ }
+
+ ret = dbwrap_store_bystring(db, state->kidstr,
+ string_term_tdb_data(state->ksidstr),
+ TDB_INSERT);
+ if (!NT_STATUS_IS_OK(ret)) {
+ DEBUG(0, ("Error storing ID -> SID: %s\n", nt_errstr(ret)));
+ /* try to remove the previous stored SID -> ID map */
+ dbwrap_delete_bystring(db, state->ksidstr);
+ goto done;
+ }
+
+ DEBUG(10, ("Stored %s <-> %s\n", state->ksidstr, state->kidstr));
+
+ done:
+ talloc_free(tmp_ctx);
+ return ret;
+}
+
+NTSTATUS idmap_tdb_common_set_mapping(struct idmap_domain * dom,
+ const struct id_map * map)
+{
+ struct idmap_tdb_common_context *ctx;
+ struct idmap_tdb_common_set_mapping_context state;
+ NTSTATUS ret;
+ struct dom_sid_buf ksidstr;
+ char *kidstr = NULL;
+
+ if (!map || !map->sid) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ /* TODO: should we filter a set_mapping using low/high filters ? */
+
+ ctx =
+ talloc_get_type_abort(dom->private_data,
+ struct idmap_tdb_common_context);
+
+ switch (map->xid.type) {
+
+ case ID_TYPE_UID:
+ kidstr =
+ talloc_asprintf(ctx, "UID %lu", (unsigned long)map->xid.id);
+ break;
+
+ case ID_TYPE_GID:
+ kidstr =
+ talloc_asprintf(ctx, "GID %lu", (unsigned long)map->xid.id);
+ break;
+
+ default:
+ DEBUG(2, ("INVALID unix ID type: 0x%02x\n", map->xid.type));
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ if (kidstr == NULL) {
+ DEBUG(0, ("ERROR: Out of memory!\n"));
+ ret = NT_STATUS_NO_MEMORY;
+ goto done;
+ }
+
+ state.ksidstr = dom_sid_str_buf(map->sid, &ksidstr);
+ state.kidstr = kidstr;
+
+ ret = dbwrap_trans_do(ctx->db, idmap_tdb_common_set_mapping_action,
+ &state);
+
+ done:
+ talloc_free(kidstr);
+ return ret;
+}
+
+/*
+ * Create a new mapping for an unmapped SID, also allocating a new ID.
+ * This should be run inside a transaction.
+ *
+ * TODO:
+ * Properly integrate this with multi domain idmap config:
+ * Currently, the allocator is default-config only.
+ */
+NTSTATUS idmap_tdb_common_new_mapping(struct idmap_domain * dom,
+ struct id_map * map)
+{
+ NTSTATUS ret;
+ struct idmap_tdb_common_context *ctx;
+
+ ctx =
+ talloc_get_type_abort(dom->private_data,
+ struct idmap_tdb_common_context);
+
+ ret = idmap_rw_new_mapping(dom, ctx->rw_ops, map);
+
+ return ret;
+}
+
+/*
+ lookup a set of unix ids
+*/
+NTSTATUS idmap_tdb_common_unixids_to_sids(struct idmap_domain * dom,
+ struct id_map ** ids)
+{
+ NTSTATUS ret;
+ size_t i, num_mapped = 0;
+ struct idmap_tdb_common_context *ctx;
+
+ NTSTATUS(*unixid_to_sid_fn) (struct idmap_domain * dom,
+ struct id_map * map);
+ ctx =
+ talloc_get_type_abort(dom->private_data,
+ struct idmap_tdb_common_context);
+
+ if (ctx->unixid_to_sid_fn == NULL) {
+ unixid_to_sid_fn = idmap_tdb_common_unixid_to_sid;
+ } else {
+ unixid_to_sid_fn = ctx->unixid_to_sid_fn;
+ }
+
+ /* initialize the status to avoid surprise */
+ for (i = 0; ids[i]; i++) {
+ ids[i]->status = ID_UNKNOWN;
+ }
+
+ for (i = 0; ids[i]; i++) {
+ ret = unixid_to_sid_fn(dom, ids[i]);
+ if (!NT_STATUS_IS_OK(ret)) {
+
+ /* if it is just a failed mapping continue */
+ if (NT_STATUS_EQUAL(ret, NT_STATUS_NONE_MAPPED)) {
+
+ /* make sure it is marked as unmapped */
+ ids[i]->status = ID_UNMAPPED;
+ continue;
+ }
+
+ /* some fatal error occurred, return immediately */
+ goto done;
+ }
+
+ /* all ok, id is mapped */
+ ids[i]->status = ID_MAPPED;
+ num_mapped += 1;
+ }
+
+ ret = NT_STATUS_OK;
+
+done:
+
+ if (NT_STATUS_IS_OK(ret)) {
+ if (i == 0 || num_mapped == 0) {
+ ret = NT_STATUS_NONE_MAPPED;
+ } else if (num_mapped < i) {
+ ret = STATUS_SOME_UNMAPPED;
+ } else {
+ ret = NT_STATUS_OK;
+ }
+ }
+
+ return ret;
+}
+
+/*
+ default single id to sid lookup function
+*/
+NTSTATUS idmap_tdb_common_unixid_to_sid(struct idmap_domain * dom,
+ struct id_map * map)
+{
+ NTSTATUS ret;
+ TDB_DATA data;
+ char *keystr;
+ struct idmap_tdb_common_context *ctx;
+
+ if (!dom || !map) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ ctx =
+ talloc_get_type_abort(dom->private_data,
+ struct idmap_tdb_common_context);
+
+ /* apply filters before checking */
+ if (!idmap_unix_id_is_in_range(map->xid.id, dom)) {
+ DEBUG(5,
+ ("Requested id (%u) out of range (%u - %u). Filtered!\n",
+ map->xid.id, dom->low_id, dom->high_id));
+ return NT_STATUS_NONE_MAPPED;
+ }
+
+ switch (map->xid.type) {
+
+ case ID_TYPE_UID:
+ keystr =
+ talloc_asprintf(ctx, "UID %lu", (unsigned long)map->xid.id);
+ break;
+
+ case ID_TYPE_GID:
+ keystr =
+ talloc_asprintf(ctx, "GID %lu", (unsigned long)map->xid.id);
+ break;
+
+ default:
+ DEBUG(2, ("INVALID unix ID type: 0x%02x\n", map->xid.type));
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ if (keystr == NULL) {
+ DEBUG(0, ("Out of memory!\n"));
+ ret = NT_STATUS_NO_MEMORY;
+ goto done;
+ }
+
+ DEBUG(10, ("Fetching record %s\n", keystr));
+
+ /* Check if the mapping exists */
+ ret = dbwrap_fetch_bystring(ctx->db, keystr, keystr, &data);
+
+ if (!NT_STATUS_IS_OK(ret)) {
+ DEBUG(10, ("Record %s not found\n", keystr));
+ ret = NT_STATUS_NONE_MAPPED;
+ goto done;
+ }
+
+ if ((data.dsize == 0) || (data.dptr[data.dsize-1] != '\0')) {
+ DBG_DEBUG("Invalid record length %zu\n", data.dsize);
+ ret = NT_STATUS_INTERNAL_DB_ERROR;
+ goto done;
+ }
+
+ if (!string_to_sid(map->sid, (const char *)data.dptr)) {
+ DEBUG(10, ("INVALID SID (%s) in record %s\n",
+ (const char *)data.dptr, keystr));
+ ret = NT_STATUS_INTERNAL_DB_ERROR;
+ goto done;
+ }
+
+ DEBUG(10, ("Found record %s -> %s\n", keystr, (const char *)data.dptr));
+ ret = NT_STATUS_OK;
+
+ done:
+ talloc_free(keystr);
+ return ret;
+}
+
+/**********************************
+ Single sid to id lookup function.
+**********************************/
+
+NTSTATUS idmap_tdb_common_sid_to_unixid(struct idmap_domain * dom,
+ struct id_map * map)
+{
+ NTSTATUS ret;
+ TDB_DATA data;
+ struct dom_sid_buf keystr;
+ unsigned long rec_id = 0;
+ struct idmap_tdb_common_context *ctx;
+ TALLOC_CTX *tmp_ctx = talloc_stackframe();
+
+ if (!dom || !map) {
+ talloc_free(tmp_ctx);
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ ctx =
+ talloc_get_type_abort(dom->private_data,
+ struct idmap_tdb_common_context);
+
+ dom_sid_str_buf(map->sid, &keystr);
+
+ DEBUG(10, ("Fetching record %s\n", keystr.buf));
+
+ /* Check if sid is present in database */
+ ret = dbwrap_fetch_bystring(ctx->db, tmp_ctx, keystr.buf, &data);
+ if (!NT_STATUS_IS_OK(ret)) {
+ DEBUG(10, ("Record %s not found\n", keystr.buf));
+ ret = NT_STATUS_NONE_MAPPED;
+ goto done;
+ }
+
+ /* What type of record is this ? */
+ if (sscanf((const char *)data.dptr, "UID %lu", &rec_id) == 1) {
+ /* Try a UID record. */
+ map->xid.id = rec_id;
+ map->xid.type = ID_TYPE_UID;
+ DEBUG(10,
+ ("Found uid record %s -> %s \n", keystr.buf,
+ (const char *)data.dptr));
+ ret = NT_STATUS_OK;
+
+ } else if (sscanf((const char *)data.dptr, "GID %lu", &rec_id) == 1) {
+ /* Try a GID record. */
+ map->xid.id = rec_id;
+ map->xid.type = ID_TYPE_GID;
+ DEBUG(10,
+ ("Found gid record %s -> %s \n", keystr.buf,
+ (const char *)data.dptr));
+ ret = NT_STATUS_OK;
+
+ } else { /* Unknown record type ! */
+ DEBUG(2,
+ ("Found INVALID record %s -> %s\n", keystr.buf,
+ (const char *)data.dptr));
+ ret = NT_STATUS_INTERNAL_DB_ERROR;
+ goto done;
+ }
+
+ /* apply filters before returning result */
+ if (!idmap_unix_id_is_in_range(map->xid.id, dom)) {
+ DEBUG(5,
+ ("Requested id (%u) out of range (%u - %u). Filtered!\n",
+ map->xid.id, dom->low_id, dom->high_id));
+ ret = NT_STATUS_NONE_MAPPED;
+ }
+
+ done:
+ talloc_free(tmp_ctx);
+ return ret;
+}
+
+/**********************************
+ lookup a set of sids
+**********************************/
+
+struct idmap_tdb_common_sids_to_unixids_context {
+ struct idmap_domain *dom;
+ struct id_map **ids;
+ bool allocate_unmapped;
+ NTSTATUS(*sid_to_unixid_fn) (struct idmap_domain * dom,
+ struct id_map * map);
+};
+
+static NTSTATUS idmap_tdb_common_sids_to_unixids_action(struct db_context *db,
+ void *private_data)
+{
+ struct idmap_tdb_common_sids_to_unixids_context *state = private_data;
+ size_t i, num_mapped = 0, num_required = 0;
+ NTSTATUS ret = NT_STATUS_OK;
+
+ DEBUG(10, ("idmap_tdb_common_sids_to_unixids: "
+ " domain: [%s], allocate: %s\n",
+ state->dom->name, state->allocate_unmapped ? "yes" : "no"));
+
+ for (i = 0; state->ids[i]; i++) {
+ if ((state->ids[i]->status == ID_UNKNOWN) ||
+ /* retry if we could not map in previous run: */
+ (state->ids[i]->status == ID_UNMAPPED)) {
+ NTSTATUS ret2;
+
+ ret2 = state->sid_to_unixid_fn(state->dom,
+ state->ids[i]);
+
+ if (!NT_STATUS_IS_OK(ret2)) {
+
+ /* if it is just a failed mapping, continue */
+ if (NT_STATUS_EQUAL
+ (ret2, NT_STATUS_NONE_MAPPED)) {
+
+ /* make sure it is marked as unmapped */
+ state->ids[i]->status = ID_UNMAPPED;
+ ret = STATUS_SOME_UNMAPPED;
+ } else {
+ /*
+ * some fatal error occurred,
+ * return immediately
+ */
+ ret = ret2;
+ goto done;
+ }
+ } else {
+ /* all ok, id is mapped */
+ state->ids[i]->status = ID_MAPPED;
+ }
+ }
+
+ if (state->ids[i]->status == ID_MAPPED) {
+ num_mapped += 1;
+ }
+
+ if ((state->ids[i]->status == ID_UNMAPPED) &&
+ state->allocate_unmapped) {
+ ret =
+ idmap_tdb_common_new_mapping(state->dom,
+ state->ids[i]);
+ DBG_DEBUG("idmap_tdb_common_new_mapping returned %s\n",
+ nt_errstr(ret));
+ if (NT_STATUS_EQUAL(ret, STATUS_SOME_UNMAPPED)) {
+ if (state->ids[i]->status == ID_REQUIRE_TYPE) {
+ num_required += 1;
+ continue;
+ }
+ }
+ if (!NT_STATUS_IS_OK(ret)) {
+ ret = STATUS_SOME_UNMAPPED;
+ continue;
+ }
+ num_mapped += 1;
+ }
+ }
+
+done:
+
+ if (NT_STATUS_IS_OK(ret) ||
+ NT_STATUS_EQUAL(ret, STATUS_SOME_UNMAPPED)) {
+ if (i == 0 || num_mapped == 0) {
+ ret = NT_STATUS_NONE_MAPPED;
+ } else if (num_mapped < i) {
+ ret = STATUS_SOME_UNMAPPED;
+ } else {
+ ret = NT_STATUS_OK;
+ }
+ if (num_required > 0) {
+ ret = STATUS_SOME_UNMAPPED;
+ }
+ }
+
+ return ret;
+}
+
+NTSTATUS idmap_tdb_common_sids_to_unixids(struct idmap_domain * dom,
+ struct id_map ** ids)
+{
+ NTSTATUS ret;
+ int i;
+ struct idmap_tdb_common_sids_to_unixids_context state;
+ struct idmap_tdb_common_context *ctx;
+
+ ctx =
+ talloc_get_type_abort(dom->private_data,
+ struct idmap_tdb_common_context);
+
+ /* initialize the status to avoid surprise */
+ for (i = 0; ids[i]; i++) {
+ ids[i]->status = ID_UNKNOWN;
+ }
+
+ state.dom = dom;
+ state.ids = ids;
+ state.allocate_unmapped = false;
+ if (ctx->sid_to_unixid_fn == NULL) {
+ state.sid_to_unixid_fn = idmap_tdb_common_sid_to_unixid;
+ } else {
+ state.sid_to_unixid_fn = ctx->sid_to_unixid_fn;
+ }
+
+ ret = idmap_tdb_common_sids_to_unixids_action(ctx->db, &state);
+
+ if ( (NT_STATUS_EQUAL(ret, STATUS_SOME_UNMAPPED) ||
+ NT_STATUS_EQUAL(ret, NT_STATUS_NONE_MAPPED)) &&
+ !dom->read_only) {
+ state.allocate_unmapped = true;
+ ret = dbwrap_trans_do(ctx->db,
+ idmap_tdb_common_sids_to_unixids_action,
+ &state);
+ }
+
+ return ret;
+}
diff --git a/source3/winbindd/idmap_tdb_common.h b/source3/winbindd/idmap_tdb_common.h
new file mode 100644
index 0000000..3343b58
--- /dev/null
+++ b/source3/winbindd/idmap_tdb_common.h
@@ -0,0 +1,137 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ common functions for TDB based idmapping backends
+
+ Copyright (C) Christian Ambach 2012
+
+ These functions were initially copied over from idmap_tdb.c and idmap_tdb2.c
+ which are:
+
+ Copyright (C) Tim Potter 2000
+ Copyright (C) Jim McDonough <jmcd@us.ibm.com> 2003
+ Copyright (C) Jeremy Allison 2006
+ Copyright (C) Simo Sorce 2003-2006
+ Copyright (C) Michael Adam 2009-2010
+ Copyright (C) Andrew Tridgell 2007
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#ifndef _IDMAP_TDB_COMMON_H_
+#define _IDMAP_TDB_COMMON_H_
+
+#include "includes.h"
+#include "idmap.h"
+#include "dbwrap/dbwrap.h"
+
+/*
+ * this must be stored in idmap_domain->private_data
+ * when using idmap_tdb_common_get_new_id and the
+ * mapping functions idmap_tdb_common_unixid(s)_to_sids
+ *
+ * private_data can be used for backend specific
+ * configuration data (e.g. idmap script in idmap_tdb2)
+ *
+ */
+struct idmap_tdb_common_context {
+ struct db_context *db;
+ struct idmap_rw_ops *rw_ops;
+ /*
+ * what is the maximum xid to be allocated
+ * this is typically just dom->high_id
+ */
+ uint32_t max_id;
+ const char *hwmkey_uid;
+ const char *hwmkey_gid;
+ /**
+ * if not set, idmap_tdb_common_unixids_to_sid will be used by
+ * idmap_tdb_common_unixids_to_sids
+ */
+ NTSTATUS(*unixid_to_sid_fn) (struct idmap_domain *dom,
+ struct id_map * map);
+ /*
+ * if not set, idmap_tdb_common_sid_to_id will be used by
+ * idmap_tdb_common_sids_to_unixids
+ */
+ NTSTATUS(*sid_to_unixid_fn) (struct idmap_domain *dom,
+ struct id_map * map);
+ void *private_data;
+};
+
+/**
+ * Allocate a new unix-ID.
+ * For now this is for the default idmap domain only.
+ * Should be extended later on.
+ */
+NTSTATUS idmap_tdb_common_get_new_id(struct idmap_domain *dom,
+ struct unixid *id);
+
+/*
+ * store a mapping into the idmap database
+ *
+ * the entries that will be stored are
+ * UID map->xid.id => map->sid and map->sid => UID map->xid.id
+ * or
+ * GID map->xid.id => map->sid and map->sid => GID map->xid.id
+ *
+ * for example
+ * UID 12345 = S-1-5-21-297746067-1479432880-4056370663
+ * S-1-5-21-297746067-1479432880-4056370663 = UID 12345
+ *
+ */
+NTSTATUS idmap_tdb_common_set_mapping(struct idmap_domain *dom,
+ const struct id_map *map);
+
+/*
+ * Create a new mapping for an unmapped SID, also allocating a new ID.
+ * This should be run inside a transaction.
+ *
+ * TODO:
+ * Properly integrate this with multi domain idmap config:
+ * Currently, the allocator is default-config only.
+ */
+NTSTATUS idmap_tdb_common_new_mapping(struct idmap_domain *dom,
+ struct id_map *map);
+
+/*
+ * default multiple id to sid lookup function
+ *
+ * will call idmap_tdb_common_unixid_to_sid for each mapping
+ * if no other function to lookup unixid_to_sid was given in
+ * idmap_tdb_common_context
+ */
+NTSTATUS idmap_tdb_common_unixids_to_sids(struct idmap_domain *dom,
+ struct id_map **ids);
+
+/*
+ * default single id to sid lookup function
+ *
+ * will read the entries written by idmap_tdb_common_set_mapping
+ */
+NTSTATUS idmap_tdb_common_unixid_to_sid(struct idmap_domain *dom,
+ struct id_map *map);
+
+/**********************************
+ Single sid to id lookup function.
+**********************************/
+
+NTSTATUS idmap_tdb_common_sid_to_unixid(struct idmap_domain *dom,
+ struct id_map *map);
+
+NTSTATUS idmap_tdb_common_sids_to_unixids(struct idmap_domain *dom,
+ struct id_map **ids);
+
+#endif /* _IDMAP_TDB_COMMON_H_ */
diff --git a/source3/winbindd/idmap_util.c b/source3/winbindd/idmap_util.c
new file mode 100644
index 0000000..fd2ae4a
--- /dev/null
+++ b/source3/winbindd/idmap_util.c
@@ -0,0 +1,137 @@
+/*
+ Unix SMB/CIFS implementation.
+ ID Mapping
+ Copyright (C) Simo Sorce 2003
+ Copyright (C) Jeremy Allison 2006
+ Copyright (C) Michael Adam 2010
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.*/
+
+#include "includes.h"
+#include "winbindd.h"
+#include "winbindd_proto.h"
+#include "idmap.h"
+#include "idmap_cache.h"
+#include "../libcli/security/security.h"
+#include "secrets.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_IDMAP
+
+/**
+ * check whether a given unix id is inside the filter range of an idmap domain
+ */
+bool idmap_unix_id_is_in_range(uint32_t id, struct idmap_domain *dom)
+{
+ if ((dom->low_id && (id < dom->low_id)) ||
+ (dom->high_id && (id > dom->high_id)))
+ {
+ return false;
+ }
+
+ return true;
+}
+
+/**
+ * Helper for unixids_to_sids: find entry by id in mapping array,
+ * search up to IDMAP_AD_MAX_IDS entries
+ */
+struct id_map *idmap_find_map_by_id(struct id_map **maps, enum id_type type,
+ uint32_t id)
+{
+ int i;
+
+ for (i = 0; maps[i] != NULL; i++) {
+ if ((maps[i]->xid.type == type) && (maps[i]->xid.id == id)) {
+ return maps[i];
+ }
+ }
+
+ return NULL;
+}
+
+/**
+ * Helper for sids_to_unix_ids: find entry by SID in mapping array,
+ * search up to IDMAP_AD_MAX_IDS entries
+ */
+struct id_map *idmap_find_map_by_sid(struct id_map **maps, struct dom_sid *sid)
+{
+ int i;
+
+ for (i = 0; i < IDMAP_LDAP_MAX_IDS; i++) {
+ if (maps[i] == NULL) { /* end of the run */
+ return NULL;
+ }
+ if (dom_sid_equal(maps[i]->sid, sid)) {
+ return maps[i];
+ }
+ }
+
+ return NULL;
+}
+
+char *idmap_fetch_secret(const char *backend, const char *domain,
+ const char *identity)
+{
+ char *tmp, *ret;
+ int r;
+
+ r = asprintf(&tmp, "IDMAP_%s_%s", backend, domain);
+
+ if (r < 0)
+ return NULL;
+
+ /* make sure the key is case insensitive */
+ if (!strupper_m(tmp)) {
+ SAFE_FREE(tmp);
+ return NULL;
+ }
+
+ ret = secrets_fetch_generic(tmp, identity);
+
+ SAFE_FREE(tmp);
+
+ return ret;
+}
+
+struct id_map **id_map_ptrs_init(TALLOC_CTX *mem_ctx, size_t num_ids)
+{
+ struct id_map **ptrs;
+ struct id_map *maps;
+ struct dom_sid *sids;
+ size_t i;
+
+ ptrs = talloc_array(mem_ctx, struct id_map *, num_ids+1);
+ if (ptrs == NULL) {
+ return NULL;
+ }
+ maps = talloc_array(ptrs, struct id_map, num_ids);
+ if (maps == NULL) {
+ TALLOC_FREE(ptrs);
+ return NULL;
+ }
+ sids = talloc_zero_array(ptrs, struct dom_sid, num_ids);
+ if (sids == NULL) {
+ TALLOC_FREE(ptrs);
+ return NULL;
+ }
+
+ for (i=0; i<num_ids; i++) {
+ maps[i] = (struct id_map) { .sid = &sids[i] };
+ ptrs[i] = &maps[i];
+ }
+ ptrs[num_ids] = NULL;
+
+ return ptrs;
+}
diff --git a/source3/winbindd/nss_info.c b/source3/winbindd/nss_info.c
new file mode 100644
index 0000000..9c502e8
--- /dev/null
+++ b/source3/winbindd/nss_info.c
@@ -0,0 +1,377 @@
+/*
+ Unix SMB/CIFS implementation.
+ Idmap NSS headers
+
+ Copyright (C) Gerald Carter 2006
+ Copyright (C) Michael Adam 2008
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 3 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "nss_info.h"
+
+static struct nss_function_entry *backends = NULL;
+static struct nss_function_entry *default_backend = NULL;
+static struct nss_domain_entry *nss_domain_list = NULL;
+
+/**********************************************************************
+ Get idmap nss methods.
+**********************************************************************/
+
+static struct nss_function_entry *nss_get_backend(const char *name )
+{
+ struct nss_function_entry *entry = backends;
+
+ for(entry = backends; entry; entry = entry->next) {
+ if ( strequal(entry->name, name) )
+ return entry;
+ }
+
+ return NULL;
+}
+
+/*********************************************************************
+ Allow a module to register itself as a backend.
+**********************************************************************/
+
+ NTSTATUS smb_register_idmap_nss(int version, const char *name,
+ const struct nss_info_methods *methods)
+{
+ struct nss_function_entry *entry;
+
+ if ((version != SMB_NSS_INFO_INTERFACE_VERSION)) {
+ DEBUG(0, ("smb_register_idmap_nss: Failed to register idmap_nss module.\n"
+ "The module was compiled against SMB_NSS_INFO_INTERFACE_VERSION %d,\n"
+ "current SMB_NSS_INFO_INTERFACE_VERSION is %d.\n"
+ "Please recompile against the current version of samba!\n",
+ version, SMB_NSS_INFO_INTERFACE_VERSION));
+ return NT_STATUS_OBJECT_TYPE_MISMATCH;
+ }
+
+ if (!name || !name[0] || !methods) {
+ DEBUG(0,("smb_register_idmap_nss: called with NULL pointer or empty name!\n"));
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ if ( nss_get_backend(name) ) {
+ DEBUG(5,("smb_register_idmap_nss: idmap module %s "
+ "already registered!\n", name));
+ return NT_STATUS_OBJECT_NAME_COLLISION;
+ }
+
+ entry = SMB_XMALLOC_P(struct nss_function_entry);
+ entry->name = smb_xstrdup(name);
+ entry->methods = methods;
+
+ DLIST_ADD(backends, entry);
+ DEBUG(5, ("smb_register_idmap_nss: Successfully added idmap "
+ "nss backend '%s'\n", name));
+
+ return NT_STATUS_OK;
+}
+
+/********************************************************************
+ *******************************************************************/
+
+static bool parse_nss_parm(TALLOC_CTX *mem_ctx,
+ const char *config,
+ char **backend,
+ char **domain)
+{
+ char *p;
+
+ *backend = *domain = NULL;
+
+ if ( !config )
+ return False;
+
+ p = strchr( config, ':' );
+
+ /* if no : then the string must be the backend name only */
+
+ if ( !p ) {
+ *backend = talloc_strdup(mem_ctx, config);
+ return (*backend != NULL);
+ }
+
+ /* split the string and return the two parts */
+
+ if ( strlen(p+1) > 0 ) {
+ *domain = talloc_strdup(mem_ctx, p + 1);
+ }
+
+ *backend = talloc_strndup(mem_ctx, config, PTR_DIFF(p, config));
+ return (*backend != NULL);
+}
+
+static NTSTATUS nss_domain_list_add_domain(const char *domain,
+ struct nss_function_entry *nss_backend)
+{
+ struct nss_domain_entry *nss_domain;
+
+ nss_domain = talloc_zero(nss_domain_list, struct nss_domain_entry);
+ if (!nss_domain) {
+ DEBUG(0, ("nss_domain_list_add_domain: talloc() failure!\n"));
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ nss_domain->backend = nss_backend;
+ if (domain) {
+ nss_domain->domain = talloc_strdup(nss_domain, domain);
+ if (!nss_domain->domain) {
+ DEBUG(0, ("nss_domain_list_add_domain: talloc() "
+ "failure!\n"));
+ TALLOC_FREE(nss_domain);
+ return NT_STATUS_NO_MEMORY;
+ }
+ }
+
+ nss_domain->init_status = nss_domain->backend->methods->init(nss_domain);
+ if (!NT_STATUS_IS_OK(nss_domain->init_status)) {
+ DEBUG(0, ("nss_init: Failed to init backend '%s' for domain "
+ "'%s'!\n", nss_backend->name, nss_domain->domain));
+ }
+
+ DLIST_ADD(nss_domain_list, nss_domain);
+
+ DEBUG(10, ("Added domain '%s' with backend '%s' to nss_domain_list.\n",
+ domain, nss_backend->name));
+
+ return NT_STATUS_OK;
+}
+
+/********************************************************************
+ Each nss backend must not store global state, but rather be able
+ to initialize the state on a per domain basis.
+ *******************************************************************/
+
+static NTSTATUS nss_init(const char **nss_list)
+{
+ NTSTATUS status;
+ static bool nss_initialized = false;
+ int i;
+ char *backend = NULL, *domain = NULL;
+ struct nss_function_entry *nss_backend;
+ TALLOC_CTX *frame;
+
+ /* check for previous successful initializations */
+
+ if (nss_initialized) {
+ return NT_STATUS_OK;
+ }
+
+ frame = talloc_stackframe();
+
+ /* The "template" backend should always be registered as it
+ is a static module */
+
+ nss_backend = nss_get_backend("template");
+ if (nss_backend == NULL) {
+ static_init_nss_info(NULL);
+ }
+
+ /* Create the list of nss_domains (loading any shared plugins
+ as necessary) */
+
+ for ( i=0; nss_list && nss_list[i]; i++ ) {
+ bool ok;
+
+ ok = parse_nss_parm(frame, nss_list[i], &backend, &domain);
+ if (!ok) {
+ DEBUG(0,("nss_init: failed to parse \"%s\"!\n",
+ nss_list[i]));
+ continue;
+ }
+
+ DEBUG(10, ("parsed backend = '%s', domain = '%s'\n",
+ backend, domain));
+
+ /* validate the backend */
+
+ nss_backend = nss_get_backend(backend);
+ if (nss_backend == NULL) {
+ /*
+ * This is a freaking hack. We don't have proper
+ * modules for nss_info backends. Right now we have
+ * our standard nss_info backends in the ad backend.
+ */
+ status = smb_probe_module("idmap", "ad");
+ if ( !NT_STATUS_IS_OK(status) ) {
+ continue;
+ }
+ }
+
+ nss_backend = nss_get_backend(backend);
+ if (nss_backend == NULL) {
+ /* attempt to register the backend */
+ status = smb_probe_module( "nss_info", backend );
+ if ( !NT_STATUS_IS_OK(status) ) {
+ continue;
+ }
+ }
+
+ /* try again */
+ nss_backend = nss_get_backend(backend);
+ if (nss_backend == NULL) {
+ DEBUG(0, ("nss_init: unregistered backend %s!. "
+ "Skipping\n", backend));
+ continue;
+ }
+
+ /*
+ * The first config item of the list without an explicit domain
+ * is treated as the default nss info backend.
+ */
+ if ((domain == NULL) && (default_backend == NULL)) {
+ DEBUG(10, ("nss_init: using '%s' as default backend.\n",
+ backend));
+ default_backend = nss_backend;
+ }
+
+ status = nss_domain_list_add_domain(domain, nss_backend);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ /* cleanup */
+
+ TALLOC_FREE(domain);
+ TALLOC_FREE(backend);
+ }
+
+
+ if ( !nss_domain_list ) {
+ DEBUG(3,("nss_init: no nss backends configured. "
+ "Defaulting to \"template\".\n"));
+
+
+ /* we should default to use template here */
+ }
+
+ nss_initialized = true;
+
+ TALLOC_FREE(frame);
+ return NT_STATUS_OK;
+}
+
+/********************************************************************
+ *******************************************************************/
+
+static struct nss_domain_entry *find_nss_domain( const char *domain )
+{
+ NTSTATUS status;
+ struct nss_domain_entry *p;
+
+ status = nss_init( lp_winbind_nss_info() );
+ if ( !NT_STATUS_IS_OK(status) ) {
+ DEBUG(4,("find_nss_domain: Failed to init nss_info API "
+ "(%s)!\n", nt_errstr(status)));
+ return NULL;
+ }
+
+ for ( p=nss_domain_list; p; p=p->next ) {
+ if ( strequal( p->domain, domain ) )
+ break;
+ }
+
+ /* If we didn't find a match, then use the default nss backend */
+
+ if ( !p ) {
+ if (!default_backend) {
+ return NULL;
+ }
+
+ status = nss_domain_list_add_domain(domain, default_backend);
+ if (!NT_STATUS_IS_OK(status)) {
+ return NULL;
+ }
+
+ /*
+ * HACK ALERT:
+ * Here, we use the fact that the new domain was added at
+ * the beginning of the list...
+ */
+ p = nss_domain_list;
+ }
+
+ if ( !NT_STATUS_IS_OK( p->init_status ) ) {
+ p->init_status = p->backend->methods->init( p );
+ }
+
+ return p;
+}
+
+/********************************************************************
+ *******************************************************************/
+
+ NTSTATUS nss_map_to_alias( TALLOC_CTX *mem_ctx, const char *domain,
+ const char *name, char **alias )
+{
+ struct nss_domain_entry *p;
+ const struct nss_info_methods *m;
+
+ if ( (p = find_nss_domain( domain )) == NULL ) {
+ DEBUG(4,("nss_map_to_alias: Failed to find nss domain pointer for %s\n",
+ domain ));
+ return NT_STATUS_NOT_FOUND;
+ }
+
+ m = p->backend->methods;
+
+ return m->map_to_alias(mem_ctx, p, name, alias);
+}
+
+
+/********************************************************************
+ *******************************************************************/
+
+ NTSTATUS nss_map_from_alias( TALLOC_CTX *mem_ctx, const char *domain,
+ const char *alias, char **name )
+{
+ struct nss_domain_entry *p;
+ const struct nss_info_methods *m;
+
+ if ( (p = find_nss_domain( domain )) == NULL ) {
+ DEBUG(4,("nss_map_from_alias: Failed to find nss domain pointer for %s\n",
+ domain ));
+ return NT_STATUS_NOT_FOUND;
+ }
+
+ m = p->backend->methods;
+
+ return m->map_from_alias( mem_ctx, p, alias, name );
+}
+
+/********************************************************************
+ *******************************************************************/
+
+ NTSTATUS nss_close( const char *parameters )
+{
+ struct nss_domain_entry *p = nss_domain_list;
+ struct nss_domain_entry *q;
+
+ while ( p && p->backend && p->backend->methods ) {
+ /* close the backend */
+ p->backend->methods->close_fn();
+
+ /* free the memory */
+ q = p;
+ p = p->next;
+ TALLOC_FREE( q );
+ }
+
+ return NT_STATUS_OK;
+}
+
diff --git a/source3/winbindd/nss_info_template.c b/source3/winbindd/nss_info_template.c
new file mode 100644
index 0000000..c58a7fc
--- /dev/null
+++ b/source3/winbindd/nss_info_template.c
@@ -0,0 +1,80 @@
+/*
+ Unix SMB/CIFS implementation.
+ idMap nss template plugin
+
+ Copyright (C) Gerald Carter 2006
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 3 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "ads.h"
+#include "nss_info.h"
+
+/************************************************************************
+ ***********************************************************************/
+
+static NTSTATUS nss_template_init( struct nss_domain_entry *e )
+{
+ return NT_STATUS_OK;
+}
+
+/**********************************************************************
+ *********************************************************************/
+
+static NTSTATUS nss_template_map_to_alias( TALLOC_CTX *mem_ctx,
+ struct nss_domain_entry *e,
+ const char *name,
+ char **alias )
+{
+ return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+/**********************************************************************
+ *********************************************************************/
+
+static NTSTATUS nss_template_map_from_alias( TALLOC_CTX *mem_ctx,
+ struct nss_domain_entry *e,
+ const char *alias,
+ char **name )
+{
+ return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+/************************************************************************
+ ***********************************************************************/
+
+static NTSTATUS nss_template_close( void )
+{
+ return NT_STATUS_OK;
+}
+
+
+/************************************************************************
+ ***********************************************************************/
+
+static struct nss_info_methods nss_template_methods = {
+ .init = nss_template_init,
+ .map_to_alias = nss_template_map_to_alias,
+ .map_from_alias = nss_template_map_from_alias,
+ .close_fn = nss_template_close
+};
+
+NTSTATUS nss_info_template_init(TALLOC_CTX *mem_ctx)
+{
+ return smb_register_idmap_nss(SMB_NSS_INFO_INTERFACE_VERSION,
+ "template",
+ &nss_template_methods);
+}
+
diff --git a/source3/winbindd/wb_alias_members.c b/source3/winbindd/wb_alias_members.c
new file mode 100644
index 0000000..06c2292
--- /dev/null
+++ b/source3/winbindd/wb_alias_members.c
@@ -0,0 +1,137 @@
+/*
+ Unix SMB/CIFS implementation.
+ async alias_members
+ Copyright (C) Pavel Filipenský 2023
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "winbindd.h"
+#include "librpc/gen_ndr/ndr_winbind_c.h"
+#include "../libcli/security/security.h"
+
+struct wb_alias_members_state {
+ struct tevent_context *ev;
+ struct dom_sid sid;
+ struct wbint_SidArray sids;
+};
+
+static void wb_alias_members_done(struct tevent_req *subreq);
+
+struct tevent_req *wb_alias_members_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ const struct dom_sid *sid,
+ enum lsa_SidType type,
+ int max_nesting)
+{
+ struct tevent_req *req, *subreq;
+ struct wb_alias_members_state *state;
+ struct winbindd_domain *domain;
+ NTSTATUS status;
+ struct dom_sid_buf buf;
+
+ req = tevent_req_create(mem_ctx, &state, struct wb_alias_members_state);
+ if (req == NULL) {
+ return NULL;
+ }
+ D_INFO("WB command alias_members start.\nLooking up SID %s.\n",
+ dom_sid_str_buf(sid, &buf));
+
+ if (max_nesting <= 0) {
+ D_DEBUG("Finished. The depth based on 'winbind expand groups' is %d.\n", max_nesting);
+ state->sids.num_sids = 0;
+ state->sids.sids = NULL;
+ tevent_req_done(req);
+ return tevent_req_post(req, ev);
+ }
+
+ sid_copy(&state->sid, sid);
+
+ status = lookup_usergroups_cached(state,
+ &state->sid,
+ &state->sids.num_sids,
+ &state->sids.sids);
+ if (NT_STATUS_IS_OK(status)) {
+ tevent_req_done(req);
+ return tevent_req_post(req, ev);
+ }
+
+ domain = find_domain_from_sid_noinit(&state->sid);
+ if (domain == NULL) {
+ DBG_WARNING("could not find domain entry for sid %s\n",
+ dom_sid_str_buf(&state->sid, &buf));
+ tevent_req_nterror(req, NT_STATUS_NO_SUCH_ALIAS);
+ return tevent_req_post(req, ev);
+ }
+
+ subreq = dcerpc_wbint_LookupAliasMembers_send(state,
+ ev,
+ dom_child_handle(domain),
+ &state->sid,
+ type,
+ &state->sids);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, wb_alias_members_done, req);
+ return req;
+}
+
+static void wb_alias_members_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req =
+ tevent_req_callback_data(subreq, struct tevent_req);
+ struct wb_alias_members_state *state =
+ tevent_req_data(req, struct wb_alias_members_state);
+ NTSTATUS status, result;
+
+ status = dcerpc_wbint_LookupAliasMembers_recv(subreq, state, &result);
+ TALLOC_FREE(subreq);
+ if (any_nt_status_not_ok(status, result, &status)) {
+ D_WARNING("Failed with %s.\n", nt_errstr(status));
+ tevent_req_nterror(req, status);
+ return;
+ }
+ tevent_req_done(req);
+}
+
+NTSTATUS wb_alias_members_recv(struct tevent_req *req,
+ TALLOC_CTX *mem_ctx,
+ uint32_t *num_sids,
+ struct dom_sid **sids)
+{
+ struct wb_alias_members_state *state =
+ tevent_req_data(req, struct wb_alias_members_state);
+ NTSTATUS status;
+ uint32_t i;
+
+ if (tevent_req_is_nterror(req, &status)) {
+ return status;
+ }
+ *num_sids = state->sids.num_sids;
+ *sids = talloc_move(mem_ctx, &state->sids.sids);
+
+ D_INFO("WB command alias_members end.\nReceived %" PRIu32 " SID(s).\n",
+ *num_sids);
+ if (CHECK_DEBUGLVL(DBGLVL_INFO)) {
+ for (i = 0; i < *num_sids; i++) {
+ struct dom_sid_buf buf;
+ D_INFO("%" PRIu32 ": %s\n",
+ i,
+ dom_sid_str_buf(&(*sids)[i], &buf));
+ }
+ }
+ return NT_STATUS_OK;
+}
diff --git a/source3/winbindd/wb_dsgetdcname.c b/source3/winbindd/wb_dsgetdcname.c
new file mode 100644
index 0000000..0f6acaa
--- /dev/null
+++ b/source3/winbindd/wb_dsgetdcname.c
@@ -0,0 +1,255 @@
+/*
+ Unix SMB/CIFS implementation.
+ async dsgetdcname
+ Copyright (C) Volker Lendecke 2009
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "winbindd.h"
+#include "librpc/gen_ndr/ndr_winbind_c.h"
+#include "librpc/gen_ndr/ndr_netlogon.h"
+#include "lib/gencache.h"
+
+struct wb_dsgetdcname_state {
+ const char *domain_name;
+ struct GUID domain_guid;
+ struct netr_DsRGetDCNameInfo *dcinfo;
+};
+
+static void wb_dsgetdcname_done(struct tevent_req *subreq);
+
+struct tevent_req *wb_dsgetdcname_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ const char *domain_name,
+ const struct GUID *domain_guid,
+ const char *site_name,
+ uint32_t flags)
+{
+ struct tevent_req *req, *subreq;
+ struct wb_dsgetdcname_state *state;
+ struct dcerpc_binding_handle *child_binding_handle = NULL;
+ struct GUID *guid_ptr = NULL;
+
+ req = tevent_req_create(mem_ctx, &state, struct wb_dsgetdcname_state);
+ if (req == NULL) {
+ return NULL;
+ }
+
+ D_INFO("WB command dsgetdcname start.\n"
+ "Search domain name %s and site name %s.\n",
+ domain_name,
+ site_name);
+ if (strequal(domain_name, "BUILTIN")) {
+ /*
+ * This makes no sense
+ */
+ tevent_req_nterror(req, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND);
+ return tevent_req_post(req, ev);
+ }
+
+ if (strequal(domain_name, get_global_sam_name())) {
+ int role = lp_server_role();
+ if ( role != ROLE_ACTIVE_DIRECTORY_DC ) {
+ /*
+ * Two options here: Give back our own address, or say there's
+ * nobody around. Right now opting for the latter, one measure
+ * to prevent the loopback connects. This might change if
+ * needed.
+ */
+ tevent_req_nterror(req, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND);
+ return tevent_req_post(req, ev);
+ }
+ }
+
+ if (IS_DC) {
+ /*
+ * We have to figure out the DC ourselves
+ */
+ child_binding_handle = locator_child_handle();
+ } else {
+ struct winbindd_domain *domain = find_our_domain();
+ child_binding_handle = dom_child_handle(domain);
+ }
+
+ if (domain_guid != NULL) {
+ /* work around a const issue in rpccli_ autogenerated code */
+ state->domain_guid = *domain_guid;
+ guid_ptr = &state->domain_guid;
+ }
+
+ state->domain_name = talloc_strdup(state, domain_name);
+ if (tevent_req_nomem(state->domain_name, req)) {
+ return tevent_req_post(req, ev);
+ }
+
+ subreq = dcerpc_wbint_DsGetDcName_send(
+ state, ev, child_binding_handle, domain_name, guid_ptr, site_name,
+ flags, &state->dcinfo);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, wb_dsgetdcname_done, req);
+ return req;
+}
+
+static void wb_dsgetdcname_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct wb_dsgetdcname_state *state = tevent_req_data(
+ req, struct wb_dsgetdcname_state);
+ NTSTATUS status, result;
+
+ status = dcerpc_wbint_DsGetDcName_recv(subreq, state, &result);
+ TALLOC_FREE(subreq);
+ if (any_nt_status_not_ok(status, result, &status)) {
+ tevent_req_nterror(req, status);
+ return;
+ }
+ tevent_req_done(req);
+}
+
+NTSTATUS wb_dsgetdcname_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx,
+ struct netr_DsRGetDCNameInfo **pdcinfo)
+{
+ struct wb_dsgetdcname_state *state = tevent_req_data(
+ req, struct wb_dsgetdcname_state);
+ NTSTATUS status;
+
+ D_INFO("WB command dsgetdcname for %s end.\n",
+ state->domain_name);
+ if (tevent_req_is_nterror(req, &status)) {
+ D_NOTICE("Failed for %s with %s.\n",
+ state->domain_name,
+ nt_errstr(status));
+ return status;
+ }
+ *pdcinfo = talloc_move(mem_ctx, &state->dcinfo);
+ return NT_STATUS_OK;
+}
+
+NTSTATUS wb_dsgetdcname_gencache_set(const char *domname,
+ struct netr_DsRGetDCNameInfo *dcinfo)
+{
+ DATA_BLOB blob;
+ enum ndr_err_code ndr_err;
+ char *key;
+ bool ok;
+
+ key = talloc_asprintf_strupper_m(talloc_tos(), "DCINFO/%s", domname);
+ if (key == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ if (DEBUGLEVEL >= DBGLVL_DEBUG) {
+ NDR_PRINT_DEBUG(netr_DsRGetDCNameInfo, dcinfo);
+ }
+
+ ndr_err = ndr_push_struct_blob(
+ &blob, key, dcinfo,
+ (ndr_push_flags_fn_t)ndr_push_netr_DsRGetDCNameInfo);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ NTSTATUS status = ndr_map_error2ntstatus(ndr_err);
+ DBG_WARNING("ndr_push_struct_blob failed: %s\n",
+ ndr_errstr(ndr_err));
+ TALLOC_FREE(key);
+ return status;
+ }
+
+ ok = gencache_set_data_blob(key, blob, time(NULL)+3600);
+
+ if (!ok) {
+ DBG_WARNING("gencache_set_data_blob for key %s failed\n", key);
+ TALLOC_FREE(key);
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ TALLOC_FREE(key);
+ return NT_STATUS_OK;
+}
+
+struct dcinfo_parser_state {
+ NTSTATUS status;
+ TALLOC_CTX *mem_ctx;
+ struct netr_DsRGetDCNameInfo *dcinfo;
+};
+
+static void dcinfo_parser(const struct gencache_timeout *timeout,
+ DATA_BLOB blob,
+ void *private_data)
+{
+ struct dcinfo_parser_state *state = private_data;
+ enum ndr_err_code ndr_err;
+
+ if (gencache_timeout_expired(timeout)) {
+ return;
+ }
+
+ state->dcinfo = talloc(state->mem_ctx, struct netr_DsRGetDCNameInfo);
+ if (state->dcinfo == NULL) {
+ state->status = NT_STATUS_NO_MEMORY;
+ return;
+ }
+
+ ndr_err = ndr_pull_struct_blob_all(
+ &blob, state->dcinfo, state->dcinfo,
+ (ndr_pull_flags_fn_t)ndr_pull_netr_DsRGetDCNameInfo);
+
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ DBG_ERR("ndr_pull_struct_blob failed\n");
+ state->status = ndr_map_error2ntstatus(ndr_err);
+ TALLOC_FREE(state->dcinfo);
+ return;
+ }
+
+ state->status = NT_STATUS_OK;
+}
+
+NTSTATUS wb_dsgetdcname_gencache_get(TALLOC_CTX *mem_ctx,
+ const char *domname,
+ struct netr_DsRGetDCNameInfo **dcinfo)
+{
+ struct dcinfo_parser_state state;
+ char *key;
+ bool ok;
+
+ key = talloc_asprintf_strupper_m(mem_ctx, "DCINFO/%s", domname);
+ if (key == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ state = (struct dcinfo_parser_state) {
+ .status = NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND,
+ .mem_ctx = mem_ctx,
+ };
+
+ ok = gencache_parse(key, dcinfo_parser, &state);
+ TALLOC_FREE(key);
+ if (!ok) {
+ return NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND;
+ }
+
+ if (!NT_STATUS_IS_OK(state.status)) {
+ return state.status;
+ }
+
+ if (DEBUGLEVEL >= DBGLVL_DEBUG) {
+ NDR_PRINT_DEBUG(netr_DsRGetDCNameInfo, state.dcinfo);
+ }
+
+ *dcinfo = state.dcinfo;
+ return NT_STATUS_OK;
+}
diff --git a/source3/winbindd/wb_getgrsid.c b/source3/winbindd/wb_getgrsid.c
new file mode 100644
index 0000000..4fd696d
--- /dev/null
+++ b/source3/winbindd/wb_getgrsid.c
@@ -0,0 +1,403 @@
+/*
+ Unix SMB/CIFS implementation.
+ async getgrsid
+ Copyright (C) Volker Lendecke 2009
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "winbindd.h"
+#include "librpc/gen_ndr/ndr_winbind_c.h"
+#include "../libcli/security/security.h"
+#include "lib/dbwrap/dbwrap_rbt.h"
+#include "lib/dbwrap/dbwrap.h"
+
+struct wb_getgrsid_state {
+ struct tevent_context *ev;
+ struct dom_sid sid;
+ int max_nesting;
+ const char *domname;
+ const char *name;
+ enum lsa_SidType type;
+ gid_t gid;
+ struct db_context *members;
+ uint32_t num_sids;
+ struct dom_sid *sids;
+};
+
+static void wb_getgrsid_lookupsid_done(struct tevent_req *subreq);
+static void wb_getgrsid_sid2gid_done(struct tevent_req *subreq);
+static void wb_getgrsid_got_members(struct tevent_req *subreq);
+static void wb_getgrsid_got_alias_members(struct tevent_req *subreq);
+
+struct tevent_req *wb_getgrsid_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ const struct dom_sid *group_sid,
+ int max_nesting)
+{
+ struct tevent_req *req, *subreq;
+ struct wb_getgrsid_state *state;
+ struct dom_sid_buf buf;
+
+ req = tevent_req_create(mem_ctx, &state, struct wb_getgrsid_state);
+ if (req == NULL) {
+ return NULL;
+ }
+
+ D_INFO("WB command getgrsid start.\nLooking up group SID %s.\n", dom_sid_str_buf(group_sid, &buf));
+
+ sid_copy(&state->sid, group_sid);
+ state->ev = ev;
+ state->max_nesting = max_nesting;
+
+ if (dom_sid_in_domain(&global_sid_Unix_Groups, group_sid)) {
+ /* unmapped Unix groups must be resolved locally */
+ tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
+ return tevent_req_post(req, ev);
+ }
+
+ subreq = wb_lookupsid_send(state, ev, &state->sid);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, wb_getgrsid_lookupsid_done, req);
+ return req;
+}
+
+static void wb_getgrsid_lookupsid_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct wb_getgrsid_state *state = tevent_req_data(
+ req, struct wb_getgrsid_state);
+ NTSTATUS status;
+
+ status = wb_lookupsid_recv(subreq, state, &state->type,
+ &state->domname, &state->name);
+ TALLOC_FREE(subreq);
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+
+ switch (state->type) {
+ case SID_NAME_DOM_GRP:
+ case SID_NAME_ALIAS:
+ case SID_NAME_WKN_GRP:
+ /*
+ * also treat user-type SIDS (they might map to ID_TYPE_BOTH)
+ */
+ case SID_NAME_USER:
+ case SID_NAME_COMPUTER:
+ break;
+ default:
+ tevent_req_nterror(req, NT_STATUS_NO_SUCH_GROUP);
+ return;
+ }
+
+ subreq = wb_sids2xids_send(state, state->ev, &state->sid, 1);
+ if (tevent_req_nomem(subreq, req)) {
+ return;
+ }
+ tevent_req_set_callback(subreq, wb_getgrsid_sid2gid_done, req);
+}
+
+static void wb_getgrsid_sid2gid_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct wb_getgrsid_state *state = tevent_req_data(
+ req, struct wb_getgrsid_state);
+ NTSTATUS status;
+ struct unixid xids[1];
+
+ status = wb_sids2xids_recv(subreq, xids, ARRAY_SIZE(xids));
+ TALLOC_FREE(subreq);
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+
+ /*
+ * We are filtering further down in sids2xids, but that filtering
+ * depends on the actual type of the sid handed in (as determined
+ * by lookupsids). Here we need to filter for the type of object
+ * actually requested, in this case uid.
+ */
+ if (!(xids[0].type == ID_TYPE_GID || xids[0].type == ID_TYPE_BOTH)) {
+ tevent_req_nterror(req, NT_STATUS_NONE_MAPPED);
+ return;
+ }
+
+ state->gid = (gid_t)xids[0].id;
+
+ switch (state->type) {
+ case SID_NAME_USER:
+ case SID_NAME_COMPUTER: {
+ /*
+ * special treatment for a user sid that is
+ * mapped to ID_TYPE_BOTH:
+ * create a group with the sid/xid as only member
+ */
+ const char *name;
+
+ if (xids[0].type != ID_TYPE_BOTH) {
+ tevent_req_nterror(req, NT_STATUS_NO_SUCH_GROUP);
+ return;
+ }
+
+ state->members = db_open_rbt(state);
+ if (tevent_req_nomem(state->members, req)) {
+ return;
+ }
+
+ name = fill_domain_username_talloc(talloc_tos(),
+ state->domname,
+ state->name,
+ true /* can_assume */);
+ if (tevent_req_nomem(name, req)) {
+ return;
+ }
+
+ status = add_member_to_db(state->members, &state->sid, name);
+ if (!NT_STATUS_IS_OK(status)) {
+ tevent_req_nterror(req, status);
+ return;
+ }
+
+ tevent_req_done(req);
+ return;
+ }
+ case SID_NAME_ALIAS:
+ subreq = wb_alias_members_send(state,
+ state->ev,
+ &state->sid,
+ state->type,
+ state->max_nesting);
+ if (tevent_req_nomem(subreq, req)) {
+ return;
+ }
+ /* Decrement the depth based on 'winbind expand groups' */
+ state->max_nesting--;
+ tevent_req_set_callback(subreq,
+ wb_getgrsid_got_alias_members,
+ req);
+ break;
+ case SID_NAME_DOM_GRP:
+ subreq = wb_group_members_send(state,
+ state->ev,
+ &state->sid,
+ 1,
+ &state->type,
+ state->max_nesting);
+ if (tevent_req_nomem(subreq, req)) {
+ return;
+ }
+ tevent_req_set_callback(subreq, wb_getgrsid_got_members, req);
+ break;
+ case SID_NAME_WKN_GRP:
+ state->members = db_open_rbt(state);
+ if (tevent_req_nomem(state->members, req)) {
+ return;
+ }
+ tevent_req_done(req);
+ return;
+ default:
+ tevent_req_nterror(req, NT_STATUS_NO_SUCH_GROUP);
+ break;
+ }
+}
+
+static void wb_getgrsid_got_alias_members_names(struct tevent_req *subreq)
+{
+ struct tevent_req *req =
+ tevent_req_callback_data(subreq, struct tevent_req);
+ struct wb_getgrsid_state *state =
+ tevent_req_data(req, struct wb_getgrsid_state);
+ struct lsa_RefDomainList *domains = NULL;
+ struct lsa_TransNameArray *names = NULL;
+ NTSTATUS status;
+ uint32_t li;
+ uint32_t num_sids = 0;
+ struct dom_sid *sids = NULL;
+ enum lsa_SidType *types = NULL;
+
+ status = wb_lookupsids_recv(subreq, state, &domains, &names);
+
+ TALLOC_FREE(subreq);
+ if (tevent_req_nterror(req, status)) {
+ D_WARNING("Failed with %s.\n", nt_errstr(status));
+ return;
+ }
+
+ if (domains == NULL) {
+ tevent_req_nterror(req, NT_STATUS_INTERNAL_ERROR);
+ D_WARNING("Failed with NT_STATUS_INTERNAL_ERROR.\n");
+ return;
+ }
+
+ if (names == NULL) {
+ tevent_req_nterror(req, NT_STATUS_INTERNAL_ERROR);
+ D_WARNING("Failed with NT_STATUS_INTERNAL_ERROR.\n");
+ return;
+ }
+
+ state->members = db_open_rbt(state);
+ if (tevent_req_nomem(state->members, req)) {
+ return;
+ }
+
+ for (li = 0; li < state->num_sids; li++) {
+ struct lsa_TranslatedName *n = &names->names[li];
+
+ if (n->sid_type == SID_NAME_USER ||
+ n->sid_type == SID_NAME_COMPUTER) {
+ const char *name = fill_domain_username_talloc(
+ talloc_tos(),
+ domains->domains[n->sid_index].name.string,
+ n->name.string,
+ false /* can_assume */);
+ if (tevent_req_nomem(name, req)) {
+ return;
+ }
+
+ status = add_member_to_db(state->members,
+ &state->sids[li],
+ name);
+ if (!NT_STATUS_IS_OK(status)) {
+ tevent_req_nterror(req, status);
+ return;
+ }
+ } else if (n->sid_type == SID_NAME_DOM_GRP) {
+ sids = talloc_realloc(talloc_tos(),
+ sids,
+ struct dom_sid,
+ num_sids + 1);
+ if (tevent_req_nomem(sids, req)) {
+ return;
+ }
+ sids[num_sids] = state->sids[li];
+ types = talloc_realloc(talloc_tos(),
+ types,
+ enum lsa_SidType,
+ num_sids + 1);
+ if (tevent_req_nomem(types, req)) {
+ return;
+ }
+ types[num_sids] = n->sid_type;
+ num_sids++;
+ } else {
+ struct dom_sid_buf buf;
+ D_DEBUG("SID %s with sid_type=%d is ignored!\n",
+ dom_sid_str_buf(&state->sids[li], &buf),
+ n->sid_type);
+ }
+ }
+
+ TALLOC_FREE(names);
+ TALLOC_FREE(domains);
+
+ if (num_sids == 0) {
+ tevent_req_done(req);
+ return;
+ }
+ subreq = wb_group_members_send(state,
+ state->ev,
+ sids,
+ num_sids,
+ types,
+ state->max_nesting);
+ if (tevent_req_nomem(subreq, req)) {
+ return;
+ }
+ tevent_req_set_callback(subreq, wb_getgrsid_got_members, req);
+}
+
+static void wb_getgrsid_got_alias_members(struct tevent_req *subreq)
+{
+ struct tevent_req *req =
+ tevent_req_callback_data(subreq, struct tevent_req);
+ struct wb_getgrsid_state *state =
+ tevent_req_data(req, struct wb_getgrsid_state);
+ NTSTATUS status;
+
+ status = wb_alias_members_recv(subreq,
+ state,
+ &state->num_sids,
+ &state->sids);
+ TALLOC_FREE(subreq);
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+
+ subreq = wb_lookupsids_send(state,
+ state->ev,
+ state->sids,
+ state->num_sids);
+ if (tevent_req_nomem(subreq, req)) {
+ return;
+ }
+ tevent_req_set_callback(subreq,
+ wb_getgrsid_got_alias_members_names,
+ req);
+}
+
+static void wb_getgrsid_got_members(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct wb_getgrsid_state *state = tevent_req_data(
+ req, struct wb_getgrsid_state);
+ NTSTATUS status;
+ struct db_context *members_prev = state->members;
+
+ status = wb_group_members_recv(subreq, state, &state->members);
+ TALLOC_FREE(subreq);
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+ /*
+ * If we have called wb_alias_members_send(), members_prev
+ * might already contain users that are direct members of alias,
+ * add to them the users from nested groups.
+ */
+ if (members_prev != NULL) {
+ status = dbwrap_merge_dbs(state->members,
+ members_prev,
+ TDB_REPLACE);
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+ }
+ tevent_req_done(req);
+}
+
+NTSTATUS wb_getgrsid_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx,
+ const char **domname, const char **name, gid_t *gid,
+ struct db_context **members)
+{
+ struct wb_getgrsid_state *state = tevent_req_data(
+ req, struct wb_getgrsid_state);
+ NTSTATUS status;
+
+ D_INFO("WB command getgrsid end.\n");
+ if (tevent_req_is_nterror(req, &status)) {
+ D_WARNING("Failed with %s.\n", nt_errstr(status));
+ return status;
+ }
+ *domname = talloc_move(mem_ctx, &state->domname);
+ *name = talloc_move(mem_ctx, &state->name);
+ *gid = state->gid;
+ *members = talloc_move(mem_ctx, &state->members);
+ return NT_STATUS_OK;
+}
diff --git a/source3/winbindd/wb_getpwsid.c b/source3/winbindd/wb_getpwsid.c
new file mode 100644
index 0000000..7d04c39
--- /dev/null
+++ b/source3/winbindd/wb_getpwsid.c
@@ -0,0 +1,156 @@
+/*
+ Unix SMB/CIFS implementation.
+ async getpwsid
+ Copyright (C) Volker Lendecke 2009
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "winbindd.h"
+#include "librpc/gen_ndr/ndr_winbind_c.h"
+#include "../libcli/security/security.h"
+#include "lib/util/string_wrappers.h"
+#include "source3/lib/substitute.h"
+
+struct wb_getpwsid_state {
+ struct tevent_context *ev;
+ struct dom_sid sid;
+ struct wbint_userinfo *userinfo;
+ struct winbindd_pw *pw;
+};
+
+static void wb_getpwsid_queryuser_done(struct tevent_req *subreq);
+
+struct tevent_req *wb_getpwsid_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ const struct dom_sid *user_sid,
+ struct winbindd_pw *pw)
+{
+ struct tevent_req *req, *subreq;
+ struct wb_getpwsid_state *state;
+ struct dom_sid_buf buf;
+
+ req = tevent_req_create(mem_ctx, &state, struct wb_getpwsid_state);
+ if (req == NULL) {
+ return NULL;
+ }
+ D_INFO("WB command getpwsid start.\nQuery user SID %s.\n", dom_sid_str_buf(user_sid, &buf));
+ sid_copy(&state->sid, user_sid);
+ state->ev = ev;
+ state->pw = pw;
+
+ if (dom_sid_in_domain(&global_sid_Unix_Users, user_sid)) {
+ /* unmapped Unix users must be resolved locally */
+ tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
+ return tevent_req_post(req, ev);
+ }
+
+ subreq = wb_queryuser_send(state, ev, &state->sid);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, wb_getpwsid_queryuser_done, req);
+ return req;
+}
+
+static void wb_getpwsid_queryuser_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct wb_getpwsid_state *state = tevent_req_data(
+ req, struct wb_getpwsid_state);
+ struct winbindd_pw *pw = state->pw;
+ struct wbint_userinfo *info;
+ fstring acct_name;
+ const char *output_username = NULL;
+ char *mapped_name = NULL;
+ char *tmp;
+ NTSTATUS status;
+
+ status = wb_queryuser_recv(subreq, state, &state->userinfo);
+ TALLOC_FREE(subreq);
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+ info = state->userinfo;
+
+ pw->pw_uid = info->uid;
+ pw->pw_gid = info->primary_gid;
+
+ fstrcpy(acct_name, info->acct_name);
+ if (!strlower_m(acct_name)) {
+ tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
+ return;
+ }
+
+ /*
+ * TODO:
+ * This function should be called in 'idmap winbind child'. It shouldn't
+ * be a blocking call, but for this we need to add a new function for
+ * winbind.idl. This is a fix which can be backported for now.
+ */
+ status = normalize_name_map(state,
+ info->domain_name,
+ acct_name,
+ &mapped_name);
+ if (NT_STATUS_IS_OK(status) ||
+ NT_STATUS_EQUAL(status, NT_STATUS_FILE_RENAMED)) {
+ fstrcpy(acct_name, mapped_name);
+ }
+ output_username = fill_domain_username_talloc(state,
+ info->domain_name,
+ acct_name,
+ true);
+ if (output_username == NULL) {
+ tevent_req_nterror(req, NT_STATUS_NO_MEMORY);
+ return;
+ }
+
+ strlcpy(pw->pw_name, output_username, sizeof(pw->pw_name));
+
+ strlcpy(pw->pw_gecos, info->full_name ? info->full_name : "",
+ sizeof(pw->pw_gecos));
+
+ tmp = talloc_sub_specified(
+ state, info->homedir, acct_name,
+ info->primary_group_name, info->domain_name,
+ pw->pw_uid, pw->pw_gid);
+ if (tevent_req_nomem(tmp, req)) {
+ return;
+ }
+ strlcpy(pw->pw_dir, tmp, sizeof(pw->pw_dir));
+ TALLOC_FREE(tmp);
+
+ tmp = talloc_sub_specified(
+ state, info->shell, acct_name,
+ info->primary_group_name, info->domain_name,
+ pw->pw_uid, pw->pw_gid);
+ if (tevent_req_nomem(tmp, req)) {
+ return;
+ }
+ strlcpy(pw->pw_shell, tmp, sizeof(pw->pw_shell));
+ TALLOC_FREE(tmp);
+
+ strlcpy(pw->pw_passwd, "*", sizeof(pw->pw_passwd));
+
+ tevent_req_done(req);
+}
+
+NTSTATUS wb_getpwsid_recv(struct tevent_req *req)
+{
+ NTSTATUS status = tevent_req_simple_recv_ntstatus(req);
+ D_INFO("WB command getpwsid end.\nReturn status %s.\n", nt_errstr(status));
+ return status;
+}
diff --git a/source3/winbindd/wb_gettoken.c b/source3/winbindd/wb_gettoken.c
new file mode 100644
index 0000000..3930f71
--- /dev/null
+++ b/source3/winbindd/wb_gettoken.c
@@ -0,0 +1,290 @@
+/*
+ Unix SMB/CIFS implementation.
+ async gettoken
+ Copyright (C) Volker Lendecke 2009
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "util/debug.h"
+#include "winbindd.h"
+#include "librpc/gen_ndr/ndr_winbind_c.h"
+#include "../libcli/security/security.h"
+#include "passdb/machine_sid.h"
+
+struct wb_gettoken_state {
+ struct tevent_context *ev;
+ struct dom_sid usersid;
+ bool expand_local_aliases;
+ uint32_t num_sids;
+ struct dom_sid *sids;
+};
+
+static NTSTATUS wb_add_rids_to_sids(TALLOC_CTX *mem_ctx,
+ uint32_t *pnum_sids,
+ struct dom_sid **psids,
+ const struct dom_sid *domain_sid,
+ uint32_t num_rids, uint32_t *rids);
+
+static void wb_gettoken_gotuser(struct tevent_req *subreq);
+static void wb_gettoken_gotgroups(struct tevent_req *subreq);
+static void wb_gettoken_gotlocalgroups(struct tevent_req *subreq);
+static void wb_gettoken_gotbuiltins(struct tevent_req *subreq);
+
+struct tevent_req *wb_gettoken_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ const struct dom_sid *sid,
+ bool expand_local_aliases)
+{
+ struct tevent_req *req, *subreq;
+ struct wb_gettoken_state *state;
+ struct dom_sid_buf buf;
+
+ req = tevent_req_create(mem_ctx, &state, struct wb_gettoken_state);
+ if (req == NULL) {
+ return NULL;
+ }
+ sid_copy(&state->usersid, sid);
+ state->ev = ev;
+ state->expand_local_aliases = expand_local_aliases;
+
+ D_INFO("WB command gettoken start.\n"
+ "Query user SID %s (expand local aliases is %d).\n",
+ dom_sid_str_buf(sid, &buf),
+ expand_local_aliases);
+ subreq = wb_queryuser_send(state, ev, &state->usersid);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, wb_gettoken_gotuser, req);
+ return req;
+}
+
+static void wb_gettoken_gotuser(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct wb_gettoken_state *state = tevent_req_data(
+ req, struct wb_gettoken_state);
+ struct wbint_userinfo *info;
+ NTSTATUS status;
+ struct dom_sid_buf buf0, buf1;
+
+ status = wb_queryuser_recv(subreq, state, &info);
+ TALLOC_FREE(subreq);
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+
+ state->sids = talloc_array(state, struct dom_sid, 2);
+ if (tevent_req_nomem(state->sids, req)) {
+ return;
+ }
+ state->num_sids = 2;
+
+ D_DEBUG("Got user SID %s and group SID %s\n",
+ dom_sid_str_buf(&info->user_sid, &buf0),
+ dom_sid_str_buf(&info->group_sid, &buf1));
+ sid_copy(&state->sids[0], &info->user_sid);
+ sid_copy(&state->sids[1], &info->group_sid);
+
+ D_DEBUG("Looking up user groups for the user SID.\n");
+ subreq = wb_lookupusergroups_send(state, state->ev, &info->user_sid);
+ if (tevent_req_nomem(subreq, req)) {
+ return;
+ }
+ tevent_req_set_callback(subreq, wb_gettoken_gotgroups, req);
+}
+
+static void wb_gettoken_gotgroups(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct wb_gettoken_state *state = tevent_req_data(
+ req, struct wb_gettoken_state);
+ uint32_t i, num_groups;
+ struct dom_sid *groups;
+ struct winbindd_domain *domain;
+ NTSTATUS status;
+ struct dom_sid_buf buf;
+
+ status = wb_lookupusergroups_recv(subreq, state, &num_groups, &groups);
+ TALLOC_FREE(subreq);
+ if (!NT_STATUS_IS_OK(status)) {
+ tevent_req_done(req);
+ return;
+ }
+
+ D_DEBUG("Received %"PRIu32" group(s).\n", num_groups);
+ for (i = 0; i < num_groups; i++) {
+ D_DEBUG("Adding SID %s.\n", dom_sid_str_buf(&groups[i], &buf));
+ status = add_sid_to_array_unique(
+ state, &groups[i], &state->sids, &state->num_sids);
+
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+ }
+
+ if (!state->expand_local_aliases) {
+ D_DEBUG("Done. Not asked to expand local aliases.\n");
+ tevent_req_done(req);
+ return;
+ }
+
+ /*
+ * Expand our domain's aliases
+ */
+ domain = find_domain_from_sid_noinit(get_global_sam_sid());
+ if (domain == NULL) {
+ tevent_req_nterror(req, NT_STATUS_INTERNAL_ERROR);
+ return;
+ }
+
+ D_DEBUG("Expand domain's aliases for %"PRIu32" SID(s).\n",
+ state->num_sids);
+ subreq = wb_lookupuseraliases_send(state, state->ev, domain,
+ state->num_sids, state->sids);
+ if (tevent_req_nomem(subreq, req)) {
+ return;
+ }
+ tevent_req_set_callback(subreq, wb_gettoken_gotlocalgroups, req);
+}
+
+static void wb_gettoken_gotlocalgroups(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct wb_gettoken_state *state = tevent_req_data(
+ req, struct wb_gettoken_state);
+ uint32_t num_rids;
+ uint32_t *rids;
+ struct winbindd_domain *domain;
+ NTSTATUS status;
+
+ status = wb_lookupuseraliases_recv(subreq, state, &num_rids, &rids);
+ TALLOC_FREE(subreq);
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+
+ D_DEBUG("Got %"PRIu32" RID(s).\n", num_rids);
+ status = wb_add_rids_to_sids(state, &state->num_sids, &state->sids,
+ get_global_sam_sid(), num_rids, rids);
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+ TALLOC_FREE(rids);
+
+ /*
+ * Now expand the builtin groups
+ */
+
+ D_DEBUG("Expand the builtin groups for %"PRIu32" SID(s).\n",
+ state->num_sids);
+ domain = find_domain_from_sid(&global_sid_Builtin);
+ if (domain == NULL) {
+ tevent_req_nterror(req, NT_STATUS_INTERNAL_ERROR);
+ return;
+ }
+
+ subreq = wb_lookupuseraliases_send(state, state->ev, domain,
+ state->num_sids, state->sids);
+ if (tevent_req_nomem(subreq, req)) {
+ return;
+ }
+ tevent_req_set_callback(subreq, wb_gettoken_gotbuiltins, req);
+}
+
+static void wb_gettoken_gotbuiltins(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct wb_gettoken_state *state = tevent_req_data(
+ req, struct wb_gettoken_state);
+ uint32_t num_rids;
+ uint32_t *rids;
+ NTSTATUS status;
+
+ status = wb_lookupuseraliases_recv(subreq, state, &num_rids, &rids);
+ TALLOC_FREE(subreq);
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+ D_DEBUG("Got %"PRIu32" RID(s).\n", num_rids);
+ status = wb_add_rids_to_sids(state, &state->num_sids, &state->sids,
+ &global_sid_Builtin, num_rids, rids);
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+ tevent_req_done(req);
+}
+
+NTSTATUS wb_gettoken_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx,
+ uint32_t *num_sids, struct dom_sid **sids)
+{
+ struct wb_gettoken_state *state = tevent_req_data(
+ req, struct wb_gettoken_state);
+ NTSTATUS status;
+ uint32_t i;
+
+ if (tevent_req_is_nterror(req, &status)) {
+ return status;
+ }
+ *num_sids = state->num_sids;
+ D_INFO("WB command gettoken end.\nReceived %"PRIu32" SID(s).\n",
+ state->num_sids);
+
+ if (CHECK_DEBUGLVL(DBGLVL_INFO)) {
+ for (i = 0; i < state->num_sids; i++) {
+ struct dom_sid_buf sidbuf;
+ D_INFO("%"PRIu32": %s\n",
+ i,
+ dom_sid_str_buf(&state->sids[i],
+ &sidbuf));
+ }
+ }
+
+ *sids = talloc_move(mem_ctx, &state->sids);
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS wb_add_rids_to_sids(TALLOC_CTX *mem_ctx,
+ uint32_t *pnum_sids,
+ struct dom_sid **psids,
+ const struct dom_sid *domain_sid,
+ uint32_t num_rids, uint32_t *rids)
+{
+ uint32_t i;
+
+ D_DEBUG("%"PRIu32" SID(s) will be uniquely added to the SID array.\n"
+ "Before the addition the array has %"PRIu32" SID(s).\n",
+ num_rids, *pnum_sids);
+
+ for (i = 0; i < num_rids; i++) {
+ NTSTATUS status;
+ struct dom_sid sid;
+
+ sid_compose(&sid, domain_sid, rids[i]);
+ status = add_sid_to_array_unique(
+ mem_ctx, &sid, psids, pnum_sids);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ }
+ D_DEBUG("After the addition the array has %"PRIu32" SID(s).\n",
+ *pnum_sids);
+ return NT_STATUS_OK;
+}
diff --git a/source3/winbindd/wb_group_members.c b/source3/winbindd/wb_group_members.c
new file mode 100644
index 0000000..3fe7357
--- /dev/null
+++ b/source3/winbindd/wb_group_members.c
@@ -0,0 +1,489 @@
+/*
+ Unix SMB/CIFS implementation.
+ async lookupgroupmembers
+ Copyright (C) Volker Lendecke 2009
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "winbindd.h"
+#include "librpc/gen_ndr/ndr_winbind_c.h"
+#include "../librpc/gen_ndr/ndr_security.h"
+#include "../libcli/security/security.h"
+#include "lib/util/util_tdb.h"
+#include "lib/dbwrap/dbwrap.h"
+#include "lib/dbwrap/dbwrap_rbt.h"
+
+/*
+ * We have 3 sets of routines here:
+ *
+ * wb_lookupgroupmem is the low-level one-group routine
+ *
+ * wb_groups_members walks a list of groups
+ *
+ * wb_group_members finally is the high-level routine expanding groups
+ * recursively
+ */
+
+/*
+ * TODO: fill_grent_mem_domusers must be re-added
+ */
+
+/*
+ * Look up members of a single group. Essentially a wrapper around the
+ * lookup_groupmem winbindd_methods routine.
+ */
+
+struct wb_lookupgroupmem_state {
+ struct dom_sid sid;
+ struct wbint_Principals members;
+};
+
+static void wb_lookupgroupmem_done(struct tevent_req *subreq);
+
+static struct tevent_req *wb_lookupgroupmem_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ const struct dom_sid *group_sid,
+ enum lsa_SidType type)
+{
+ struct tevent_req *req, *subreq;
+ struct wb_lookupgroupmem_state *state;
+ struct winbindd_domain *domain;
+
+ req = tevent_req_create(mem_ctx, &state,
+ struct wb_lookupgroupmem_state);
+ if (req == NULL) {
+ return NULL;
+ }
+ sid_copy(&state->sid, group_sid);
+
+ domain = find_domain_from_sid_noinit(group_sid);
+ if (domain == NULL) {
+ tevent_req_nterror(req, NT_STATUS_NO_SUCH_GROUP);
+ return tevent_req_post(req, ev);
+ }
+
+ subreq = dcerpc_wbint_LookupGroupMembers_send(
+ state, ev, dom_child_handle(domain), &state->sid, type,
+ &state->members);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, wb_lookupgroupmem_done, req);
+ return req;
+}
+
+static void wb_lookupgroupmem_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct wb_lookupgroupmem_state *state = tevent_req_data(
+ req, struct wb_lookupgroupmem_state);
+ NTSTATUS status, result;
+
+ status = dcerpc_wbint_LookupGroupMembers_recv(subreq, state, &result);
+ TALLOC_FREE(subreq);
+ if (any_nt_status_not_ok(status, result, &status)) {
+ tevent_req_nterror(req, status);
+ return;
+ }
+ tevent_req_done(req);
+}
+
+static NTSTATUS wb_lookupgroupmem_recv(struct tevent_req *req,
+ TALLOC_CTX *mem_ctx,
+ uint32_t *num_members,
+ struct wbint_Principal **members)
+{
+ struct wb_lookupgroupmem_state *state = tevent_req_data(
+ req, struct wb_lookupgroupmem_state);
+ NTSTATUS status;
+
+ if (tevent_req_is_nterror(req, &status)) {
+ return status;
+ }
+
+ *num_members = state->members.num_principals;
+ *members = talloc_move(mem_ctx, &state->members.principals);
+ return NT_STATUS_OK;
+}
+
+/*
+ * Same as wb_lookupgroupmem for a list of groups
+ */
+
+struct wb_groups_members_state {
+ struct tevent_context *ev;
+ struct wbint_Principal *groups;
+ uint32_t num_groups;
+ uint32_t next_group;
+ struct wbint_Principal *all_members;
+};
+
+static NTSTATUS wb_groups_members_next_subreq(
+ struct wb_groups_members_state *state,
+ TALLOC_CTX *mem_ctx, struct tevent_req **psubreq);
+static void wb_groups_members_done(struct tevent_req *subreq);
+
+static struct tevent_req *wb_groups_members_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ uint32_t num_groups,
+ struct wbint_Principal *groups)
+{
+ struct tevent_req *req, *subreq = NULL;
+ struct wb_groups_members_state *state;
+ NTSTATUS status;
+
+ req = tevent_req_create(mem_ctx, &state,
+ struct wb_groups_members_state);
+ if (req == NULL) {
+ return NULL;
+ }
+ state->ev = ev;
+ state->groups = groups;
+ state->num_groups = num_groups;
+ state->next_group = 0;
+ state->all_members = NULL;
+
+ D_DEBUG("Looking up %"PRIu32" group(s).\n", num_groups);
+ status = wb_groups_members_next_subreq(state, state, &subreq);
+ if (tevent_req_nterror(req, status)) {
+ return tevent_req_post(req, ev);
+ }
+ if (subreq == NULL) {
+ tevent_req_done(req);
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, wb_groups_members_done, req);
+ return req;
+}
+
+static NTSTATUS wb_groups_members_next_subreq(
+ struct wb_groups_members_state *state,
+ TALLOC_CTX *mem_ctx, struct tevent_req **psubreq)
+{
+ struct tevent_req *subreq;
+ struct wbint_Principal *g;
+
+ if (state->next_group >= state->num_groups) {
+ *psubreq = NULL;
+ return NT_STATUS_OK;
+ }
+
+ g = &state->groups[state->next_group];
+ state->next_group += 1;
+
+ subreq = wb_lookupgroupmem_send(mem_ctx, state->ev, &g->sid, g->type);
+ if (subreq == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ *psubreq = subreq;
+ return NT_STATUS_OK;
+}
+
+static void wb_groups_members_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct wb_groups_members_state *state = tevent_req_data(
+ req, struct wb_groups_members_state);
+ uint32_t i, num_all_members;
+ uint32_t num_members = 0;
+ struct wbint_Principal *members = NULL;
+ NTSTATUS status;
+
+ status = wb_lookupgroupmem_recv(subreq, state, &num_members, &members);
+ TALLOC_FREE(subreq);
+
+ /*
+ * In this error handling here we might have to be a bit more generous
+ * and just continue if an error occurred.
+ */
+
+ if (!NT_STATUS_IS_OK(status)) {
+ if (!NT_STATUS_EQUAL(
+ status, NT_STATUS_TRUSTED_DOMAIN_FAILURE)) {
+ tevent_req_nterror(req, status);
+ return;
+ }
+ num_members = 0;
+ }
+
+ num_all_members = talloc_array_length(state->all_members);
+
+ D_DEBUG("Adding %"PRIu32" new member(s) to existing %"PRIu32" member(s)\n",
+ num_members,
+ num_all_members);
+
+ state->all_members = talloc_realloc(
+ state, state->all_members, struct wbint_Principal,
+ num_all_members + num_members);
+ if ((num_all_members + num_members != 0)
+ && tevent_req_nomem(state->all_members, req)) {
+ return;
+ }
+ for (i=0; i<num_members; i++) {
+ struct wbint_Principal *src, *dst;
+ src = &members[i];
+ dst = &state->all_members[num_all_members + i];
+ sid_copy(&dst->sid, &src->sid);
+ dst->name = talloc_move(state->all_members, &src->name);
+ dst->type = src->type;
+ }
+ TALLOC_FREE(members);
+
+ status = wb_groups_members_next_subreq(state, state, &subreq);
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+ if (subreq == NULL) {
+ tevent_req_done(req);
+ return;
+ }
+ tevent_req_set_callback(subreq, wb_groups_members_done, req);
+}
+
+static NTSTATUS wb_groups_members_recv(struct tevent_req *req,
+ TALLOC_CTX *mem_ctx,
+ uint32_t *num_members,
+ struct wbint_Principal **members)
+{
+ struct wb_groups_members_state *state = tevent_req_data(
+ req, struct wb_groups_members_state);
+ NTSTATUS status;
+
+ if (tevent_req_is_nterror(req, &status)) {
+ return status;
+ }
+ *num_members = talloc_array_length(state->all_members);
+ *members = talloc_move(mem_ctx, &state->all_members);
+ return NT_STATUS_OK;
+}
+
+
+/*
+ * This is the routine expanding a list of groups up to a certain level. We
+ * collect the users in a rbt database: We have to add them without duplicates,
+ * and the db is indexed by SID.
+ */
+
+struct wb_group_members_state {
+ struct tevent_context *ev;
+ int depth;
+ struct db_context *users;
+ struct wbint_Principal *groups;
+};
+
+static NTSTATUS wb_group_members_next_subreq(
+ struct wb_group_members_state *state,
+ TALLOC_CTX *mem_ctx, struct tevent_req **psubreq);
+static void wb_group_members_done(struct tevent_req *subreq);
+
+struct tevent_req *wb_group_members_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ const struct dom_sid *sid,
+ uint32_t num_sids,
+ enum lsa_SidType *type,
+ int max_depth)
+{
+ struct tevent_req *req, *subreq = NULL;
+ struct wb_group_members_state *state;
+ NTSTATUS status;
+ struct dom_sid_buf buf;
+ uint32_t i;
+
+ req = tevent_req_create(mem_ctx, &state,
+ struct wb_group_members_state);
+ if (req == NULL) {
+ return NULL;
+ }
+ D_INFO("WB command group_members start (max_depth=%d).\n", max_depth);
+ for (i = 0; i < num_sids; i++) {
+ D_INFO("Looking up members of group SID %s with SID type %d\n",
+ dom_sid_str_buf(&sid[i], &buf),
+ type[i]);
+ }
+
+ state->ev = ev;
+ state->depth = max_depth;
+ state->users = db_open_rbt(state);
+ if (tevent_req_nomem(state->users, req)) {
+ return tevent_req_post(req, ev);
+ }
+
+ state->groups = talloc_array(state, struct wbint_Principal, num_sids);
+ if (tevent_req_nomem(state->groups, req)) {
+ return tevent_req_post(req, ev);
+ }
+
+ for (i = 0; i < num_sids; i++) {
+ state->groups[i].name = NULL;
+ sid_copy(&state->groups[i].sid, &sid[i]);
+ state->groups[i].type = type[i];
+ }
+
+ status = wb_group_members_next_subreq(state, state, &subreq);
+ if (tevent_req_nterror(req, status)) {
+ return tevent_req_post(req, ev);
+ }
+ if (subreq == NULL) {
+ tevent_req_done(req);
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, wb_group_members_done, req);
+ return req;
+}
+
+static NTSTATUS wb_group_members_next_subreq(
+ struct wb_group_members_state *state,
+ TALLOC_CTX *mem_ctx, struct tevent_req **psubreq)
+{
+ struct tevent_req *subreq;
+
+ if ((talloc_array_length(state->groups) == 0)
+ || (state->depth <= 0)) {
+ *psubreq = NULL;
+ D_DEBUG("Finished. The depth is %d.\n", state->depth);
+ return NT_STATUS_OK;
+ }
+ state->depth -= 1;
+
+ D_DEBUG("The depth is decremented to %d.\n", state->depth);
+ subreq = wb_groups_members_send(
+ mem_ctx, state->ev, talloc_array_length(state->groups),
+ state->groups);
+ if (subreq == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ *psubreq = subreq;
+ return NT_STATUS_OK;
+}
+
+NTSTATUS add_member_to_db(struct db_context *db, struct dom_sid *sid,
+ const char *name)
+{
+ size_t len = ndr_size_dom_sid(sid, 0);
+ uint8_t sidbuf[len];
+ TDB_DATA key = { .dptr = sidbuf, .dsize = sizeof(sidbuf) };
+ NTSTATUS status;
+
+ sid_linearize(sidbuf, sizeof(sidbuf), sid);
+
+ status = dbwrap_store(db, key, string_term_tdb_data(name), 0);
+ return status;
+}
+
+static void wb_group_members_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct wb_group_members_state *state = tevent_req_data(
+ req, struct wb_group_members_state);
+ uint32_t i, num_groups, new_groups;
+ uint32_t num_members = 0;
+ struct wbint_Principal *members = NULL;
+ NTSTATUS status;
+
+ status = wb_groups_members_recv(subreq, state, &num_members, &members);
+ TALLOC_FREE(subreq);
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+
+ new_groups = 0;
+ for (i=0; i<num_members; i++) {
+ switch (members[i].type) {
+ case SID_NAME_DOM_GRP:
+ case SID_NAME_ALIAS:
+ case SID_NAME_WKN_GRP:
+ new_groups += 1;
+ break;
+ default:
+ /* Ignore everything else */
+ break;
+ }
+ }
+
+ num_groups = 0;
+ TALLOC_FREE(state->groups);
+ state->groups = talloc_array(state, struct wbint_Principal,
+ new_groups);
+
+ /*
+ * Collect the users into state->users and the groups into
+ * state->groups for the next iteration.
+ */
+
+ for (i=0; i<num_members; i++) {
+ switch (members[i].type) {
+ case SID_NAME_USER:
+ case SID_NAME_COMPUTER: {
+ /*
+ * Add a copy of members[i] to state->users
+ */
+ status = add_member_to_db(state->users, &members[i].sid,
+ members[i].name);
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+
+ break;
+ }
+ case SID_NAME_DOM_GRP:
+ case SID_NAME_ALIAS:
+ case SID_NAME_WKN_GRP: {
+ struct wbint_Principal *g;
+ /*
+ * Save members[i] for the next round
+ */
+ g = &state->groups[num_groups];
+ sid_copy(&g->sid, &members[i].sid);
+ g->name = talloc_move(state->groups, &members[i].name);
+ g->type = members[i].type;
+ num_groups += 1;
+ break;
+ }
+ default:
+ /* Ignore everything else */
+ break;
+ }
+ }
+
+ status = wb_group_members_next_subreq(state, state, &subreq);
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+ if (subreq == NULL) {
+ tevent_req_done(req);
+ return;
+ }
+ tevent_req_set_callback(subreq, wb_group_members_done, req);
+}
+
+NTSTATUS wb_group_members_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx,
+ struct db_context **members)
+{
+ struct wb_group_members_state *state = tevent_req_data(
+ req, struct wb_group_members_state);
+ NTSTATUS status;
+
+ D_INFO("WB command group_members end.\n");
+ if (tevent_req_is_nterror(req, &status)) {
+ D_WARNING("Failed with %s.\n", nt_errstr(status));
+ return status;
+ }
+ *members = talloc_move(mem_ctx, &state->users);
+ return NT_STATUS_OK;
+}
diff --git a/source3/winbindd/wb_lookupname.c b/source3/winbindd/wb_lookupname.c
new file mode 100644
index 0000000..12dbfbe
--- /dev/null
+++ b/source3/winbindd/wb_lookupname.c
@@ -0,0 +1,123 @@
+/*
+ Unix SMB/CIFS implementation.
+ async lookupname
+ Copyright (C) Volker Lendecke 2009
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "winbindd.h"
+#include "librpc/gen_ndr/ndr_winbind_c.h"
+#include "../libcli/security/security.h"
+
+struct wb_lookupname_state {
+ struct tevent_context *ev;
+ const char *dom_name;
+ const char *name;
+ uint32_t flags;
+ struct dom_sid sid;
+ enum lsa_SidType type;
+};
+
+static void wb_lookupname_done(struct tevent_req *subreq);
+
+struct tevent_req *wb_lookupname_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ const char *namespace,
+ const char *dom_name,
+ const char *name,
+ uint32_t flags)
+{
+ struct tevent_req *req, *subreq;
+ struct wb_lookupname_state *state;
+ struct winbindd_domain *domain;
+
+ req = tevent_req_create(mem_ctx, &state, struct wb_lookupname_state);
+ if (req == NULL) {
+ return NULL;
+ }
+
+ D_INFO("WB command lookupname start.\n"
+ "Search namespace '%s' and domain '%s' for name '%s'.\n",
+ namespace, dom_name, name);
+ state->ev = ev;
+ state->flags = flags;
+
+ /*
+ * Uppercase domain and name so that we become cache-friendly
+ */
+ state->dom_name = talloc_strdup_upper(state, dom_name);
+ if (tevent_req_nomem(state->dom_name, req)) {
+ return tevent_req_post(req, ev);
+ }
+ state->name = talloc_strdup_upper(state, name);
+ if (tevent_req_nomem(state->name, req)) {
+ return tevent_req_post(req, ev);
+ }
+
+ domain = find_lookup_domain_from_name(namespace);
+ if (domain == NULL) {
+ D_WARNING("Could not find domain for %s\n", namespace);
+ tevent_req_nterror(req, NT_STATUS_NONE_MAPPED);
+ return tevent_req_post(req, ev);
+ }
+
+ subreq = dcerpc_wbint_LookupName_send(
+ state, ev, dom_child_handle(domain),
+ state->dom_name, state->name,
+ flags, &state->type, &state->sid);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, wb_lookupname_done, req);
+ return req;
+}
+
+static void wb_lookupname_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct wb_lookupname_state *state = tevent_req_data(
+ req, struct wb_lookupname_state);
+ NTSTATUS status, result;
+
+ status = dcerpc_wbint_LookupName_recv(subreq, state, &result);
+ TALLOC_FREE(subreq);
+ if (any_nt_status_not_ok(status, result, &status)) {
+ tevent_req_nterror(req, status);
+ return;
+ }
+ tevent_req_done(req);
+}
+
+NTSTATUS wb_lookupname_recv(struct tevent_req *req, struct dom_sid *sid,
+ enum lsa_SidType *type)
+{
+ struct wb_lookupname_state *state = tevent_req_data(
+ req, struct wb_lookupname_state);
+ NTSTATUS status;
+ struct dom_sid_buf buf;
+
+ if (tevent_req_is_nterror(req, &status)) {
+ return status;
+ }
+ sid_copy(sid, &state->sid);
+ *type = state->type;
+ D_INFO("WB command lookupname end.\n"
+ "Found SID %s with SID type %d.\n",
+ dom_sid_str_buf(sid, &buf),
+ *type);
+ return NT_STATUS_OK;
+}
diff --git a/source3/winbindd/wb_lookupsid.c b/source3/winbindd/wb_lookupsid.c
new file mode 100644
index 0000000..31820f9
--- /dev/null
+++ b/source3/winbindd/wb_lookupsid.c
@@ -0,0 +1,114 @@
+/*
+ Unix SMB/CIFS implementation.
+ async lookupsid
+ Copyright (C) Volker Lendecke 2009
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "winbindd.h"
+#include "librpc/gen_ndr/ndr_winbind_c.h"
+#include "../libcli/security/security.h"
+
+struct wb_lookupsid_state {
+ struct tevent_context *ev;
+ struct winbindd_domain *lookup_domain;
+ struct dom_sid sid;
+ enum lsa_SidType type;
+ const char *domname;
+ const char *name;
+};
+
+static void wb_lookupsid_done(struct tevent_req *subreq);
+
+struct tevent_req *wb_lookupsid_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ const struct dom_sid *sid)
+{
+ struct tevent_req *req, *subreq;
+ struct wb_lookupsid_state *state;
+ struct dom_sid_buf buf;
+
+ req = tevent_req_create(mem_ctx, &state, struct wb_lookupsid_state);
+ if (req == NULL) {
+ return NULL;
+ }
+
+ D_INFO("WB command lookupsid start.\n");
+ sid_copy(&state->sid, sid);
+ state->ev = ev;
+
+ state->lookup_domain = find_lookup_domain_from_sid(sid);
+ if (state->lookup_domain == NULL) {
+ D_WARNING("Could not find domain for sid %s\n",
+ dom_sid_str_buf(sid, &buf));
+ tevent_req_nterror(req, NT_STATUS_NONE_MAPPED);
+ return tevent_req_post(req, ev);
+ }
+
+ D_DEBUG("Looking up SID %s in domain %s.\n",
+ dom_sid_str_buf(&state->sid, &buf),
+ state->lookup_domain->name);
+ subreq = dcerpc_wbint_LookupSid_send(
+ state, ev, dom_child_handle(state->lookup_domain),
+ &state->sid, &state->type, &state->domname, &state->name);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, wb_lookupsid_done, req);
+ return req;
+}
+
+static void wb_lookupsid_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct wb_lookupsid_state *state = tevent_req_data(
+ req, struct wb_lookupsid_state);
+ NTSTATUS status, result;
+
+ status = dcerpc_wbint_LookupSid_recv(subreq, state, &result);
+ TALLOC_FREE(subreq);
+ if (any_nt_status_not_ok(status, result, &status)) {
+ tevent_req_nterror(req, status);
+ return;
+ }
+ tevent_req_done(req);
+}
+
+NTSTATUS wb_lookupsid_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx,
+ enum lsa_SidType *type, const char **domain,
+ const char **name)
+{
+ struct wb_lookupsid_state *state = tevent_req_data(
+ req, struct wb_lookupsid_state);
+ NTSTATUS status;
+ struct dom_sid_buf buf;
+
+ D_INFO("WB command lookupsid end.\n");
+ if (tevent_req_is_nterror(req, &status)) {
+ D_WARNING("Failed with %s.\n", nt_errstr(status));
+ return status;
+ }
+ *type = state->type;
+ *domain = talloc_move(mem_ctx, &state->domname);
+ *name = talloc_move(mem_ctx, &state->name);
+ D_INFO("SID %s has name '%s' with type '%d' in domain '%s'.\n",
+ dom_sid_str_buf(&state->sid, &buf),
+ *name,
+ *type,
+ *domain);
+ return NT_STATUS_OK;
+}
diff --git a/source3/winbindd/wb_lookupsids.c b/source3/winbindd/wb_lookupsids.c
new file mode 100644
index 0000000..828e79e
--- /dev/null
+++ b/source3/winbindd/wb_lookupsids.c
@@ -0,0 +1,699 @@
+/*
+ Unix SMB/CIFS implementation.
+ async lookupsids
+ Copyright (C) Volker Lendecke 2011
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "winbindd.h"
+#include "lib/util_unixsids.h"
+#include "librpc/gen_ndr/ndr_winbind_c.h"
+#include "../libcli/security/security.h"
+#include "passdb/machine_sid.h"
+#include "lsa.h"
+
+struct wb_lookupsids_domain {
+ struct winbindd_domain *domain;
+
+ /*
+ * Array of sids to be passed into wbint_LookupSids. Preallocated with
+ * num_sids.
+ */
+ struct lsa_SidArray sids;
+
+ /*
+ * Indexes into wb_lookupsids_state->sids and thus
+ * wb_lookupsids_state->res_names. Preallocated with num_sids.
+ */
+ uint32_t *sid_indexes;
+};
+
+struct wb_translated_name {
+ const char *domain_name;
+ const char *name;
+ enum lsa_SidType type;
+};
+
+static struct wb_lookupsids_domain *wb_lookupsids_get_domain(
+ const struct dom_sid *sid, TALLOC_CTX *mem_ctx,
+ struct wb_lookupsids_domain **domains, uint32_t num_sids);
+
+struct wb_lookupsids_state {
+ struct tevent_context *ev;
+
+ /*
+ * SIDs passed in
+ */
+ struct dom_sid *sids;
+ uint32_t num_sids;
+
+ /*
+ * The domains we're using for bulk lookup via wbint_LookupRids or
+ * wbint_LookupSids. We expect very few domains, so we do a
+ * talloc_realloc and rely on talloc_array_length.
+ */
+ struct wb_lookupsids_domain *domains;
+ uint32_t domains_done;
+
+ /*
+ * These SIDs are looked up individually via
+ * wbint_LookupSid. Preallocated with num_sids.
+ */
+ uint32_t *single_sids;
+ uint32_t num_single_sids;
+ uint32_t single_sids_done;
+
+ /*
+ * Intermediate store for wbint_LookupRids to passdb. These are
+ * spliced into res_domains/res_names in wb_lookupsids_move_name.
+ */
+ struct wbint_RidArray rids;
+ const char *domain_name;
+ struct wbint_Principals rid_names;
+
+ /*
+ * Intermediate results for wbint_LookupSids. These results are
+ * spliced into res_domains/res_names in wb_lookupsids_move_name.
+ */
+ struct lsa_RefDomainList tmp_domains;
+ struct lsa_TransNameArray tmp_names;
+
+ /*
+ * Results
+ */
+ struct lsa_RefDomainList *res_domains;
+ /*
+ * Indexed as "sids" in this structure
+ */
+ struct lsa_TransNameArray *res_names;
+};
+
+static bool wb_lookupsids_next(struct tevent_req *req,
+ struct wb_lookupsids_state *state);
+static void wb_lookupsids_single_done(struct tevent_req *subreq);
+static void wb_lookupsids_lookuprids_done(struct tevent_req *subreq);
+static void wb_lookupsids_done(struct tevent_req *subreq);
+
+struct tevent_req *wb_lookupsids_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct dom_sid *sids,
+ uint32_t num_sids)
+{
+ struct tevent_req *req;
+ struct wb_lookupsids_state *state;
+ uint32_t i;
+
+ req = tevent_req_create(mem_ctx, &state, struct wb_lookupsids_state);
+ if (req == NULL) {
+ return NULL;
+ }
+
+ D_INFO("WB command lookupsids start.\nLooking up %"PRIu32" SID(s)\n",
+ num_sids);
+ if (CHECK_DEBUGLVL(DBGLVL_INFO)) {
+ for (i = 0; i < num_sids; i++) {
+ struct dom_sid_buf buf;
+ D_INFO("%"PRIu32": %s\n",
+ i, dom_sid_str_buf(&sids[i], &buf));
+ }
+ }
+
+ state->ev = ev;
+ state->sids = sids;
+ state->num_sids = num_sids;
+
+ state->single_sids = talloc_zero_array(state, uint32_t, num_sids);
+ if (tevent_req_nomem(state->single_sids, req)) {
+ return tevent_req_post(req, ev);
+ }
+
+ state->res_domains = talloc_zero(state, struct lsa_RefDomainList);
+ if (tevent_req_nomem(state->res_domains, req)) {
+ return tevent_req_post(req, ev);
+ }
+ state->res_domains->domains = talloc_zero_array(
+ state->res_domains, struct lsa_DomainInfo, num_sids);
+ if (tevent_req_nomem(state->res_domains->domains, req)) {
+ return tevent_req_post(req, ev);
+ }
+
+ state->res_names = talloc_zero(state, struct lsa_TransNameArray);
+ if (tevent_req_nomem(state->res_names, req)) {
+ return tevent_req_post(req, ev);
+ }
+ state->res_names->names = talloc_zero_array(
+ state->res_names, struct lsa_TranslatedName, num_sids);
+ if (tevent_req_nomem(state->res_names->names, req)) {
+ return tevent_req_post(req, ev);
+ }
+
+ if (num_sids == 0) {
+ tevent_req_done(req);
+ return tevent_req_post(req, ev);
+ }
+
+ for (i=0; i<num_sids; i++) {
+ struct wb_lookupsids_domain *d;
+
+ d = wb_lookupsids_get_domain(&sids[i], state, &state->domains,
+ num_sids);
+ if (d != NULL) {
+ d->sids.sids[d->sids.num_sids].sid = &sids[i];
+ d->sid_indexes[d->sids.num_sids] = i;
+ d->sids.num_sids += 1;
+ } else {
+ state->single_sids[state->num_single_sids] = i;
+ state->num_single_sids += 1;
+ }
+ }
+
+ if (!wb_lookupsids_next(req, state)) {
+ return tevent_req_post(req, ev);
+ }
+ return req;
+}
+
+static bool wb_lookupsids_next(struct tevent_req *req,
+ struct wb_lookupsids_state *state)
+{
+ struct tevent_req *subreq;
+
+ if (state->domains_done < talloc_array_length(state->domains)) {
+ struct wb_lookupsids_domain *d;
+ uint32_t i;
+
+ d = &state->domains[state->domains_done];
+
+ if (d->domain->internal) {
+ /*
+ * This is only our local SAM,
+ * see wb_lookupsids_bulk() and
+ * wb_lookupsids_get_domain().
+ */
+ state->rids.num_rids = d->sids.num_sids;
+ state->rids.rids = talloc_array(state, uint32_t,
+ state->rids.num_rids);
+ if (tevent_req_nomem(state->rids.rids, req)) {
+ return false;
+ }
+ for (i=0; i<state->rids.num_rids; i++) {
+ sid_peek_rid(d->sids.sids[i].sid,
+ &state->rids.rids[i]);
+ }
+ subreq = dcerpc_wbint_LookupRids_send(
+ state, state->ev, dom_child_handle(d->domain),
+ &d->domain->sid, &state->rids, &state->domain_name,
+ &state->rid_names);
+ if (tevent_req_nomem(subreq, req)) {
+ return false;
+ }
+ tevent_req_set_callback(
+ subreq, wb_lookupsids_lookuprids_done, req);
+ return true;
+ }
+
+ subreq = dcerpc_wbint_LookupSids_send(
+ state, state->ev, dom_child_handle(d->domain),
+ &d->sids, &state->tmp_domains, &state->tmp_names);
+ if (tevent_req_nomem(subreq, req)) {
+ return false;
+ }
+ tevent_req_set_callback(subreq, wb_lookupsids_done, req);
+ return true;
+ }
+
+ if (state->single_sids_done < state->num_single_sids) {
+ uint32_t sid_idx;
+ const struct dom_sid *sid;
+
+ sid_idx = state->single_sids[state->single_sids_done];
+ sid = &state->sids[sid_idx];
+
+ subreq = wb_lookupsid_send(state, state->ev, sid);
+ if (tevent_req_nomem(subreq, req)) {
+ return false;
+ }
+ tevent_req_set_callback(subreq, wb_lookupsids_single_done,
+ req);
+ return true;
+ }
+
+ tevent_req_done(req);
+ return false;
+}
+
+/*
+ * Decide whether to do bulk lookupsids. We have optimizations for
+ * passdb via lookuprids and to remote DCs via lookupsids.
+ */
+
+static bool wb_lookupsids_bulk(const struct dom_sid *sid)
+{
+ struct dom_sid_buf sidbuf;
+
+ if (sid->num_auths != 5) {
+ /*
+ * Only do "S-1-5-21-x-y-z-rid" domains via bulk
+ * lookup
+ */
+ DBG_DEBUG("No bulk setup for SID %s with %"PRIi8" subauths\n",
+ dom_sid_str_buf(sid, &sidbuf),
+ sid->num_auths);
+ return false;
+ }
+
+ if (sid_check_is_in_our_sam(sid)) {
+ /*
+ * Passdb lookup via lookuprids
+ */
+ DBG_DEBUG("%s is in our domain\n",
+ dom_sid_str_buf(sid, &sidbuf));
+ return true;
+ }
+
+ if (IS_DC) {
+ /*
+ * Bulk lookups to trusted DCs
+ */
+ return (find_domain_from_sid_noinit(sid) != NULL);
+ }
+
+ if (lp_server_role() != ROLE_DOMAIN_MEMBER) {
+ /*
+ * Don't do bulk lookups as standalone, the only bulk
+ * lookup left is for domain members.
+ */
+ return false;
+ }
+
+ if (sid_check_is_in_unix_groups(sid) ||
+ sid_check_is_unix_groups(sid) ||
+ sid_check_is_in_unix_users(sid) ||
+ sid_check_is_unix_users(sid) ||
+ sid_check_is_in_builtin(sid) ||
+ sid_check_is_builtin(sid) ||
+ sid_check_is_wellknown_domain(sid, NULL) ||
+ sid_check_is_in_wellknown_domain(sid))
+ {
+ /*
+ * These are locally done piece by piece anyway, no
+ * need for bulk optimizations.
+ */
+ return false;
+ }
+
+ /*
+ * All other SIDs are sent to the DC we're connected to as
+ * member via a single lsa_lookupsids call.
+ */
+ return true;
+}
+
+static struct wb_lookupsids_domain *wb_lookupsids_get_domain(
+ const struct dom_sid *sid, TALLOC_CTX *mem_ctx,
+ struct wb_lookupsids_domain **pdomains, uint32_t num_sids)
+{
+ struct wb_lookupsids_domain *domains, *domain;
+ struct winbindd_domain *wb_domain;
+ uint32_t i, num_domains;
+
+ if (!wb_lookupsids_bulk(sid)) {
+ D_DEBUG("wb_lookupsids_bulk() is FALSE\n");
+ return NULL;
+ }
+ D_DEBUG("wb_lookupsids_bulk() is TRUE\n");
+
+ domains = *pdomains;
+ num_domains = talloc_array_length(domains);
+
+ wb_domain = find_lookup_domain_from_sid(sid);
+ if (wb_domain == NULL) {
+ return NULL;
+ }
+
+ D_DEBUG("Searching %"PRIu32" domain(s) for domain '%s'\n",
+ num_domains, wb_domain->name);
+ for (i=0; i<num_domains; i++) {
+ if (domains[i].domain != wb_domain) {
+ continue;
+ }
+
+ if (!domains[i].domain->internal) {
+ /*
+ * If it's not our local sam,
+ * we can re-use the domain without
+ * checking the sid.
+ *
+ * Note the wb_lookupsids_bulk() above
+ * already caught special SIDs,
+ * e.g. the unix and builtin domains.
+ */
+ return &domains[i];
+ }
+
+ if (dom_sid_compare_domain(sid, &domains[i].domain->sid) == 0) {
+ /*
+ * If it's out local sam we can also use it.
+ */
+ return &domains[i];
+ }
+
+ /*
+ * I'm not sure if this can be triggered,
+ * as wb_lookupsids_bulk() should also catch this,
+ * but we need to make sure that we don't use
+ * wbint_LookupRids() without a SID match.
+ */
+ return NULL;
+ }
+
+ domains = talloc_realloc(
+ mem_ctx, domains, struct wb_lookupsids_domain, num_domains+1);
+ if (domains == NULL) {
+ return NULL;
+ }
+ *pdomains = domains;
+
+ domain = &domains[num_domains];
+ domain->domain = wb_domain;
+
+ domain->sids.sids = talloc_zero_array(domains, struct lsa_SidPtr, num_sids);
+ if (domains->sids.sids == NULL) {
+ goto fail;
+ }
+ domain->sids.num_sids = 0;
+
+ domain->sid_indexes = talloc_zero_array(domains, uint32_t, num_sids);
+ if (domain->sid_indexes == NULL) {
+ TALLOC_FREE(domain->sids.sids);
+ goto fail;
+ }
+ return domain;
+
+fail:
+ /*
+ * Realloc to the state it was in before
+ */
+ *pdomains = talloc_realloc(
+ mem_ctx, domains, struct wb_lookupsids_domain, num_domains);
+ return NULL;
+}
+
+static bool wb_lookupsids_find_dom_idx(struct lsa_DomainInfo *domain,
+ struct lsa_RefDomainList *list,
+ uint32_t *idx)
+{
+ uint32_t i;
+ struct lsa_DomainInfo *new_domain;
+
+ for (i=0; i<list->count; i++) {
+ if (dom_sid_equal(domain->sid, list->domains[i].sid)) {
+ *idx = i;
+ return true;
+ }
+ }
+
+ new_domain = &list->domains[list->count];
+
+ new_domain->name.string = talloc_strdup(
+ list->domains, domain->name.string);
+ if (new_domain->name.string == NULL) {
+ return false;
+ }
+
+ new_domain->sid = dom_sid_dup(list->domains, domain->sid);
+ if (new_domain->sid == NULL) {
+ return false;
+ }
+
+ *idx = list->count;
+ list->count += 1;
+ return true;
+}
+
+static bool wb_lookupsids_move_name(struct lsa_RefDomainList *src_domains,
+ struct lsa_TranslatedName *src_name,
+ struct lsa_RefDomainList *dst_domains,
+ struct lsa_TransNameArray *dst_names,
+ uint32_t dst_name_index)
+{
+ struct lsa_TranslatedName *dst_name;
+ struct lsa_DomainInfo *src_domain;
+ uint32_t src_domain_index;
+ uint32_t dst_domain_index = UINT32_MAX;
+ bool ok;
+
+ src_domain_index = src_name->sid_index;
+ if ((src_domain_index != UINT32_MAX) && (src_domains != NULL)) {
+ if (src_domain_index >= src_domains->count) {
+ return false;
+ }
+ src_domain = &src_domains->domains[src_domain_index];
+
+ ok = wb_lookupsids_find_dom_idx(src_domain,
+ dst_domains,
+ &dst_domain_index);
+ if (!ok) {
+ return false;
+ }
+ }
+
+ dst_name = &dst_names->names[dst_name_index];
+
+ dst_name->sid_type = src_name->sid_type;
+ dst_name->name.string = talloc_move(dst_names->names,
+ &src_name->name.string);
+ dst_name->sid_index = dst_domain_index;
+ dst_names->count += 1;
+
+ return true;
+}
+
+static void wb_lookupsids_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct wb_lookupsids_state *state = tevent_req_data(
+ req, struct wb_lookupsids_state);
+ struct wb_lookupsids_domain *d;
+ uint32_t i;
+
+ NTSTATUS status, result;
+
+ status = dcerpc_wbint_LookupSids_recv(subreq, state, &result);
+ TALLOC_FREE(subreq);
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+ if (NT_STATUS_LOOKUP_ERR(result)) {
+ tevent_req_nterror(req, result);
+ return;
+ }
+
+ /*
+ * Look at the individual states in the translated names.
+ */
+
+ d = &state->domains[state->domains_done];
+
+ for (i=0; i<state->tmp_names.count; i++) {
+ uint32_t res_sid_index = d->sid_indexes[i];
+
+ if (!wb_lookupsids_move_name(
+ &state->tmp_domains, &state->tmp_names.names[i],
+ state->res_domains, state->res_names,
+ res_sid_index)) {
+ tevent_req_oom(req);
+ return;
+ }
+ }
+ state->domains_done += 1;
+ wb_lookupsids_next(req, state);
+}
+
+static void wb_lookupsids_single_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct wb_lookupsids_state *state = tevent_req_data(
+ req, struct wb_lookupsids_state);
+ const char *domain_name = NULL;
+ const char *name = NULL;
+ enum lsa_SidType type = SID_NAME_UNKNOWN;
+ uint32_t res_sid_index;
+ uint32_t src_rid;
+
+ struct dom_sid src_domain_sid;
+ struct lsa_DomainInfo src_domain;
+ struct lsa_RefDomainList src_domains;
+ struct lsa_RefDomainList *psrc_domains = NULL;
+ struct lsa_TranslatedName src_name;
+
+ uint32_t domain_idx = UINT32_MAX;
+ NTSTATUS status;
+ bool ok;
+
+ status = wb_lookupsid_recv(subreq, talloc_tos(), &type,
+ &domain_name, &name);
+ TALLOC_FREE(subreq);
+ if (NT_STATUS_LOOKUP_ERR(status)) {
+ tevent_req_nterror(req, status);
+ return;
+ }
+
+ res_sid_index = state->single_sids[state->single_sids_done];
+
+ if ((domain_name != NULL) && (domain_name[0] != '\0')) {
+ /*
+ * Build structs with the domain name for
+ * wb_lookupsids_move_name(). If we didn't get a name, we will
+ * pass NULL and UINT32_MAX.
+ */
+
+ sid_copy(&src_domain_sid, &state->sids[res_sid_index]);
+ if (type != SID_NAME_DOMAIN) {
+ sid_split_rid(&src_domain_sid, &src_rid);
+ }
+
+ src_domain.name.string = domain_name;
+ src_domain.sid = &src_domain_sid;
+
+ src_domains.count = 1;
+ src_domains.domains = &src_domain;
+ psrc_domains = &src_domains;
+
+ domain_idx = 0;
+ }
+
+ src_name.sid_type = type;
+ src_name.name.string = name;
+ src_name.sid_index = domain_idx;
+
+ ok = wb_lookupsids_move_name(psrc_domains,
+ &src_name,
+ state->res_domains,
+ state->res_names,
+ res_sid_index);
+ if (!ok) {
+ tevent_req_oom(req);
+ return;
+ }
+ state->single_sids_done += 1;
+ wb_lookupsids_next(req, state);
+}
+
+static void wb_lookupsids_lookuprids_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct wb_lookupsids_state *state = tevent_req_data(
+ req, struct wb_lookupsids_state);
+ struct dom_sid src_domain_sid;
+ struct lsa_DomainInfo src_domain;
+ struct lsa_RefDomainList src_domains;
+ NTSTATUS status, result;
+ struct wb_lookupsids_domain *d;
+ uint32_t i;
+
+ status = dcerpc_wbint_LookupRids_recv(subreq, state, &result);
+ TALLOC_FREE(subreq);
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+ if (NT_STATUS_LOOKUP_ERR(result)) {
+ tevent_req_nterror(req, result);
+ return;
+ }
+
+ /*
+ * Look at the individual states in the translated names.
+ */
+
+ d = &state->domains[state->domains_done];
+
+ sid_copy(&src_domain_sid, get_global_sam_sid());
+ src_domain.name.string = get_global_sam_name();
+ src_domain.sid = &src_domain_sid;
+ src_domains.count = 1;
+ src_domains.domains = &src_domain;
+
+ for (i=0; i<state->rid_names.num_principals; i++) {
+ struct lsa_TranslatedName src_name;
+ uint32_t res_sid_index;
+
+ /*
+ * Fake up structs for wb_lookupsids_move_name
+ */
+ res_sid_index = d->sid_indexes[i];
+
+ src_name.sid_type = state->rid_names.principals[i].type;
+ src_name.name.string = state->rid_names.principals[i].name;
+ src_name.sid_index = 0;
+
+ if (!wb_lookupsids_move_name(
+ &src_domains, &src_name,
+ state->res_domains, state->res_names,
+ res_sid_index)) {
+ tevent_req_oom(req);
+ return;
+ }
+ }
+
+ state->domains_done += 1;
+ wb_lookupsids_next(req, state);
+}
+
+NTSTATUS wb_lookupsids_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx,
+ struct lsa_RefDomainList **domains,
+ struct lsa_TransNameArray **names)
+{
+ struct wb_lookupsids_state *state = tevent_req_data(
+ req, struct wb_lookupsids_state);
+ NTSTATUS status;
+
+ D_INFO("WB command lookupsids end.\n");
+ if (tevent_req_is_nterror(req, &status)) {
+ D_WARNING("Failed with %s.\n", nt_errstr(status));
+ return status;
+ }
+
+ /*
+ * The returned names need to match the given sids,
+ * if not we have a bug in the code!
+ */
+ if (state->res_names->count != state->num_sids) {
+ D_WARNING("Got %"PRIu32" returned name(s), but expected %"PRIu32"!\n",
+ state->res_names->count, state->num_sids);
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+
+ /*
+ * Not strictly needed, but it might make debugging in the callers
+ * easier in future, if the talloc_array_length() returns the
+ * expected result...
+ */
+ state->res_domains->domains = talloc_realloc(state->res_domains,
+ state->res_domains->domains,
+ struct lsa_DomainInfo,
+ state->res_domains->count);
+
+ *domains = talloc_move(mem_ctx, &state->res_domains);
+ *names = talloc_move(mem_ctx, &state->res_names);
+ D_INFO("Returning %"PRIu32" domain(s) and %"PRIu32" name(s).\n",
+ (*domains)->count,
+ (*names)->count);
+ return NT_STATUS_OK;
+}
diff --git a/source3/winbindd/wb_lookupuseraliases.c b/source3/winbindd/wb_lookupuseraliases.c
new file mode 100644
index 0000000..a9ad7d4
--- /dev/null
+++ b/source3/winbindd/wb_lookupuseraliases.c
@@ -0,0 +1,108 @@
+/*
+ Unix SMB/CIFS implementation.
+ async lookupuseraliases
+ Copyright (C) Volker Lendecke 2009
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "winbindd.h"
+#include "librpc/gen_ndr/ndr_winbind_c.h"
+#include "libcli/security/dom_sid.h"
+
+struct wb_lookupuseraliases_state {
+ struct tevent_context *ev;
+ struct wbint_SidArray sids;
+ struct wbint_RidArray rids;
+};
+
+static void wb_lookupuseraliases_done(struct tevent_req *subreq);
+
+struct tevent_req *wb_lookupuseraliases_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct winbindd_domain *domain,
+ uint32_t num_sids,
+ const struct dom_sid *sids)
+{
+ struct tevent_req *req, *subreq;
+ struct wb_lookupuseraliases_state *state;
+ uint32_t i;
+
+ req = tevent_req_create(mem_ctx, &state,
+ struct wb_lookupuseraliases_state);
+ if (req == NULL) {
+ return NULL;
+ }
+
+ D_INFO("WB command lookupuseraliases start.\n"
+ "Query domain %s for max %"PRIu32" SID(s).\n",
+ domain->name, num_sids);
+
+ for (i = 0; i < num_sids; i++) {
+ struct dom_sid_buf buf;
+ D_INFO("%"PRIu32": SID %s\n", i, dom_sid_str_buf(&sids[i], &buf));
+ }
+ state->sids.num_sids = num_sids;
+ state->sids.sids = discard_const_p(struct dom_sid, sids);
+
+ subreq = dcerpc_wbint_LookupUserAliases_send(
+ state, ev, dom_child_handle(domain), &state->sids, &state->rids);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, wb_lookupuseraliases_done, req);
+ return req;
+}
+
+static void wb_lookupuseraliases_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct wb_lookupuseraliases_state *state = tevent_req_data(
+ req, struct wb_lookupuseraliases_state);
+ NTSTATUS status, result;
+
+ status = dcerpc_wbint_LookupUserAliases_recv(subreq, state, &result);
+ TALLOC_FREE(subreq);
+ if (any_nt_status_not_ok(status, result, &status)) {
+ D_WARNING("LookupUserAliases failed with %s.\n",
+ nt_errstr(status));
+ tevent_req_nterror(req, status);
+ return;
+ }
+ tevent_req_done(req);
+}
+
+NTSTATUS wb_lookupuseraliases_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx,
+ uint32_t *num_aliases, uint32_t **aliases)
+{
+ struct wb_lookupuseraliases_state *state = tevent_req_data(
+ req, struct wb_lookupuseraliases_state);
+ NTSTATUS status;
+ uint32_t i;
+
+ if (tevent_req_is_nterror(req, &status)) {
+ return status;
+ }
+ *num_aliases = state->rids.num_rids;
+ D_INFO("WB command lookupuseraliases end.\nGot %"PRIu32" alias(es):\n",
+ *num_aliases);
+ for (i = 0; i < *num_aliases; i++) {
+ D_INFO("%"PRIu32": RID %"PRIu32"\n", i, state->rids.rids[i]);
+ }
+
+ *aliases = talloc_move(mem_ctx, &state->rids.rids);
+ return NT_STATUS_OK;
+}
diff --git a/source3/winbindd/wb_lookupusergroups.c b/source3/winbindd/wb_lookupusergroups.c
new file mode 100644
index 0000000..7f359ee
--- /dev/null
+++ b/source3/winbindd/wb_lookupusergroups.c
@@ -0,0 +1,120 @@
+/*
+ Unix SMB/CIFS implementation.
+ async lookupusergroups
+ Copyright (C) Volker Lendecke 2009
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "winbindd.h"
+#include "librpc/gen_ndr/ndr_winbind_c.h"
+#include "../libcli/security/security.h"
+
+struct wb_lookupusergroups_state {
+ struct tevent_context *ev;
+ struct dom_sid sid;
+ struct wbint_SidArray sids;
+};
+
+static void wb_lookupusergroups_done(struct tevent_req *subreq);
+
+struct tevent_req *wb_lookupusergroups_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ const struct dom_sid *sid)
+{
+ struct tevent_req *req, *subreq;
+ struct wb_lookupusergroups_state *state;
+ struct winbindd_domain *domain;
+ NTSTATUS status;
+ struct dom_sid_buf buf;
+
+ req = tevent_req_create(mem_ctx, &state,
+ struct wb_lookupusergroups_state);
+ if (req == NULL) {
+ return NULL;
+ }
+ D_INFO("WB command lookupusergroups start.\nLooking up SID %s.\n",
+ dom_sid_str_buf(sid, &buf));
+ sid_copy(&state->sid, sid);
+
+ status = lookup_usergroups_cached(state,
+ &state->sid,
+ &state->sids.num_sids,
+ &state->sids.sids);
+ if (NT_STATUS_IS_OK(status)) {
+ tevent_req_done(req);
+ return tevent_req_post(req, ev);
+ }
+
+ domain = find_domain_from_sid_noinit(&state->sid);
+ if (domain == NULL) {
+ DBG_WARNING("could not find domain entry for sid %s\n",
+ dom_sid_str_buf(&state->sid, &buf));
+ tevent_req_nterror(req, NT_STATUS_NO_SUCH_DOMAIN);
+ return tevent_req_post(req, ev);
+ }
+
+ subreq = dcerpc_wbint_LookupUserGroups_send(
+ state, ev, dom_child_handle(domain), &state->sid, &state->sids);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, wb_lookupusergroups_done, req);
+ return req;
+}
+
+static void wb_lookupusergroups_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct wb_lookupusergroups_state *state = tevent_req_data(
+ req, struct wb_lookupusergroups_state);
+ NTSTATUS status, result;
+
+ status = dcerpc_wbint_LookupUserGroups_recv(subreq, state, &result);
+ TALLOC_FREE(subreq);
+ if (any_nt_status_not_ok(status, result, &status)) {
+ D_WARNING("Failed with %s.\n", nt_errstr(status));
+ tevent_req_nterror(req, status);
+ return;
+ }
+ tevent_req_done(req);
+}
+
+NTSTATUS wb_lookupusergroups_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx,
+ uint32_t *num_sids, struct dom_sid **sids)
+{
+ struct wb_lookupusergroups_state *state = tevent_req_data(
+ req, struct wb_lookupusergroups_state);
+ NTSTATUS status;
+ uint32_t i;
+
+ if (tevent_req_is_nterror(req, &status)) {
+ return status;
+ }
+ *num_sids = state->sids.num_sids;
+ *sids = talloc_move(mem_ctx, &state->sids.sids);
+
+ D_INFO("WB command lookupusergroups end.\nReceived %"PRIu32" SID(s).\n",
+ *num_sids);
+ if (CHECK_DEBUGLVL(DBGLVL_INFO)) {
+ for (i = 0; i < *num_sids; i++) {
+ struct dom_sid_buf buf;
+ D_INFO("%"PRIu32": %s\n",
+ i, dom_sid_str_buf(&(*sids)[i], &buf));
+ }
+ }
+ return NT_STATUS_OK;
+}
diff --git a/source3/winbindd/wb_next_grent.c b/source3/winbindd/wb_next_grent.c
new file mode 100644
index 0000000..5c2d447
--- /dev/null
+++ b/source3/winbindd/wb_next_grent.c
@@ -0,0 +1,169 @@
+/*
+ Unix SMB/CIFS implementation.
+ async next_grent
+ Copyright (C) Volker Lendecke 2009
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "winbindd.h"
+#include "librpc/gen_ndr/ndr_winbind_c.h"
+#include "passdb/machine_sid.h"
+
+struct wb_next_grent_state {
+ struct tevent_context *ev;
+ int max_nesting;
+ struct getgrent_state *gstate;
+ struct winbindd_gr *gr;
+ struct db_context *members;
+};
+
+static void wb_next_grent_fetch_done(struct tevent_req *subreq);
+static void wb_next_grent_getgrsid_done(struct tevent_req *subreq);
+
+static void wb_next_grent_send_do(struct tevent_req *req,
+ struct wb_next_grent_state *state)
+{
+ struct tevent_req *subreq;
+
+ if (state->gstate->next_group >= state->gstate->num_groups) {
+ TALLOC_FREE(state->gstate->groups);
+
+ state->gstate->domain = wb_next_domain(state->gstate->domain);
+ if (state->gstate->domain == NULL) {
+ tevent_req_nterror(req, NT_STATUS_NO_MORE_ENTRIES);
+ return;
+ }
+
+ subreq = wb_query_group_list_send(state, state->ev,
+ state->gstate->domain);
+ if (tevent_req_nomem(subreq, req)) {
+ return;
+ }
+ tevent_req_set_callback(subreq, wb_next_grent_fetch_done, req);
+ return;
+ }
+
+ subreq = wb_getgrsid_send(
+ state, state->ev,
+ &state->gstate->groups[state->gstate->next_group].sid,
+ state->max_nesting);
+ if (tevent_req_nomem(subreq, req)) {
+ return;
+ }
+ tevent_req_set_callback(subreq, wb_next_grent_getgrsid_done, req);
+}
+
+struct tevent_req *wb_next_grent_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ int max_nesting,
+ struct getgrent_state *gstate,
+ struct winbindd_gr *gr)
+{
+ struct tevent_req *req;
+ struct wb_next_grent_state *state;
+
+ req = tevent_req_create(mem_ctx, &state, struct wb_next_grent_state);
+ if (req == NULL) {
+ return NULL;
+ }
+
+ D_INFO("WB command next_grent start.\n");
+
+ state->ev = ev;
+ state->gstate = gstate;
+ state->gr = gr;
+ state->max_nesting = max_nesting;
+
+ wb_next_grent_send_do(req, state);
+ if (!tevent_req_is_in_progress(req)) {
+ return tevent_req_post(req, ev);
+ }
+
+ return req;
+}
+
+static void wb_next_grent_fetch_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct wb_next_grent_state *state = tevent_req_data(
+ req, struct wb_next_grent_state);
+ NTSTATUS status;
+
+ status = wb_query_group_list_recv(subreq, state->gstate,
+ &state->gstate->num_groups,
+ &state->gstate->groups);
+ TALLOC_FREE(subreq);
+ if (!NT_STATUS_IS_OK(status)) {
+ /* Ignore errors here, just log it */
+ D_DEBUG("query_group_list for domain %s returned %s\n",
+ state->gstate->domain->name, nt_errstr(status));
+ state->gstate->num_groups = 0;
+ }
+
+ state->gstate->next_group = 0;
+
+ wb_next_grent_send_do(req, state);
+}
+
+static void wb_next_grent_getgrsid_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct wb_next_grent_state *state = tevent_req_data(
+ req, struct wb_next_grent_state);
+ const char *domname, *name;
+ NTSTATUS status;
+
+ status = wb_getgrsid_recv(subreq, talloc_tos(), &domname, &name,
+ &state->gr->gr_gid, &state->members);
+ TALLOC_FREE(subreq);
+
+ if (NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED)) {
+ state->gstate->next_group += 1;
+
+ wb_next_grent_send_do(req, state);
+
+ return;
+ } else if (tevent_req_nterror(req, status)) {
+ return;
+ }
+
+ if (!fill_grent(talloc_tos(), state->gr, domname, name,
+ state->gr->gr_gid)) {
+ D_WARNING("fill_grent failed\n");
+ tevent_req_nterror(req, NT_STATUS_NO_MEMORY);
+ return;
+ }
+ state->gstate->next_group += 1;
+ tevent_req_done(req);
+}
+
+NTSTATUS wb_next_grent_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx,
+ struct db_context **members)
+{
+ struct wb_next_grent_state *state = tevent_req_data(
+ req, struct wb_next_grent_state);
+ NTSTATUS status;
+
+ D_INFO("WB command next_grent end.\n");
+ if (tevent_req_is_nterror(req, &status)) {
+ D_WARNING("Failed with %s.\n", nt_errstr(status));
+ return status;
+ }
+ *members = talloc_move(mem_ctx, &state->members);
+ return NT_STATUS_OK;
+}
diff --git a/source3/winbindd/wb_next_pwent.c b/source3/winbindd/wb_next_pwent.c
new file mode 100644
index 0000000..f000c64
--- /dev/null
+++ b/source3/winbindd/wb_next_pwent.c
@@ -0,0 +1,162 @@
+/*
+ Unix SMB/CIFS implementation.
+ async next_pwent
+ Copyright (C) Volker Lendecke 2009
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "winbindd.h"
+#include "librpc/gen_ndr/ndr_winbind_c.h"
+#include "libcli/security/dom_sid.h"
+#include "passdb/machine_sid.h"
+
+struct wb_next_pwent_state {
+ struct tevent_context *ev;
+ struct getpwent_state *gstate;
+ struct dom_sid next_sid;
+ struct winbindd_pw *pw;
+};
+
+static void wb_next_pwent_fetch_done(struct tevent_req *subreq);
+static void wb_next_pwent_fill_done(struct tevent_req *subreq);
+
+static void wb_next_pwent_send_do(struct tevent_req *req,
+ struct wb_next_pwent_state *state)
+{
+ struct tevent_req *subreq;
+ struct dom_sid_buf buf, buf1;
+
+ if (state->gstate->next_user >= state->gstate->rids.num_rids) {
+ TALLOC_FREE(state->gstate->rids.rids);
+ state->gstate->rids.num_rids = 0;
+
+ state->gstate->domain = wb_next_domain(state->gstate->domain);
+ if (state->gstate->domain == NULL) {
+ tevent_req_nterror(req, NT_STATUS_NO_MORE_ENTRIES);
+ return;
+ }
+
+ D_DEBUG("Query user RID list for domain %s.\n",
+ state->gstate->domain->name);
+ subreq = dcerpc_wbint_QueryUserRidList_send(
+ state, state->ev,
+ dom_child_handle(state->gstate->domain),
+ &state->gstate->rids);
+ if (tevent_req_nomem(subreq, req)) {
+ return;
+ }
+
+ tevent_req_set_callback(subreq, wb_next_pwent_fetch_done, req);
+ return;
+ }
+
+ sid_compose(&state->next_sid, &state->gstate->domain->sid,
+ state->gstate->rids.rids[state->gstate->next_user]);
+
+ D_DEBUG("Get pw for SID %s composed from domain SID %s and RID %"PRIu32".\n",
+ dom_sid_str_buf(&state->next_sid, &buf),
+ dom_sid_str_buf(&state->gstate->domain->sid, &buf1),
+ state->gstate->rids.rids[state->gstate->next_user]);
+ subreq = wb_getpwsid_send(state, state->ev, &state->next_sid,
+ state->pw);
+ if (tevent_req_nomem(subreq, req)) {
+ return;
+ }
+
+ tevent_req_set_callback(subreq, wb_next_pwent_fill_done, req);
+}
+
+struct tevent_req *wb_next_pwent_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct getpwent_state *gstate,
+ struct winbindd_pw *pw)
+{
+ struct tevent_req *req;
+ struct wb_next_pwent_state *state;
+
+ req = tevent_req_create(mem_ctx, &state, struct wb_next_pwent_state);
+ if (req == NULL) {
+ return NULL;
+ }
+ D_INFO("WB command next_pwent start.\n");
+ state->ev = ev;
+ state->gstate = gstate;
+ state->pw = pw;
+
+ wb_next_pwent_send_do(req, state);
+ if (!tevent_req_is_in_progress(req)) {
+ return tevent_req_post(req, ev);
+ }
+
+ return req;
+}
+
+static void wb_next_pwent_fetch_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct wb_next_pwent_state *state = tevent_req_data(
+ req, struct wb_next_pwent_state);
+ NTSTATUS status, result;
+
+ status = dcerpc_wbint_QueryUserRidList_recv(subreq, state->gstate,
+ &result);
+ TALLOC_FREE(subreq);
+ if (any_nt_status_not_ok(status, result, &status)) {
+ /* Ignore errors here, just log it */
+ D_DEBUG("query_user_list for domain %s returned %s\n",
+ state->gstate->domain->name,
+ nt_errstr(status));
+ state->gstate->rids.num_rids = 0;
+ }
+
+ state->gstate->next_user = 0;
+
+ wb_next_pwent_send_do(req, state);
+}
+
+static void wb_next_pwent_fill_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct wb_next_pwent_state *state = tevent_req_data(
+ req, struct wb_next_pwent_state);
+ NTSTATUS status;
+
+ status = wb_getpwsid_recv(subreq);
+ TALLOC_FREE(subreq);
+ /*
+ * When you try to enumerate users with 'getent passwd' and the user
+ * doesn't have a uid set we should just move on.
+ */
+ if (NT_STATUS_EQUAL(status, NT_STATUS_NO_SUCH_USER)) {
+ state->gstate->next_user += 1;
+
+ wb_next_pwent_send_do(req, state);
+
+ return;
+ } else if (tevent_req_nterror(req, status)) {
+ return;
+ }
+ state->gstate->next_user += 1;
+ tevent_req_done(req);
+}
+
+NTSTATUS wb_next_pwent_recv(struct tevent_req *req)
+{
+ D_INFO("WB command next_pwent end.\n");
+ return tevent_req_simple_recv_ntstatus(req);
+}
diff --git a/source3/winbindd/wb_query_group_list.c b/source3/winbindd/wb_query_group_list.c
new file mode 100644
index 0000000..a71e162
--- /dev/null
+++ b/source3/winbindd/wb_query_group_list.c
@@ -0,0 +1,94 @@
+/*
+ Unix SMB/CIFS implementation.
+ async query_group_list
+ Copyright (C) Volker Lendecke 2009
+ Copyright (C) Michael Adam 2015
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "winbindd.h"
+#include "librpc/gen_ndr/ndr_winbind_c.h"
+
+
+struct wb_query_group_list_state {
+ struct wbint_Principals groups;
+};
+
+static void wb_query_group_list_done(struct tevent_req *subreq);
+
+struct tevent_req *wb_query_group_list_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct winbindd_domain *domain)
+{
+ struct tevent_req *req, *subreq;
+ struct wb_query_group_list_state *state;
+
+ req = tevent_req_create(mem_ctx, &state,
+ struct wb_query_group_list_state);
+ if (req == NULL) {
+ return NULL;
+ }
+
+ D_INFO("WB command group_list start.\nQuery domain %s\n", domain->name);
+ subreq = dcerpc_wbint_QueryGroupList_send(state, ev,
+ dom_child_handle(domain),
+ &state->groups);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+
+ tevent_req_set_callback(subreq, wb_query_group_list_done, req);
+ return req;
+}
+
+static void wb_query_group_list_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct wb_query_group_list_state *state = tevent_req_data(
+ req, struct wb_query_group_list_state);
+ NTSTATUS status, result;
+
+ status = dcerpc_wbint_QueryGroupList_recv(subreq, state, &result);
+ TALLOC_FREE(subreq);
+ if (any_nt_status_not_ok(status, result, &status)) {
+ tevent_req_nterror(req, status);
+ return;
+ }
+
+ tevent_req_done(req);
+}
+
+NTSTATUS wb_query_group_list_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx,
+ uint32_t *num_groups,
+ struct wbint_Principal **groups)
+{
+ struct wb_query_group_list_state *state = tevent_req_data(
+ req, struct wb_query_group_list_state);
+ NTSTATUS status;
+
+ if (tevent_req_is_nterror(req, &status)) {
+ D_WARNING("Failed with: %s\n", nt_errstr(status));
+ return status;
+ }
+
+ *num_groups = state->groups.num_principals;
+ *groups = talloc_move(mem_ctx, &state->groups.principals);
+
+ D_INFO("WB command group_list end.\n"
+ "Returning %"PRIu32" group(s).\n", *num_groups);
+ return NT_STATUS_OK;
+}
diff --git a/source3/winbindd/wb_query_user_list.c b/source3/winbindd/wb_query_user_list.c
new file mode 100644
index 0000000..c3f52e5
--- /dev/null
+++ b/source3/winbindd/wb_query_user_list.c
@@ -0,0 +1,146 @@
+/*
+ Unix SMB/CIFS implementation.
+ async query_user_list
+ Copyright (C) Volker Lendecke 2009
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "winbindd.h"
+#include "librpc/gen_ndr/ndr_winbind_c.h"
+#include "lib/util/strv.h"
+
+struct wb_query_user_list_state {
+ struct tevent_context *ev;
+ struct winbindd_domain *domain;
+ struct wbint_RidArray rids;
+ const char *domain_name;
+ struct wbint_Principals names;
+ char *users;
+};
+
+static void wb_query_user_list_gotrids(struct tevent_req *subreq);
+static void wb_query_user_list_done(struct tevent_req *subreq);
+
+struct tevent_req *wb_query_user_list_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct winbindd_domain *domain)
+{
+ struct tevent_req *req, *subreq;
+ struct wb_query_user_list_state *state;
+
+ req = tevent_req_create(mem_ctx, &state,
+ struct wb_query_user_list_state);
+ if (req == NULL) {
+ return NULL;
+ }
+
+ D_INFO("WB command user_list start.\nQuery users in domain %s.\n",
+ domain->name);
+ state->ev = ev;
+ state->domain = domain;
+
+ subreq = dcerpc_wbint_QueryUserRidList_send(
+ state, ev, dom_child_handle(domain), &state->rids);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, wb_query_user_list_gotrids, req);
+ return req;
+}
+
+static void wb_query_user_list_gotrids(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct wb_query_user_list_state *state = tevent_req_data(
+ req, struct wb_query_user_list_state);
+ NTSTATUS status, result;
+
+ status = dcerpc_wbint_QueryUserRidList_recv(subreq, state, &result);
+ TALLOC_FREE(subreq);
+ if (any_nt_status_not_ok(status, result, &status)) {
+ tevent_req_nterror(req, status);
+ return;
+ }
+
+ D_DEBUG("dcerpc_wbint_QueryUserRidList returned %"PRIu32" users\n",
+ state->rids.num_rids);
+
+ subreq = dcerpc_wbint_LookupRids_send(
+ state, state->ev, dom_child_handle(state->domain),
+ &state->domain->sid, &state->rids,
+ &state->domain_name, &state->names);
+ if (tevent_req_nomem(subreq, req)) {
+ return;
+ }
+ tevent_req_set_callback(subreq, wb_query_user_list_done, req);
+}
+
+static void wb_query_user_list_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct wb_query_user_list_state *state = tevent_req_data(
+ req, struct wb_query_user_list_state);
+ NTSTATUS status, result;
+ uint32_t i;
+
+ status = dcerpc_wbint_LookupRids_recv(subreq, state, &result);
+ TALLOC_FREE(subreq);
+ if (any_nt_status_not_ok(status, result, &status)) {
+ tevent_req_nterror(req, status);
+ return;
+ }
+ D_DEBUG("Processing %"PRIu32" principal(s).\n",
+ state->names.num_principals);
+ for (i=0; i<state->names.num_principals; i++) {
+ struct wbint_Principal *p = &state->names.principals[i];
+ const char *name;
+ int ret;
+
+ name = fill_domain_username_talloc(state, state->domain_name, p->name, true);
+ if (name == NULL) {
+ tevent_req_nterror(req, NT_STATUS_NO_MEMORY);
+ return;
+ }
+ ret = strv_add(state, &state->users, name);
+ if (ret != 0) {
+ tevent_req_nterror(req, map_nt_error_from_unix(ret));
+ return;
+ }
+ D_DEBUG("%"PRIu32": Adding user %s\n", i, name);
+ }
+
+ tevent_req_done(req);
+}
+
+NTSTATUS wb_query_user_list_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx,
+ char **users)
+{
+ struct wb_query_user_list_state *state = tevent_req_data(
+ req, struct wb_query_user_list_state);
+ NTSTATUS status;
+
+ D_INFO("WB command user_list end.\n");
+ if (tevent_req_is_nterror(req, &status)) {
+ D_WARNING("Failed with: %s\n", nt_errstr(status));
+ return status;
+ }
+
+ *users = talloc_move(mem_ctx, &state->users);
+
+ return NT_STATUS_OK;
+}
diff --git a/source3/winbindd/wb_queryuser.c b/source3/winbindd/wb_queryuser.c
new file mode 100644
index 0000000..c2758f1
--- /dev/null
+++ b/source3/winbindd/wb_queryuser.c
@@ -0,0 +1,480 @@
+/*
+ Unix SMB/CIFS implementation.
+ async queryuser
+ Copyright (C) Volker Lendecke 2009
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "util/debug.h"
+#include "winbindd.h"
+#include "librpc/gen_ndr/ndr_winbind_c.h"
+#include "../libcli/security/security.h"
+#include "libsmb/samlogon_cache.h"
+#include "librpc/gen_ndr/ndr_winbind.h"
+
+struct wb_queryuser_state {
+ struct tevent_context *ev;
+ struct wbint_userinfo *info;
+ const struct wb_parent_idmap_config *idmap_cfg;
+ bool tried_dclookup;
+};
+
+static void wb_queryuser_idmap_setup_done(struct tevent_req *subreq);
+static void wb_queryuser_got_uid(struct tevent_req *subreq);
+static void wb_queryuser_got_domain(struct tevent_req *subreq);
+static void wb_queryuser_got_dc(struct tevent_req *subreq);
+static void wb_queryuser_got_gid(struct tevent_req *subreq);
+static void wb_queryuser_got_group_name(struct tevent_req *subreq);
+static void wb_queryuser_done(struct tevent_req *subreq);
+
+struct tevent_req *wb_queryuser_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ const struct dom_sid *user_sid)
+{
+ struct tevent_req *req, *subreq;
+ struct wb_queryuser_state *state;
+ struct wbint_userinfo *info;
+ struct dom_sid_buf buf;
+
+ req = tevent_req_create(mem_ctx, &state, struct wb_queryuser_state);
+ if (req == NULL) {
+ return NULL;
+ }
+ D_INFO("WB command queryuser start.\nQuery user sid %s\n",
+ dom_sid_str_buf(user_sid, &buf));
+ state->ev = ev;
+
+ state->info = talloc_zero(state, struct wbint_userinfo);
+ if (tevent_req_nomem(state->info, req)) {
+ return tevent_req_post(req, ev);
+ }
+ info = state->info;
+
+ info->primary_gid = (gid_t)-1;
+
+ sid_copy(&info->user_sid, user_sid);
+
+ subreq = wb_parent_idmap_setup_send(state, state->ev);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, wb_queryuser_idmap_setup_done, req);
+ return req;
+}
+
+static void wb_queryuser_idmap_setup_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct wb_queryuser_state *state = tevent_req_data(
+ req, struct wb_queryuser_state);
+ NTSTATUS status;
+ struct dom_sid_buf buf;
+
+ status = wb_parent_idmap_setup_recv(subreq, &state->idmap_cfg);
+ TALLOC_FREE(subreq);
+ if (tevent_req_nterror(req, status)) {
+ D_WARNING("wb_parent_idmap_setup_recv() failed with %s.\n",
+ nt_errstr(status));
+ return;
+ }
+
+ D_DEBUG("Convert the user SID %s to XID.\n",
+ dom_sid_str_buf(&state->info->user_sid, &buf));
+ subreq = wb_sids2xids_send(
+ state, state->ev, &state->info->user_sid, 1);
+ if (tevent_req_nomem(subreq, req)) {
+ return;
+ }
+ tevent_req_set_callback(subreq, wb_queryuser_got_uid, req);
+ return;
+}
+
+static void wb_queryuser_got_uid(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct wb_queryuser_state *state = tevent_req_data(
+ req, struct wb_queryuser_state);
+ struct wbint_userinfo *info = state->info;
+ struct netr_SamInfo3 *info3;
+ struct dcerpc_binding_handle *child_binding_handle = NULL;
+ struct unixid xid;
+ uint32_t user_rid = 0;
+ NTSTATUS status;
+ struct dom_sid_buf buf, buf1;
+
+ status = wb_sids2xids_recv(subreq, &xid, 1);
+ TALLOC_FREE(subreq);
+ if (tevent_req_nterror(req, status)) {
+ D_WARNING("wb_sids2xids_recv() failed with %s.\n",
+ nt_errstr(status));
+ return;
+ }
+
+ if ((xid.type != ID_TYPE_UID) && (xid.type != ID_TYPE_BOTH)) {
+ D_WARNING("XID type is %d, should be ID_TYPE_UID or ID_TYPE_BOTH.\n",
+ xid.type);
+ tevent_req_nterror(req, NT_STATUS_NO_SUCH_USER);
+ return;
+ }
+
+ D_DEBUG("Received XID %"PRIu32" for SID %s.\n",
+ xid.id,
+ dom_sid_str_buf(&info->user_sid, &buf1));
+ info->uid = xid.id;
+
+ /*
+ * Default the group sid to "Domain Users" in the user's
+ * domain. The samlogon cache or the query_user call later on
+ * can override this.
+ * TODO: There is still missing functionality to set the correct group
+ * sid using samlogon cache (needs to use S4USelf).
+ * Once this is done, remove the workaround in test_membership_user() in
+ * source4/torture/local/nss_tests.c
+ */
+ sid_copy(&info->group_sid, &info->user_sid);
+ sid_split_rid(&info->group_sid, &user_rid);
+ sid_append_rid(&info->group_sid,
+ user_rid == DOMAIN_RID_GUEST ? DOMAIN_RID_GUESTS
+ : DOMAIN_RID_USERS);
+
+ D_DEBUG("Preconfigured 'Domain Users' RID %u was used to create group SID %s from user SID %s.\n",
+ DOMAIN_RID_USERS,
+ dom_sid_str_buf(&info->group_sid, &buf),
+ dom_sid_str_buf(&info->user_sid, &buf1));
+
+ info->homedir = talloc_strdup(info, lp_template_homedir());
+ D_DEBUG("Setting 'homedir' to the template '%s'.\n", info->homedir);
+ if (tevent_req_nomem(info->homedir, req)) {
+ return;
+ }
+
+ info->shell = talloc_strdup(info, lp_template_shell());
+ D_DEBUG("Setting 'shell' to the template '%s'.\n", info->shell);
+ if (tevent_req_nomem(info->shell, req)) {
+ return;
+ }
+
+ info3 = netsamlogon_cache_get(state, &info->user_sid);
+ if (info3 != NULL) {
+ D_DEBUG("Filling data received from netsamlogon_cache\n");
+ sid_compose(&info->group_sid, info3->base.domain_sid,
+ info3->base.primary_gid);
+ info->acct_name = talloc_move(
+ info, &info3->base.account_name.string);
+ info->full_name = talloc_move(
+ info, &info3->base.full_name.string);
+
+ info->domain_name = talloc_move(
+ state, &info3->base.logon_domain.string);
+
+ TALLOC_FREE(info3);
+ }
+
+ NDR_PRINT_DEBUG_LEVEL(DBGLVL_DEBUG, wbint_userinfo, state->info);
+ if (info->domain_name == NULL) {
+ D_DEBUG("Domain name is empty, calling wb_lookupsid_send() to get it.\n");
+ subreq = wb_lookupsid_send(state, state->ev, &info->user_sid);
+ if (tevent_req_nomem(subreq, req)) {
+ return;
+ }
+ tevent_req_set_callback(subreq, wb_queryuser_got_domain, req);
+ return;
+ }
+
+ /*
+ * Note wb_sids2xids_send/recv was called before,
+ * so we're sure that wb_parent_idmap_setup_send/recv
+ * was already called.
+ */
+ child_binding_handle = idmap_child_handle();
+ D_DEBUG("Domain name is set, calling dcerpc_wbint_GetNssInfo_send()\n");
+ subreq = dcerpc_wbint_GetNssInfo_send(
+ state, state->ev, child_binding_handle, info);
+ if (tevent_req_nomem(subreq, req)) {
+ return;
+ }
+ tevent_req_set_callback(subreq, wb_queryuser_done, req);
+}
+
+static void wb_queryuser_got_domain(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct wb_queryuser_state *state = tevent_req_data(
+ req, struct wb_queryuser_state);
+ struct wbint_userinfo *info = state->info;
+ enum lsa_SidType type;
+ struct dcerpc_binding_handle *child_binding_handle = NULL;
+ NTSTATUS status;
+
+ status = wb_lookupsid_recv(subreq, state, &type,
+ &info->domain_name, &info->acct_name);
+ TALLOC_FREE(subreq);
+ if (tevent_req_nterror(req, status)) {
+ D_WARNING("wb_lookupsid_recv failed with %s.\n",
+ nt_errstr(status));
+ return;
+ }
+
+ NDR_PRINT_DEBUG_LEVEL(DBGLVL_DEBUG, wbint_userinfo, state->info);
+ switch (type) {
+ case SID_NAME_USER:
+ case SID_NAME_COMPUTER:
+ /*
+ * user case: we only need the account name from lookup_sids
+ */
+ break;
+ case SID_NAME_DOM_GRP:
+ case SID_NAME_ALIAS:
+ case SID_NAME_WKN_GRP:
+ /*
+ * also treat group-type SIDs (they might map to ID_TYPE_BOTH)
+ */
+ sid_copy(&info->group_sid, &info->user_sid);
+ break;
+ default:
+ D_WARNING("Unknown type:%d, return NT_STATUS_NO_SUCH_USER.\n",
+ type);
+ tevent_req_nterror(req, NT_STATUS_NO_SUCH_USER);
+ return;
+ }
+
+ /*
+ * Note wb_sids2xids_send/recv was called before,
+ * so we're sure that wb_parent_idmap_setup_send/recv
+ * was already called.
+ */
+ child_binding_handle = idmap_child_handle();
+ D_DEBUG("About to call dcerpc_wbint_GetNssInfo_send()\n");
+ subreq = dcerpc_wbint_GetNssInfo_send(
+ state, state->ev, child_binding_handle, info);
+ if (tevent_req_nomem(subreq, req)) {
+ return;
+ }
+ tevent_req_set_callback(subreq, wb_queryuser_done, req);
+}
+
+static void wb_queryuser_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct wb_queryuser_state *state = tevent_req_data(
+ req, struct wb_queryuser_state);
+ struct wbint_userinfo *info = state->info;
+ NTSTATUS status, result;
+ bool need_group_name = false;
+ const char *tmpl = NULL;
+
+ status = dcerpc_wbint_GetNssInfo_recv(subreq, info, &result);
+ TALLOC_FREE(subreq);
+ if (tevent_req_nterror(req, status)) {
+ D_WARNING("GetNssInfo failed with %s.\n", nt_errstr(status));
+ return;
+ }
+
+ if (NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) &&
+ !state->tried_dclookup) {
+ D_DEBUG("GetNssInfo got DOMAIN_CONTROLLER_NOT_FOUND, calling wb_dsgetdcname_send()\n");
+ subreq = wb_dsgetdcname_send(
+ state, state->ev, state->info->domain_name, NULL, NULL,
+ DS_RETURN_DNS_NAME);
+ if (tevent_req_nomem(subreq, req)) {
+ return;
+ }
+ tevent_req_set_callback(subreq, wb_queryuser_got_dc, req);
+ return;
+ }
+
+ /*
+ * Ignore failure in "result" here. We'll try to fill in stuff
+ * that misses further down.
+ */
+
+ if (state->info->primary_gid == (gid_t)-1) {
+ D_DEBUG("Calling wb_sids2xids_send() to resolve primary gid.\n");
+ subreq = wb_sids2xids_send(
+ state, state->ev, &info->group_sid, 1);
+ if (tevent_req_nomem(subreq, req)) {
+ return;
+ }
+ tevent_req_set_callback(subreq, wb_queryuser_got_gid, req);
+ return;
+ }
+
+ tmpl = lp_template_homedir();
+ if(strstr_m(tmpl, "%g") || strstr_m(tmpl, "%G")) {
+ need_group_name = true;
+ }
+ tmpl = lp_template_shell();
+ if(strstr_m(tmpl, "%g") || strstr_m(tmpl, "%G")) {
+ need_group_name = true;
+ }
+
+ if (need_group_name && state->info->primary_group_name == NULL) {
+ D_DEBUG("Calling wb_lookupsid_send() to resolve primary group name.\n");
+ subreq = wb_lookupsid_send(state, state->ev, &info->group_sid);
+ if (tevent_req_nomem(subreq, req)) {
+ return;
+ }
+ tevent_req_set_callback(subreq, wb_queryuser_got_group_name,
+ req);
+ return;
+ }
+
+ NDR_PRINT_DEBUG_LEVEL(DBGLVL_DEBUG, wbint_userinfo, state->info);
+ tevent_req_done(req);
+}
+
+static void wb_queryuser_got_dc(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct wb_queryuser_state *state = tevent_req_data(
+ req, struct wb_queryuser_state);
+ struct wbint_userinfo *info = state->info;
+ struct netr_DsRGetDCNameInfo *dcinfo;
+ struct dcerpc_binding_handle *child_binding_handle = NULL;
+ NTSTATUS status;
+
+ status = wb_dsgetdcname_recv(subreq, state, &dcinfo);
+ TALLOC_FREE(subreq);
+ if (tevent_req_nterror(req, status)) {
+ D_WARNING("wb_dsgetdcname_recv() failed with %s.\n",
+ nt_errstr(status));
+ return;
+ }
+
+ state->tried_dclookup = true;
+
+ D_DEBUG("Got DC name, calling wb_dsgetdcname_gencache_set().\n");
+ status = wb_dsgetdcname_gencache_set(info->domain_name, dcinfo);
+ if (tevent_req_nterror(req, status)) {
+ D_WARNING("wb_dsgetdcname_gencache_set() failed with %s.\n",
+ nt_errstr(status));
+ return;
+ }
+ NDR_PRINT_DEBUG_LEVEL(DBGLVL_DEBUG, wbint_userinfo, state->info);
+
+ /*
+ * Note wb_sids2xids_send/recv was called before,
+ * so we're sure that wb_parent_idmap_setup_send/recv
+ * was already called.
+ */
+ child_binding_handle = idmap_child_handle();
+ subreq = dcerpc_wbint_GetNssInfo_send(
+ state, state->ev, child_binding_handle, info);
+ if (tevent_req_nomem(subreq, req)) {
+ return;
+ }
+ tevent_req_set_callback(subreq, wb_queryuser_done, req);
+}
+
+static void wb_queryuser_got_gid(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct wb_queryuser_state *state = tevent_req_data(
+ req, struct wb_queryuser_state);
+ struct unixid xid;
+ NTSTATUS status;
+ bool need_group_name = false;
+ const char *tmpl = NULL;
+ struct dom_sid_buf buf;
+
+ status = wb_sids2xids_recv(subreq, &xid, 1);
+ TALLOC_FREE(subreq);
+ if (tevent_req_nterror(req, status)) {
+ D_WARNING("wb_sids2xids_recv() failed with %s.\n",
+ nt_errstr(status));
+ return;
+ }
+
+ D_DEBUG("Got XID %"PRIu32" with type %d.\n", xid.id, xid.type);
+ if ((xid.type != ID_TYPE_GID) && (xid.type != ID_TYPE_BOTH)) {
+ D_WARNING("Returning NT_STATUS_NO_SUCH_USER\n"
+ "xid.type must be ID_TYPE_UID or ID_TYPE_BOTH.\n");
+ tevent_req_nterror(req, NT_STATUS_NO_SUCH_USER);
+ return;
+ }
+
+ state->info->primary_gid = xid.id;
+
+ tmpl = lp_template_homedir();
+ if(strstr_m(tmpl, "%g") || strstr_m(tmpl, "%G")) {
+ need_group_name = true;
+ }
+ tmpl = lp_template_shell();
+ if(strstr_m(tmpl, "%g") || strstr_m(tmpl, "%G")) {
+ need_group_name = true;
+ }
+
+ NDR_PRINT_DEBUG_LEVEL(DBGLVL_DEBUG, wbint_userinfo, state->info);
+
+ if (need_group_name && state->info->primary_group_name == NULL) {
+ D_DEBUG("Calling wb_lookupsid_send for group SID %s.\n",
+ dom_sid_str_buf(&state->info->group_sid, &buf));
+ subreq = wb_lookupsid_send(state, state->ev,
+ &state->info->group_sid);
+ if (tevent_req_nomem(subreq, req)) {
+ return;
+ }
+ tevent_req_set_callback(subreq, wb_queryuser_got_group_name,
+ req);
+ return;
+ }
+
+ D_DEBUG("No need to lookup primary group name. Request is done!\n");
+ tevent_req_done(req);
+}
+
+static void wb_queryuser_got_group_name(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct wb_queryuser_state *state = tevent_req_data(
+ req, struct wb_queryuser_state);
+ enum lsa_SidType type;
+ NTSTATUS status;
+ const char *domain_name;
+
+ status = wb_lookupsid_recv(subreq, state->info, &type, &domain_name,
+ &state->info->primary_group_name);
+ TALLOC_FREE(subreq);
+ if (tevent_req_nterror(req, status)) {
+ D_WARNING("wb_lookupsid_recv() failed with %s.\n",
+ nt_errstr(status));
+ return;
+ }
+ tevent_req_done(req);
+}
+
+NTSTATUS wb_queryuser_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx,
+ struct wbint_userinfo **pinfo)
+{
+ struct wb_queryuser_state *state = tevent_req_data(
+ req, struct wb_queryuser_state);
+ NTSTATUS status;
+
+ D_INFO("WB command queryuser end.\n");
+ NDR_PRINT_DEBUG_LEVEL(DBGLVL_INFO, wbint_userinfo, state->info);
+ if (tevent_req_is_nterror(req, &status)) {
+ return status;
+ }
+ *pinfo = talloc_move(mem_ctx, &state->info);
+ return NT_STATUS_OK;
+}
diff --git a/source3/winbindd/wb_seqnum.c b/source3/winbindd/wb_seqnum.c
new file mode 100644
index 0000000..7affd76
--- /dev/null
+++ b/source3/winbindd/wb_seqnum.c
@@ -0,0 +1,78 @@
+/*
+ Unix SMB/CIFS implementation.
+ async seqnum
+ Copyright (C) Volker Lendecke 2009
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "winbindd.h"
+#include "librpc/gen_ndr/ndr_winbind_c.h"
+
+struct wb_seqnum_state {
+ uint32_t seqnum;
+};
+
+static void wb_seqnum_done(struct tevent_req *subreq);
+
+struct tevent_req *wb_seqnum_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct winbindd_domain *domain)
+{
+ struct tevent_req *req, *subreq;
+ struct wb_seqnum_state *state;
+
+ req = tevent_req_create(mem_ctx, &state, struct wb_seqnum_state);
+ if (req == NULL) {
+ return NULL;
+ }
+ subreq = dcerpc_wbint_QuerySequenceNumber_send(
+ state, ev, dom_child_handle(domain), &state->seqnum);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, wb_seqnum_done, req);
+ return req;
+}
+
+static void wb_seqnum_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct wb_seqnum_state *state = tevent_req_data(
+ req, struct wb_seqnum_state);
+ NTSTATUS status, result;
+
+ status = dcerpc_wbint_QuerySequenceNumber_recv(subreq, state, &result);
+ TALLOC_FREE(subreq);
+ if (any_nt_status_not_ok(status, result, &status)) {
+ tevent_req_nterror(req, status);
+ return;
+ }
+ tevent_req_done(req);
+}
+
+NTSTATUS wb_seqnum_recv(struct tevent_req *req, uint32_t *seqnum)
+{
+ struct wb_seqnum_state *state = tevent_req_data(
+ req, struct wb_seqnum_state);
+ NTSTATUS status;
+
+ if (tevent_req_is_nterror(req, &status)) {
+ return status;
+ }
+ *seqnum = state->seqnum;
+ return NT_STATUS_OK;
+}
diff --git a/source3/winbindd/wb_seqnums.c b/source3/winbindd/wb_seqnums.c
new file mode 100644
index 0000000..24955ad
--- /dev/null
+++ b/source3/winbindd/wb_seqnums.c
@@ -0,0 +1,153 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ async seqnums, update the seqnums in winbindd_cache.c
+
+ Copyright (C) Volker Lendecke 2009
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "winbindd.h"
+#include "librpc/gen_ndr/ndr_winbind_c.h"
+
+struct wb_seqnums_state {
+ int num_domains;
+ int num_received;
+
+ struct tevent_req **subreqs;
+ struct winbindd_domain **domains;
+ NTSTATUS *statuses;
+ uint32_t *seqnums;
+};
+
+static void wb_seqnums_done(struct tevent_req *subreq);
+
+struct tevent_req *wb_seqnums_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev)
+{
+ struct tevent_req *req;
+ struct wb_seqnums_state *state;
+ struct winbindd_domain *domain;
+ int i;
+
+ req = tevent_req_create(mem_ctx, &state, struct wb_seqnums_state);
+ if (req == NULL) {
+ return NULL;
+ }
+ state->num_received = 0;
+ state->num_domains = 0;
+
+ for (domain = domain_list(); domain != NULL; domain = domain->next) {
+ state->num_domains += 1;
+ }
+
+ state->subreqs = talloc_array(state, struct tevent_req *,
+ state->num_domains);
+ state->domains = talloc_zero_array(state, struct winbindd_domain *,
+ state->num_domains);
+ state->statuses = talloc_array(state, NTSTATUS, state->num_domains);
+ state->seqnums = talloc_array(state, uint32_t, state->num_domains);
+
+ if ((state->subreqs == NULL) || (state->domains == NULL) ||
+ (state->statuses == NULL) || (state->seqnums == NULL)) {
+ tevent_req_nterror(req, NT_STATUS_NO_MEMORY);
+ return tevent_req_post(req, ev);
+ }
+
+ i = 0;
+
+ for (domain = domain_list(); domain != NULL; domain = domain->next) {
+ state->domains[i] = domain;
+ state->subreqs[i] = wb_seqnum_send(state->subreqs, ev, domain);
+ if (tevent_req_nomem(state->subreqs[i], req)) {
+ /* Don't even start all the other requests */
+ TALLOC_FREE(state->subreqs);
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(state->subreqs[i], wb_seqnums_done,
+ req);
+ i += 1;
+ }
+ return req;
+}
+
+static void wb_seqnums_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct wb_seqnums_state *state = tevent_req_data(
+ req, struct wb_seqnums_state);
+ NTSTATUS status;
+ uint32_t seqnum;
+ int i;
+
+ status = wb_seqnum_recv(subreq, &seqnum);
+
+ for (i=0; i<state->num_domains; i++) {
+ if (subreq == state->subreqs[i]) {
+ break;
+ }
+ }
+ if (i < state->num_domains) {
+ /* found one */
+
+ state->subreqs[i] = NULL;
+ state->statuses[i] = status;
+ if (NT_STATUS_IS_OK(status)) {
+ state->seqnums[i] = seqnum;
+
+ /*
+ * This first assignment might be removed
+ * later
+ */
+ state->domains[i]->sequence_number = seqnum;
+
+ if (!wcache_store_seqnum(state->domains[i]->name,
+ state->seqnums[i],
+ time(NULL))) {
+ DEBUG(1, ("wcache_store_seqnum failed for "
+ "domain %s\n",
+ state->domains[i]->name));
+ }
+ }
+ }
+
+ TALLOC_FREE(subreq);
+
+ state->num_received += 1;
+
+ if (state->num_received >= state->num_domains) {
+ tevent_req_done(req);
+ }
+}
+
+NTSTATUS wb_seqnums_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx,
+ int *num_domains, struct winbindd_domain ***domains,
+ NTSTATUS **statuses, uint32_t **seqnums)
+{
+ struct wb_seqnums_state *state = tevent_req_data(
+ req, struct wb_seqnums_state);
+ NTSTATUS status;
+
+ if (tevent_req_is_nterror(req, &status)) {
+ return status;
+ }
+ *num_domains = state->num_domains;
+ *domains = talloc_move(mem_ctx, &state->domains);
+ *statuses = talloc_move(mem_ctx, &state->statuses);
+ *seqnums = talloc_move(mem_ctx, &state->seqnums);
+ return NT_STATUS_OK;
+}
diff --git a/source3/winbindd/wb_sids2xids.c b/source3/winbindd/wb_sids2xids.c
new file mode 100644
index 0000000..f0f6c23
--- /dev/null
+++ b/source3/winbindd/wb_sids2xids.c
@@ -0,0 +1,786 @@
+/*
+ Unix SMB/CIFS implementation.
+ async sids2xids
+ Copyright (C) Volker Lendecke 2011
+ Copyright (C) Michael Adam 2012
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "winbindd.h"
+#include "../libcli/security/security.h"
+#include "idmap_cache.h"
+#include "librpc/gen_ndr/ndr_winbind_c.h"
+#include "librpc/gen_ndr/ndr_netlogon.h"
+#include "lsa.h"
+
+struct wb_sids2xids_state {
+ struct tevent_context *ev;
+
+ const struct wb_parent_idmap_config *cfg;
+
+ struct dom_sid *sids;
+ uint32_t num_sids;
+
+ struct wbint_TransIDArray all_ids;
+
+ /* Used to translated the idx back into all_ids.ids[idx] */
+ uint32_t *tmp_idx;
+
+ uint32_t lookup_count;
+ struct dom_sid *lookup_sids;
+
+ struct wbint_TransIDArray map_ids_in;
+ struct wbint_TransIDArray map_ids_out;
+
+ /*
+ * Domain array to use for the idmap call. The output from
+ * lookupsids cannot be used directly since for migrated
+ * objects the returned domain SID can be different than the
+ * original one. The new domain SID cannot be combined with
+ * the RID from the previous domain.
+ *
+ * The proper way would be asking for the correct RID in the
+ * new domain, but this approach avoids id mappings for
+ * invalid SIDs.
+ */
+ struct lsa_RefDomainList idmap_doms;
+
+ uint32_t dom_index;
+ struct lsa_RefDomainList idmap_dom;
+ bool tried_dclookup;
+};
+
+static void wb_sids2xids_idmap_setup_done(struct tevent_req *subreq);
+static bool wb_sids2xids_in_cache(struct dom_sid *sid, struct id_map *map);
+static void wb_sids2xids_lookupsids_done(struct tevent_req *subreq);
+static void wb_sids2xids_done(struct tevent_req *subreq);
+static void wb_sids2xids_gotdc(struct tevent_req *subreq);
+static void wb_sids2xids_next_sids2unix(struct tevent_req *req);
+static enum id_type lsa_SidType_to_id_type(const enum lsa_SidType sid_type);
+
+struct tevent_req *wb_sids2xids_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ const struct dom_sid *sids,
+ const uint32_t num_sids)
+{
+ struct tevent_req *req, *subreq;
+ struct wb_sids2xids_state *state;
+ uint32_t i;
+ uint32_t num_valid = 0;
+
+ req = tevent_req_create(mem_ctx, &state,
+ struct wb_sids2xids_state);
+ if (req == NULL) {
+ return NULL;
+ }
+
+ D_INFO("WB command sids2xids start.\n"
+ "Resolving %"PRIu32" SID(s).\n", num_sids);
+
+ state->ev = ev;
+
+ state->num_sids = num_sids;
+
+ state->sids = talloc_zero_array(state, struct dom_sid, num_sids);
+ if (tevent_req_nomem(state->sids, req)) {
+ return tevent_req_post(req, ev);
+ }
+
+ for (i = 0; i < num_sids; i++) {
+ sid_copy(&state->sids[i], &sids[i]);
+ }
+
+ state->all_ids.num_ids = num_sids;
+ state->all_ids.ids = talloc_zero_array(state, struct wbint_TransID, num_sids);
+ if (tevent_req_nomem(state->all_ids.ids, req)) {
+ return tevent_req_post(req, ev);
+ }
+
+ state->tmp_idx = talloc_zero_array(state, uint32_t, num_sids);
+ if (tevent_req_nomem(state->tmp_idx, req)) {
+ return tevent_req_post(req, ev);
+ }
+
+ state->lookup_sids = talloc_zero_array(state, struct dom_sid, num_sids);
+ if (tevent_req_nomem(state->lookup_sids, req)) {
+ return tevent_req_post(req, ev);
+ }
+
+ state->map_ids_in.ids = talloc_zero_array(state, struct wbint_TransID, num_sids);
+ if (tevent_req_nomem(state->map_ids_in.ids, req)) {
+ return tevent_req_post(req, ev);
+ }
+
+ /*
+ * Extract those sids that can not be resolved from cache
+ * into a separate list to be handed to id mapping, keeping
+ * the same index.
+ */
+ for (i=0; i<state->num_sids; i++) {
+ struct wbint_TransID *cur_id = &state->all_ids.ids[i];
+ struct dom_sid domain_sid;
+ struct dom_sid_buf buf;
+ struct id_map map = { .status = ID_UNMAPPED, };
+ uint32_t rid = 0;
+ bool in_cache;
+
+ sid_copy(&domain_sid, &state->sids[i]);
+ sid_split_rid(&domain_sid, &rid);
+
+ /*
+ * Start with an invalid entry.
+ */
+ *cur_id = (struct wbint_TransID) {
+ .type_hint = ID_TYPE_NOT_SPECIFIED,
+ .domain_index = UINT32_MAX - 1, /* invalid */
+ .rid = rid,
+ .xid = {
+ .id = UINT32_MAX,
+ .type = ID_TYPE_NOT_SPECIFIED,
+ },
+ };
+
+ D_DEBUG("%"PRIu32": SID %s\n",
+ i, dom_sid_str_buf(&state->sids[i], &buf));
+
+ in_cache = wb_sids2xids_in_cache(&state->sids[i], &map);
+ if (in_cache) {
+ /*
+ * We used to ignore map.status and just rely
+ * on map.xid.type.
+ *
+ * Lets keep this logic for now...
+ */
+
+ cur_id->xid = map.xid;
+ cur_id->domain_index = UINT32_MAX; /* this marks it as filled entry */
+ num_valid += 1;
+ continue;
+ }
+ }
+
+ D_DEBUG("Found %"PRIu32" (out of %"PRIu32") SID(s) in cache.\n",
+ num_valid, num_sids);
+ if (num_valid == num_sids) {
+ tevent_req_done(req);
+ return tevent_req_post(req, ev);
+ }
+
+ subreq = wb_parent_idmap_setup_send(state, state->ev);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, wb_sids2xids_idmap_setup_done, req);
+ return req;
+}
+
+static void wb_sids2xids_idmap_setup_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct wb_sids2xids_state *state = tevent_req_data(
+ req, struct wb_sids2xids_state);
+ NTSTATUS status;
+ uint32_t i;
+
+ status = wb_parent_idmap_setup_recv(subreq, &state->cfg);
+ TALLOC_FREE(subreq);
+ if (tevent_req_nterror(req, status)) {
+ D_WARNING("Failed with %s.\n", nt_errstr(status));
+ return;
+ }
+ SMB_ASSERT(state->cfg->num_doms > 0);
+ D_DEBUG("We will loop over %"PRIu32" SID(s) (skipping those already resolved via cache) and over %"PRIu32" domain(s).\n",
+ state->num_sids,
+ state->cfg->num_doms);
+
+ /*
+ * Now we build a list with all domain
+ * with non cached entries
+ */
+ for (i=0; i<state->num_sids; i++) {
+ struct wbint_TransID *t = &state->all_ids.ids[i];
+ struct dom_sid domain_sid;
+ const char *domain_name = NULL;
+ int domain_index;
+ uint32_t rid = 0;
+ uint32_t di;
+ struct dom_sid_buf buf0, buf1;
+
+ D_DEBUG("%"PRIu32": Processing SID %s\n",
+ i,
+ dom_sid_str_buf(&state->sids[i], &buf0));
+ if (t->domain_index == UINT32_MAX) {
+ /* ignore already filled entries */
+ D_DEBUG("%"PRIu32": Ignoring already resolved SID %s\n",
+ i,
+ dom_sid_str_buf(&state->sids[i], &buf0));
+ continue;
+ }
+
+ sid_copy(&domain_sid, &state->sids[i]);
+ sid_split_rid(&domain_sid, &rid);
+ D_DEBUG("%"PRIu32": Split SID %s into domain SID %s and RID %"PRIu32"\n",
+ i,
+ dom_sid_str_buf(&state->sids[i], &buf0),
+ dom_sid_str_buf(&domain_sid, &buf1),
+ rid);
+
+ if (t->type_hint == ID_TYPE_NOT_SPECIFIED) {
+ const char *tmp_name = NULL;
+ enum lsa_SidType sid_type = SID_NAME_USE_NONE;
+ const struct dom_sid *tmp_authority_sid = NULL;
+ const char *tmp_authority_name = NULL;
+
+ /*
+ * Try to get a type hint from for predefined sids
+ */
+ status = dom_sid_lookup_predefined_sid(&state->sids[i],
+ &tmp_name,
+ &sid_type,
+ &tmp_authority_sid,
+ &tmp_authority_name);
+ if (NT_STATUS_IS_OK(status)) {
+ t->type_hint = lsa_SidType_to_id_type(sid_type);
+ D_DEBUG("Got a type hint: %d from predefined SID.\n",
+ t->type_hint);
+ }
+ }
+
+ D_DEBUG("Looping over %"PRIu32" domain(s) to find domain SID %s.\n",
+ state->cfg->num_doms,
+ dom_sid_str_buf(&domain_sid, &buf0));
+ for (di = 0; di < state->cfg->num_doms; di++) {
+ struct wb_parent_idmap_config_dom *dom =
+ &state->cfg->doms[di];
+ bool match;
+
+ match = dom_sid_equal(&domain_sid, &dom->sid);
+ if (!match) {
+ continue;
+ }
+
+ domain_name = dom->name;
+ D_DEBUG("Found domain '%s'.\n", domain_name);
+ break;
+ }
+ if (domain_name == NULL) {
+ struct winbindd_domain *wb_domain = NULL;
+
+ D_DEBUG("Could not find a domain for domain SID %s. Trying to fill the domain name from list of known domains.\n",
+ dom_sid_str_buf(&domain_sid, &buf0));
+ /*
+ * Try to fill the name if we already know it
+ */
+ wb_domain = find_domain_from_sid_noinit(&state->sids[i]);
+ if (wb_domain != NULL) {
+ domain_name = wb_domain->name;
+ D_DEBUG("Found domain '%s' in list of known domains.\n", domain_name);
+ }
+ }
+ if (domain_name == NULL) {
+ domain_name = "";
+ D_DEBUG("Not found domain in list of known domains, setting empty domain name.\n");
+ }
+
+ if (t->type_hint == ID_TYPE_NOT_SPECIFIED) {
+ if (domain_name[0] != '\0') {
+ /*
+ * We know the domain, we indicate this
+ * by passing ID_TYPE_BOTH as a hint
+ *
+ * Maybe that's already enough for the backend
+ */
+ t->type_hint = ID_TYPE_BOTH;
+ D_DEBUG("Setting type hint ID_TYPE_BOTH for domain '%s'.\n", domain_name);
+ }
+ }
+
+ domain_index = init_lsa_ref_domain_list(state,
+ &state->idmap_doms,
+ domain_name,
+ &domain_sid);
+ if (domain_index == -1) {
+ tevent_req_oom(req);
+ return;
+ }
+ t->domain_index = domain_index;
+ }
+
+ /*
+ * We defer lookupsids because it requires domain controller
+ * interaction.
+ *
+ * First we ask the idmap child without explicit type hints.
+ * In most cases mappings already exist in the backend and
+ * a type_hint is not needed.
+ */
+ wb_sids2xids_next_sids2unix(req);
+}
+
+static bool wb_sids2xids_in_cache(struct dom_sid *sid, struct id_map *map)
+{
+ struct unixid id;
+ bool expired;
+
+ if (!winbindd_use_idmap_cache()) {
+ return false;
+ }
+ if (idmap_cache_find_sid2unixid(sid, &id, &expired)) {
+ if (expired && is_domain_online(find_our_domain())) {
+ return false;
+ }
+ map->sid = sid;
+ map->xid = id;
+ map->status = ID_MAPPED;
+ return true;
+ }
+ return false;
+}
+
+static void wb_sids2xids_lookupsids_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct wb_sids2xids_state *state = tevent_req_data(
+ req, struct wb_sids2xids_state);
+ struct lsa_RefDomainList *domains = NULL;
+ struct lsa_TransNameArray *names = NULL;
+ NTSTATUS status;
+ uint32_t li;
+
+ status = wb_lookupsids_recv(subreq, state, &domains, &names);
+ TALLOC_FREE(subreq);
+ if (tevent_req_nterror(req, status)) {
+ D_WARNING("Failed with %s.\n", nt_errstr(status));
+ return;
+ }
+
+ if (domains == NULL) {
+ tevent_req_nterror(req, NT_STATUS_INTERNAL_ERROR);
+ D_WARNING("Failed with NT_STATUS_INTERNAL_ERROR.\n");
+ return;
+ }
+
+ if (names == NULL) {
+ tevent_req_nterror(req, NT_STATUS_INTERNAL_ERROR);
+ D_WARNING("Failed with NT_STATUS_INTERNAL_ERROR.\n");
+ return;
+ }
+
+ for (li = 0; li < state->lookup_count; li++) {
+ struct lsa_TranslatedName *n = &names->names[li];
+ uint32_t ai = state->tmp_idx[li];
+ struct wbint_TransID *t = &state->all_ids.ids[ai];
+ enum id_type type_hint;
+
+ type_hint = lsa_SidType_to_id_type(n->sid_type);
+ if (type_hint != ID_TYPE_NOT_SPECIFIED) {
+ /*
+ * We know it's a valid user or group.
+ */
+ t->type_hint = type_hint;
+ continue;
+ }
+
+ if (n->sid_index == UINT32_MAX) {
+ /*
+ * The domain is not known, there's
+ * no point to try mapping again.
+ * mark is done and add a negative cache
+ * entry.
+ */
+ t->domain_index = UINT32_MAX; /* mark as valid */
+ idmap_cache_set_sid2unixid(&state->sids[ai], &t->xid);
+ continue;
+ }
+
+ if (n->sid_index >= domains->count) {
+ tevent_req_nterror(req, NT_STATUS_INTERNAL_ERROR);
+ D_WARNING("Failed with NT_STATUS_INTERNAL_ERROR.\n");
+ return;
+ }
+
+ if (domains->domains[n->sid_index].name.string == NULL) {
+ tevent_req_nterror(req, NT_STATUS_INTERNAL_ERROR);
+ D_WARNING("Failed with NT_STATUS_INTERNAL_ERROR.\n");
+ return;
+ }
+ if (domains->domains[n->sid_index].sid == NULL) {
+ tevent_req_nterror(req, NT_STATUS_INTERNAL_ERROR);
+ D_WARNING("Failed with NT_STATUS_INTERNAL_ERROR.\n");
+ return;
+ }
+
+ if (t->type_hint != ID_TYPE_NOT_SPECIFIED) {
+ /*
+ * We already tried with a type hint there's
+ * no point to try mapping again with ID_TYPE_BOTH.
+ *
+ * Mark is done and add a negative cache entry.
+ */
+ t->domain_index = UINT32_MAX; /* mark as valid */
+ idmap_cache_set_sid2unixid(&state->sids[ai], &t->xid);
+ continue;
+ }
+
+ /*
+ * We only know the domain exists, but the user doesn't
+ */
+ t->type_hint = ID_TYPE_BOTH;
+ }
+
+ TALLOC_FREE(names);
+ TALLOC_FREE(domains);
+
+ /*
+ * Now that we have type_hints for the remaining sids,
+ * we need to restart with the first domain.
+ */
+ state->dom_index = 0;
+ wb_sids2xids_next_sids2unix(req);
+}
+
+static void wb_sids2xids_next_sids2unix(struct tevent_req *req)
+{
+ struct wb_sids2xids_state *state = tevent_req_data(
+ req, struct wb_sids2xids_state);
+ struct tevent_req *subreq = NULL;
+ struct dcerpc_binding_handle *child_binding_handle = NULL;
+ const struct wbint_TransIDArray *src = NULL;
+ struct wbint_TransIDArray *dst = NULL;
+ uint32_t si;
+
+ next_domain:
+ state->tried_dclookup = false;
+
+ D_DEBUG("Processing next domain (dom_index=%"PRIu32", idmap_doms.count=%"PRIu32", lookup_count=%"PRIu32").\n",
+ state->dom_index,
+ state->idmap_doms.count,
+ state->lookup_count);
+ if (state->dom_index == state->idmap_doms.count) {
+ if (state->lookup_count != 0) {
+ /*
+ * We already called wb_lookupsids_send()
+ * before, so we're done.
+ */
+ D_DEBUG("We already called wb_lookupsids_send() before, so we're done.\n");
+ tevent_req_done(req);
+ return;
+ }
+
+ for (si=0; si < state->num_sids; si++) {
+ struct wbint_TransID *t = &state->all_ids.ids[si];
+
+ if (t->domain_index == UINT32_MAX) {
+ /* ignore already filled entries */
+ continue;
+ }
+
+ state->tmp_idx[state->lookup_count] = si;
+ sid_copy(&state->lookup_sids[state->lookup_count],
+ &state->sids[si]);
+ state->lookup_count += 1;
+ }
+
+ D_DEBUG("Prepared %"PRIu32" SID(s) for lookup wb_lookupsids_send().\n",
+ state->lookup_count);
+ if (state->lookup_count == 0) {
+ /*
+ * no wb_lookupsids_send() needed...
+ */
+ tevent_req_done(req);
+ return;
+ }
+
+ subreq = wb_lookupsids_send(state,
+ state->ev,
+ state->lookup_sids,
+ state->lookup_count);
+ if (tevent_req_nomem(subreq, req)) {
+ return;
+ }
+ tevent_req_set_callback(subreq, wb_sids2xids_lookupsids_done, req);
+ return;
+ }
+
+ src = &state->all_ids;
+ dst = &state->map_ids_in;
+ dst->num_ids = 0;
+
+ for (si=0; si < src->num_ids; si++) {
+ if (src->ids[si].domain_index != state->dom_index) {
+ continue;
+ }
+
+ state->tmp_idx[dst->num_ids] = si;
+ dst->ids[dst->num_ids] = src->ids[si];
+ dst->ids[dst->num_ids].domain_index = 0;
+ dst->num_ids += 1;
+ }
+
+ if (dst->num_ids == 0) {
+ state->dom_index += 1;
+ D_DEBUG("Go to next domain.\n");
+ goto next_domain;
+ }
+
+ state->idmap_dom = (struct lsa_RefDomainList) {
+ .count = 1,
+ .domains = &state->idmap_doms.domains[state->dom_index],
+ .max_size = 1
+ };
+
+ /*
+ * dcerpc_wbint_Sids2UnixIDs_send/recv will
+ * allocate a new array for the response
+ * and overwrite _ids->ids pointer.
+ *
+ * So we better make a temporary copy
+ * of state->map_ids_in (which contains the request array)
+ * into state->map_ids_out.
+ *
+ * That makes it possible to reuse the pre-allocated
+ * state->map_ids_in.ids array.
+ */
+ state->map_ids_out = state->map_ids_in;
+ child_binding_handle = idmap_child_handle();
+ subreq = dcerpc_wbint_Sids2UnixIDs_send(
+ state, state->ev, child_binding_handle, &state->idmap_dom,
+ &state->map_ids_out);
+ if (tevent_req_nomem(subreq, req)) {
+ return;
+ }
+ tevent_req_set_callback(subreq, wb_sids2xids_done, req);
+}
+
+static enum id_type lsa_SidType_to_id_type(const enum lsa_SidType sid_type)
+{
+ enum id_type type;
+
+ switch(sid_type) {
+ case SID_NAME_COMPUTER:
+ case SID_NAME_USER:
+ type = ID_TYPE_UID;
+ break;
+ case SID_NAME_DOM_GRP:
+ case SID_NAME_ALIAS:
+ case SID_NAME_WKN_GRP:
+ type = ID_TYPE_GID;
+ break;
+ default:
+ type = ID_TYPE_NOT_SPECIFIED;
+ break;
+ }
+
+ return type;
+}
+
+static void wb_sids2xids_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct wb_sids2xids_state *state = tevent_req_data(
+ req, struct wb_sids2xids_state);
+ NTSTATUS status, result;
+ const struct wbint_TransIDArray *src = NULL;
+ struct wbint_TransIDArray *dst = NULL;
+ uint32_t si;
+
+ status = dcerpc_wbint_Sids2UnixIDs_recv(subreq, state, &result);
+ TALLOC_FREE(subreq);
+
+ if (tevent_req_nterror(req, status)) {
+ D_WARNING("Failed with %s.\n", nt_errstr(status));
+ return;
+ }
+
+ if (NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) &&
+ !state->tried_dclookup) {
+
+ struct lsa_DomainInfo *d;
+
+ D_DEBUG("Domain controller not found. Calling wb_dsgetdcname_send() to get it.\n");
+ d = &state->idmap_doms.domains[state->dom_index];
+
+ subreq = wb_dsgetdcname_send(
+ state, state->ev, d->name.string, NULL, NULL,
+ DS_RETURN_DNS_NAME);
+ if (tevent_req_nomem(subreq, req)) {
+ return;
+ }
+ tevent_req_set_callback(subreq, wb_sids2xids_gotdc, req);
+ return;
+ }
+
+ src = &state->map_ids_out;
+ dst = &state->all_ids;
+
+ if (any_nt_status_not_ok(status, result, &status)) {
+ D_DEBUG("Either status %s or result %s is not ok. Report SIDs as not mapped.\n",
+ nt_errstr(status),
+ nt_errstr(result));
+ /*
+ * All we can do here is to report "not mapped"
+ */
+ src = &state->map_ids_in;
+ for (si=0; si < src->num_ids; si++) {
+ src->ids[si].xid.type = ID_TYPE_NOT_SPECIFIED;
+ }
+ }
+
+ if (src->num_ids != state->map_ids_in.num_ids) {
+ tevent_req_nterror(req, NT_STATUS_INTERNAL_ERROR);
+ D_WARNING("Number of mapped SIDs does not match. Failing with NT_STATUS_INTERNAL_ERROR.\n");
+ return;
+ }
+
+ for (si=0; si < src->num_ids; si++) {
+ uint32_t di = state->tmp_idx[si];
+
+ if (src->ids[si].xid.type == ID_TYPE_WB_REQUIRE_TYPE) {
+ if (state->lookup_count == 0) {
+ D_DEBUG("The backend asks for more information (a type_hint), we'll do a lookupsids later.\n");
+ /*
+ * The backend asks for more information
+ * (a type_hint), we'll do a lookupsids
+ * later.
+ */
+ continue;
+ }
+
+ /*
+ * lookupsids was not able to provide a type_hint that
+ * satisfied the backend.
+ *
+ * Make sure we don't expose ID_TYPE_WB_REQUIRE_TYPE
+ * outside of winbindd!
+ */
+ D_DEBUG("lookupsids was not able to provide a type_hint that satisfied the backend. Make sure we don't expose ID_TYPE_WB_REQUIRE_TYPE outside of winbindd!\n");
+ src->ids[si].xid.type = ID_TYPE_NOT_SPECIFIED;
+ }
+
+ if (src->ids[si].xid.type != ID_TYPE_NOT_SPECIFIED) {
+ dst->ids[di].xid = src->ids[si].xid;
+ D_DEBUG("%"PRIu32": Setting XID %"PRIu32"\n",
+ si, src->ids[si].xid.id);
+ }
+ dst->ids[di].domain_index = UINT32_MAX; /* mark as valid */
+ idmap_cache_set_sid2unixid(&state->sids[di], &dst->ids[di].xid);
+ }
+
+ state->map_ids_in.num_ids = 0;
+ if (NT_STATUS_IS_OK(status)) {
+ /*
+ * If we got a valid response, we expect
+ * state->map_ids_out.ids to be a new allocated
+ * array, which we want to free early.
+ */
+ SMB_ASSERT(state->map_ids_out.ids != state->map_ids_in.ids);
+ TALLOC_FREE(state->map_ids_out.ids);
+ }
+ state->map_ids_out = (struct wbint_TransIDArray) { .num_ids = 0, };
+
+ state->dom_index += 1;
+
+ wb_sids2xids_next_sids2unix(req);
+}
+
+static void wb_sids2xids_gotdc(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct wb_sids2xids_state *state = tevent_req_data(
+ req, struct wb_sids2xids_state);
+ struct dcerpc_binding_handle *child_binding_handle = NULL;
+ struct netr_DsRGetDCNameInfo *dcinfo;
+ NTSTATUS status;
+
+ status = wb_dsgetdcname_recv(subreq, state, &dcinfo);
+ TALLOC_FREE(subreq);
+ if (tevent_req_nterror(req, status)) {
+ D_WARNING("Failed with %s.\n", nt_errstr(status));
+ return;
+ }
+
+ state->tried_dclookup = true;
+
+ {
+ struct lsa_DomainInfo *d =
+ &state->idmap_doms.domains[state->dom_index];
+ const char *dom_name = d->name.string;
+
+ status = wb_dsgetdcname_gencache_set(dom_name, dcinfo);
+ if (tevent_req_nterror(req, status)) {
+ D_WARNING("Failed with %s.\n", nt_errstr(status));
+ return;
+ }
+ }
+
+ /*
+ * dcerpc_wbint_Sids2UnixIDs_send/recv will
+ * allocate a new array for the response
+ * and overwrite _ids->ids pointer.
+ *
+ * So we better make a temporary copy
+ * of state->map_ids_in (which contains the request array)
+ * into state->map_ids_out.
+ *
+ * That makes it possible to reuse the pre-allocated
+ * state->map_ids_in.ids array.
+ */
+ state->map_ids_out = state->map_ids_in;
+ child_binding_handle = idmap_child_handle();
+ subreq = dcerpc_wbint_Sids2UnixIDs_send(
+ state, state->ev, child_binding_handle, &state->idmap_dom,
+ &state->map_ids_out);
+ if (tevent_req_nomem(subreq, req)) {
+ return;
+ }
+ tevent_req_set_callback(subreq, wb_sids2xids_done, req);
+}
+
+NTSTATUS wb_sids2xids_recv(struct tevent_req *req,
+ struct unixid xids[], uint32_t num_xids)
+{
+ struct wb_sids2xids_state *state = tevent_req_data(
+ req, struct wb_sids2xids_state);
+ NTSTATUS status;
+ uint32_t i;
+
+ if (tevent_req_is_nterror(req, &status)) {
+ D_WARNING("Failed with %s.\n", nt_errstr(status));
+ return status;
+ }
+
+ if (num_xids != state->num_sids) {
+ D_WARNING("Error. We have resolved only %"PRIu32" XID(s), but caller asked for %"PRIu32".\n",
+ state->num_sids, num_xids);
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+
+ D_INFO("WB command sids2xids end.\n");
+ for (i=0; i<state->num_sids; i++) {
+ struct dom_sid_buf buf;
+ xids[i] = state->all_ids.ids[i].xid;
+ D_INFO("%"PRIu32": Found XID %"PRIu32" for SID %s\n",
+ i,
+ xids[i].id,
+ dom_sid_str_buf(&state->sids[i], &buf));
+ }
+
+ return NT_STATUS_OK;
+}
diff --git a/source3/winbindd/wb_xids2sids.c b/source3/winbindd/wb_xids2sids.c
new file mode 100644
index 0000000..86bd7f9
--- /dev/null
+++ b/source3/winbindd/wb_xids2sids.c
@@ -0,0 +1,422 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * async xids2sids
+ * Copyright (C) Volker Lendecke 2015
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "includes.h"
+#include "winbindd.h"
+#include "../libcli/security/security.h"
+#include "idmap_cache.h"
+#include "librpc/gen_ndr/ndr_winbind_c.h"
+#include "librpc/gen_ndr/ndr_netlogon.h"
+#include "passdb/lookup_sid.h"
+
+struct wb_xids2sids_dom_state {
+ struct tevent_context *ev;
+ struct unixid *all_xids;
+ const bool *cached;
+ size_t num_all_xids;
+ struct dom_sid *all_sids;
+ const struct wb_parent_idmap_config_dom *dom_map;
+ bool tried_dclookup;
+
+ size_t num_dom_xids;
+ struct unixid *dom_xids;
+ struct dom_sid *dom_sids;
+};
+
+static void wb_xids2sids_dom_done(struct tevent_req *subreq);
+static void wb_xids2sids_dom_gotdc(struct tevent_req *subreq);
+
+static struct tevent_req *wb_xids2sids_dom_send(
+ TALLOC_CTX *mem_ctx, struct tevent_context *ev,
+ const struct wb_parent_idmap_config_dom *dom_map,
+ struct unixid *xids,
+ const bool *cached,
+ size_t num_xids,
+ struct dom_sid *sids)
+{
+ struct tevent_req *req, *subreq;
+ struct wb_xids2sids_dom_state *state;
+ struct dcerpc_binding_handle *child_binding_handle = NULL;
+ size_t i;
+
+ req = tevent_req_create(mem_ctx, &state,
+ struct wb_xids2sids_dom_state);
+ if (req == NULL) {
+ return NULL;
+ }
+
+ D_DEBUG("Searching for %zu xid(s) in domain %s.\n",
+ num_xids,
+ dom_map->name);
+
+ state->ev = ev;
+ state->all_xids = xids;
+ state->cached = cached;
+ state->num_all_xids = num_xids;
+ state->all_sids = sids;
+ state->dom_map = dom_map;
+
+ state->dom_xids = talloc_array(state, struct unixid, num_xids);
+ if (tevent_req_nomem(state->dom_xids, req)) {
+ return tevent_req_post(req, ev);
+ }
+ state->dom_sids = talloc_array(state, struct dom_sid, num_xids);
+ if (tevent_req_nomem(state->dom_sids, req)) {
+ return tevent_req_post(req, ev);
+ }
+
+ for (i=0; i<num_xids; i++) {
+ struct unixid id = state->all_xids[i];
+
+ if ((id.id < dom_map->low_id) || (id.id > dom_map->high_id)) {
+ /* out of range */
+ D_DEBUG("%zu: XID %"PRIu32" is out of range.\n",
+ i, id.id);
+ continue;
+ }
+ if (state->cached[i]) {
+ /* already found in cache */
+ D_DEBUG("%zu: XID %"PRIu32" is already found in cache.\n",
+ i, id.id);
+ continue;
+ }
+ if (!is_null_sid(&state->all_sids[i])) {
+ /* already mapped in a previously asked domain */
+ D_DEBUG("%zu: XID %"PRIu32" is already mapped in a previously asked domain.\n",
+ i, id.id);
+ continue;
+ }
+ D_DEBUG("%zu: XID %"PRIu32" will be looked up via dcerpc_wbint_UnixIDs2Sids_send().\n",
+ i, id.id);
+ state->dom_xids[state->num_dom_xids++] = id;
+ }
+
+ if (state->num_dom_xids == 0) {
+ tevent_req_done(req);
+ return tevent_req_post(req, ev);
+ }
+
+ child_binding_handle = idmap_child_handle();
+ subreq = dcerpc_wbint_UnixIDs2Sids_send(
+ state, ev, child_binding_handle, dom_map->name, dom_map->sid,
+ state->num_dom_xids, state->dom_xids, state->dom_sids);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, wb_xids2sids_dom_done, req);
+ return req;
+}
+
+static void wb_xids2sids_dom_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct wb_xids2sids_dom_state *state = tevent_req_data(
+ req, struct wb_xids2sids_dom_state);
+ const struct wb_parent_idmap_config_dom *dom_map = state->dom_map;
+ NTSTATUS status, result;
+ size_t i;
+ size_t dom_sid_idx;
+
+ status = dcerpc_wbint_UnixIDs2Sids_recv(subreq, state, &result);
+ TALLOC_FREE(subreq);
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+
+ if (NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) &&
+ !state->tried_dclookup) {
+
+ subreq = wb_dsgetdcname_send(
+ state, state->ev, state->dom_map->name, NULL, NULL,
+ DS_RETURN_DNS_NAME);
+ if (tevent_req_nomem(subreq, req)) {
+ return;
+ }
+ tevent_req_set_callback(subreq, wb_xids2sids_dom_gotdc, req);
+ return;
+ }
+
+ if (!NT_STATUS_EQUAL(result, NT_STATUS_NONE_MAPPED) &&
+ tevent_req_nterror(req, result)) {
+ return;
+ }
+
+ dom_sid_idx = 0;
+
+ D_DEBUG("Processing response for %zu xid(s).\n", state->num_all_xids);
+ for (i=0; i<state->num_all_xids; i++) {
+ struct unixid *id = &state->all_xids[i];
+ struct dom_sid_buf buf;
+
+ if ((id->id < dom_map->low_id) || (id->id > dom_map->high_id)) {
+ /* out of range */
+ continue;
+ }
+ if (state->cached[i]) {
+ /* already found in cache */
+ continue;
+ }
+ if (!is_null_sid(&state->all_sids[i])) {
+ /* already mapped in a previously asked domain */
+ continue;
+ }
+
+ sid_copy(&state->all_sids[i], &state->dom_sids[dom_sid_idx]);
+ *id = state->dom_xids[dom_sid_idx];
+ D_DEBUG("%zu: XID %"PRIu32" mapped to SID %s.\n",
+ i,
+ id->id,
+ dom_sid_str_buf(&state->all_sids[i], &buf));
+
+ dom_sid_idx += 1;
+ }
+
+ tevent_req_done(req);
+}
+
+static void wb_xids2sids_dom_gotdc(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct wb_xids2sids_dom_state *state = tevent_req_data(
+ req, struct wb_xids2sids_dom_state);
+ struct dcerpc_binding_handle *child_binding_handle = NULL;
+ struct netr_DsRGetDCNameInfo *dcinfo;
+ NTSTATUS status;
+
+ status = wb_dsgetdcname_recv(subreq, state, &dcinfo);
+ TALLOC_FREE(subreq);
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+
+ state->tried_dclookup = true;
+
+ status = wb_dsgetdcname_gencache_set(state->dom_map->name, dcinfo);
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+
+ child_binding_handle = idmap_child_handle();
+ subreq = dcerpc_wbint_UnixIDs2Sids_send(
+ state, state->ev, child_binding_handle, state->dom_map->name,
+ state->dom_map->sid, state->num_dom_xids,
+ state->dom_xids, state->dom_sids);
+ if (tevent_req_nomem(subreq, req)) {
+ return;
+ }
+ tevent_req_set_callback(subreq, wb_xids2sids_dom_done, req);
+}
+
+static NTSTATUS wb_xids2sids_dom_recv(struct tevent_req *req)
+{
+ return tevent_req_simple_recv_ntstatus(req);
+}
+
+struct wb_xids2sids_state {
+ struct tevent_context *ev;
+ struct unixid *xids;
+ size_t num_xids;
+ struct dom_sid *sids;
+ bool *cached;
+
+ size_t dom_idx;
+ const struct wb_parent_idmap_config *cfg;
+};
+
+static void wb_xids2sids_idmap_setup_done(struct tevent_req *subreq);
+static void wb_xids2sids_done(struct tevent_req *subreq);
+
+struct tevent_req *wb_xids2sids_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ const struct unixid *xids,
+ uint32_t num_xids)
+{
+ struct tevent_req *req, *subreq;
+ struct wb_xids2sids_state *state;
+
+ req = tevent_req_create(mem_ctx, &state,
+ struct wb_xids2sids_state);
+ if (req == NULL) {
+ return NULL;
+ }
+
+ D_INFO("WB command xids2sids start.\nLooking up %"PRIu32" XID(s).\n",
+ num_xids);
+
+ state->ev = ev;
+ state->num_xids = num_xids;
+
+ state->xids = talloc_array(state, struct unixid, num_xids);
+ if (tevent_req_nomem(state->xids, req)) {
+ return tevent_req_post(req, ev);
+ }
+ memcpy(state->xids, xids, num_xids * sizeof(struct unixid));
+
+ state->sids = talloc_zero_array(state, struct dom_sid, num_xids);
+ if (tevent_req_nomem(state->sids, req)) {
+ return tevent_req_post(req, ev);
+ }
+
+ state->cached = talloc_zero_array(state, bool, num_xids);
+ if (tevent_req_nomem(state->cached, req)) {
+ return tevent_req_post(req, ev);
+ }
+
+ if (winbindd_use_idmap_cache()) {
+ uint32_t i;
+
+ for (i=0; i<num_xids; i++) {
+ struct dom_sid sid = {0};
+ bool ok, expired = true;
+
+ ok = idmap_cache_find_xid2sid(
+ &xids[i], &sid, &expired);
+ if (ok && !expired) {
+ struct dom_sid_buf buf;
+ DBG_DEBUG("Found %cID in cache: %s\n",
+ xids[i].type == ID_TYPE_UID?'U':'G',
+ dom_sid_str_buf(&sid, &buf));
+
+ sid_copy(&state->sids[i], &sid);
+ state->cached[i] = true;
+ }
+ }
+ }
+
+ subreq = wb_parent_idmap_setup_send(state, state->ev);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, wb_xids2sids_idmap_setup_done, req);
+ return req;
+}
+
+static void wb_xids2sids_idmap_setup_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct wb_xids2sids_state *state = tevent_req_data(
+ req, struct wb_xids2sids_state);
+ NTSTATUS status;
+
+ status = wb_parent_idmap_setup_recv(subreq, &state->cfg);
+ TALLOC_FREE(subreq);
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+ SMB_ASSERT(state->cfg->num_doms > 0);
+
+ subreq = wb_xids2sids_dom_send(
+ state, state->ev,
+ &state->cfg->doms[state->dom_idx],
+ state->xids, state->cached, state->num_xids, state->sids);
+ if (tevent_req_nomem(subreq, req)) {
+ return;
+ }
+ tevent_req_set_callback(subreq, wb_xids2sids_done, req);
+ return;
+}
+
+static void wb_xids2sids_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct wb_xids2sids_state *state = tevent_req_data(
+ req, struct wb_xids2sids_state);
+ size_t i;
+ NTSTATUS status;
+
+ status = wb_xids2sids_dom_recv(subreq);
+ TALLOC_FREE(subreq);
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+
+ state->dom_idx += 1;
+ if (state->dom_idx < state->cfg->num_doms) {
+ const struct wb_parent_idmap_config_dom *dom_map =
+ &state->cfg->doms[state->dom_idx];
+
+ subreq = wb_xids2sids_dom_send(state,
+ state->ev,
+ dom_map,
+ state->xids,
+ state->cached,
+ state->num_xids,
+ state->sids);
+ if (tevent_req_nomem(subreq, req)) {
+ return;
+ }
+ tevent_req_set_callback(subreq, wb_xids2sids_done, req);
+ return;
+ }
+
+
+ for (i = 0; i < state->num_xids; i++) {
+ /*
+ * Prime the cache after an xid2sid call. It's important that we
+ * use the xid value returned from the backend for the xid value
+ * passed to idmap_cache_set_sid2unixid(), not the input to
+ * wb_xids2sids_send: the input carries what was asked for,
+ * e.g. a ID_TYPE_UID. The result from the backend something the
+ * idmap child possibly changed to ID_TYPE_BOTH.
+ *
+ * And of course If the value was from the cache don't update
+ * the cache.
+ */
+
+ if (state->cached[i]) {
+ continue;
+ }
+
+ idmap_cache_set_sid2unixid(&state->sids[i], &state->xids[i]);
+ }
+
+ tevent_req_done(req);
+ return;
+}
+
+NTSTATUS wb_xids2sids_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx,
+ struct dom_sid **sids)
+{
+ struct wb_xids2sids_state *state = tevent_req_data(
+ req, struct wb_xids2sids_state);
+ NTSTATUS status;
+ size_t i;
+
+ D_INFO("WB command xids2sids end.\n");
+ if (tevent_req_is_nterror(req, &status)) {
+ D_WARNING("wb_sids_to_xids failed: %s\n", nt_errstr(status));
+ return status;
+ }
+
+ *sids = talloc_move(mem_ctx, &state->sids);
+ if (CHECK_DEBUGLVL(DBGLVL_INFO)) {
+ for (i = 0; i < state->num_xids; i++) {
+ struct dom_sid_buf buf;
+ D_INFO("%zu: XID %"PRIu32" mapped to SID %s\n",
+ i,
+ state->xids[i].id,
+ dom_sid_str_buf(&((*sids)[i]), &buf));
+ }
+ }
+ return NT_STATUS_OK;
+}
diff --git a/source3/winbindd/winbindd.c b/source3/winbindd/winbindd.c
new file mode 100644
index 0000000..29a24a9
--- /dev/null
+++ b/source3/winbindd/winbindd.c
@@ -0,0 +1,1742 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Winbind daemon for ntdom nss module
+
+ Copyright (C) by Tim Potter 2000-2002
+ Copyright (C) Andrew Tridgell 2002
+ Copyright (C) Jelmer Vernooij 2003
+ Copyright (C) Volker Lendecke 2004
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "lib/cmdline/cmdline.h"
+#include "winbindd.h"
+#include "nsswitch/winbind_client.h"
+#include "nsswitch/wb_reqtrans.h"
+#include "ntdomain.h"
+#include "librpc/rpc/dcesrv_core.h"
+#include "librpc/gen_ndr/ndr_lsa_scompat.h"
+#include "librpc/gen_ndr/ndr_samr_scompat.h"
+#include "librpc/gen_ndr/ndr_winbind_scompat.h"
+#include "secrets.h"
+#include "rpc_client/cli_netlogon.h"
+#include "idmap.h"
+#include "lib/addrchange.h"
+#include "auth.h"
+#include "messages.h"
+#include "../lib/util/pidfile.h"
+#include "util_cluster.h"
+#include "source4/lib/messaging/irpc.h"
+#include "source4/lib/messaging/messaging.h"
+#include "lib/param/param.h"
+#include "lib/async_req/async_sock.h"
+#include "libsmb/samlogon_cache.h"
+#include "libcli/auth/netlogon_creds_cli.h"
+#include "passdb.h"
+#include "lib/util/tevent_req_profile.h"
+#include "lib/gencache.h"
+#include "rpc_server/rpc_config.h"
+#include "lib/global_contexts.h"
+#include "source3/lib/substitute.h"
+#include "winbindd_traceid.h"
+#include "lib/util/util_process.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_WINBIND
+
+#define SCRUB_CLIENTS_INTERVAL 5
+
+static bool client_is_idle(struct winbindd_cli_state *state);
+static void remove_client(struct winbindd_cli_state *state);
+
+static bool interactive = False;
+
+/* Reload configuration */
+
+
+
+static void winbindd_status(void)
+{
+ struct winbindd_cli_state *tmp;
+
+ DEBUG(0, ("winbindd status:\n"));
+
+ /* Print client state information */
+
+ DEBUG(0, ("\t%d clients currently active\n", winbindd_num_clients()));
+
+ if (DEBUGLEVEL >= 2 && winbindd_num_clients()) {
+ DEBUG(2, ("\tclient list:\n"));
+ for(tmp = winbindd_client_list(); tmp; tmp = tmp->next) {
+ DEBUGADD(2, ("\t\tpid %lu, sock %d (%s)\n",
+ (unsigned long)tmp->pid, tmp->sock,
+ client_is_idle(tmp) ? "idle" : "active"));
+ }
+ }
+}
+
+/*
+ handle stdin becoming readable when we are in --foreground mode
+ */
+static void winbindd_stdin_handler(struct tevent_context *ev,
+ struct tevent_fd *fde,
+ uint16_t flags,
+ void *private_data)
+{
+ char c;
+ if (read(0, &c, 1) != 1) {
+ bool *is_parent = talloc_get_type_abort(private_data, bool);
+
+ /* we have reached EOF on stdin, which means the
+ parent has exited. Shutdown the server */
+ DEBUG(0,("EOF on stdin (is_parent=%d)\n",
+ (int)*is_parent));
+ winbindd_terminate(*is_parent);
+ }
+}
+
+bool winbindd_setup_stdin_handler(bool parent, bool foreground)
+{
+ bool *is_parent;
+
+ if (foreground) {
+ struct stat st;
+
+ is_parent = talloc(global_event_context(), bool);
+ if (!is_parent) {
+ return false;
+ }
+
+ *is_parent = parent;
+
+ /* if we are running in the foreground then look for
+ EOF on stdin, and exit if it happens. This allows
+ us to die if the parent process dies
+ Only do this on a pipe or socket, no other device.
+ */
+ if (fstat(0, &st) != 0) {
+ return false;
+ }
+ if (S_ISFIFO(st.st_mode) || S_ISSOCK(st.st_mode)) {
+ tevent_add_fd(global_event_context(),
+ is_parent,
+ 0,
+ TEVENT_FD_READ,
+ winbindd_stdin_handler,
+ is_parent);
+ }
+ }
+
+ return true;
+}
+
+static void winbindd_sig_chld_handler(struct tevent_context *ev,
+ struct tevent_signal *se,
+ int signum,
+ int count,
+ void *siginfo,
+ void *private_data)
+{
+ pid_t pid;
+
+ while ((pid = waitpid(-1, NULL, WNOHANG)) > 0) {
+ winbind_child_died(pid);
+ }
+}
+
+static bool winbindd_setup_sig_chld_handler(void)
+{
+ struct tevent_signal *se;
+
+ se = tevent_add_signal(global_event_context(),
+ global_event_context(),
+ SIGCHLD, 0,
+ winbindd_sig_chld_handler,
+ NULL);
+ if (!se) {
+ return false;
+ }
+
+ return true;
+}
+
+static void winbindd_sig_usr2_handler(struct tevent_context *ev,
+ struct tevent_signal *se,
+ int signum,
+ int count,
+ void *siginfo,
+ void *private_data)
+{
+ winbindd_status();
+}
+
+static bool winbindd_setup_sig_usr2_handler(void)
+{
+ struct tevent_signal *se;
+
+ se = tevent_add_signal(global_event_context(),
+ global_event_context(),
+ SIGUSR2, 0,
+ winbindd_sig_usr2_handler,
+ NULL);
+ if (!se) {
+ return false;
+ }
+
+ return true;
+}
+
+/* React on 'smbcontrol winbindd shutdown' in the same way as on SIGTERM*/
+static void msg_shutdown(struct messaging_context *msg,
+ void *private_data,
+ uint32_t msg_type,
+ struct server_id server_id,
+ DATA_BLOB *data)
+{
+ /* only the parent waits for this message */
+ DEBUG(0,("Got shutdown message\n"));
+ winbindd_terminate(true);
+}
+
+
+static void winbind_msg_validate_cache(struct messaging_context *msg_ctx,
+ void *private_data,
+ uint32_t msg_type,
+ struct server_id server_id,
+ DATA_BLOB *data)
+{
+ uint8_t ret;
+ pid_t child_pid;
+ NTSTATUS status;
+
+ DEBUG(10, ("winbindd_msg_validate_cache: got validate-cache "
+ "message.\n"));
+
+ /*
+ * call the validation code from a child:
+ * so we don't block the main winbindd and the validation
+ * code can safely use fork/waitpid...
+ */
+ child_pid = fork();
+
+ if (child_pid == -1) {
+ DEBUG(1, ("winbind_msg_validate_cache: Could not fork: %s\n",
+ strerror(errno)));
+ return;
+ }
+
+ if (child_pid != 0) {
+ /* parent */
+ DEBUG(5, ("winbind_msg_validate_cache: child created with "
+ "pid %d.\n", (int)child_pid));
+ return;
+ }
+
+ /* child */
+
+ status = winbindd_reinit_after_fork(NULL, NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(1, ("winbindd_reinit_after_fork failed: %s\n",
+ nt_errstr(status)));
+ _exit(0);
+ }
+
+ /* install default SIGCHLD handler: validation code uses fork/waitpid */
+ CatchSignal(SIGCHLD, SIG_DFL);
+
+ process_set_title("wb: check cache", "validate cache child");
+
+ ret = (uint8_t)winbindd_validate_cache_nobackup();
+ DEBUG(10, ("winbindd_msg_validata_cache: got return value %d\n", ret));
+ messaging_send_buf(msg_ctx, server_id, MSG_WINBIND_VALIDATE_CACHE, &ret,
+ (size_t)1);
+ _exit(0);
+}
+
+static struct winbindd_bool_dispatch_table {
+ enum winbindd_cmd cmd;
+ bool (*fn)(struct winbindd_cli_state *state);
+ const char *cmd_name;
+} bool_dispatch_table[] = {
+ { WINBINDD_INTERFACE_VERSION,
+ winbindd_interface_version,
+ "INTERFACE_VERSION" },
+ { WINBINDD_INFO,
+ winbindd_info,
+ "INFO" },
+ { WINBINDD_PING,
+ winbindd_ping,
+ "PING" },
+ { WINBINDD_DOMAIN_NAME,
+ winbindd_domain_name,
+ "DOMAIN_NAME" },
+ { WINBINDD_NETBIOS_NAME,
+ winbindd_netbios_name,
+ "NETBIOS_NAME" },
+ { WINBINDD_DC_INFO,
+ winbindd_dc_info,
+ "DC_INFO" },
+ { WINBINDD_CCACHE_NTLMAUTH,
+ winbindd_ccache_ntlm_auth,
+ "NTLMAUTH" },
+ { WINBINDD_CCACHE_SAVE,
+ winbindd_ccache_save,
+ "CCACHE_SAVE" },
+ { WINBINDD_PRIV_PIPE_DIR,
+ winbindd_priv_pipe_dir,
+ "WINBINDD_PRIV_PIPE_DIR" },
+ { WINBINDD_LIST_TRUSTDOM,
+ winbindd_list_trusted_domains,
+ "LIST_TRUSTDOM" },
+};
+
+struct winbindd_async_dispatch_table {
+ enum winbindd_cmd cmd;
+ const char *cmd_name;
+ struct tevent_req *(*send_req)(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct winbindd_cli_state *cli,
+ struct winbindd_request *request);
+ NTSTATUS (*recv_req)(struct tevent_req *req,
+ struct winbindd_response *presp);
+};
+
+static struct winbindd_async_dispatch_table async_nonpriv_table[] = {
+ { WINBINDD_LOOKUPSID, "LOOKUPSID",
+ winbindd_lookupsid_send, winbindd_lookupsid_recv },
+ { WINBINDD_LOOKUPSIDS, "LOOKUPSIDS",
+ winbindd_lookupsids_send, winbindd_lookupsids_recv },
+ { WINBINDD_LOOKUPNAME, "LOOKUPNAME",
+ winbindd_lookupname_send, winbindd_lookupname_recv },
+ { WINBINDD_SIDS_TO_XIDS, "SIDS_TO_XIDS",
+ winbindd_sids_to_xids_send, winbindd_sids_to_xids_recv },
+ { WINBINDD_XIDS_TO_SIDS, "XIDS_TO_SIDS",
+ winbindd_xids_to_sids_send, winbindd_xids_to_sids_recv },
+ { WINBINDD_GETPWSID, "GETPWSID",
+ winbindd_getpwsid_send, winbindd_getpwsid_recv },
+ { WINBINDD_GETPWNAM, "GETPWNAM",
+ winbindd_getpwnam_send, winbindd_getpwnam_recv },
+ { WINBINDD_GETPWUID, "GETPWUID",
+ winbindd_getpwuid_send, winbindd_getpwuid_recv },
+ { WINBINDD_GETSIDALIASES, "GETSIDALIASES",
+ winbindd_getsidaliases_send, winbindd_getsidaliases_recv },
+ { WINBINDD_GETUSERDOMGROUPS, "GETUSERDOMGROUPS",
+ winbindd_getuserdomgroups_send, winbindd_getuserdomgroups_recv },
+ { WINBINDD_GETGROUPS, "GETGROUPS",
+ winbindd_getgroups_send, winbindd_getgroups_recv },
+ { WINBINDD_SHOW_SEQUENCE, "SHOW_SEQUENCE",
+ winbindd_show_sequence_send, winbindd_show_sequence_recv },
+ { WINBINDD_GETGRGID, "GETGRGID",
+ winbindd_getgrgid_send, winbindd_getgrgid_recv },
+ { WINBINDD_GETGRNAM, "GETGRNAM",
+ winbindd_getgrnam_send, winbindd_getgrnam_recv },
+ { WINBINDD_GETUSERSIDS, "GETUSERSIDS",
+ winbindd_getusersids_send, winbindd_getusersids_recv },
+ { WINBINDD_LOOKUPRIDS, "LOOKUPRIDS",
+ winbindd_lookuprids_send, winbindd_lookuprids_recv },
+ { WINBINDD_SETPWENT, "SETPWENT",
+ winbindd_setpwent_send, winbindd_setpwent_recv },
+ { WINBINDD_GETPWENT, "GETPWENT",
+ winbindd_getpwent_send, winbindd_getpwent_recv },
+ { WINBINDD_ENDPWENT, "ENDPWENT",
+ winbindd_endpwent_send, winbindd_endpwent_recv },
+ { WINBINDD_DSGETDCNAME, "DSGETDCNAME",
+ winbindd_dsgetdcname_send, winbindd_dsgetdcname_recv },
+ { WINBINDD_GETDCNAME, "GETDCNAME",
+ winbindd_getdcname_send, winbindd_getdcname_recv },
+ { WINBINDD_SETGRENT, "SETGRENT",
+ winbindd_setgrent_send, winbindd_setgrent_recv },
+ { WINBINDD_GETGRENT, "GETGRENT",
+ winbindd_getgrent_send, winbindd_getgrent_recv },
+ { WINBINDD_ENDGRENT, "ENDGRENT",
+ winbindd_endgrent_send, winbindd_endgrent_recv },
+ { WINBINDD_LIST_USERS, "LIST_USERS",
+ winbindd_list_users_send, winbindd_list_users_recv },
+ { WINBINDD_LIST_GROUPS, "LIST_GROUPS",
+ winbindd_list_groups_send, winbindd_list_groups_recv },
+ { WINBINDD_CHECK_MACHACC, "CHECK_MACHACC",
+ winbindd_check_machine_acct_send, winbindd_check_machine_acct_recv },
+ { WINBINDD_PING_DC, "PING_DC",
+ winbindd_ping_dc_send, winbindd_ping_dc_recv },
+ { WINBINDD_PAM_AUTH, "PAM_AUTH",
+ winbindd_pam_auth_send, winbindd_pam_auth_recv },
+ { WINBINDD_PAM_LOGOFF, "PAM_LOGOFF",
+ winbindd_pam_logoff_send, winbindd_pam_logoff_recv },
+ { WINBINDD_PAM_CHAUTHTOK, "PAM_CHAUTHTOK",
+ winbindd_pam_chauthtok_send, winbindd_pam_chauthtok_recv },
+ { WINBINDD_PAM_CHNG_PSWD_AUTH_CRAP, "PAM_CHNG_PSWD_AUTH_CRAP",
+ winbindd_pam_chng_pswd_auth_crap_send,
+ winbindd_pam_chng_pswd_auth_crap_recv },
+ { WINBINDD_WINS_BYIP, "WINS_BYIP",
+ winbindd_wins_byip_send, winbindd_wins_byip_recv },
+ { WINBINDD_WINS_BYNAME, "WINS_BYNAME",
+ winbindd_wins_byname_send, winbindd_wins_byname_recv },
+ { WINBINDD_DOMAIN_INFO, "DOMAIN_INFO",
+ winbindd_domain_info_send, winbindd_domain_info_recv },
+
+ { 0, NULL, NULL, NULL }
+};
+
+static struct winbindd_async_dispatch_table async_priv_table[] = {
+ { WINBINDD_ALLOCATE_UID, "ALLOCATE_UID",
+ winbindd_allocate_uid_send, winbindd_allocate_uid_recv },
+ { WINBINDD_ALLOCATE_GID, "ALLOCATE_GID",
+ winbindd_allocate_gid_send, winbindd_allocate_gid_recv },
+ { WINBINDD_CHANGE_MACHACC, "CHANGE_MACHACC",
+ winbindd_change_machine_acct_send, winbindd_change_machine_acct_recv },
+ { WINBINDD_PAM_AUTH_CRAP, "PAM_AUTH_CRAP",
+ winbindd_pam_auth_crap_send, winbindd_pam_auth_crap_recv },
+
+ { 0, NULL, NULL, NULL }
+};
+
+struct process_request_state {
+ struct winbindd_cli_state *cli_state;
+ struct tevent_context *ev;
+};
+
+static void process_request_done(struct tevent_req *subreq);
+static void process_request_written(struct tevent_req *subreq);
+
+static struct tevent_req *process_request_send(
+ TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct winbindd_cli_state *cli_state)
+{
+ struct tevent_req *req, *subreq;
+ struct process_request_state *state;
+ struct winbindd_async_dispatch_table *atable;
+ enum winbindd_cmd cmd = cli_state->request->cmd;
+ size_t i;
+ bool ok;
+ static uint64_t request_index = 1;
+
+ /*
+ * debug traceid values:
+ * 0 .. inactive
+ * 1 .. not processing a winbind request, but in other code (timers)
+ * >=2 .. winbind request processing
+ */
+ if (debug_traceid_get() != 0) {
+ request_index = ++request_index == 0 ? 2 : request_index;
+ debug_traceid_set(request_index);
+ }
+ req = tevent_req_create(mem_ctx, &state,
+ struct process_request_state);
+ if (req == NULL) {
+ return NULL;
+ }
+ state->cli_state = cli_state;
+ state->ev = ev;
+
+ ok = tevent_req_set_profile(req);
+ if (!ok) {
+ return tevent_req_post(req, ev);
+ }
+
+ SMB_ASSERT(cli_state->mem_ctx == NULL);
+ cli_state->mem_ctx = talloc_named(cli_state, 0, "winbind request");
+ if (tevent_req_nomem(cli_state->mem_ctx, req)) {
+ return tevent_req_post(req, ev);
+ }
+
+ cli_state->response = talloc_zero(
+ cli_state->mem_ctx,
+ struct winbindd_response);
+ if (tevent_req_nomem(cli_state->response, req)) {
+ return tevent_req_post(req, ev);
+ }
+ cli_state->response->result = WINBINDD_PENDING;
+ cli_state->response->length = sizeof(struct winbindd_response);
+
+ /* Remember who asked us. */
+ cli_state->pid = cli_state->request->pid;
+ memcpy(cli_state->client_name,
+ cli_state->request->client_name,
+ sizeof(cli_state->client_name));
+
+ cli_state->cmd_name = "unknown request";
+ cli_state->recv_fn = NULL;
+
+ /* client is newest */
+ winbindd_promote_client(cli_state);
+
+ for (atable = async_nonpriv_table; atable->send_req; atable += 1) {
+ if (cmd == atable->cmd) {
+ break;
+ }
+ }
+
+ if ((atable->send_req == NULL) && cli_state->privileged) {
+ for (atable = async_priv_table; atable->send_req;
+ atable += 1) {
+ if (cmd == atable->cmd) {
+ break;
+ }
+ }
+ }
+
+ if (atable->send_req != NULL) {
+ cli_state->cmd_name = atable->cmd_name;
+ cli_state->recv_fn = atable->recv_req;
+
+ DBG_NOTICE("[%s (%d)] Handling async request: %s\n",
+ cli_state->client_name,
+ (int)cli_state->pid,
+ cli_state->cmd_name);
+
+ subreq = atable->send_req(
+ state,
+ state->ev,
+ cli_state,
+ cli_state->request);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, process_request_done, req);
+ return req;
+ }
+
+ for (i=0; i<ARRAY_SIZE(bool_dispatch_table); i++) {
+ if (cmd == bool_dispatch_table[i].cmd) {
+ break;
+ }
+ }
+
+ ok = false;
+
+ if (i < ARRAY_SIZE(bool_dispatch_table)) {
+ cli_state->cmd_name = bool_dispatch_table[i].cmd_name;
+
+ DBG_DEBUG("process_request: request fn %s\n",
+ bool_dispatch_table[i].cmd_name);
+ ok = bool_dispatch_table[i].fn(cli_state);
+ }
+
+ cli_state->response->result = ok ? WINBINDD_OK : WINBINDD_ERROR;
+
+ TALLOC_FREE(cli_state->io_req);
+ TALLOC_FREE(cli_state->request);
+
+ subreq = wb_resp_write_send(
+ state,
+ state->ev,
+ cli_state->out_queue,
+ cli_state->sock,
+ cli_state->response);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, process_request_written, req);
+
+ cli_state->io_req = subreq;
+
+ return req;
+}
+
+static void process_request_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct process_request_state *state = tevent_req_data(
+ req, struct process_request_state);
+ struct winbindd_cli_state *cli_state = state->cli_state;
+ NTSTATUS status;
+ bool ok;
+
+ status = cli_state->recv_fn(subreq, cli_state->response);
+ TALLOC_FREE(subreq);
+
+ DBG_NOTICE("[%s(%d):%s]: %s\n",
+ cli_state->client_name,
+ (int)cli_state->pid,
+ cli_state->cmd_name,
+ nt_errstr(status));
+
+ ok = NT_STATUS_IS_OK(status);
+ cli_state->response->result = ok ? WINBINDD_OK : WINBINDD_ERROR;
+
+ TALLOC_FREE(cli_state->io_req);
+ TALLOC_FREE(cli_state->request);
+
+ subreq = wb_resp_write_send(
+ state,
+ state->ev,
+ cli_state->out_queue,
+ cli_state->sock,
+ cli_state->response);
+ if (tevent_req_nomem(subreq, req)) {
+ return;
+ }
+ tevent_req_set_callback(subreq, process_request_written, req);
+
+ cli_state->io_req = subreq;
+}
+
+static void process_request_written(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct process_request_state *state = tevent_req_data(
+ req, struct process_request_state);
+ struct winbindd_cli_state *cli_state = state->cli_state;
+ ssize_t ret;
+ int err;
+
+ cli_state->io_req = NULL;
+
+ ret = wb_resp_write_recv(subreq, &err);
+ TALLOC_FREE(subreq);
+ if (ret == -1) {
+ tevent_req_nterror(req, map_nt_error_from_unix(err));
+ return;
+ }
+
+ DBG_DEBUG("[%s(%d):%s]: delivered response to client\n",
+ cli_state->client_name,
+ (int)cli_state->pid,
+ cli_state->cmd_name);
+
+ TALLOC_FREE(cli_state->mem_ctx);
+ cli_state->response = NULL;
+ cli_state->cmd_name = "no request";
+ cli_state->recv_fn = NULL;
+
+ tevent_req_done(req);
+}
+
+static NTSTATUS process_request_recv(
+ struct tevent_req *req,
+ TALLOC_CTX *mem_ctx,
+ struct tevent_req_profile **profile)
+{
+ NTSTATUS status;
+
+ if (tevent_req_is_nterror(req, &status)) {
+ tevent_req_received(req);
+ return status;
+ }
+
+ *profile = tevent_req_move_profile(req, mem_ctx);
+ tevent_req_received(req);
+ return NT_STATUS_OK;
+}
+
+/*
+ * This is the main event loop of winbind requests. It goes through a
+ * state-machine of 3 read/write requests, 4 if you have extra data to send.
+ *
+ * An idle winbind client has a read request of 4 bytes outstanding,
+ * finalizing function is request_len_recv, checking the length. request_recv
+ * then processes the packet. The processing function then at some point has
+ * to call request_finished which schedules sending the response.
+ */
+
+static void winbind_client_request_read(struct tevent_req *req);
+static void winbind_client_activity(struct tevent_req *req);
+static void winbind_client_processed(struct tevent_req *req);
+
+/* Process a new connection by adding it to the client connection list */
+
+static void new_connection(int listen_sock, bool privileged)
+{
+ struct sockaddr_un sunaddr;
+ struct winbindd_cli_state *state;
+ struct tevent_req *req;
+ socklen_t len;
+ int sock;
+
+ /* Accept connection */
+
+ len = sizeof(sunaddr);
+
+ sock = accept(listen_sock, (struct sockaddr *)(void *)&sunaddr, &len);
+
+ if (sock == -1) {
+ if (errno != EINTR) {
+ D_ERR("Failed to accept socket: %s\n", strerror(errno));
+ }
+ return;
+ }
+ smb_set_close_on_exec(sock);
+
+ D_INFO("Accepted client socket %d\n", sock);
+
+ /* Create new connection structure */
+
+ if ((state = talloc_zero(NULL, struct winbindd_cli_state)) == NULL) {
+ close(sock);
+ return;
+ }
+
+ state->sock = sock;
+
+ state->out_queue = tevent_queue_create(state, "winbind client reply");
+ if (state->out_queue == NULL) {
+ close(sock);
+ TALLOC_FREE(state);
+ return;
+ }
+
+ state->privileged = privileged;
+
+ req = wb_req_read_send(state, global_event_context(), state->sock,
+ WINBINDD_MAX_EXTRA_DATA);
+ if (req == NULL) {
+ TALLOC_FREE(state);
+ close(sock);
+ return;
+ }
+ tevent_req_set_callback(req, winbind_client_request_read, state);
+ state->io_req = req;
+
+ /* Add to connection list */
+
+ winbindd_add_client(state);
+}
+
+static void winbind_client_request_read(struct tevent_req *req)
+{
+ struct winbindd_cli_state *state = tevent_req_callback_data(
+ req, struct winbindd_cli_state);
+ ssize_t ret;
+ int err;
+
+ state->io_req = NULL;
+
+ ret = wb_req_read_recv(req, state, &state->request, &err);
+ TALLOC_FREE(req);
+ if (ret == -1) {
+ if (err == EPIPE) {
+ DEBUG(6, ("closing socket %d, client exited\n",
+ state->sock));
+ } else {
+ DEBUG(2, ("Could not read client request from fd %d: "
+ "%s\n", state->sock, strerror(err)));
+ }
+ close(state->sock);
+ state->sock = -1;
+ remove_client(state);
+ return;
+ }
+
+ req = wait_for_read_send(state, global_event_context(), state->sock,
+ true);
+ if (req == NULL) {
+ DEBUG(0, ("winbind_client_request_read[%d:%s]:"
+ " wait_for_read_send failed - removing client\n",
+ (int)state->pid, state->cmd_name));
+ remove_client(state);
+ return;
+ }
+ tevent_req_set_callback(req, winbind_client_activity, state);
+ state->io_req = req;
+
+ req = process_request_send(state, global_event_context(), state);
+ if (req == NULL) {
+ DBG_ERR("process_request_send failed\n");
+ remove_client(state);
+ return;
+ }
+ tevent_req_set_callback(req, winbind_client_processed, state);
+}
+
+static void winbind_client_activity(struct tevent_req *req)
+{
+ struct winbindd_cli_state *state =
+ tevent_req_callback_data(req, struct winbindd_cli_state);
+ int err;
+ bool has_data;
+
+ has_data = wait_for_read_recv(req, &err);
+
+ if (has_data) {
+ DEBUG(0, ("winbind_client_activity[%d:%s]:"
+ "unexpected data from client - removing client\n",
+ (int)state->pid, state->cmd_name));
+ } else {
+ if (err == EPIPE) {
+ DEBUG(6, ("winbind_client_activity[%d:%s]: "
+ "client has closed connection - removing "
+ "client\n",
+ (int)state->pid, state->cmd_name));
+ } else {
+ DEBUG(2, ("winbind_client_activity[%d:%s]: "
+ "client socket error (%s) - removing "
+ "client\n",
+ (int)state->pid, state->cmd_name,
+ strerror(err)));
+ }
+ }
+
+ remove_client(state);
+}
+
+static void winbind_client_processed(struct tevent_req *req)
+{
+ struct winbindd_cli_state *cli_state = tevent_req_callback_data(
+ req, struct winbindd_cli_state);
+ struct tevent_req_profile *profile = NULL;
+ struct timeval start, stop, diff;
+ int threshold;
+ NTSTATUS status;
+
+ status = process_request_recv(req, cli_state, &profile);
+ TALLOC_FREE(req);
+ if (!NT_STATUS_IS_OK(status)) {
+ DBG_DEBUG("process_request failed: %s\n", nt_errstr(status));
+ remove_client(cli_state);
+ return;
+ }
+
+ tevent_req_profile_get_start(profile, NULL, &start);
+ tevent_req_profile_get_stop(profile, NULL, &stop);
+ diff = tevent_timeval_until(&start, &stop);
+
+ threshold = lp_parm_int(-1, "winbind", "request profile threshold", 60);
+
+ if (diff.tv_sec >= threshold) {
+ int depth;
+ char *str;
+
+ depth = lp_parm_int(
+ -1,
+ "winbind",
+ "request profile depth",
+ INT_MAX);
+
+ DBG_ERR("request took %u.%.6u seconds\n",
+ (unsigned)diff.tv_sec, (unsigned)diff.tv_usec);
+
+ str = tevent_req_profile_string(
+ talloc_tos(), profile, 0, depth);
+ if (str != NULL) {
+ /* No "\n", already contained in "str" */
+ DEBUGADD(0, ("%s", str));
+ }
+ TALLOC_FREE(str);
+ }
+
+ TALLOC_FREE(profile);
+
+ req = wb_req_read_send(
+ cli_state,
+ global_event_context(),
+ cli_state->sock,
+ WINBINDD_MAX_EXTRA_DATA);
+ if (req == NULL) {
+ remove_client(cli_state);
+ return;
+ }
+ tevent_req_set_callback(req, winbind_client_request_read, cli_state);
+ cli_state->io_req = req;
+}
+
+/* Remove a client connection from client connection list */
+
+static void remove_client(struct winbindd_cli_state *state)
+{
+ /* It's a dead client - hold a funeral */
+
+ if (state == NULL) {
+ return;
+ }
+
+ /*
+ * We need to remove a pending wb_req_read_*
+ * or wb_resp_write_* request before closing the
+ * socket.
+ *
+ * This is important as they might have used tevent_add_fd() and we
+ * use the epoll * backend on linux. So we must remove the tevent_fd
+ * before closing the fd.
+ *
+ * Otherwise we might hit a race with close_conns_after_fork() (via
+ * winbindd_reinit_after_fork()) where a file descriptor
+ * is still open in a child, which means it's still active in
+ * the parents epoll queue, but the related tevent_fd is already
+ * already gone in the parent.
+ *
+ * See bug #11141.
+ */
+ TALLOC_FREE(state->io_req);
+
+ if (state->sock != -1) {
+ char c = 0;
+ int nwritten;
+
+ /* tell client, we are closing ... */
+ nwritten = write(state->sock, &c, sizeof(c));
+ if (nwritten == -1) {
+ DEBUG(2, ("final write to client failed: %s\n",
+ strerror(errno)));
+ }
+
+ /* Close socket */
+
+ close(state->sock);
+ state->sock = -1;
+ }
+
+ TALLOC_FREE(state->mem_ctx);
+
+ /* Remove from list and free */
+
+ winbindd_remove_client(state);
+ TALLOC_FREE(state);
+}
+
+/* Is a client idle? */
+
+static bool client_is_idle(struct winbindd_cli_state *state) {
+ return (state->request == NULL &&
+ state->response == NULL &&
+ !state->pwent_state && !state->grent_state);
+}
+
+/* Shutdown client connection which has been idle for the longest time */
+
+static bool remove_idle_client(void)
+{
+ struct winbindd_cli_state *state, *remove_state = NULL;
+ int nidle = 0;
+
+ for (state = winbindd_client_list(); state; state = state->next) {
+ if (client_is_idle(state)) {
+ nidle++;
+ /* list is sorted by access time */
+ remove_state = state;
+ }
+ }
+
+ if (remove_state) {
+ DEBUG(5,("Found %d idle client connections, shutting down sock %d, pid %u\n",
+ nidle, remove_state->sock, (unsigned int)remove_state->pid));
+ remove_client(remove_state);
+ return True;
+ }
+
+ return False;
+}
+
+/*
+ * Terminate all clients whose requests have taken longer than
+ * "winbind request timeout" seconds to process, or have been
+ * idle for more than "winbind request timeout" seconds.
+ */
+
+static void remove_timed_out_clients(void)
+{
+ struct winbindd_cli_state *state, *prev = NULL;
+ time_t curr_time = time(NULL);
+ int timeout_val = lp_winbind_request_timeout();
+
+ for (state = winbindd_client_list_tail(); state; state = prev) {
+ time_t expiry_time;
+
+ prev = winbindd_client_list_prev(state);
+ expiry_time = state->last_access + timeout_val;
+
+ if (curr_time <= expiry_time) {
+ /* list is sorted, previous clients in
+ list are newer */
+ break;
+ }
+
+ if (client_is_idle(state)) {
+ DEBUG(5,("Idle client timed out, "
+ "shutting down sock %d, pid %u\n",
+ state->sock,
+ (unsigned int)state->pid));
+ } else {
+ DEBUG(5,("Client request timed out, "
+ "shutting down sock %d, pid %u\n",
+ state->sock,
+ (unsigned int)state->pid));
+ }
+
+ remove_client(state);
+ }
+}
+
+static void winbindd_scrub_clients_handler(struct tevent_context *ev,
+ struct tevent_timer *te,
+ struct timeval current_time,
+ void *private_data)
+{
+ remove_timed_out_clients();
+ if (tevent_add_timer(ev, ev,
+ timeval_current_ofs(SCRUB_CLIENTS_INTERVAL, 0),
+ winbindd_scrub_clients_handler, NULL) == NULL) {
+ DEBUG(0, ("winbindd: failed to reschedule client scrubber\n"));
+ exit(1);
+ }
+}
+
+struct winbindd_listen_state {
+ bool privileged;
+ int fd;
+};
+
+static void winbindd_listen_fde_handler(struct tevent_context *ev,
+ struct tevent_fd *fde,
+ uint16_t flags,
+ void *private_data)
+{
+ struct winbindd_listen_state *s = talloc_get_type_abort(private_data,
+ struct winbindd_listen_state);
+
+ while (winbindd_num_clients() > lp_winbind_max_clients() - 1) {
+ DEBUG(5,("winbindd: Exceeding %d client "
+ "connections, removing idle "
+ "connection.\n", lp_winbind_max_clients()));
+ if (!remove_idle_client()) {
+ DEBUG(0,("winbindd: Exceeding %d "
+ "client connections, no idle "
+ "connection found\n",
+ lp_winbind_max_clients()));
+ break;
+ }
+ }
+ remove_timed_out_clients();
+ new_connection(s->fd, s->privileged);
+}
+
+/*
+ * Winbindd socket accessor functions
+ */
+
+static bool winbindd_setup_listeners(void)
+{
+ struct winbindd_listen_state *pub_state = NULL;
+ struct winbindd_listen_state *priv_state = NULL;
+ struct tevent_fd *fde;
+ int rc;
+ char *socket_path;
+
+ pub_state = talloc(global_event_context(),
+ struct winbindd_listen_state);
+ if (!pub_state) {
+ goto failed;
+ }
+
+ pub_state->privileged = false;
+ pub_state->fd = create_pipe_sock(
+ lp_winbindd_socket_directory(), WINBINDD_SOCKET_NAME, 0755);
+ if (pub_state->fd == -1) {
+ goto failed;
+ }
+ rc = listen(pub_state->fd, 5);
+ if (rc < 0) {
+ goto failed;
+ }
+
+ fde = tevent_add_fd(global_event_context(), pub_state, pub_state->fd,
+ TEVENT_FD_READ, winbindd_listen_fde_handler,
+ pub_state);
+ if (fde == NULL) {
+ close(pub_state->fd);
+ goto failed;
+ }
+ tevent_fd_set_auto_close(fde);
+
+ priv_state = talloc(global_event_context(),
+ struct winbindd_listen_state);
+ if (!priv_state) {
+ goto failed;
+ }
+
+ socket_path = get_winbind_priv_pipe_dir();
+ if (socket_path == NULL) {
+ goto failed;
+ }
+
+ priv_state->privileged = true;
+ priv_state->fd = create_pipe_sock(
+ socket_path, WINBINDD_SOCKET_NAME, 0750);
+ TALLOC_FREE(socket_path);
+ if (priv_state->fd == -1) {
+ goto failed;
+ }
+ rc = listen(priv_state->fd, 5);
+ if (rc < 0) {
+ goto failed;
+ }
+
+ fde = tevent_add_fd(global_event_context(), priv_state,
+ priv_state->fd, TEVENT_FD_READ,
+ winbindd_listen_fde_handler, priv_state);
+ if (fde == NULL) {
+ close(priv_state->fd);
+ goto failed;
+ }
+ tevent_fd_set_auto_close(fde);
+
+ winbindd_scrub_clients_handler(global_event_context(), NULL,
+ timeval_current(), NULL);
+ return true;
+failed:
+ TALLOC_FREE(pub_state);
+ TALLOC_FREE(priv_state);
+ return false;
+}
+
+static void winbindd_register_handlers(struct messaging_context *msg_ctx,
+ bool foreground)
+{
+ bool scan_trusts = true;
+ NTSTATUS status;
+ struct tevent_timer *te = NULL;
+
+ /* Setup signal handlers */
+
+ if (!winbindd_setup_sig_term_handler(true))
+ exit(1);
+ if (!winbindd_setup_stdin_handler(true, foreground))
+ exit(1);
+ if (!winbindd_setup_sig_hup_handler(NULL))
+ exit(1);
+ if (!winbindd_setup_sig_chld_handler())
+ exit(1);
+ if (!winbindd_setup_sig_usr2_handler())
+ exit(1);
+
+ CatchSignal(SIGPIPE, SIG_IGN); /* Ignore sigpipe */
+
+ /*
+ * Ensure all cache and idmap caches are consistent
+ * and initialized before we startup.
+ */
+ if (!winbindd_cache_validate_and_initialize()) {
+ exit(1);
+ }
+
+ /* React on 'smbcontrol winbindd reload-config' in the same way
+ as to SIGHUP signal */
+ messaging_register(msg_ctx, NULL,
+ MSG_SMB_CONF_UPDATED,
+ winbindd_msg_reload_services_parent);
+ messaging_register(msg_ctx, NULL,
+ MSG_SHUTDOWN, msg_shutdown);
+
+ /* Handle online/offline messages. */
+ messaging_register(msg_ctx, NULL,
+ MSG_WINBIND_OFFLINE, winbind_msg_offline);
+ messaging_register(msg_ctx, NULL,
+ MSG_WINBIND_ONLINE, winbind_msg_online);
+ messaging_register(msg_ctx, NULL,
+ MSG_WINBIND_ONLINESTATUS, winbind_msg_onlinestatus);
+
+ /* Handle domain online/offline messages for domains */
+ messaging_register(global_messaging_context(), NULL,
+ MSG_WINBIND_DOMAIN_OFFLINE, winbind_msg_domain_offline);
+ messaging_register(global_messaging_context(), NULL,
+ MSG_WINBIND_DOMAIN_ONLINE, winbind_msg_domain_online);
+
+ messaging_register(msg_ctx, NULL,
+ MSG_WINBIND_VALIDATE_CACHE,
+ winbind_msg_validate_cache);
+
+ messaging_register(msg_ctx, NULL,
+ MSG_WINBIND_DUMP_DOMAIN_LIST,
+ winbind_msg_dump_domain_list);
+
+ messaging_register(msg_ctx, NULL,
+ MSG_WINBIND_IP_DROPPED,
+ winbind_msg_ip_dropped_parent);
+
+ /* Register handler for MSG_DEBUG. */
+ messaging_register(msg_ctx, NULL,
+ MSG_DEBUG,
+ winbind_msg_debug);
+
+ messaging_register(msg_ctx, NULL,
+ MSG_WINBIND_DISCONNECT_DC,
+ winbind_disconnect_dc_parent);
+
+ netsamlogon_cache_init(); /* Non-critical */
+
+ /* clear the cached list of trusted domains */
+
+ wcache_tdc_clear();
+
+ if (!init_domain_list()) {
+ DEBUG(0,("unable to initialize domain list\n"));
+ exit(1);
+ }
+
+ status = init_idmap_child(global_event_context());
+ if (NT_STATUS_IS_ERR(status)) {
+ DBG_ERR("Unable to start idmap child: %s\n", nt_errstr(status));
+ exit(1);
+ }
+
+ status = init_locator_child(global_event_context());
+ if (NT_STATUS_IS_ERR(status)) {
+ DBG_ERR("Unable to start locator child: %s\n", nt_errstr(status));
+ exit(1);
+ }
+
+ smb_nscd_flush_user_cache();
+ smb_nscd_flush_group_cache();
+
+ if (!lp_winbind_scan_trusted_domains()) {
+ scan_trusts = false;
+ }
+
+ if (!lp_allow_trusted_domains()) {
+ scan_trusts = false;
+ }
+
+ if (IS_DC) {
+ scan_trusts = false;
+ }
+
+ if (scan_trusts) {
+ if (tevent_add_timer(global_event_context(), NULL, timeval_zero(),
+ rescan_trusted_domains, NULL) == NULL) {
+ DEBUG(0, ("Could not trigger rescan_trusted_domains()\n"));
+ exit(1);
+ }
+ }
+
+ te = tevent_add_timer(global_event_context(),
+ NULL,
+ timeval_zero(),
+ winbindd_ping_offline_domains,
+ NULL);
+ if (te == NULL) {
+ DBG_ERR("Failed to schedule winbindd_ping_offline_domains()\n");
+ exit(1);
+ }
+
+ status = wb_irpc_register();
+
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0, ("Could not register IRPC handlers\n"));
+ exit(1);
+ }
+}
+
+struct winbindd_addrchanged_state {
+ struct addrchange_context *ctx;
+ struct tevent_context *ev;
+ struct messaging_context *msg_ctx;
+};
+
+static void winbindd_addr_changed(struct tevent_req *req);
+
+static void winbindd_init_addrchange(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct messaging_context *msg_ctx)
+{
+ struct winbindd_addrchanged_state *state;
+ struct tevent_req *req;
+ NTSTATUS status;
+
+ state = talloc(mem_ctx, struct winbindd_addrchanged_state);
+ if (state == NULL) {
+ DEBUG(10, ("talloc failed\n"));
+ return;
+ }
+ state->ev = ev;
+ state->msg_ctx = msg_ctx;
+
+ status = addrchange_context_create(state, &state->ctx);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(10, ("addrchange_context_create failed: %s\n",
+ nt_errstr(status)));
+ TALLOC_FREE(state);
+ return;
+ }
+ req = addrchange_send(state, ev, state->ctx);
+ if (req == NULL) {
+ DEBUG(0, ("addrchange_send failed\n"));
+ TALLOC_FREE(state);
+ return;
+ }
+ tevent_req_set_callback(req, winbindd_addr_changed, state);
+}
+
+static void winbindd_addr_changed(struct tevent_req *req)
+{
+ struct winbindd_addrchanged_state *state = tevent_req_callback_data(
+ req, struct winbindd_addrchanged_state);
+ enum addrchange_type type;
+ struct sockaddr_storage addr;
+ NTSTATUS status;
+
+ status = addrchange_recv(req, &type, &addr);
+ TALLOC_FREE(req);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(10, ("addrchange_recv failed: %s, stop listening\n",
+ nt_errstr(status)));
+ TALLOC_FREE(state);
+ return;
+ }
+ if (type == ADDRCHANGE_DEL) {
+ char addrstr[INET6_ADDRSTRLEN];
+ DATA_BLOB blob;
+
+ print_sockaddr(addrstr, sizeof(addrstr), &addr);
+
+ DEBUG(3, ("winbindd: kernel (AF_NETLINK) dropped ip %s\n",
+ addrstr));
+
+ blob = data_blob_const(addrstr, strlen(addrstr)+1);
+
+ status = messaging_send(state->msg_ctx,
+ messaging_server_id(state->msg_ctx),
+ MSG_WINBIND_IP_DROPPED, &blob);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(10, ("messaging_send failed: %s - ignoring\n",
+ nt_errstr(status)));
+ }
+ }
+ req = addrchange_send(state, state->ev, state->ctx);
+ if (req == NULL) {
+ DEBUG(0, ("addrchange_send failed\n"));
+ TALLOC_FREE(state);
+ return;
+ }
+ tevent_req_set_callback(req, winbindd_addr_changed, state);
+}
+
+/* Main function */
+
+int main(int argc, const char **argv)
+{
+ static bool log_stdout = False;
+ struct samba_cmdline_daemon_cfg *cmdline_daemon_cfg = NULL;
+ struct poptOption long_options[] = {
+ POPT_AUTOHELP
+ {
+ .longName = "no-caching",
+ .shortName = 'n',
+ .argInfo = POPT_ARG_NONE,
+ .arg = NULL,
+ .val = 'n',
+ .descrip = "Disable caching",
+ },
+ POPT_COMMON_SAMBA
+ POPT_COMMON_DAEMON
+ POPT_COMMON_VERSION
+ POPT_TABLEEND
+ };
+ const struct loadparm_substitution *lp_sub =
+ loadparm_s3_global_substitution();
+ poptContext pc;
+ int opt;
+ TALLOC_CTX *frame;
+ NTSTATUS status;
+ bool ok;
+ const struct dcesrv_endpoint_server *ep_server = NULL;
+ struct dcesrv_context *dce_ctx = NULL;
+ size_t winbindd_socket_dir_len = 0;
+ char *winbindd_priv_socket_dir = NULL;
+ size_t winbindd_priv_socket_dir_len = 0;
+
+ setproctitle_init(argc, discard_const(argv), environ);
+
+ /*
+ * Do this before any other talloc operation
+ */
+ talloc_enable_null_tracking();
+ frame = talloc_stackframe();
+
+ /*
+ * We want total control over the permissions on created files,
+ * so set our umask to 0.
+ */
+ umask(0);
+
+ smb_init_locale();
+
+ /* glibc (?) likes to print "User defined signal 1" and exit if a
+ SIGUSR[12] is received before a handler is installed */
+
+ CatchSignal(SIGUSR1, SIG_IGN);
+ CatchSignal(SIGUSR2, SIG_IGN);
+
+ ok = samba_cmdline_init(frame,
+ SAMBA_CMDLINE_CONFIG_SERVER,
+ true /* require_smbconf */);
+ if (!ok) {
+ DBG_ERR("Failed to setup cmdline parser\n");
+ TALLOC_FREE(frame);
+ exit(1);
+ }
+
+ cmdline_daemon_cfg = samba_cmdline_get_daemon_cfg();
+
+ pc = samba_popt_get_context(getprogname(), argc, argv, long_options, 0);
+ if (pc == NULL) {
+ DBG_ERR("Failed to setup popt parser!\n");
+ TALLOC_FREE(frame);
+ exit(1);
+ }
+
+ while ((opt = poptGetNextOpt(pc)) != -1) {
+ switch (opt) {
+ case 'n':
+ winbindd_set_use_cache(false);
+ break;
+ default:
+ d_fprintf(stderr, "\nInvalid option %s: %s\n\n",
+ poptBadOption(pc, 0), poptStrerror(opt));
+ poptPrintUsage(pc, stderr, 0);
+ exit(1);
+ }
+ }
+
+ /* Set environment variable so we don't recursively call ourselves.
+ This may also be useful interactively. */
+ if ( !winbind_off() ) {
+ DEBUG(0,("Failed to disable recursive winbindd calls. Exiting.\n"));
+ exit(1);
+ }
+
+ /* Initialise for running in non-root mode */
+ sec_init();
+
+ set_remote_machine_name("winbindd", False);
+
+ dump_core_setup("winbindd", lp_logfile(talloc_tos(), lp_sub));
+ if (cmdline_daemon_cfg->daemon && cmdline_daemon_cfg->interactive) {
+ d_fprintf(stderr,"\nERROR: "
+ "Option -i|--interactive is not allowed together with -D|--daemon\n\n");
+ poptPrintUsage(pc, stderr, 0);
+ exit(1);
+ }
+
+ log_stdout = (debug_get_log_type() == DEBUG_STDOUT);
+ if (cmdline_daemon_cfg->interactive) {
+ /*
+ * libcmdline POPT_DAEMON callback sets "fork" to false if "-i"
+ * for interactive is passed on the commandline. Set it back to
+ * true. TODO: check if this is correct, smbd and nmbd don't do
+ * this.
+ */
+ cmdline_daemon_cfg->fork = true;
+ log_stdout = true;
+ }
+
+ if (log_stdout && cmdline_daemon_cfg->fork) {
+ d_fprintf(stderr, "\nERROR: "
+ "Can't log to stdout (-S) unless daemon is in "
+ "foreground (-F) or interactive (-i)\n\n");
+ poptPrintUsage(pc, stderr, 0);
+ exit(1);
+ }
+
+ poptFreeContext(pc);
+
+ reopen_logs();
+
+ DBG_STARTUP_NOTICE("winbindd version %s started.\n%s\n",
+ samba_version_string(),
+ samba_copyright_string());
+
+ /* After parsing the configuration file we setup the core path one more time
+ * as the log file might have been set in the configuration and cores's
+ * path is by default basename(lp_logfile()).
+ */
+ dump_core_setup("winbindd", lp_logfile(talloc_tos(), lp_sub));
+
+ if (lp_server_role() == ROLE_ACTIVE_DIRECTORY_DC) {
+ if (!lp_parm_bool(-1, "server role check", "inhibit", false)) {
+ DBG_ERR("server role = 'active directory domain controller' not compatible with running the winbindd binary. \n");
+ DEBUGADD(0, ("You should start 'samba' instead, and it will control starting the internal AD DC winbindd implementation, which is not the same as this one\n"));
+ exit(1);
+ }
+ /* Main 'samba' daemon will notify */
+ daemon_sd_notifications(false);
+ }
+
+ if (lp_security() == SEC_ADS) {
+ const char *realm = lp_realm();
+ const char *workgroup = lp_workgroup();
+
+ if (workgroup == NULL || strlen(workgroup) == 0) {
+ DBG_ERR("For 'secuirity = ADS' mode, the 'workgroup' "
+ "parameter is required to be set!\n");
+ exit(1);
+ }
+
+ if (realm == NULL || strlen(realm) == 0) {
+ DBG_ERR("For 'secuirity = ADS' mode, the 'realm' "
+ "parameter is required to be set!\n");
+ exit(1);
+ }
+ }
+
+ winbindd_socket_dir_len = strlen(lp_winbindd_socket_directory());
+ if (winbindd_socket_dir_len > 0) {
+ size_t winbindd_socket_len =
+ winbindd_socket_dir_len + 1 +
+ strlen(WINBINDD_SOCKET_NAME);
+ struct sockaddr_un un = {
+ .sun_family = AF_UNIX,
+ };
+ size_t sun_path_len = sizeof(un.sun_path);
+
+ if (winbindd_socket_len >= sun_path_len) {
+ DBG_ERR("The winbind socket path [%s/%s] is too long "
+ "(%zu >= %zu)\n",
+ lp_winbindd_socket_directory(),
+ WINBINDD_SOCKET_NAME,
+ winbindd_socket_len,
+ sun_path_len);
+ exit(1);
+ }
+ } else {
+ DBG_ERR("'winbindd_socket_directory' parameter is empty\n");
+ exit(1);
+ }
+
+ winbindd_priv_socket_dir = get_winbind_priv_pipe_dir();
+ winbindd_priv_socket_dir_len = strlen(winbindd_priv_socket_dir);
+ if (winbindd_priv_socket_dir_len > 0) {
+ size_t winbindd_priv_socket_len =
+ winbindd_priv_socket_dir_len + 1 +
+ strlen(WINBINDD_SOCKET_NAME);
+ struct sockaddr_un un = {
+ .sun_family = AF_UNIX,
+ };
+ size_t sun_path_len = sizeof(un.sun_path);
+
+ if (winbindd_priv_socket_len >= sun_path_len) {
+ DBG_ERR("The winbind privileged socket path [%s/%s] is too long "
+ "(%zu >= %zu)\n",
+ winbindd_priv_socket_dir,
+ WINBINDD_SOCKET_NAME,
+ winbindd_priv_socket_len,
+ sun_path_len);
+ exit(1);
+ }
+ } else {
+ DBG_ERR("'winbindd_priv_socket_directory' parameter is empty\n");
+ exit(1);
+ }
+ TALLOC_FREE(winbindd_priv_socket_dir);
+
+ if (!cluster_probe_ok()) {
+ exit(1);
+ }
+
+ /* Initialise messaging system */
+
+ if (global_messaging_context() == NULL) {
+ exit(1);
+ }
+
+ if (!winbindd_reload_services_file(NULL)) {
+ DEBUG(0, ("error opening config file\n"));
+ exit(1);
+ }
+
+ {
+ size_t i;
+ const char *idmap_backend;
+ const char *invalid_backends[] = {
+ "ad", "rfc2307", "rid",
+ };
+
+ idmap_backend = lp_idmap_default_backend();
+ for (i = 0; i < ARRAY_SIZE(invalid_backends); i++) {
+ ok = strequal(idmap_backend, invalid_backends[i]);
+ if (ok) {
+ DBG_ERR("FATAL: Invalid idmap backend %s "
+ "configured as the default backend!\n",
+ idmap_backend);
+ exit(1);
+ }
+ }
+ }
+
+ ok = directory_create_or_exist(lp_lock_directory(), 0755);
+ if (!ok) {
+ DEBUG(0, ("Failed to create directory %s for lock files - %s\n",
+ lp_lock_directory(), strerror(errno)));
+ exit(1);
+ }
+
+ ok = directory_create_or_exist(lp_pid_directory(), 0755);
+ if (!ok) {
+ DEBUG(0, ("Failed to create directory %s for pid files - %s\n",
+ lp_pid_directory(), strerror(errno)));
+ exit(1);
+ }
+
+ load_interfaces();
+
+ if (!secrets_init()) {
+
+ DEBUG(0,("Could not initialize domain trust account secrets. Giving up\n"));
+ return False;
+ }
+
+ status = rpccli_pre_open_netlogon_creds();
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0, ("rpccli_pre_open_netlogon_creds() - %s\n",
+ nt_errstr(status)));
+ exit(1);
+ }
+
+ /* Unblock all signals we are interested in as they may have been
+ blocked by the parent process. */
+
+ BlockSignals(False, SIGINT);
+ BlockSignals(False, SIGQUIT);
+ BlockSignals(False, SIGTERM);
+ BlockSignals(False, SIGUSR1);
+ BlockSignals(False, SIGUSR2);
+ BlockSignals(False, SIGHUP);
+ BlockSignals(False, SIGCHLD);
+
+ if (!interactive) {
+ become_daemon(cmdline_daemon_cfg->fork,
+ cmdline_daemon_cfg->no_process_group,
+ log_stdout);
+ } else {
+ daemon_status("winbindd", "Starting process ...");
+ }
+
+ pidfile_create(lp_pid_directory(), "winbindd");
+
+#ifdef HAVE_SETPGID
+ /*
+ * If we're interactive we want to set our own process group for
+ * signal management.
+ */
+ if (cmdline_daemon_cfg->interactive &&
+ !cmdline_daemon_cfg->no_process_group)
+ {
+ setpgid( (pid_t)0, (pid_t)0);
+ }
+#endif
+
+ TimeInit();
+
+ /* Don't use winbindd_reinit_after_fork here as
+ * we're just starting up and haven't created any
+ * winbindd-specific resources we must free yet. JRA.
+ */
+
+ status = reinit_after_fork(global_messaging_context(),
+ global_event_context(),
+ false);
+ if (!NT_STATUS_IS_OK(status)) {
+ exit_daemon("Winbindd reinit_after_fork() failed", map_errno_from_nt_status(status));
+ }
+
+ if (lp_winbind_debug_traceid()) {
+ winbind_debug_traceid_setup(global_event_context());
+ winbind_debug_call_depth_setup(debug_call_depth_addr());
+ tevent_thread_call_depth_set_callback(
+ debuglevel_get() > 1 ? winbind_call_flow : NULL,
+ NULL);
+ }
+ ok = initialize_password_db(true, global_event_context());
+ if (!ok) {
+ exit_daemon("Failed to initialize passdb backend! "
+ "Check the 'passdb backend' variable in your "
+ "smb.conf file.", EINVAL);
+ }
+
+ /*
+ * Do not initialize the parent-child-pipe before becoming
+ * a daemon: this is used to detect a died parent in the child
+ * process.
+ */
+ status = init_before_fork();
+ if (!NT_STATUS_IS_OK(status)) {
+ exit_daemon(nt_errstr(status), map_errno_from_nt_status(status));
+ }
+
+ winbindd_register_handlers(global_messaging_context(),
+ !cmdline_daemon_cfg->fork);
+
+ if (!messaging_parent_dgm_cleanup_init(global_messaging_context())) {
+ exit(1);
+ }
+
+ status = init_system_session_info(NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ exit_daemon("Winbindd failed to setup system user info", map_errno_from_nt_status(status));
+ }
+
+ DBG_INFO("Registering DCE/RPC endpoint servers\n");
+
+ ep_server = winbind_get_ep_server();
+ if (ep_server == NULL) {
+ DBG_ERR("Failed to get 'winbind' endpoint server\n");
+ exit(1);
+ }
+ status = dcerpc_register_ep_server(ep_server);
+ if (!NT_STATUS_IS_OK(status)) {
+ DBG_ERR("Failed to register 'winbind' endpoint "
+ "server: %s\n", nt_errstr(status));
+ exit(1);
+ }
+
+ dce_ctx = global_dcesrv_context();
+
+ DBG_INFO("Initializing DCE/RPC registered endpoint servers\n");
+
+ /* Init all registered ep servers */
+ status = dcesrv_init_registered_ep_servers(dce_ctx);
+ if (!NT_STATUS_IS_OK(status)) {
+ DBG_ERR("Failed to init DCE/RPC endpoint servers: %s\n",
+ nt_errstr(status));
+ exit(1);
+ }
+
+ winbindd_init_addrchange(NULL, global_event_context(),
+ global_messaging_context());
+
+ /* setup listen sockets */
+
+ if (!winbindd_setup_listeners()) {
+ exit_daemon("Winbindd failed to setup listeners", EPIPE);
+ }
+
+ irpc_add_name(winbind_imessaging_context(), "winbind_server");
+
+ TALLOC_FREE(frame);
+
+ if (!interactive) {
+ daemon_ready("winbindd");
+ }
+
+ gpupdate_init();
+
+ /* Loop waiting for requests */
+ while (1) {
+ frame = talloc_stackframe();
+
+ if (tevent_loop_once(global_event_context()) == -1) {
+ DEBUG(1, ("tevent_loop_once() failed: %s\n",
+ strerror(errno)));
+ return 1;
+ }
+
+ TALLOC_FREE(frame);
+ }
+
+ return 0;
+}
diff --git a/source3/winbindd/winbindd.h b/source3/winbindd/winbindd.h
new file mode 100644
index 0000000..53430a6
--- /dev/null
+++ b/source3/winbindd/winbindd.h
@@ -0,0 +1,370 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Winbind daemon for ntdom nss module
+
+ Copyright (C) Tim Potter 2000
+ Copyright (C) Jim McDonough <jmcd@us.ibm.com> 2003
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 3 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef _WINBINDD_H
+#define _WINBINDD_H
+
+#include "nsswitch/winbind_struct_protocol.h"
+#include "nsswitch/libwbclient/wbclient.h"
+#include "librpc/gen_ndr/dcerpc.h"
+#include "librpc/gen_ndr/winbind.h"
+
+#include "../lib/util/tevent_ntstatus.h"
+
+#ifdef HAVE_LIBNSCD
+#include <libnscd.h>
+#endif
+
+#ifdef HAVE_SYS_MMAN_H
+#include <sys/mman.h>
+#endif
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_WINBIND
+
+#define WB_REPLACE_CHAR '_'
+
+struct winbind_internal_pipes;
+struct ads_struct;
+
+struct winbindd_cli_state {
+ struct winbindd_cli_state *prev, *next; /* Linked list pointers */
+ int sock; /* Open socket from client */
+ pid_t pid; /* pid of client */
+ char client_name[32]; /* The process name of the client */
+ time_t last_access; /* Time of last access (read or write) */
+ bool privileged; /* Is the client 'privileged' */
+
+ TALLOC_CTX *mem_ctx; /* memory per request */
+ const char *cmd_name;
+ NTSTATUS (*recv_fn)(struct tevent_req *req,
+ struct winbindd_response *presp);
+ struct winbindd_request *request; /* Request from client */
+ struct tevent_queue *out_queue;
+ struct winbindd_response *response; /* Response to client */
+ struct tevent_req *io_req; /* wb_req_read_* or wb_resp_write_* */
+
+ struct getpwent_state *pwent_state; /* State for getpwent() */
+ struct getgrent_state *grent_state; /* State for getgrent() */
+};
+
+struct getpwent_state {
+ struct winbindd_domain *domain;
+ uint32_t next_user;
+ struct wbint_RidArray rids;
+};
+
+struct getgrent_state {
+ struct winbindd_domain *domain;
+ uint32_t next_group;
+ uint32_t num_groups;
+ struct wbint_Principal *groups;
+};
+
+/* Our connection to the DC */
+
+struct winbindd_cm_conn {
+ struct cli_state *cli;
+
+ enum dcerpc_AuthLevel auth_level;
+
+ struct rpc_pipe_client *samr_pipe;
+ struct policy_handle sam_connect_handle, sam_domain_handle;
+
+ struct rpc_pipe_client *lsa_pipe;
+ struct rpc_pipe_client *lsa_pipe_tcp;
+ struct policy_handle lsa_policy;
+
+ struct rpc_pipe_client *netlogon_pipe;
+ struct netlogon_creds_cli_context *netlogon_creds_ctx;
+ bool netlogon_force_reauth;
+};
+
+/* Async child */
+
+struct winbindd_domain;
+
+struct winbindd_child {
+ pid_t pid;
+ struct winbindd_domain *domain;
+ char *logfilename;
+
+ int sock;
+ struct tevent_fd *monitor_fde; /* Watch for dead children/sockets */
+ struct tevent_queue *queue;
+ struct dcerpc_binding_handle *binding_handle;
+
+ struct tevent_timer *lockout_policy_event;
+ struct tevent_timer *machine_password_change_event;
+};
+
+/* Structures to hold per domain information */
+
+struct winbindd_domain {
+ char *name; /* Domain name (NetBIOS) */
+ char *alt_name; /* alt Domain name, if any (FQDN for ADS) */
+ char *forest_name; /* Name of the AD forest we're in */
+ struct dom_sid sid; /* SID for this domain */
+ enum netr_SchannelType secure_channel_type;
+ uint32_t domain_flags; /* Domain flags from netlogon.h */
+ uint32_t domain_type; /* Domain type from netlogon.h */
+ uint32_t domain_trust_attribs; /* Trust attribs from netlogon.h */
+ struct winbindd_domain *routing_domain;
+ bool initialized; /* Did we already ask for the domain mode? */
+ bool native_mode; /* is this a win2k domain in native mode ? */
+ bool active_directory; /* is this a win2k active directory ? */
+ bool primary; /* is this our primary domain ? */
+ bool internal; /* BUILTIN and member SAM */
+ bool rodc; /* Are we an RODC for this AD domain? (do some operations locally) */
+ bool online; /* is this domain available ? */
+ time_t startup_time; /* When we set "startup" true. monotonic clock */
+ bool startup; /* are we in the first 30 seconds after startup_time ? */
+
+ bool can_do_ncacn_ip_tcp;
+
+ /*
+ * Lookup methods for this domain (LDAP or RPC). The backend
+ * methods are used by the cache layer.
+ */
+ struct winbindd_methods *backend;
+
+ struct {
+ struct winbind_internal_pipes *samr_pipes;
+ struct ads_struct *ads_conn;
+ } backend_data;
+
+ /* A working DC */
+ bool force_dc;
+ char *dcname;
+ const char *ping_dcname;
+ struct sockaddr_storage dcaddr;
+
+ /* Sequence number stuff */
+
+ time_t last_seq_check;
+ uint32_t sequence_number;
+ NTSTATUS last_status;
+
+ /* The smb connection */
+
+ struct winbindd_cm_conn conn;
+
+ /* The child pid we're talking to */
+
+ struct winbindd_child *children;
+
+ struct tevent_queue *queue;
+ struct dcerpc_binding_handle *binding_handle;
+
+ struct tevent_req *check_online_event;
+
+ /* Linked list info */
+
+ struct winbindd_domain *prev, *next;
+};
+
+struct wb_parent_idmap_config_dom {
+ unsigned low_id;
+ unsigned high_id;
+ const char *name;
+ struct dom_sid sid;
+};
+
+struct wb_parent_idmap_config {
+ struct tevent_queue *queue;
+ uint32_t num_doms;
+ bool initialized;
+ struct wb_parent_idmap_config_dom *doms;
+};
+
+struct wb_acct_info {
+ const char *acct_name; /* account name */
+ const char *acct_desc; /* account name */
+ uint32_t rid; /* domain-relative RID */
+};
+
+/* per-domain methods. This is how LDAP vs RPC is selected
+ */
+struct winbindd_methods {
+ /* does this backend provide a consistent view of the data? (ie. is the primary group
+ always correct) */
+ bool consistent;
+
+ /* get a list of users, returning a wbint_userinfo for each one */
+ NTSTATUS (*query_user_list)(struct winbindd_domain *domain,
+ TALLOC_CTX *mem_ctx,
+ uint32_t **rids);
+
+ /* get a list of domain groups */
+ NTSTATUS (*enum_dom_groups)(struct winbindd_domain *domain,
+ TALLOC_CTX *mem_ctx,
+ uint32_t *num_entries,
+ struct wb_acct_info **info);
+
+ /* get a list of domain local groups */
+ NTSTATUS (*enum_local_groups)(struct winbindd_domain *domain,
+ TALLOC_CTX *mem_ctx,
+ uint32_t *num_entries,
+ struct wb_acct_info **info);
+
+ /* convert one user or group name to a sid */
+ NTSTATUS (*name_to_sid)(struct winbindd_domain *domain,
+ TALLOC_CTX *mem_ctx,
+ const char *domain_name,
+ const char *name,
+ uint32_t flags,
+ const char **pdom_name,
+ struct dom_sid *sid,
+ enum lsa_SidType *type);
+
+ /* convert a sid to a user or group name */
+ NTSTATUS (*sid_to_name)(struct winbindd_domain *domain,
+ TALLOC_CTX *mem_ctx,
+ const struct dom_sid *sid,
+ char **domain_name,
+ char **name,
+ enum lsa_SidType *type);
+
+ NTSTATUS (*rids_to_names)(struct winbindd_domain *domain,
+ TALLOC_CTX *mem_ctx,
+ const struct dom_sid *domain_sid,
+ uint32_t *rids,
+ size_t num_rids,
+ char **domain_name,
+ char ***names,
+ enum lsa_SidType **types);
+
+ /* lookup all groups that a user is a member of. The backend
+ can also choose to lookup by username or rid for this
+ function */
+ NTSTATUS (*lookup_usergroups)(struct winbindd_domain *domain,
+ TALLOC_CTX *mem_ctx,
+ const struct dom_sid *user_sid,
+ uint32_t *num_groups, struct dom_sid **user_gids);
+
+ /* Lookup all aliases that the sids delivered are member of. This is
+ * to implement 'domain local groups' correctly */
+ NTSTATUS (*lookup_useraliases)(struct winbindd_domain *domain,
+ TALLOC_CTX *mem_ctx,
+ uint32_t num_sids,
+ const struct dom_sid *sids,
+ uint32_t *num_aliases,
+ uint32_t **alias_rids);
+
+ /* find all members of the group with the specified group_rid */
+ NTSTATUS (*lookup_groupmem)(struct winbindd_domain *domain,
+ TALLOC_CTX *mem_ctx,
+ const struct dom_sid *group_sid,
+ enum lsa_SidType type,
+ uint32_t *num_names,
+ struct dom_sid **sid_mem, char ***names,
+ uint32_t **name_types);
+
+ /* find all members of the alias with the specified alias_sid */
+ NTSTATUS (*lookup_aliasmem)(struct winbindd_domain *domain,
+ TALLOC_CTX *mem_ctx,
+ const struct dom_sid *alias_sid,
+ enum lsa_SidType type,
+ uint32_t *num_sids,
+ struct dom_sid **sid_mem);
+
+ /* return the lockout policy */
+ NTSTATUS (*lockout_policy)(struct winbindd_domain *domain,
+ TALLOC_CTX *mem_ctx,
+ struct samr_DomInfo12 *lockout_policy);
+
+ /* return the lockout policy */
+ NTSTATUS (*password_policy)(struct winbindd_domain *domain,
+ TALLOC_CTX *mem_ctx,
+ struct samr_DomInfo1 *password_policy);
+
+ /* enumerate trusted domains */
+ NTSTATUS (*trusted_domains)(struct winbindd_domain *domain,
+ TALLOC_CTX *mem_ctx,
+ struct netr_DomainTrustList *trusts);
+};
+
+/* Filled out by IDMAP backends */
+struct winbindd_idmap_methods {
+ /* Called when backend is first loaded */
+ bool (*init)(void);
+
+ bool (*get_sid_from_uid)(uid_t uid, struct dom_sid *sid);
+ bool (*get_sid_from_gid)(gid_t gid, struct dom_sid *sid);
+
+ bool (*get_uid_from_sid)(struct dom_sid *sid, uid_t *uid);
+ bool (*get_gid_from_sid)(struct dom_sid *sid, gid_t *gid);
+
+ /* Called when backend is unloaded */
+ bool (*close)(void);
+ /* Called to dump backend status */
+ void (*status)(void);
+};
+
+/* Data structures for dealing with the trusted domain cache */
+
+struct winbindd_tdc_domain {
+ const char *domain_name;
+ const char *dns_name;
+ struct dom_sid sid;
+ uint32_t trust_flags;
+ uint32_t trust_attribs;
+ uint32_t trust_type;
+};
+
+struct WINBINDD_MEMORY_CREDS {
+ struct WINBINDD_MEMORY_CREDS *next, *prev;
+ const char *username; /* lookup key. */
+ uid_t uid;
+ int ref_count;
+ size_t len;
+ uint8_t *nt_hash; /* Base pointer for the following 2 */
+ uint8_t *lm_hash;
+ char *pass;
+};
+
+struct WINBINDD_CCACHE_ENTRY {
+ struct WINBINDD_CCACHE_ENTRY *next, *prev;
+ const char *principal_name;
+ const char *ccname;
+ const char *service;
+ const char *username;
+ const char *realm;
+ const char *canon_principal;
+ const char *canon_realm;
+ struct WINBINDD_MEMORY_CREDS *cred_ptr;
+ int ref_count;
+ uid_t uid;
+ time_t create_time;
+ time_t renew_until;
+ time_t refresh_time;
+ struct tevent_timer *event;
+};
+
+#include "winbindd/winbindd_proto.h"
+
+#define WINBINDD_ESTABLISH_LOOP 30
+#define WINBINDD_RESCAN_FREQ lp_winbind_cache_time()
+#define WINBINDD_PAM_AUTH_KRB5_RENEW_TIME 2592000 /* one month */
+#define DOM_SEQUENCE_NONE ((uint32_t)-1)
+
+#endif /* _WINBINDD_H */
diff --git a/source3/winbindd/winbindd_ads.c b/source3/winbindd/winbindd_ads.c
new file mode 100644
index 0000000..7e572e5
--- /dev/null
+++ b/source3/winbindd/winbindd_ads.c
@@ -0,0 +1,1604 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Winbind ADS backend functions
+
+ Copyright (C) Andrew Tridgell 2001
+ Copyright (C) Andrew Bartlett <abartlet@samba.org> 2003
+ Copyright (C) Gerald (Jerry) Carter 2004
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "winbindd.h"
+#include "winbindd_ads.h"
+#include "libsmb/namequery.h"
+#include "rpc_client/rpc_client.h"
+#include "../librpc/gen_ndr/ndr_netlogon_c.h"
+#include "../libds/common/flags.h"
+#include "ads.h"
+#include "../libcli/ldap/ldap_ndr.h"
+#include "../libcli/security/security.h"
+#include "../libds/common/flag_mapping.h"
+#include "libsmb/samlogon_cache.h"
+#include "passdb.h"
+#include "auth/credentials/credentials.h"
+
+#ifdef HAVE_ADS
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_WINBIND
+
+extern struct winbindd_methods reconnect_methods;
+extern struct winbindd_methods msrpc_methods;
+
+#define WINBIND_CCACHE_NAME "MEMORY:winbind_ccache"
+
+/**
+ * Check if cached connection can be reused. If the connection cannot
+ * be reused the ADS_STRUCT is freed and the pointer is set to NULL.
+ */
+static void ads_cached_connection_reuse(ADS_STRUCT **adsp)
+{
+
+ ADS_STRUCT *ads = *adsp;
+
+ if (ads != NULL) {
+ time_t expire;
+ time_t now = time(NULL);
+
+ expire = MIN(ads->auth.tgt_expire, ads->auth.tgs_expire);
+
+ DEBUG(7, ("Current tickets expire in %d seconds (at %d, time "
+ "is now %d)\n", (uint32_t)expire - (uint32_t)now,
+ (uint32_t) expire, (uint32_t) now));
+
+ if ( ads->config.realm && (expire > now)) {
+ return;
+ } else {
+ /* we own this ADS_STRUCT so make sure it goes away */
+ DEBUG(7,("Deleting expired krb5 credential cache\n"));
+ TALLOC_FREE(ads);
+ ads_kdestroy(WINBIND_CCACHE_NAME);
+ *adsp = NULL;
+ }
+ }
+}
+
+/**
+ * @brief Establish a connection to a DC
+ *
+ * @param[out] adsp ADS_STRUCT that will be created
+ * @param[in] target_realm Realm of domain to connect to
+ * @param[in] target_dom_name 'workgroup' name of domain to connect to
+ * @param[in] ldap_server DNS name of server to connect to
+ * @param[in] password Our machine account secret
+ * @param[in] auth_realm Realm of local domain for creating krb token
+ * @param[in] renewable Renewable ticket time
+ *
+ * @return ADS_STATUS
+ */
+static ADS_STATUS ads_cached_connection_connect(const char *target_realm,
+ const char *target_dom_name,
+ const char *ldap_server,
+ char *password,
+ char *auth_realm,
+ time_t renewable,
+ TALLOC_CTX *mem_ctx,
+ ADS_STRUCT **adsp)
+{
+ TALLOC_CTX *tmp_ctx = talloc_stackframe();
+ ADS_STRUCT *ads;
+ ADS_STATUS status;
+ struct sockaddr_storage dc_ss;
+ fstring dc_name;
+ enum credentials_use_kerberos krb5_state;
+
+ if (auth_realm == NULL) {
+ TALLOC_FREE(tmp_ctx);
+ return ADS_ERROR_NT(NT_STATUS_UNSUCCESSFUL);
+ }
+
+ /* we don't want this to affect the users ccache */
+ setenv("KRB5CCNAME", WINBIND_CCACHE_NAME, 1);
+
+ ads = ads_init(tmp_ctx,
+ target_realm,
+ target_dom_name,
+ ldap_server,
+ ADS_SASL_SEAL);
+ if (!ads) {
+ DEBUG(1,("ads_init for domain %s failed\n", target_dom_name));
+ status = ADS_ERROR(LDAP_NO_MEMORY);
+ goto out;
+ }
+
+ ADS_TALLOC_CONST_FREE(ads->auth.password);
+ ADS_TALLOC_CONST_FREE(ads->auth.realm);
+
+ ads->auth.renewable = renewable;
+ ads->auth.password = talloc_strdup(ads, password);
+ if (ads->auth.password == NULL) {
+ status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
+ goto out;
+ }
+
+ /* In FIPS mode, client use kerberos is forced to required. */
+ krb5_state = lp_client_use_kerberos();
+ switch (krb5_state) {
+ case CRED_USE_KERBEROS_REQUIRED:
+ ads->auth.flags &= ~ADS_AUTH_DISABLE_KERBEROS;
+ ads->auth.flags &= ~ADS_AUTH_ALLOW_NTLMSSP;
+ break;
+ case CRED_USE_KERBEROS_DESIRED:
+ ads->auth.flags &= ~ADS_AUTH_DISABLE_KERBEROS;
+ ads->auth.flags |= ADS_AUTH_ALLOW_NTLMSSP;
+ break;
+ case CRED_USE_KERBEROS_DISABLED:
+ ads->auth.flags |= ADS_AUTH_DISABLE_KERBEROS;
+ ads->auth.flags |= ADS_AUTH_ALLOW_NTLMSSP;
+ break;
+ }
+
+ ads->auth.realm = talloc_asprintf_strupper_m(ads, "%s", auth_realm);
+ if (ads->auth.realm == NULL) {
+ status = ADS_ERROR_NT(NT_STATUS_INTERNAL_ERROR);
+ goto out;
+ }
+
+ /* Setup the server affinity cache. We don't reaally care
+ about the name. Just setup affinity and the KRB5_CONFIG
+ file. */
+ get_dc_name(ads->server.workgroup, ads->server.realm, dc_name, &dc_ss);
+
+ status = ads_connect(ads);
+ if (!ADS_ERR_OK(status)) {
+ DEBUG(1,("ads_connect for domain %s failed: %s\n",
+ target_dom_name, ads_errstr(status)));
+ goto out;
+ }
+
+ *adsp = talloc_move(mem_ctx, &ads);
+out:
+ TALLOC_FREE(tmp_ctx);
+ return status;
+}
+
+ADS_STATUS ads_idmap_cached_connection(const char *dom_name,
+ TALLOC_CTX *mem_ctx,
+ ADS_STRUCT **adsp)
+{
+ TALLOC_CTX *tmp_ctx = talloc_stackframe();
+ char *ldap_server = NULL;
+ char *realm = NULL;
+ char *password = NULL;
+ struct winbindd_domain *wb_dom = NULL;
+ ADS_STATUS status;
+
+ if (IS_AD_DC) {
+ /*
+ * Make sure we never try to use LDAP against
+ * a trusted domain as AD DC.
+ */
+ TALLOC_FREE(tmp_ctx);
+ return ADS_ERROR_NT(NT_STATUS_REQUEST_NOT_ACCEPTED);
+ }
+
+ ads_cached_connection_reuse(adsp);
+ if (*adsp != NULL) {
+ TALLOC_FREE(tmp_ctx);
+ return ADS_SUCCESS;
+ }
+
+ /*
+ * At this point we only have the NetBIOS domain name.
+ * Check if we can get server name and realm from SAF cache
+ * and the domain list.
+ */
+ ldap_server = saf_fetch(tmp_ctx, dom_name);
+
+ DBG_DEBUG("ldap_server from saf cache: '%s'\n",
+ ldap_server ? ldap_server : "");
+
+ wb_dom = find_domain_from_name(dom_name);
+ if (wb_dom == NULL) {
+ DBG_DEBUG("could not find domain '%s'\n", dom_name);
+ status = ADS_ERROR_NT(NT_STATUS_UNSUCCESSFUL);
+ goto out;
+ }
+
+ DBG_DEBUG("find_domain_from_name found realm '%s' for "
+ " domain '%s'\n", wb_dom->alt_name, dom_name);
+
+ if (!get_trust_pw_clear(dom_name, &password, NULL, NULL)) {
+ status = ADS_ERROR_NT(NT_STATUS_CANT_ACCESS_DOMAIN_INFO);
+ goto out;
+ }
+
+ if (IS_DC) {
+ SMB_ASSERT(wb_dom->alt_name != NULL);
+ realm = talloc_strdup(tmp_ctx, wb_dom->alt_name);
+ } else {
+ struct winbindd_domain *our_domain = wb_dom;
+
+ /* always give preference to the alt_name in our
+ primary domain if possible */
+
+ if (!wb_dom->primary) {
+ our_domain = find_our_domain();
+ }
+
+ if (our_domain->alt_name != NULL) {
+ realm = talloc_strdup(tmp_ctx, our_domain->alt_name);
+ } else {
+ realm = talloc_strdup(tmp_ctx, lp_realm());
+ }
+ }
+
+ if (realm == NULL) {
+ status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
+ goto out;
+ }
+
+ status = ads_cached_connection_connect(
+ wb_dom->alt_name, /* realm to connect to. */
+ dom_name, /* 'workgroup' name for ads_init */
+ ldap_server, /* DNS name to connect to. */
+ password, /* password for auth realm. */
+ realm, /* realm used for krb5 ticket. */
+ 0, /* renewable ticket time. */
+ mem_ctx, /* memory context for ads struct */
+ adsp); /* Returns ads struct. */
+
+out:
+ TALLOC_FREE(tmp_ctx);
+ SAFE_FREE(password);
+
+ return status;
+}
+
+/*
+ return our ads connections structure for a domain. We keep the connection
+ open to make things faster
+*/
+static ADS_STATUS ads_cached_connection(struct winbindd_domain *domain,
+ ADS_STRUCT **adsp)
+{
+ TALLOC_CTX *tmp_ctx = talloc_stackframe();
+ ADS_STATUS status;
+ char *password = NULL;
+ char *realm = NULL;
+
+ if (IS_AD_DC) {
+ /*
+ * Make sure we never try to use LDAP against
+ * a trusted domain as AD DC.
+ */
+ TALLOC_FREE(tmp_ctx);
+ return ADS_ERROR_NT(NT_STATUS_REQUEST_NOT_ACCEPTED);
+ }
+
+ DBG_DEBUG("ads_cached_connection\n");
+
+ ads_cached_connection_reuse(&domain->backend_data.ads_conn);
+ if (domain->backend_data.ads_conn != NULL) {
+ *adsp = domain->backend_data.ads_conn;
+ TALLOC_FREE(tmp_ctx);
+ return ADS_SUCCESS;
+ }
+
+ /* the machine acct password might have change - fetch it every time */
+
+ if (!get_trust_pw_clear(domain->name, &password, NULL, NULL)) {
+ status = ADS_ERROR_NT(NT_STATUS_CANT_ACCESS_DOMAIN_INFO);
+ goto out;
+ }
+
+ if ( IS_DC ) {
+ SMB_ASSERT(domain->alt_name != NULL);
+ realm = talloc_strdup(tmp_ctx, domain->alt_name);
+ } else {
+ struct winbindd_domain *our_domain = domain;
+
+
+ /* always give preference to the alt_name in our
+ primary domain if possible */
+
+ if ( !domain->primary )
+ our_domain = find_our_domain();
+
+ if (our_domain->alt_name != NULL) {
+ realm = talloc_strdup(tmp_ctx, our_domain->alt_name );
+ } else {
+ realm = talloc_strdup(tmp_ctx, lp_realm() );
+ }
+ }
+
+ if (realm == NULL) {
+ status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
+ goto out;
+ }
+
+ status = ads_cached_connection_connect(
+ domain->alt_name,
+ domain->name, NULL,
+ password,
+ realm,
+ WINBINDD_PAM_AUTH_KRB5_RENEW_TIME,
+ domain,
+ &domain->backend_data.ads_conn);
+ if (!ADS_ERR_OK(status)) {
+ /* if we get ECONNREFUSED then it might be a NT4
+ server, fall back to MSRPC */
+ if (status.error_type == ENUM_ADS_ERROR_SYSTEM &&
+ status.err.rc == ECONNREFUSED) {
+ /* 'reconnect_methods' is the MS-RPC backend. */
+ DBG_NOTICE("Trying MSRPC methods for domain '%s'\n",
+ domain->name);
+ domain->backend = &reconnect_methods;
+ }
+ goto out;
+ }
+
+ *adsp = domain->backend_data.ads_conn;
+out:
+ TALLOC_FREE(tmp_ctx);
+ SAFE_FREE(password);
+
+ return status;
+}
+
+/* Query display info for a realm. This is the basic user list fn */
+static NTSTATUS query_user_list(struct winbindd_domain *domain,
+ TALLOC_CTX *mem_ctx,
+ uint32_t **prids)
+{
+ ADS_STRUCT *ads = NULL;
+ const char *attrs[] = { "sAMAccountType", "objectSid", NULL };
+ int count;
+ uint32_t *rids = NULL;
+ ADS_STATUS rc;
+ LDAPMessage *res = NULL;
+ LDAPMessage *msg = NULL;
+ NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
+
+ DEBUG(3,("ads: query_user_list\n"));
+
+ if ( !winbindd_can_contact_domain( domain ) ) {
+ DEBUG(10,("query_user_list: No incoming trust for domain %s\n",
+ domain->name));
+ return NT_STATUS_OK;
+ }
+
+ rc = ads_cached_connection(domain, &ads);
+ if (!ADS_ERR_OK(rc)) {
+ domain->last_status = NT_STATUS_SERVER_DISABLED;
+ goto done;
+ }
+
+ rc = ads_search_retry(ads, &res, "(objectCategory=user)", attrs);
+ if (!ADS_ERR_OK(rc)) {
+ DEBUG(1,("query_user_list ads_search: %s\n", ads_errstr(rc)));
+ status = ads_ntstatus(rc);
+ goto done;
+ } else if (!res) {
+ DEBUG(1,("query_user_list ads_search returned NULL res\n"));
+ goto done;
+ }
+
+ count = ads_count_replies(ads, res);
+ if (count == 0) {
+ DEBUG(1,("query_user_list: No users found\n"));
+ goto done;
+ }
+
+ rids = talloc_zero_array(mem_ctx, uint32_t, count);
+ if (rids == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ goto done;
+ }
+
+ count = 0;
+
+ for (msg = ads_first_entry(ads, res); msg; msg = ads_next_entry(ads, msg)) {
+ struct dom_sid user_sid;
+ uint32_t atype;
+ bool ok;
+
+ ok = ads_pull_uint32(ads, msg, "sAMAccountType", &atype);
+ if (!ok) {
+ DBG_INFO("Object lacks sAMAccountType attribute\n");
+ continue;
+ }
+ if (ds_atype_map(atype) != SID_NAME_USER) {
+ DBG_INFO("Not a user account? atype=0x%x\n", atype);
+ continue;
+ }
+
+ if (!ads_pull_sid(ads, msg, "objectSid", &user_sid)) {
+ char *dn = ads_get_dn(ads, talloc_tos(), msg);
+ DBG_INFO("No sid for %s !?\n", dn);
+ TALLOC_FREE(dn);
+ continue;
+ }
+
+ if (!dom_sid_in_domain(&domain->sid, &user_sid)) {
+ struct dom_sid_buf sidstr, domstr;
+ DBG_WARNING("Got sid %s in domain %s\n",
+ dom_sid_str_buf(&user_sid, &sidstr),
+ dom_sid_str_buf(&domain->sid, &domstr));
+ continue;
+ }
+
+ sid_split_rid(&user_sid, &rids[count]);
+ count += 1;
+ }
+
+ rids = talloc_realloc(mem_ctx, rids, uint32_t, count);
+ if (prids != NULL) {
+ *prids = rids;
+ } else {
+ TALLOC_FREE(rids);
+ }
+
+ status = NT_STATUS_OK;
+
+ DBG_NOTICE("ads query_user_list gave %d entries\n", count);
+
+done:
+ ads_msgfree(ads, res);
+ return status;
+}
+
+/* list all domain groups */
+static NTSTATUS enum_dom_groups(struct winbindd_domain *domain,
+ TALLOC_CTX *mem_ctx,
+ uint32_t *num_entries,
+ struct wb_acct_info **info)
+{
+ ADS_STRUCT *ads = NULL;
+ const char *attrs[] = {"userPrincipalName", "sAMAccountName",
+ "name", "objectSid", NULL};
+ int i, count;
+ ADS_STATUS rc;
+ LDAPMessage *res = NULL;
+ LDAPMessage *msg = NULL;
+ NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
+ const char *filter;
+ bool enum_dom_local_groups = False;
+
+ *num_entries = 0;
+
+ DEBUG(3,("ads: enum_dom_groups\n"));
+
+ if ( !winbindd_can_contact_domain( domain ) ) {
+ DEBUG(10,("enum_dom_groups: No incoming trust for domain %s\n",
+ domain->name));
+ return NT_STATUS_OK;
+ }
+
+ /* only grab domain local groups for our domain */
+ if ( domain->active_directory && strequal(lp_realm(), domain->alt_name) ) {
+ enum_dom_local_groups = True;
+ }
+
+ /* Workaround ADS LDAP bug present in MS W2K3 SP0 and W2K SP4 w/o
+ * rollup-fixes:
+ *
+ * According to Section 5.1(4) of RFC 2251 if a value of a type is it's
+ * default value, it MUST be absent. In case of extensible matching the
+ * "dnattr" boolean defaults to FALSE and so it must be only be present
+ * when set to TRUE.
+ *
+ * When it is set to FALSE and the OpenLDAP lib (correctly) encodes a
+ * filter using bitwise matching rule then the buggy AD fails to decode
+ * the extensible match. As a workaround set it to TRUE and thereby add
+ * the dnAttributes "dn" field to cope with those older AD versions.
+ * It should not harm and won't put any additional load on the AD since
+ * none of the dn components have a bitmask-attribute.
+ *
+ * Thanks to Ralf Haferkamp for input and testing - Guenther */
+
+ filter = talloc_asprintf(mem_ctx, "(&(objectCategory=group)"
+ "(&(groupType:dn:"ADS_LDAP_MATCHING_RULE_BIT_AND":=%d)"
+ "(!(groupType:dn:"ADS_LDAP_MATCHING_RULE_BIT_AND":=%d))))",
+ GROUP_TYPE_SECURITY_ENABLED,
+ enum_dom_local_groups ? GROUP_TYPE_BUILTIN_LOCAL_GROUP : GROUP_TYPE_RESOURCE_GROUP);
+
+ if (filter == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ goto done;
+ }
+
+ rc = ads_cached_connection(domain, &ads);
+ if (!ADS_ERR_OK(rc)) {
+ domain->last_status = NT_STATUS_SERVER_DISABLED;
+ goto done;
+ }
+
+ rc = ads_search_retry(ads, &res, filter, attrs);
+ if (!ADS_ERR_OK(rc)) {
+ status = ads_ntstatus(rc);
+ DEBUG(1,("enum_dom_groups ads_search: %s\n", ads_errstr(rc)));
+ goto done;
+ } else if (!res) {
+ DEBUG(1,("enum_dom_groups ads_search returned NULL res\n"));
+ goto done;
+ }
+
+ count = ads_count_replies(ads, res);
+ if (count == 0) {
+ DEBUG(1,("enum_dom_groups: No groups found\n"));
+ goto done;
+ }
+
+ (*info) = talloc_zero_array(mem_ctx, struct wb_acct_info, count);
+ if (!*info) {
+ status = NT_STATUS_NO_MEMORY;
+ goto done;
+ }
+
+ i = 0;
+
+ for (msg = ads_first_entry(ads, res); msg; msg = ads_next_entry(ads, msg)) {
+ char *name, *gecos;
+ struct dom_sid sid;
+ uint32_t rid;
+
+ name = ads_pull_username(ads, (*info), msg);
+ gecos = ads_pull_string(ads, (*info), msg, "name");
+ if (!ads_pull_sid(ads, msg, "objectSid", &sid)) {
+ DEBUG(1,("No sid for %s !?\n", name));
+ continue;
+ }
+
+ if (!sid_peek_check_rid(&domain->sid, &sid, &rid)) {
+ DEBUG(1,("No rid for %s !?\n", name));
+ continue;
+ }
+
+ (*info)[i].acct_name = name;
+ (*info)[i].acct_desc = gecos;
+ (*info)[i].rid = rid;
+ i++;
+ }
+
+ (*num_entries) = i;
+
+ status = NT_STATUS_OK;
+
+ DEBUG(3,("ads enum_dom_groups gave %d entries\n", (*num_entries)));
+
+done:
+ if (res)
+ ads_msgfree(ads, res);
+
+ return status;
+}
+
+/* list all domain local groups */
+static NTSTATUS enum_local_groups(struct winbindd_domain *domain,
+ TALLOC_CTX *mem_ctx,
+ uint32_t *num_entries,
+ struct wb_acct_info **info)
+{
+ /*
+ * This is a stub function only as we returned the domain
+ * local groups in enum_dom_groups() if the domain->native field
+ * was true. This is a simple performance optimization when
+ * using LDAP.
+ *
+ * if we ever need to enumerate domain local groups separately,
+ * then this optimization in enum_dom_groups() will need
+ * to be split out
+ */
+ *num_entries = 0;
+
+ return NT_STATUS_OK;
+}
+
+/* convert a single name to a sid in a domain - use rpc methods */
+static NTSTATUS name_to_sid(struct winbindd_domain *domain,
+ TALLOC_CTX *mem_ctx,
+ const char *domain_name,
+ const char *name,
+ uint32_t flags,
+ const char **pdom_name,
+ struct dom_sid *sid,
+ enum lsa_SidType *type)
+{
+ return msrpc_methods.name_to_sid(domain, mem_ctx, domain_name, name,
+ flags, pdom_name, sid, type);
+}
+
+/* convert a domain SID to a user or group name - use rpc methods */
+static NTSTATUS sid_to_name(struct winbindd_domain *domain,
+ TALLOC_CTX *mem_ctx,
+ const struct dom_sid *sid,
+ char **domain_name,
+ char **name,
+ enum lsa_SidType *type)
+{
+ return msrpc_methods.sid_to_name(domain, mem_ctx, sid,
+ domain_name, name, type);
+}
+
+/* convert a list of rids to names - use rpc methods */
+static NTSTATUS rids_to_names(struct winbindd_domain *domain,
+ TALLOC_CTX *mem_ctx,
+ const struct dom_sid *sid,
+ uint32_t *rids,
+ size_t num_rids,
+ char **domain_name,
+ char ***names,
+ enum lsa_SidType **types)
+{
+ return msrpc_methods.rids_to_names(domain, mem_ctx, sid,
+ rids, num_rids,
+ domain_name, names, types);
+}
+
+/* Lookup groups a user is a member of - alternate method, for when
+ tokenGroups are not available. */
+static NTSTATUS lookup_usergroups_member(struct winbindd_domain *domain,
+ TALLOC_CTX *mem_ctx,
+ const char *user_dn,
+ struct dom_sid *primary_group,
+ uint32_t *p_num_groups, struct dom_sid **user_sids)
+{
+ ADS_STATUS rc;
+ NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
+ int count;
+ LDAPMessage *res = NULL;
+ LDAPMessage *msg = NULL;
+ char *ldap_exp;
+ ADS_STRUCT *ads = NULL;
+ const char *group_attrs[] = {"objectSid", NULL};
+ char *escaped_dn;
+ uint32_t num_groups = 0;
+
+ DEBUG(3,("ads: lookup_usergroups_member\n"));
+
+ if ( !winbindd_can_contact_domain( domain ) ) {
+ DEBUG(10,("lookup_usergroups_members: No incoming trust for domain %s\n",
+ domain->name));
+ return NT_STATUS_OK;
+ }
+
+ rc = ads_cached_connection(domain, &ads);
+ if (!ADS_ERR_OK(rc)) {
+ domain->last_status = NT_STATUS_SERVER_DISABLED;
+ goto done;
+ }
+
+ if (!(escaped_dn = escape_ldap_string(talloc_tos(), user_dn))) {
+ status = NT_STATUS_NO_MEMORY;
+ goto done;
+ }
+
+ ldap_exp = talloc_asprintf(mem_ctx,
+ "(&(member=%s)(objectCategory=group)"
+ "(groupType:dn:"ADS_LDAP_MATCHING_RULE_BIT_AND":=%d))",
+ escaped_dn,
+ GROUP_TYPE_SECURITY_ENABLED);
+ if (!ldap_exp) {
+ DEBUG(1,("lookup_usergroups(dn=%s) asprintf failed!\n", user_dn));
+ TALLOC_FREE(escaped_dn);
+ status = NT_STATUS_NO_MEMORY;
+ goto done;
+ }
+
+ TALLOC_FREE(escaped_dn);
+
+ rc = ads_search_retry(ads, &res, ldap_exp, group_attrs);
+
+ if (!ADS_ERR_OK(rc)) {
+ DEBUG(1,("lookup_usergroups ads_search member=%s: %s\n", user_dn, ads_errstr(rc)));
+ return ads_ntstatus(rc);
+ } else if (!res) {
+ DEBUG(1,("lookup_usergroups ads_search returned NULL res\n"));
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+
+
+ count = ads_count_replies(ads, res);
+
+ *user_sids = NULL;
+ num_groups = 0;
+
+ /* always add the primary group to the sid array */
+ status = add_sid_to_array(mem_ctx, primary_group, user_sids,
+ &num_groups);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto done;
+ }
+
+ if (count > 0) {
+ for (msg = ads_first_entry(ads, res); msg;
+ msg = ads_next_entry(ads, msg)) {
+ struct dom_sid group_sid;
+
+ if (!ads_pull_sid(ads, msg, "objectSid", &group_sid)) {
+ DEBUG(1,("No sid for this group ?!?\n"));
+ continue;
+ }
+
+ /* ignore Builtin groups from ADS - Guenther */
+ if (sid_check_is_in_builtin(&group_sid)) {
+ continue;
+ }
+
+ status = add_sid_to_array(mem_ctx, &group_sid,
+ user_sids, &num_groups);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto done;
+ }
+ }
+
+ }
+
+ *p_num_groups = num_groups;
+ status = (user_sids != NULL) ? NT_STATUS_OK : NT_STATUS_NO_MEMORY;
+
+ DEBUG(3,("ads lookup_usergroups (member) succeeded for dn=%s\n", user_dn));
+done:
+ if (res)
+ ads_msgfree(ads, res);
+
+ return status;
+}
+
+/* Lookup groups a user is a member of - alternate method, for when
+ tokenGroups are not available. */
+static NTSTATUS lookup_usergroups_memberof(struct winbindd_domain *domain,
+ TALLOC_CTX *mem_ctx,
+ const char *user_dn,
+ struct dom_sid *primary_group,
+ uint32_t *p_num_groups,
+ struct dom_sid **user_sids)
+{
+ ADS_STATUS rc;
+ NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
+ ADS_STRUCT *ads = NULL;
+ const char *attrs[] = {"memberOf", NULL};
+ uint32_t num_groups = 0;
+ struct dom_sid *group_sids = NULL;
+ size_t i;
+ char **strings = NULL;
+ size_t num_strings = 0, num_sids = 0;
+
+
+ DEBUG(3,("ads: lookup_usergroups_memberof\n"));
+
+ if ( !winbindd_can_contact_domain( domain ) ) {
+ DEBUG(10,("lookup_usergroups_memberof: No incoming trust for "
+ "domain %s\n", domain->name));
+ return NT_STATUS_OK;
+ }
+
+ rc = ads_cached_connection(domain, &ads);
+ if (!ADS_ERR_OK(rc)) {
+ domain->last_status = NT_STATUS_SERVER_DISABLED;
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ rc = ads_search_retry_extended_dn_ranged(ads, mem_ctx, user_dn, attrs,
+ ADS_EXTENDED_DN_HEX_STRING,
+ &strings, &num_strings);
+
+ if (!ADS_ERR_OK(rc)) {
+ DEBUG(1,("lookup_usergroups_memberof ads_search "
+ "member=%s: %s\n", user_dn, ads_errstr(rc)));
+ return ads_ntstatus(rc);
+ }
+
+ *user_sids = NULL;
+ num_groups = 0;
+
+ /* always add the primary group to the sid array */
+ status = add_sid_to_array(mem_ctx, primary_group, user_sids,
+ &num_groups);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto done;
+ }
+
+ group_sids = talloc_zero_array(mem_ctx, struct dom_sid, num_strings + 1);
+ if (!group_sids) {
+ status = NT_STATUS_NO_MEMORY;
+ goto done;
+ }
+
+ for (i=0; i<num_strings; i++) {
+ rc = ads_get_sid_from_extended_dn(mem_ctx, strings[i],
+ ADS_EXTENDED_DN_HEX_STRING,
+ &(group_sids)[i]);
+ if (!ADS_ERR_OK(rc)) {
+ /* ignore members without SIDs */
+ if (NT_STATUS_EQUAL(ads_ntstatus(rc),
+ NT_STATUS_NOT_FOUND)) {
+ continue;
+ }
+ else {
+ status = ads_ntstatus(rc);
+ goto done;
+ }
+ }
+ num_sids++;
+ }
+
+ if (i == 0) {
+ DEBUG(1,("No memberOf for this user?!?\n"));
+ status = NT_STATUS_NO_MEMORY;
+ goto done;
+ }
+
+ for (i=0; i<num_sids; i++) {
+
+ /* ignore Builtin groups from ADS - Guenther */
+ if (sid_check_is_in_builtin(&group_sids[i])) {
+ continue;
+ }
+
+ status = add_sid_to_array(mem_ctx, &group_sids[i], user_sids,
+ &num_groups);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto done;
+ }
+
+ }
+
+ *p_num_groups = num_groups;
+ status = (*user_sids != NULL) ? NT_STATUS_OK : NT_STATUS_NO_MEMORY;
+
+ DEBUG(3,("ads lookup_usergroups (memberof) succeeded for dn=%s\n",
+ user_dn));
+
+done:
+ TALLOC_FREE(strings);
+ TALLOC_FREE(group_sids);
+
+ return status;
+}
+
+
+/* Lookup groups a user is a member of. */
+static NTSTATUS lookup_usergroups(struct winbindd_domain *domain,
+ TALLOC_CTX *mem_ctx,
+ const struct dom_sid *sid,
+ uint32_t *p_num_groups, struct dom_sid **user_sids)
+{
+ ADS_STRUCT *ads = NULL;
+ const char *attrs[] = {"tokenGroups", "primaryGroupID", NULL};
+ ADS_STATUS rc;
+ int count;
+ LDAPMessage *msg = NULL;
+ char *user_dn = NULL;
+ struct dom_sid *sids;
+ int i;
+ struct dom_sid primary_group;
+ uint32_t primary_group_rid;
+ NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
+ uint32_t num_groups = 0;
+ struct dom_sid_buf buf;
+
+ DEBUG(3,("ads: lookup_usergroups\n"));
+ *p_num_groups = 0;
+
+ status = lookup_usergroups_cached(mem_ctx, sid,
+ p_num_groups, user_sids);
+ if (NT_STATUS_IS_OK(status)) {
+ return NT_STATUS_OK;
+ }
+
+ if ( !winbindd_can_contact_domain( domain ) ) {
+ DEBUG(10,("lookup_usergroups: No incoming trust for domain %s\n",
+ domain->name));
+
+ /* Tell the cache manager not to remember this one */
+
+ return NT_STATUS_SYNCHRONIZATION_REQUIRED;
+ }
+
+ rc = ads_cached_connection(domain, &ads);
+ if (!ADS_ERR_OK(rc)) {
+ domain->last_status = NT_STATUS_SERVER_DISABLED;
+ status = NT_STATUS_SERVER_DISABLED;
+ goto done;
+ }
+
+ rc = ads_search_retry_sid(ads, &msg, sid, attrs);
+
+ if (!ADS_ERR_OK(rc)) {
+ status = ads_ntstatus(rc);
+ DEBUG(1, ("lookup_usergroups(sid=%s) ads_search tokenGroups: "
+ "%s\n",
+ dom_sid_str_buf(sid, &buf),
+ ads_errstr(rc)));
+ goto done;
+ }
+
+ count = ads_count_replies(ads, msg);
+ if (count != 1) {
+ status = NT_STATUS_UNSUCCESSFUL;
+ DEBUG(1,("lookup_usergroups(sid=%s) ads_search tokenGroups: "
+ "invalid number of results (count=%d)\n",
+ dom_sid_str_buf(sid, &buf),
+ count));
+ goto done;
+ }
+
+ if (!msg) {
+ DEBUG(1,("lookup_usergroups(sid=%s) ads_search tokenGroups: NULL msg\n",
+ dom_sid_str_buf(sid, &buf)));
+ status = NT_STATUS_UNSUCCESSFUL;
+ goto done;
+ }
+
+ user_dn = ads_get_dn(ads, mem_ctx, msg);
+ if (user_dn == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ goto done;
+ }
+
+ if (!ads_pull_uint32(ads, msg, "primaryGroupID", &primary_group_rid)) {
+ DEBUG(1,("%s: No primary group for sid=%s !?\n",
+ domain->name,
+ dom_sid_str_buf(sid, &buf)));
+ goto done;
+ }
+
+ sid_compose(&primary_group, &domain->sid, primary_group_rid);
+
+ count = ads_pull_sids(ads, mem_ctx, msg, "tokenGroups", &sids);
+
+ /* there must always be at least one group in the token,
+ unless we are talking to a buggy Win2k server */
+
+ /* actually this only happens when the machine account has no read
+ * permissions on the tokenGroup attribute - gd */
+
+ if (count == 0) {
+
+ /* no tokenGroups */
+
+ /* lookup what groups this user is a member of by DN search on
+ * "memberOf" */
+
+ status = lookup_usergroups_memberof(domain, mem_ctx, user_dn,
+ &primary_group,
+ &num_groups, user_sids);
+ *p_num_groups = num_groups;
+ if (NT_STATUS_IS_OK(status)) {
+ goto done;
+ }
+
+ /* lookup what groups this user is a member of by DN search on
+ * "member" */
+
+ status = lookup_usergroups_member(domain, mem_ctx, user_dn,
+ &primary_group,
+ &num_groups, user_sids);
+ *p_num_groups = num_groups;
+ goto done;
+ }
+
+ *user_sids = NULL;
+ num_groups = 0;
+
+ status = add_sid_to_array(mem_ctx, &primary_group, user_sids,
+ &num_groups);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto done;
+ }
+
+ for (i=0;i<count;i++) {
+
+ /* ignore Builtin groups from ADS - Guenther */
+ if (sid_check_is_in_builtin(&sids[i])) {
+ continue;
+ }
+
+ status = add_sid_to_array_unique(mem_ctx, &sids[i],
+ user_sids, &num_groups);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto done;
+ }
+ }
+
+ *p_num_groups = (uint32_t)num_groups;
+ status = (*user_sids != NULL) ? NT_STATUS_OK : NT_STATUS_NO_MEMORY;
+
+ DEBUG(3,("ads lookup_usergroups (tokenGroups) succeeded for sid=%s\n",
+ dom_sid_str_buf(sid, &buf)));
+done:
+ TALLOC_FREE(user_dn);
+ ads_msgfree(ads, msg);
+ return status;
+}
+
+/* Lookup aliases a user is member of - use rpc methods */
+static NTSTATUS lookup_useraliases(struct winbindd_domain *domain,
+ TALLOC_CTX *mem_ctx,
+ uint32_t num_sids, const struct dom_sid *sids,
+ uint32_t *num_aliases, uint32_t **alias_rids)
+{
+ return msrpc_methods.lookup_useraliases(domain, mem_ctx, num_sids, sids,
+ num_aliases, alias_rids);
+}
+
+static NTSTATUS add_primary_group_members(
+ ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, uint32_t rid,
+ char ***all_members, size_t *num_all_members)
+{
+ char *filter;
+ NTSTATUS status = NT_STATUS_NO_MEMORY;
+ ADS_STATUS rc;
+ const char *attrs[] = { "dn", NULL };
+ LDAPMessage *res = NULL;
+ LDAPMessage *msg;
+ char **members;
+ size_t num_members;
+ ads_control args;
+
+ filter = talloc_asprintf(
+ mem_ctx, "(&(objectCategory=user)(primaryGroupID=%u))",
+ (unsigned)rid);
+ if (filter == NULL) {
+ goto done;
+ }
+
+ args.control = ADS_EXTENDED_DN_OID;
+ args.val = ADS_EXTENDED_DN_HEX_STRING;
+ args.critical = True;
+
+ rc = ads_do_search_all_args(ads, ads->config.bind_path,
+ LDAP_SCOPE_SUBTREE, filter, attrs, &args,
+ &res);
+
+ if (!ADS_ERR_OK(rc)) {
+ status = ads_ntstatus(rc);
+ DEBUG(1,("%s: ads_search: %s\n", __func__, ads_errstr(rc)));
+ goto done;
+ }
+ if (res == NULL) {
+ DEBUG(1,("%s: ads_search returned NULL res\n", __func__));
+ goto done;
+ }
+
+ num_members = ads_count_replies(ads, res);
+
+ DEBUG(10, ("%s: Got %ju primary group members\n", __func__,
+ (uintmax_t)num_members));
+
+ if (num_members == 0) {
+ status = NT_STATUS_OK;
+ goto done;
+ }
+
+ members = talloc_realloc(mem_ctx, *all_members, char *,
+ *num_all_members + num_members);
+ if (members == NULL) {
+ DEBUG(1, ("%s: talloc_realloc failed\n", __func__));
+ goto done;
+ }
+ *all_members = members;
+
+ for (msg = ads_first_entry(ads, res); msg != NULL;
+ msg = ads_next_entry(ads, msg)) {
+ char *dn;
+
+ dn = ads_get_dn(ads, members, msg);
+ if (dn == NULL) {
+ DEBUG(1, ("%s: ads_get_dn failed\n", __func__));
+ continue;
+ }
+
+ members[*num_all_members] = dn;
+ *num_all_members += 1;
+ }
+
+ status = NT_STATUS_OK;
+done:
+ if (res != NULL) {
+ ads_msgfree(ads, res);
+ }
+ TALLOC_FREE(filter);
+ return status;
+}
+
+/*
+ find the members of a group, given a group rid and domain
+ */
+static NTSTATUS lookup_groupmem(struct winbindd_domain *domain,
+ TALLOC_CTX *mem_ctx,
+ const struct dom_sid *group_sid,
+ enum lsa_SidType type,
+ uint32_t *num_names,
+ struct dom_sid **sid_mem, char ***names,
+ uint32_t **name_types)
+{
+ ADS_STATUS rc;
+ ADS_STRUCT *ads = NULL;
+ char *ldap_exp;
+ NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
+ char *sidbinstr;
+ char **members = NULL;
+ size_t i;
+ size_t num_members = 0;
+ ads_control args;
+ struct dom_sid *sid_mem_nocache = NULL;
+ char **names_nocache = NULL;
+ enum lsa_SidType *name_types_nocache = NULL;
+ char **domains_nocache = NULL; /* only needed for rpccli_lsa_lookup_sids */
+ uint32_t num_nocache = 0;
+ TALLOC_CTX *tmp_ctx = NULL;
+ uint32_t rid;
+ struct dom_sid_buf buf;
+
+ DEBUG(10,("ads: lookup_groupmem %s sid=%s\n", domain->name,
+ dom_sid_str_buf(group_sid, &buf)));
+
+ *num_names = 0;
+
+ tmp_ctx = talloc_new(mem_ctx);
+ if (!tmp_ctx) {
+ DEBUG(1, ("ads: lookup_groupmem: talloc failed\n"));
+ status = NT_STATUS_NO_MEMORY;
+ goto done;
+ }
+
+ if (!sid_peek_rid(group_sid, &rid)) {
+ DEBUG(1, ("%s: sid_peek_rid failed\n", __func__));
+ status = NT_STATUS_INVALID_PARAMETER;
+ goto done;
+ }
+
+ if ( !winbindd_can_contact_domain( domain ) ) {
+ DEBUG(10,("lookup_groupmem: No incoming trust for domain %s\n",
+ domain->name));
+ return NT_STATUS_OK;
+ }
+
+ rc = ads_cached_connection(domain, &ads);
+ if (!ADS_ERR_OK(rc)) {
+ domain->last_status = NT_STATUS_SERVER_DISABLED;
+ goto done;
+ }
+
+ if ((sidbinstr = ldap_encode_ndr_dom_sid(talloc_tos(), group_sid)) == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ goto done;
+ }
+
+ /* search for all members of the group */
+ ldap_exp = talloc_asprintf(tmp_ctx, "(objectSid=%s)", sidbinstr);
+ TALLOC_FREE(sidbinstr);
+ if (ldap_exp == NULL) {
+ DEBUG(1, ("ads: lookup_groupmem: talloc_asprintf for ldap_exp failed!\n"));
+ status = NT_STATUS_NO_MEMORY;
+ goto done;
+ }
+
+ args.control = ADS_EXTENDED_DN_OID;
+ args.val = ADS_EXTENDED_DN_HEX_STRING;
+ args.critical = True;
+
+ rc = ads_ranged_search(ads, tmp_ctx, LDAP_SCOPE_SUBTREE, ads->config.bind_path,
+ ldap_exp, &args, "member", &members, &num_members);
+
+ if (!ADS_ERR_OK(rc)) {
+ DEBUG(0,("ads_ranged_search failed with: %s\n", ads_errstr(rc)));
+ status = NT_STATUS_UNSUCCESSFUL;
+ goto done;
+ }
+
+ DEBUG(10, ("ads lookup_groupmem: got %d sids via extended dn call\n", (int)num_members));
+
+ status = add_primary_group_members(ads, mem_ctx, rid,
+ &members, &num_members);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(10, ("%s: add_primary_group_members failed: %s\n",
+ __func__, nt_errstr(status)));
+ goto done;
+ }
+
+ DEBUG(10, ("%s: Got %d sids after adding primary group members\n",
+ __func__, (int)num_members));
+
+ /* Now that we have a list of sids, we need to get the
+ * lists of names and name_types belonging to these sids.
+ * even though conceptually not quite clean, we use the
+ * RPC call lsa_lookup_sids for this since it can handle a
+ * list of sids. ldap calls can just resolve one sid at a time.
+ *
+ * At this stage, the sids are still hidden in the exetended dn
+ * member output format. We actually do a little better than
+ * stated above: In extracting the sids from the member strings,
+ * we try to resolve as many sids as possible from the
+ * cache. Only the rest is passed to the lsa_lookup_sids call. */
+
+ if (num_members) {
+ (*sid_mem) = talloc_zero_array(mem_ctx, struct dom_sid, num_members);
+ (*names) = talloc_zero_array(mem_ctx, char *, num_members);
+ (*name_types) = talloc_zero_array(mem_ctx, uint32_t, num_members);
+ (sid_mem_nocache) = talloc_zero_array(tmp_ctx, struct dom_sid, num_members);
+
+ if ((members == NULL) || (*sid_mem == NULL) ||
+ (*names == NULL) || (*name_types == NULL) ||
+ (sid_mem_nocache == NULL))
+ {
+ DEBUG(1, ("ads: lookup_groupmem: talloc failed\n"));
+ status = NT_STATUS_NO_MEMORY;
+ goto done;
+ }
+ }
+ else {
+ (*sid_mem) = NULL;
+ (*names) = NULL;
+ (*name_types) = NULL;
+ }
+
+ for (i=0; i<num_members; i++) {
+ enum lsa_SidType name_type;
+ char *name, *domain_name;
+ struct dom_sid sid;
+
+ rc = ads_get_sid_from_extended_dn(tmp_ctx, members[i], args.val,
+ &sid);
+ if (!ADS_ERR_OK(rc)) {
+ if (NT_STATUS_EQUAL(ads_ntstatus(rc),
+ NT_STATUS_NOT_FOUND)) {
+ /* Group members can be objects, like Exchange
+ * Public Folders, that don't have a SID. Skip
+ * them. */
+ continue;
+ }
+ else {
+ status = ads_ntstatus(rc);
+ goto done;
+ }
+ }
+ if (lookup_cached_sid(mem_ctx, &sid, &domain_name, &name,
+ &name_type)) {
+ DEBUG(10,("ads: lookup_groupmem: got sid %s from "
+ "cache\n",
+ dom_sid_str_buf(&sid, &buf)));
+ sid_copy(&(*sid_mem)[*num_names], &sid);
+ (*names)[*num_names] = fill_domain_username_talloc(
+ *names,
+ domain_name,
+ name,
+ true);
+
+ (*name_types)[*num_names] = name_type;
+ (*num_names)++;
+ }
+ else {
+ DEBUG(10, ("ads: lookup_groupmem: sid %s not found in "
+ "cache\n",
+ dom_sid_str_buf(&sid, &buf)));
+ sid_copy(&(sid_mem_nocache)[num_nocache], &sid);
+ num_nocache++;
+ }
+ }
+
+ DEBUG(10, ("ads: lookup_groupmem: %d sids found in cache, "
+ "%d left for lsa_lookupsids\n", *num_names, num_nocache));
+
+ /* handle sids not resolved from cache by lsa_lookup_sids */
+ if (num_nocache > 0) {
+
+ status = winbindd_lookup_sids(tmp_ctx,
+ domain,
+ num_nocache,
+ sid_mem_nocache,
+ &domains_nocache,
+ &names_nocache,
+ &name_types_nocache);
+
+ if (!(NT_STATUS_IS_OK(status) ||
+ NT_STATUS_EQUAL(status, STATUS_SOME_UNMAPPED) ||
+ NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED)))
+ {
+ DEBUG(1, ("lsa_lookupsids call failed with %s "
+ "- retrying...\n", nt_errstr(status)));
+
+ status = winbindd_lookup_sids(tmp_ctx,
+ domain,
+ num_nocache,
+ sid_mem_nocache,
+ &domains_nocache,
+ &names_nocache,
+ &name_types_nocache);
+ }
+
+ if (NT_STATUS_IS_OK(status) ||
+ NT_STATUS_EQUAL(status, STATUS_SOME_UNMAPPED))
+ {
+ /* Copy the entries over from the "_nocache" arrays
+ * to the result arrays, skipping the gaps the
+ * lookup_sids call left. */
+ for (i=0; i < num_nocache; i++) {
+ if (((names_nocache)[i] != NULL) &&
+ ((name_types_nocache)[i] != SID_NAME_UNKNOWN))
+ {
+ sid_copy(&(*sid_mem)[*num_names],
+ &sid_mem_nocache[i]);
+ (*names)[*num_names] =
+ fill_domain_username_talloc(
+ *names,
+ domains_nocache[i],
+ names_nocache[i],
+ true);
+ (*name_types)[*num_names] = name_types_nocache[i];
+ (*num_names)++;
+ }
+ }
+ }
+ else if (NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED)) {
+ DEBUG(10, ("lookup_groupmem: lsa_lookup_sids could "
+ "not map any SIDs at all.\n"));
+ /* Don't handle this as an error here.
+ * There is nothing left to do with respect to the
+ * overall result... */
+ }
+ else if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(10, ("lookup_groupmem: Error looking up %d "
+ "sids via rpc_lsa_lookup_sids: %s\n",
+ (int)num_members, nt_errstr(status)));
+ goto done;
+ }
+ }
+
+ status = NT_STATUS_OK;
+ DEBUG(3,("ads lookup_groupmem for sid=%s succeeded\n",
+ dom_sid_str_buf(group_sid, &buf)));
+
+done:
+
+ TALLOC_FREE(tmp_ctx);
+
+ return status;
+}
+
+static NTSTATUS lookup_aliasmem(struct winbindd_domain *domain,
+ TALLOC_CTX *mem_ctx,
+ const struct dom_sid *sid,
+ enum lsa_SidType type,
+ uint32_t *num_sids,
+ struct dom_sid **sids)
+{
+ char **names = NULL;
+ uint32_t *name_types = NULL;
+ struct dom_sid_buf buf;
+
+ DBG_DEBUG("ads: lookup_aliasmem %s sid=%s\n",
+ domain->name,
+ dom_sid_str_buf(sid, &buf));
+ /* Search for alias and group membership uses the same LDAP command. */
+ return lookup_groupmem(domain,
+ mem_ctx,
+ sid,
+ type,
+ num_sids,
+ sids,
+ &names,
+ &name_types);
+}
+
+/* find the lockout policy of a domain - use rpc methods */
+static NTSTATUS lockout_policy(struct winbindd_domain *domain,
+ TALLOC_CTX *mem_ctx,
+ struct samr_DomInfo12 *policy)
+{
+ return msrpc_methods.lockout_policy(domain, mem_ctx, policy);
+}
+
+/* find the password policy of a domain - use rpc methods */
+static NTSTATUS password_policy(struct winbindd_domain *domain,
+ TALLOC_CTX *mem_ctx,
+ struct samr_DomInfo1 *policy)
+{
+ return msrpc_methods.password_policy(domain, mem_ctx, policy);
+}
+
+/* get a list of trusted domains */
+static NTSTATUS trusted_domains(struct winbindd_domain *domain,
+ TALLOC_CTX *mem_ctx,
+ struct netr_DomainTrustList *trusts)
+{
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+ WERROR werr;
+ uint32_t i;
+ uint32_t flags;
+ struct rpc_pipe_client *cli;
+ struct dcerpc_binding_handle *b;
+
+ DEBUG(3,("ads: trusted_domains\n"));
+
+ ZERO_STRUCTP(trusts);
+
+ /* If this is our primary domain or a root in our forest,
+ query for all trusts. If not, then just look for domain
+ trusts in the target forest */
+
+ if (domain->primary || domain_is_forest_root(domain)) {
+ flags = NETR_TRUST_FLAG_OUTBOUND |
+ NETR_TRUST_FLAG_INBOUND |
+ NETR_TRUST_FLAG_IN_FOREST;
+ } else {
+ flags = NETR_TRUST_FLAG_IN_FOREST;
+ }
+
+ result = cm_connect_netlogon(domain, &cli);
+
+ if (!NT_STATUS_IS_OK(result)) {
+ DEBUG(5, ("trusted_domains: Could not open a connection to %s "
+ "for PIPE_NETLOGON (%s)\n",
+ domain->name, nt_errstr(result)));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ b = cli->binding_handle;
+
+ result = dcerpc_netr_DsrEnumerateDomainTrusts(b, mem_ctx,
+ cli->desthost,
+ flags,
+ trusts,
+ &werr);
+ if (!NT_STATUS_IS_OK(result)) {
+ return result;
+ }
+
+ if (!W_ERROR_IS_OK(werr)) {
+ return werror_to_ntstatus(werr);
+ }
+ if (trusts->count == 0) {
+ return NT_STATUS_OK;
+ }
+
+ /* Copy across names and sids */
+
+ for (i = 0; i < trusts->count; i++) {
+ struct netr_DomainTrust *trust = &trusts->array[i];
+ struct winbindd_domain d;
+
+ ZERO_STRUCT(d);
+
+ /*
+ * drop external trusts if this is not our primary
+ * domain. This means that the returned number of
+ * domains may be less that the ones actually trusted
+ * by the DC.
+ */
+
+ if ((trust->trust_attributes
+ & LSA_TRUST_ATTRIBUTE_QUARANTINED_DOMAIN) &&
+ !domain->primary )
+ {
+ DEBUG(10,("trusted_domains: Skipping external trusted "
+ "domain %s because it is outside of our "
+ "primary domain\n",
+ trust->netbios_name));
+ continue;
+ }
+
+ /* add to the trusted domain cache */
+
+ d.name = discard_const_p(char, trust->netbios_name);
+ d.alt_name = discard_const_p(char, trust->dns_name);
+
+ if (trust->sid) {
+ sid_copy(&d.sid, trust->sid);
+ } else {
+ sid_copy(&d.sid, &global_sid_NULL);
+ }
+
+ if ( domain->primary ) {
+ DEBUG(10,("trusted_domains(ads): Searching "
+ "trusted domain list of %s and storing "
+ "trust flags for domain %s\n",
+ domain->name, d.alt_name));
+
+ d.domain_flags = trust->trust_flags;
+ d.domain_type = trust->trust_type;
+ d.domain_trust_attribs = trust->trust_attributes;
+
+ wcache_tdc_add_domain( &d );
+ } else if (domain_is_forest_root(domain)) {
+ /* Check if we already have this record. If
+ * we are following our forest root that is not
+ * our primary domain, we want to keep trust
+ * flags from the perspective of our primary
+ * domain not our forest root. */
+ struct winbindd_tdc_domain *exist = NULL;
+
+ exist = wcache_tdc_fetch_domain(
+ talloc_tos(), trust->netbios_name);
+ if (!exist) {
+ DEBUG(10,("trusted_domains(ads): Searching "
+ "trusted domain list of %s and "
+ "storing trust flags for domain "
+ "%s\n", domain->name, d.alt_name));
+ d.domain_flags = trust->trust_flags;
+ d.domain_type = trust->trust_type;
+ d.domain_trust_attribs =
+ trust->trust_attributes;
+
+ wcache_tdc_add_domain( &d );
+ }
+ TALLOC_FREE(exist);
+ } else {
+ /* This gets a little tricky. If we are
+ following a transitive forest trust, then
+ innerit the flags, type, and attribs from
+ the domain we queried to make sure we don't
+ record the view of the trust from the wrong
+ side. Always view it from the side of our
+ primary domain. --jerry */
+ struct winbindd_tdc_domain *parent = NULL;
+
+ DEBUG(10,("trusted_domains(ads): Searching "
+ "trusted domain list of %s and inheriting "
+ "trust flags for domain %s\n",
+ domain->name, d.alt_name));
+
+ parent = wcache_tdc_fetch_domain(talloc_tos(),
+ domain->name);
+ if (parent) {
+ d.domain_flags = parent->trust_flags;
+ d.domain_type = parent->trust_type;
+ d.domain_trust_attribs = parent->trust_attribs;
+ } else {
+ d.domain_flags = domain->domain_flags;
+ d.domain_type = domain->domain_type;
+ d.domain_trust_attribs =
+ domain->domain_trust_attribs;
+ }
+ TALLOC_FREE(parent);
+
+ /*
+ * We need to pass the modified properties
+ * to the caller.
+ */
+ trust->trust_flags = d.domain_flags;
+ trust->trust_type = d.domain_type;
+ trust->trust_attributes = d.domain_trust_attribs;
+
+ wcache_tdc_add_domain( &d );
+ }
+ }
+ return result;
+}
+
+/* the ADS backend methods are exposed via this structure */
+struct winbindd_methods ads_methods = {
+ True,
+ query_user_list,
+ enum_dom_groups,
+ enum_local_groups,
+ name_to_sid,
+ sid_to_name,
+ rids_to_names,
+ lookup_usergroups,
+ lookup_useraliases,
+ lookup_groupmem,
+ lookup_aliasmem,
+ lockout_policy,
+ password_policy,
+ trusted_domains,
+};
+
+#endif
diff --git a/source3/winbindd/winbindd_ads.h b/source3/winbindd/winbindd_ads.h
new file mode 100644
index 0000000..0fd9774
--- /dev/null
+++ b/source3/winbindd/winbindd_ads.h
@@ -0,0 +1,34 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Winbind ADS backend functions
+
+ Copyright (C) Volker Lendecke 2017
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef __WINBINDD_ADS_H__
+#define __WINBINDD_ADS_H__
+
+
+#include "ads.h"
+
+extern struct winbindd_methods ads_methods;
+
+ADS_STATUS ads_idmap_cached_connection(const char *dom_name,
+ TALLOC_CTX *mem_ctx,
+ ADS_STRUCT **adsp);
+
+#endif
diff --git a/source3/winbindd/winbindd_allocate_gid.c b/source3/winbindd/winbindd_allocate_gid.c
new file mode 100644
index 0000000..2841d96
--- /dev/null
+++ b/source3/winbindd/winbindd_allocate_gid.c
@@ -0,0 +1,121 @@
+/*
+ Unix SMB/CIFS implementation.
+ async implementation of WINBINDD_ALLOCATE_GID
+ Copyright (C) Volker Lendecke 2009
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "winbindd.h"
+#include "librpc/gen_ndr/ndr_winbind_c.h"
+
+struct winbindd_allocate_gid_state {
+ struct tevent_context *ev;
+ uint64_t gid;
+};
+
+static void winbindd_allocate_gid_initialized(struct tevent_req *subreq);
+static void winbindd_allocate_gid_done(struct tevent_req *subreq);
+
+struct tevent_req *winbindd_allocate_gid_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct winbindd_cli_state *cli,
+ struct winbindd_request *request)
+{
+ struct tevent_req *req, *subreq;
+ struct winbindd_allocate_gid_state *state;
+
+ req = tevent_req_create(mem_ctx, &state,
+ struct winbindd_allocate_gid_state);
+ if (req == NULL) {
+ return NULL;
+ }
+ state->ev = ev;
+
+ DEBUG(3, ("allocate_gid\n"));
+
+ subreq = wb_parent_idmap_setup_send(state, ev);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(
+ subreq, winbindd_allocate_gid_initialized, req);
+ return req;
+}
+
+static void winbindd_allocate_gid_initialized(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct winbindd_allocate_gid_state *state = tevent_req_data(
+ req, struct winbindd_allocate_gid_state);
+ NTSTATUS status;
+ const struct wb_parent_idmap_config *cfg = NULL;
+ struct dcerpc_binding_handle *child_binding_handle = NULL;
+
+ status = wb_parent_idmap_setup_recv(subreq, &cfg);
+ TALLOC_FREE(subreq);
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+ if (cfg->num_doms == 0) {
+ /*
+ * idmap_tdb also returns UNSUCCESSFUL if a range is full
+ */
+ tevent_req_nterror(req, NT_STATUS_UNSUCCESSFUL);
+ return;
+ }
+
+ child_binding_handle = idmap_child_handle();
+
+ subreq = dcerpc_wbint_AllocateGid_send(
+ state, state->ev, child_binding_handle, &state->gid);
+ if (tevent_req_nomem(subreq, req)) {
+ return;
+ }
+ tevent_req_set_callback(subreq, winbindd_allocate_gid_done, req);
+}
+
+static void winbindd_allocate_gid_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct winbindd_allocate_gid_state *state = tevent_req_data(
+ req, struct winbindd_allocate_gid_state);
+ NTSTATUS status, result;
+
+ status = dcerpc_wbint_AllocateGid_recv(subreq, state, &result);
+ TALLOC_FREE(subreq);
+ if (any_nt_status_not_ok(status, result, &status)) {
+ tevent_req_nterror(req, status);
+ return;
+ }
+ tevent_req_done(req);
+}
+
+NTSTATUS winbindd_allocate_gid_recv(struct tevent_req *req,
+ struct winbindd_response *response)
+{
+ struct winbindd_allocate_gid_state *state = tevent_req_data(
+ req, struct winbindd_allocate_gid_state);
+ NTSTATUS status;
+
+ if (tevent_req_is_nterror(req, &status)) {
+ DEBUG(5, ("Could not allocate gid: %s\n", nt_errstr(status)));
+ return status;
+ }
+ response->data.gid = state->gid;
+ return NT_STATUS_OK;
+}
diff --git a/source3/winbindd/winbindd_allocate_uid.c b/source3/winbindd/winbindd_allocate_uid.c
new file mode 100644
index 0000000..64711f1
--- /dev/null
+++ b/source3/winbindd/winbindd_allocate_uid.c
@@ -0,0 +1,122 @@
+/*
+ Unix SMB/CIFS implementation.
+ async implementation of WINBINDD_ALLOCATE_UID
+ Copyright (C) Volker Lendecke 2009
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "winbindd.h"
+#include "librpc/gen_ndr/ndr_winbind_c.h"
+
+struct winbindd_allocate_uid_state {
+ struct tevent_context *ev;
+ uint64_t uid;
+};
+
+static void winbindd_allocate_uid_initialized(struct tevent_req *subreq);
+static void winbindd_allocate_uid_done(struct tevent_req *subreq);
+
+struct tevent_req *winbindd_allocate_uid_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct winbindd_cli_state *cli,
+ struct winbindd_request *request)
+{
+ struct tevent_req *req, *subreq;
+ struct winbindd_allocate_uid_state *state;
+
+ req = tevent_req_create(mem_ctx, &state,
+ struct winbindd_allocate_uid_state);
+ if (req == NULL) {
+ return NULL;
+ }
+ state->ev = ev;
+
+ DEBUG(3, ("allocate_uid\n"));
+
+ subreq = wb_parent_idmap_setup_send(state, ev);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, winbindd_allocate_uid_initialized, req);
+ return req;
+}
+
+static void winbindd_allocate_uid_initialized(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct dcerpc_binding_handle *child_binding_handle = NULL;
+ struct winbindd_allocate_uid_state *state = tevent_req_data(
+ req, struct winbindd_allocate_uid_state);
+ const struct wb_parent_idmap_config *cfg = NULL;
+ NTSTATUS status;
+
+ status = wb_parent_idmap_setup_recv(subreq, &cfg);
+ TALLOC_FREE(subreq);
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+ if (cfg->num_doms == 0) {
+ /*
+ * idmap_tdb also returns UNSUCCESSFUL if a range is full
+ */
+ tevent_req_nterror(req, NT_STATUS_UNSUCCESSFUL);
+ return;
+ }
+
+ child_binding_handle = idmap_child_handle();
+
+ subreq = dcerpc_wbint_AllocateUid_send(state,
+ state->ev,
+ child_binding_handle,
+ &state->uid);
+ if (tevent_req_nomem(subreq, req)) {
+ return;
+ }
+ tevent_req_set_callback(subreq, winbindd_allocate_uid_done, req);
+}
+
+static void winbindd_allocate_uid_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct winbindd_allocate_uid_state *state = tevent_req_data(
+ req, struct winbindd_allocate_uid_state);
+ NTSTATUS status, result;
+
+ status = dcerpc_wbint_AllocateUid_recv(subreq, state, &result);
+ TALLOC_FREE(subreq);
+ if (any_nt_status_not_ok(status, result, &status)) {
+ tevent_req_nterror(req, status);
+ return;
+ }
+ tevent_req_done(req);
+}
+
+NTSTATUS winbindd_allocate_uid_recv(struct tevent_req *req,
+ struct winbindd_response *response)
+{
+ struct winbindd_allocate_uid_state *state = tevent_req_data(
+ req, struct winbindd_allocate_uid_state);
+ NTSTATUS status;
+
+ if (tevent_req_is_nterror(req, &status)) {
+ DEBUG(5, ("Could not allocate uid: %s\n", nt_errstr(status)));
+ return status;
+ }
+ response->data.uid = state->uid;
+ return NT_STATUS_OK;
+}
diff --git a/source3/winbindd/winbindd_cache.c b/source3/winbindd/winbindd_cache.c
new file mode 100644
index 0000000..ca2341e
--- /dev/null
+++ b/source3/winbindd/winbindd_cache.c
@@ -0,0 +1,4930 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Winbind cache backend functions
+
+ Copyright (C) Andrew Tridgell 2001
+ Copyright (C) Gerald Carter 2003-2007
+ Copyright (C) Volker Lendecke 2005
+ Copyright (C) Guenther Deschner 2005
+ Copyright (C) Michael Adam 2007
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "system/filesys.h"
+#include "winbindd.h"
+#include "tdb_validate.h"
+#include "../libcli/auth/libcli_auth.h"
+#include "../librpc/gen_ndr/ndr_winbind.h"
+#include "ads.h"
+#include "nss_info.h"
+#include "../libcli/security/security.h"
+#include "passdb/machine_sid.h"
+#include "util_tdb.h"
+#include "libsmb/samlogon_cache.h"
+#include "lib/namemap_cache.h"
+#include "lib/util/string_wrappers.h"
+
+#include "lib/crypto/gnutls_helpers.h"
+#include <gnutls/crypto.h>
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_WINBIND
+
+#define WINBINDD_CACHE_VER1 1 /* initial db version */
+#define WINBINDD_CACHE_VER2 2 /* second version with timeouts for NDR entries */
+
+#define WINBINDD_CACHE_VERSION WINBINDD_CACHE_VER2
+#define WINBINDD_CACHE_VERSION_KEYSTR "WINBINDD_CACHE_VERSION"
+
+extern struct winbindd_methods reconnect_methods;
+#ifdef HAVE_ADS
+extern struct winbindd_methods reconnect_ads_methods;
+#endif
+extern struct winbindd_methods builtin_passdb_methods;
+extern struct winbindd_methods sam_passdb_methods;
+
+static void wcache_flush_cache(void);
+
+static bool opt_nocache = False;
+
+/*
+ * JRA. KEEP THIS LIST UP TO DATE IF YOU ADD CACHE ENTRIES.
+ * Here are the list of entry types that are *not* stored
+ * as form struct cache_entry in the cache.
+ */
+
+static const char *non_centry_keys[] = {
+ "SEQNUM/",
+ "WINBINDD_OFFLINE",
+ WINBINDD_CACHE_VERSION_KEYSTR,
+ NULL
+};
+
+bool winbindd_use_idmap_cache(void)
+{
+ return !opt_nocache;
+}
+
+bool winbindd_use_cache(void)
+{
+ return !opt_nocache;
+}
+
+void winbindd_set_use_cache(bool use_cache)
+{
+ opt_nocache = !use_cache;
+}
+
+void winbindd_flush_caches(void)
+{
+ /* We need to invalidate cached user list entries on a SIGHUP
+ otherwise cached access denied errors due to restrict anonymous
+ hang around until the sequence number changes. */
+
+ if (!wcache_invalidate_cache()) {
+ DBG_ERR("invalidating the cache failed; revalidate the cache\n");
+ if (!winbindd_cache_validate_and_initialize()) {
+ exit(1);
+ }
+ }
+}
+
+/************************************************************************
+ Is this key a non-centry type ?
+************************************************************************/
+
+static bool is_non_centry_key(TDB_DATA kbuf)
+{
+ int i;
+
+ if (kbuf.dptr == NULL || kbuf.dsize == 0) {
+ return false;
+ }
+ for (i = 0; non_centry_keys[i] != NULL; i++) {
+ size_t namelen = strlen(non_centry_keys[i]);
+ if (kbuf.dsize < namelen) {
+ continue;
+ }
+ if (strncmp(non_centry_keys[i], (const char *)kbuf.dptr, namelen) == 0) {
+ return true;
+ }
+ }
+ return false;
+}
+
+struct winbind_cache {
+ TDB_CONTEXT *tdb;
+};
+
+struct cache_entry {
+ NTSTATUS status;
+ uint32_t sequence_number;
+ uint64_t timeout;
+ uint8_t *data;
+ uint32_t len, ofs;
+};
+
+void (*smb_panic_fn)(const char *const why) = smb_panic;
+
+static struct winbind_cache *wcache;
+
+static char *wcache_path(void)
+{
+ /*
+ * Data needs to be kept persistent in state directory for
+ * running with "winbindd offline logon".
+ */
+ return state_path(talloc_tos(), "winbindd_cache.tdb");
+}
+
+static void winbindd_domain_init_backend(struct winbindd_domain *domain)
+{
+ if (domain->backend != NULL) {
+ return;
+ }
+
+ if (domain->internal) {
+ domain->backend = &builtin_passdb_methods;
+ }
+
+ if (dom_sid_equal(&domain->sid, &global_sid_Builtin)) {
+ domain->initialized = true;
+ }
+
+ if (strequal(domain->name, get_global_sam_name()) &&
+ sid_check_is_our_sam(&domain->sid))
+ {
+ domain->backend = &sam_passdb_methods;
+ }
+
+ if (!domain->initialized) {
+ /* We do not need a connection to an RW DC for cache operation */
+ init_dc_connection(domain, false);
+ }
+
+#ifdef HAVE_ADS
+ if (domain->backend == NULL) {
+ struct winbindd_domain *our_domain = domain;
+
+ /* find our domain first so we can figure out if we
+ are joined to a kerberized domain */
+
+ if (!domain->primary) {
+ our_domain = find_our_domain();
+ }
+
+ if ((our_domain->active_directory || IS_DC)
+ && domain->active_directory
+ && !lp_winbind_rpc_only())
+ {
+ DBG_INFO("Setting ADS methods for domain %s\n",
+ domain->name);
+ domain->backend = &reconnect_ads_methods;
+ }
+ }
+#endif /* HAVE_ADS */
+
+ if (domain->backend == NULL) {
+ DBG_INFO("Setting MS-RPC methods for domain %s\n", domain->name);
+ domain->backend = &reconnect_methods;
+ }
+}
+
+/* get the winbind_cache structure */
+static struct winbind_cache *get_cache(struct winbindd_domain *domain)
+{
+ struct winbind_cache *ret = wcache;
+
+ winbindd_domain_init_backend(domain);
+
+ if (ret != NULL) {
+ return ret;
+ }
+
+ ret = SMB_XMALLOC_P(struct winbind_cache);
+ ZERO_STRUCTP(ret);
+
+ wcache = ret;
+ wcache_flush_cache();
+
+ return ret;
+}
+
+/*
+ free a centry structure
+*/
+static void centry_free(struct cache_entry *centry)
+{
+ if (!centry)
+ return;
+ SAFE_FREE(centry->data);
+ free(centry);
+}
+
+static bool centry_check_bytes(struct cache_entry *centry, size_t nbytes)
+{
+ if (centry->len - centry->ofs < nbytes) {
+ DBG_ERR("centry corruption? needed %u bytes, have %d\n",
+ (unsigned int)nbytes,
+ centry->len - centry->ofs);
+ return false;
+ }
+ return true;
+}
+
+/*
+ pull a uint64_t from a cache entry
+*/
+static uint64_t centry_uint64_t(struct cache_entry *centry)
+{
+ uint64_t ret;
+
+ if (!centry_check_bytes(centry, 8)) {
+ smb_panic_fn("centry_uint64_t");
+ }
+ ret = BVAL(centry->data, centry->ofs);
+ centry->ofs += 8;
+ return ret;
+}
+
+/*
+ pull a uint32_t from a cache entry
+*/
+static uint32_t centry_uint32(struct cache_entry *centry)
+{
+ uint32_t ret;
+
+ if (!centry_check_bytes(centry, 4)) {
+ smb_panic_fn("centry_uint32");
+ }
+ ret = IVAL(centry->data, centry->ofs);
+ centry->ofs += 4;
+ return ret;
+}
+
+/*
+ pull a uint16_t from a cache entry
+*/
+static uint16_t centry_uint16(struct cache_entry *centry)
+{
+ uint16_t ret;
+ if (!centry_check_bytes(centry, 2)) {
+ smb_panic_fn("centry_uint16");
+ }
+ ret = SVAL(centry->data, centry->ofs);
+ centry->ofs += 2;
+ return ret;
+}
+
+/*
+ pull a uint8_t from a cache entry
+*/
+static uint8_t centry_uint8(struct cache_entry *centry)
+{
+ uint8_t ret;
+ if (!centry_check_bytes(centry, 1)) {
+ smb_panic_fn("centry_uint8");
+ }
+ ret = CVAL(centry->data, centry->ofs);
+ centry->ofs += 1;
+ return ret;
+}
+
+/*
+ pull a NTTIME from a cache entry
+*/
+static NTTIME centry_nttime(struct cache_entry *centry)
+{
+ NTTIME ret;
+ if (!centry_check_bytes(centry, 8)) {
+ smb_panic_fn("centry_nttime");
+ }
+ ret = IVAL(centry->data, centry->ofs);
+ centry->ofs += 4;
+ ret += (uint64_t)IVAL(centry->data, centry->ofs) << 32;
+ centry->ofs += 4;
+ return ret;
+}
+
+/*
+ pull a time_t from a cache entry. time_t stored portably as a 64-bit time.
+*/
+static time_t centry_time(struct cache_entry *centry)
+{
+ return (time_t)centry_nttime(centry);
+}
+
+/* pull a string from a cache entry, using the supplied
+ talloc context
+*/
+static char *centry_string(struct cache_entry *centry, TALLOC_CTX *mem_ctx)
+{
+ uint32_t len;
+ char *ret;
+
+ len = centry_uint8(centry);
+
+ if (len == 0xFF) {
+ /* a deliberate NULL string */
+ return NULL;
+ }
+
+ if (!centry_check_bytes(centry, (size_t)len)) {
+ smb_panic_fn("centry_string");
+ }
+
+ ret = talloc_array(mem_ctx, char, len+1);
+ if (!ret) {
+ smb_panic_fn("centry_string out of memory\n");
+ }
+ memcpy(ret,centry->data + centry->ofs, len);
+ ret[len] = 0;
+ centry->ofs += len;
+ return ret;
+}
+
+/* pull a hash16 from a cache entry, using the supplied
+ talloc context
+*/
+static char *centry_hash16(struct cache_entry *centry, TALLOC_CTX *mem_ctx)
+{
+ uint32_t len;
+ char *ret;
+
+ len = centry_uint8(centry);
+
+ if (len != 16) {
+ DBG_ERR("centry corruption? hash len (%u) != 16\n",
+ len );
+ return NULL;
+ }
+
+ if (!centry_check_bytes(centry, 16)) {
+ return NULL;
+ }
+
+ ret = talloc_array(mem_ctx, char, 16);
+ if (!ret) {
+ smb_panic_fn("centry_hash out of memory\n");
+ }
+ memcpy(ret,centry->data + centry->ofs, 16);
+ centry->ofs += 16;
+ return ret;
+}
+
+/* pull a sid from a cache entry, using the supplied
+ talloc context
+*/
+static bool centry_sid(struct cache_entry *centry, struct dom_sid *sid)
+{
+ char *sid_string;
+ bool ret;
+
+ sid_string = centry_string(centry, talloc_tos());
+ if (sid_string == NULL) {
+ return false;
+ }
+ ret = string_to_sid(sid, sid_string);
+ TALLOC_FREE(sid_string);
+ return ret;
+}
+
+
+/*
+ pull a NTSTATUS from a cache entry
+*/
+static NTSTATUS centry_ntstatus(struct cache_entry *centry)
+{
+ NTSTATUS status;
+
+ status = NT_STATUS(centry_uint32(centry));
+ return status;
+}
+
+
+/* the server is considered down if it can't give us a sequence number */
+static bool wcache_server_down(struct winbindd_domain *domain)
+{
+ bool ret;
+
+ if (!wcache->tdb)
+ return false;
+
+ ret = (domain->sequence_number == DOM_SEQUENCE_NONE);
+
+ if (ret)
+ DBG_DEBUG("wcache_server_down: server for Domain %s down\n",
+ domain->name );
+ return ret;
+}
+
+struct wcache_seqnum_state {
+ uint32_t *seqnum;
+ uint32_t *last_seq_check;
+};
+
+static int wcache_seqnum_parser(TDB_DATA key, TDB_DATA data,
+ void *private_data)
+{
+ struct wcache_seqnum_state *state = private_data;
+
+ if (data.dsize != 8) {
+ DBG_DEBUG("wcache_fetch_seqnum: invalid data size %d\n",
+ (int)data.dsize);
+ return -1;
+ }
+
+ *state->seqnum = IVAL(data.dptr, 0);
+ *state->last_seq_check = IVAL(data.dptr, 4);
+ return 0;
+}
+
+static bool wcache_fetch_seqnum(const char *domain_name, uint32_t *seqnum,
+ uint32_t *last_seq_check)
+{
+ struct wcache_seqnum_state state = {
+ .seqnum = seqnum, .last_seq_check = last_seq_check
+ };
+ size_t len = strlen(domain_name);
+ char keystr[len+8];
+ TDB_DATA key = { .dptr = (uint8_t *)keystr, .dsize = sizeof(keystr) };
+ int ret;
+
+ if (wcache->tdb == NULL) {
+ DBG_DEBUG("wcache_fetch_seqnum: tdb == NULL\n");
+ return false;
+ }
+
+ snprintf(keystr, sizeof(keystr), "SEQNUM/%s", domain_name);
+
+ ret = tdb_parse_record(wcache->tdb, key, wcache_seqnum_parser,
+ &state);
+ return (ret == 0);
+}
+
+static NTSTATUS fetch_cache_seqnum( struct winbindd_domain *domain, time_t now )
+{
+ uint32_t last_check, time_diff;
+
+ if (!wcache_fetch_seqnum(domain->name, &domain->sequence_number,
+ &last_check)) {
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+ domain->last_seq_check = last_check;
+
+ /* have we expired? */
+
+ time_diff = now - domain->last_seq_check;
+ if ((int)time_diff > lp_winbind_cache_time()) {
+ DBG_DEBUG("fetch_cache_seqnum: timeout [%s][%u @ %u]\n",
+ domain->name, domain->sequence_number,
+ (uint32_t)domain->last_seq_check);
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ DBG_DEBUG("fetch_cache_seqnum: success [%s][%u @ %u]\n",
+ domain->name, domain->sequence_number,
+ (uint32_t)domain->last_seq_check);
+
+ return NT_STATUS_OK;
+}
+
+bool wcache_store_seqnum(const char *domain_name, uint32_t seqnum,
+ time_t last_seq_check)
+{
+ size_t len = strlen(domain_name);
+ char keystr[len+8];
+ TDB_DATA key = { .dptr = (uint8_t *)keystr, .dsize = sizeof(keystr) };
+ uint8_t buf[8];
+ int ret;
+
+ if (wcache->tdb == NULL) {
+ DBG_DEBUG("wcache_store_seqnum: wcache->tdb == NULL\n");
+ return false;
+ }
+
+ snprintf(keystr, sizeof(keystr), "SEQNUM/%s", domain_name);
+
+ SIVAL(buf, 0, seqnum);
+ SIVAL(buf, 4, last_seq_check);
+
+ ret = tdb_store(wcache->tdb, key, make_tdb_data(buf, sizeof(buf)),
+ TDB_REPLACE);
+ if (ret != 0) {
+ DBG_DEBUG("tdb_store_bystring failed: %s\n",
+ tdb_errorstr(wcache->tdb));
+ return false;
+ }
+
+ DBG_DEBUG("wcache_store_seqnum: success [%s][%u @ %u]\n",
+ domain_name, seqnum, (unsigned)last_seq_check);
+
+ return true;
+}
+
+static bool store_cache_seqnum( struct winbindd_domain *domain )
+{
+ return wcache_store_seqnum(domain->name, domain->sequence_number,
+ domain->last_seq_check);
+}
+
+/*
+ refresh the domain sequence number on timeout.
+*/
+
+static void refresh_sequence_number(struct winbindd_domain *domain)
+{
+ NTSTATUS status;
+ unsigned time_diff;
+ time_t t = time(NULL);
+ unsigned cache_time = lp_winbind_cache_time();
+
+ if (is_domain_offline(domain)) {
+ return;
+ }
+
+ get_cache( domain );
+
+ time_diff = t - domain->last_seq_check;
+
+ /* see if we have to refetch the domain sequence number */
+ if ((time_diff < cache_time) &&
+ (domain->sequence_number != DOM_SEQUENCE_NONE) &&
+ NT_STATUS_IS_OK(domain->last_status)) {
+ DBG_DEBUG("refresh_sequence_number: %s time ok\n", domain->name);
+ goto done;
+ }
+
+ /* try to get the sequence number from the tdb cache first */
+ /* this will update the timestamp as well */
+
+ status = fetch_cache_seqnum( domain, t );
+ if (NT_STATUS_IS_OK(status) &&
+ (domain->sequence_number != DOM_SEQUENCE_NONE) &&
+ NT_STATUS_IS_OK(domain->last_status)) {
+ goto done;
+ }
+
+ /* just use the current time */
+ domain->last_status = NT_STATUS_OK;
+ domain->sequence_number = time(NULL);
+ domain->last_seq_check = time(NULL);
+
+ /* save the new sequence number in the cache */
+ store_cache_seqnum( domain );
+
+done:
+ DBG_DEBUG("refresh_sequence_number: %s seq number is now %d\n",
+ domain->name, domain->sequence_number);
+
+ return;
+}
+
+/*
+ decide if a cache entry has expired
+*/
+static bool centry_expired(struct winbindd_domain *domain, const char *keystr, struct cache_entry *centry)
+{
+ /* If we've been told to be offline - stay in that state... */
+ if (lp_winbind_offline_logon() && get_global_winbindd_state_offline()) {
+ DBG_DEBUG("centry_expired: Key %s for domain %s valid as winbindd is globally offline.\n",
+ keystr, domain->name );
+ return false;
+ }
+
+ /* when the domain is offline return the cached entry.
+ * This deals with transient offline states... */
+
+ if (!domain->online) {
+ DBG_DEBUG("centry_expired: Key %s for domain %s valid as domain is offline.\n",
+ keystr, domain->name );
+ return false;
+ }
+
+ /* if the server is OK and our cache entry came from when it was down then
+ the entry is invalid */
+ if ((domain->sequence_number != DOM_SEQUENCE_NONE) &&
+ (centry->sequence_number == DOM_SEQUENCE_NONE)) {
+ DBG_DEBUG("centry_expired: Key %s for domain %s invalid sequence.\n",
+ keystr, domain->name );
+ return true;
+ }
+
+ /* if the server is down or the cache entry is not older than the
+ current sequence number or it did not timeout then it is OK */
+ if (wcache_server_down(domain)
+ || ((centry->sequence_number == domain->sequence_number)
+ && ((time_t)centry->timeout > time(NULL)))) {
+ DBG_DEBUG("centry_expired: Key %s for domain %s is good.\n",
+ keystr, domain->name );
+ return false;
+ }
+
+ DBG_DEBUG("centry_expired: Key %s for domain %s expired\n",
+ keystr, domain->name );
+
+ /* it's expired */
+ return true;
+}
+
+static struct cache_entry *wcache_fetch_raw(char *kstr)
+{
+ TDB_DATA data;
+ struct cache_entry *centry;
+ TDB_DATA key;
+
+ key = string_tdb_data(kstr);
+ data = tdb_fetch(wcache->tdb, key);
+ if (!data.dptr) {
+ /* a cache miss */
+ return NULL;
+ }
+
+ centry = SMB_XMALLOC_P(struct cache_entry);
+ centry->data = (unsigned char *)data.dptr;
+ centry->len = data.dsize;
+ centry->ofs = 0;
+
+ if (centry->len < 16) {
+ /* huh? corrupt cache? */
+ DBG_DEBUG("wcache_fetch_raw: Corrupt cache for key %s "
+ "(len < 16)?\n", kstr);
+ centry_free(centry);
+ return NULL;
+ }
+
+ centry->status = centry_ntstatus(centry);
+ centry->sequence_number = centry_uint32(centry);
+ centry->timeout = centry_uint64_t(centry);
+
+ return centry;
+}
+
+static bool is_my_own_sam_domain(struct winbindd_domain *domain)
+{
+ if (strequal(domain->name, get_global_sam_name()) &&
+ sid_check_is_our_sam(&domain->sid)) {
+ return true;
+ }
+
+ return false;
+}
+
+static bool is_builtin_domain(struct winbindd_domain *domain)
+{
+ if (strequal(domain->name, "BUILTIN") &&
+ sid_check_is_builtin(&domain->sid)) {
+ return true;
+ }
+
+ return false;
+}
+
+/*
+ fetch an entry from the cache, with a varargs key. auto-fetch the sequence
+ number and return status
+*/
+static struct cache_entry *wcache_fetch(struct winbind_cache *cache,
+ struct winbindd_domain *domain,
+ const char *format, ...) PRINTF_ATTRIBUTE(3,4);
+static struct cache_entry *wcache_fetch(struct winbind_cache *cache,
+ struct winbindd_domain *domain,
+ const char *format, ...)
+{
+ va_list ap;
+ char *kstr;
+ struct cache_entry *centry;
+ int ret;
+
+ if (!winbindd_use_cache() ||
+ is_my_own_sam_domain(domain) ||
+ is_builtin_domain(domain)) {
+ return NULL;
+ }
+
+ refresh_sequence_number(domain);
+
+ va_start(ap, format);
+ ret = vasprintf(&kstr, format, ap);
+ va_end(ap);
+
+ if (ret == -1) {
+ return NULL;
+ }
+
+ centry = wcache_fetch_raw(kstr);
+ if (centry == NULL) {
+ free(kstr);
+ return NULL;
+ }
+
+ if (centry_expired(domain, kstr, centry)) {
+
+ DBG_DEBUG("wcache_fetch: entry %s expired for domain %s\n",
+ kstr, domain->name );
+
+ centry_free(centry);
+ free(kstr);
+ return NULL;
+ }
+
+ DBG_DEBUG("wcache_fetch: returning entry %s for domain %s\n",
+ kstr, domain->name );
+
+ free(kstr);
+ return centry;
+}
+
+static void wcache_delete(const char *format, ...) PRINTF_ATTRIBUTE(1,2);
+static void wcache_delete(const char *format, ...)
+{
+ va_list ap;
+ char *kstr;
+ TDB_DATA key;
+ int ret;
+
+ va_start(ap, format);
+ ret = vasprintf(&kstr, format, ap);
+ va_end(ap);
+
+ if (ret == -1) {
+ return;
+ }
+
+ key = string_tdb_data(kstr);
+
+ tdb_delete(wcache->tdb, key);
+ free(kstr);
+}
+
+/*
+ make sure we have at least len bytes available in a centry
+*/
+static void centry_expand(struct cache_entry *centry, uint32_t len)
+{
+ if (centry->len - centry->ofs >= len)
+ return;
+ centry->len *= 2;
+ centry->data = SMB_REALLOC_ARRAY(centry->data, unsigned char,
+ centry->len);
+ if (!centry->data) {
+ DBG_ERR("out of memory: needed %d bytes in centry_expand\n", centry->len);
+ smb_panic_fn("out of memory in centry_expand");
+ }
+}
+
+/*
+ push a uint64_t into a centry
+*/
+static void centry_put_uint64_t(struct cache_entry *centry, uint64_t v)
+{
+ centry_expand(centry, 8);
+ SBVAL(centry->data, centry->ofs, v);
+ centry->ofs += 8;
+}
+
+/*
+ push a uint32_t into a centry
+*/
+static void centry_put_uint32(struct cache_entry *centry, uint32_t v)
+{
+ centry_expand(centry, 4);
+ SIVAL(centry->data, centry->ofs, v);
+ centry->ofs += 4;
+}
+
+/*
+ push a uint16_t into a centry
+*/
+static void centry_put_uint16(struct cache_entry *centry, uint16_t v)
+{
+ centry_expand(centry, 2);
+ SSVAL(centry->data, centry->ofs, v);
+ centry->ofs += 2;
+}
+
+/*
+ push a uint8_t into a centry
+*/
+static void centry_put_uint8(struct cache_entry *centry, uint8_t v)
+{
+ centry_expand(centry, 1);
+ SCVAL(centry->data, centry->ofs, v);
+ centry->ofs += 1;
+}
+
+/*
+ push a string into a centry
+ */
+static void centry_put_string(struct cache_entry *centry, const char *s)
+{
+ int len;
+
+ if (!s) {
+ /* null strings are marked as len 0xFFFF */
+ centry_put_uint8(centry, 0xFF);
+ return;
+ }
+
+ len = strlen(s);
+ /* can't handle more than 254 char strings. Truncating is probably best */
+ if (len > 254) {
+ DBG_DEBUG("centry_put_string: truncating len (%d) to: 254\n", len);
+ len = 254;
+ }
+ centry_put_uint8(centry, len);
+ centry_expand(centry, len);
+ memcpy(centry->data + centry->ofs, s, len);
+ centry->ofs += len;
+}
+
+/*
+ push a 16 byte hash into a centry - treat as 16 byte string.
+ */
+static void centry_put_hash16(struct cache_entry *centry, const uint8_t val[16])
+{
+ centry_put_uint8(centry, 16);
+ centry_expand(centry, 16);
+ memcpy(centry->data + centry->ofs, val, 16);
+ centry->ofs += 16;
+}
+
+static void centry_put_sid(struct cache_entry *centry, const struct dom_sid *sid)
+{
+ struct dom_sid_buf sid_string;
+ centry_put_string(centry, dom_sid_str_buf(sid, &sid_string));
+}
+
+
+/*
+ put NTSTATUS into a centry
+*/
+static void centry_put_ntstatus(struct cache_entry *centry, NTSTATUS status)
+{
+ uint32_t status_value = NT_STATUS_V(status);
+ centry_put_uint32(centry, status_value);
+}
+
+
+/*
+ push a NTTIME into a centry
+*/
+static void centry_put_nttime(struct cache_entry *centry, NTTIME nt)
+{
+ centry_expand(centry, 8);
+ SIVAL(centry->data, centry->ofs, nt & 0xFFFFFFFF);
+ centry->ofs += 4;
+ SIVAL(centry->data, centry->ofs, nt >> 32);
+ centry->ofs += 4;
+}
+
+/*
+ push a time_t into a centry - use a 64 bit size.
+ NTTIME here is being used as a convenient 64-bit size.
+*/
+static void centry_put_time(struct cache_entry *centry, time_t t)
+{
+ NTTIME nt = (NTTIME)t;
+ centry_put_nttime(centry, nt);
+}
+
+/*
+ start a centry for output. When finished, call centry_end()
+*/
+static struct cache_entry *centry_start(struct winbindd_domain *domain,
+ NTSTATUS status)
+{
+ struct cache_entry *centry;
+
+ if (!wcache->tdb)
+ return NULL;
+
+ centry = SMB_XMALLOC_P(struct cache_entry);
+
+ centry->len = 8192; /* reasonable default */
+ centry->data = SMB_XMALLOC_ARRAY(uint8_t, centry->len);
+ centry->ofs = 0;
+ centry->sequence_number = domain->sequence_number;
+ centry->timeout = lp_winbind_cache_time() + time(NULL);
+ centry_put_ntstatus(centry, status);
+ centry_put_uint32(centry, centry->sequence_number);
+ centry_put_uint64_t(centry, centry->timeout);
+ return centry;
+}
+
+/*
+ finish a centry and write it to the tdb
+*/
+static void centry_end(struct cache_entry *centry, const char *format, ...) PRINTF_ATTRIBUTE(2,3);
+static void centry_end(struct cache_entry *centry, const char *format, ...)
+{
+ va_list ap;
+ char *kstr;
+ TDB_DATA key, data;
+ int ret;
+
+ if (!winbindd_use_cache()) {
+ return;
+ }
+
+ va_start(ap, format);
+ ret = vasprintf(&kstr, format, ap);
+ va_end(ap);
+
+ if (ret == -1) {
+ return;
+ }
+
+ key = string_tdb_data(kstr);
+ data.dptr = centry->data;
+ data.dsize = centry->ofs;
+
+ tdb_store(wcache->tdb, key, data, TDB_REPLACE);
+ free(kstr);
+}
+
+static void wcache_save_name_to_sid(struct winbindd_domain *domain,
+ NTSTATUS status, const char *domain_name,
+ const char *name, const struct dom_sid *sid,
+ enum lsa_SidType type)
+{
+ bool ok;
+
+ ok = namemap_cache_set_name2sid(domain_name, name, sid, type,
+ time(NULL) + lp_winbind_cache_time());
+ if (!ok) {
+ DBG_DEBUG("namemap_cache_set_name2sid failed\n");
+ }
+
+ /*
+ * Don't store the reverse mapping. The name came from user
+ * input, and we might not have the correct capitalization,
+ * which is important for nsswitch.
+ */
+}
+
+static void wcache_save_sid_to_name(struct winbindd_domain *domain, NTSTATUS status,
+ const struct dom_sid *sid, const char *domain_name, const char *name, enum lsa_SidType type)
+{
+ bool ok;
+
+ ok = namemap_cache_set_sid2name(sid, domain_name, name, type,
+ time(NULL) + lp_winbind_cache_time());
+ if (!ok) {
+ DBG_DEBUG("namemap_cache_set_sid2name failed\n");
+ }
+
+ if (type != SID_NAME_UNKNOWN) {
+ ok = namemap_cache_set_name2sid(
+ domain_name, name, sid, type,
+ time(NULL) + lp_winbind_cache_time());
+ if (!ok) {
+ DBG_DEBUG("namemap_cache_set_name2sid failed\n");
+ }
+ }
+}
+
+static void wcache_save_lockout_policy(struct winbindd_domain *domain,
+ NTSTATUS status,
+ struct samr_DomInfo12 *lockout_policy)
+{
+ struct cache_entry *centry;
+
+ centry = centry_start(domain, status);
+ if (!centry)
+ return;
+
+ centry_put_nttime(centry, lockout_policy->lockout_duration);
+ centry_put_nttime(centry, lockout_policy->lockout_window);
+ centry_put_uint16(centry, lockout_policy->lockout_threshold);
+
+ centry_end(centry, "LOC_POL/%s", domain->name);
+
+ DBG_DEBUG("wcache_save_lockout_policy: %s\n", domain->name);
+
+ centry_free(centry);
+}
+
+
+
+static void wcache_save_password_policy(struct winbindd_domain *domain,
+ NTSTATUS status,
+ struct samr_DomInfo1 *policy)
+{
+ struct cache_entry *centry;
+
+ centry = centry_start(domain, status);
+ if (!centry)
+ return;
+
+ centry_put_uint16(centry, policy->min_password_length);
+ centry_put_uint16(centry, policy->password_history_length);
+ centry_put_uint32(centry, policy->password_properties);
+ centry_put_nttime(centry, policy->max_password_age);
+ centry_put_nttime(centry, policy->min_password_age);
+
+ centry_end(centry, "PWD_POL/%s", domain->name);
+
+ DBG_DEBUG("wcache_save_password_policy: %s\n", domain->name);
+
+ centry_free(centry);
+}
+
+/***************************************************************************
+ ***************************************************************************/
+
+static void wcache_save_username_alias(struct winbindd_domain *domain,
+ NTSTATUS status,
+ const char *name, const char *alias)
+{
+ struct cache_entry *centry;
+ fstring uname;
+
+ if ( (centry = centry_start(domain, status)) == NULL )
+ return;
+
+ centry_put_string( centry, alias );
+
+ fstrcpy(uname, name);
+ (void)strupper_m(uname);
+ centry_end(centry, "NSS/NA/%s", uname);
+
+ DBG_DEBUG("wcache_save_username_alias: %s -> %s\n", name, alias );
+
+ centry_free(centry);
+}
+
+static void wcache_save_alias_username(struct winbindd_domain *domain,
+ NTSTATUS status,
+ const char *alias, const char *name)
+{
+ struct cache_entry *centry;
+ fstring uname;
+
+ if ( (centry = centry_start(domain, status)) == NULL )
+ return;
+
+ centry_put_string( centry, name );
+
+ fstrcpy(uname, alias);
+ (void)strupper_m(uname);
+ centry_end(centry, "NSS/AN/%s", uname);
+
+ DBG_DEBUG("wcache_save_alias_username: %s -> %s\n", alias, name );
+
+ centry_free(centry);
+}
+
+/***************************************************************************
+ ***************************************************************************/
+
+NTSTATUS resolve_username_to_alias( TALLOC_CTX *mem_ctx,
+ struct winbindd_domain *domain,
+ const char *name, char **alias )
+{
+ struct winbind_cache *cache = get_cache(domain);
+ struct cache_entry *centry = NULL;
+ NTSTATUS status;
+ char *upper_name;
+
+ if ( domain->internal )
+ return NT_STATUS_NOT_SUPPORTED;
+
+ if (!cache->tdb)
+ goto do_query;
+
+ upper_name = talloc_strdup_upper(mem_ctx, name);
+ if (upper_name == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ centry = wcache_fetch(cache, domain, "NSS/NA/%s", upper_name);
+
+ talloc_free(upper_name);
+
+ if (!centry)
+ goto do_query;
+
+ status = centry->status;
+
+ if (!NT_STATUS_IS_OK(status)) {
+ centry_free(centry);
+ return status;
+ }
+
+ *alias = centry_string( centry, mem_ctx );
+
+ centry_free(centry);
+
+ DBG_DEBUG("resolve_username_to_alias: [Cached] - mapped %s to %s\n",
+ name, *alias ? *alias : "(none)");
+
+ return (*alias) ? NT_STATUS_OK : NT_STATUS_OBJECT_NAME_NOT_FOUND;
+
+do_query:
+
+ /* If its not in cache and we are offline, then fail */
+
+ if (is_domain_offline(domain)) {
+ DBG_DEBUG("resolve_username_to_alias: rejecting query "
+ "in offline mode\n");
+ return NT_STATUS_NOT_FOUND;
+ }
+
+ status = nss_map_to_alias( mem_ctx, domain->name, name, alias );
+
+ if ( NT_STATUS_IS_OK( status ) ) {
+ wcache_save_username_alias(domain, status, name, *alias);
+ }
+
+ if ( NT_STATUS_EQUAL( status, NT_STATUS_NONE_MAPPED ) ) {
+ wcache_save_username_alias(domain, status, name, "(NULL)");
+ }
+
+ DBG_INFO("resolve_username_to_alias: backend query returned %s\n",
+ nt_errstr(status));
+
+ if ( NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) ) {
+ set_domain_offline( domain );
+ }
+
+ return status;
+}
+
+/***************************************************************************
+ ***************************************************************************/
+
+NTSTATUS resolve_alias_to_username( TALLOC_CTX *mem_ctx,
+ struct winbindd_domain *domain,
+ const char *alias, char **name )
+{
+ struct winbind_cache *cache = get_cache(domain);
+ struct cache_entry *centry = NULL;
+ NTSTATUS status;
+ char *upper_name;
+
+ if ( domain->internal )
+ return NT_STATUS_NOT_SUPPORTED;
+
+ if (!cache->tdb)
+ goto do_query;
+
+ upper_name = talloc_strdup(mem_ctx, alias);
+ if (upper_name == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ if (!strupper_m(upper_name)) {
+ talloc_free(upper_name);
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ centry = wcache_fetch(cache, domain, "NSS/AN/%s", upper_name);
+
+ talloc_free(upper_name);
+
+ if (!centry)
+ goto do_query;
+
+ status = centry->status;
+
+ if (!NT_STATUS_IS_OK(status)) {
+ centry_free(centry);
+ return status;
+ }
+
+ *name = centry_string( centry, mem_ctx );
+
+ centry_free(centry);
+
+ DBG_DEBUG("resolve_alias_to_username: [Cached] - mapped %s to %s\n",
+ alias, *name ? *name : "(none)");
+
+ return (*name) ? NT_STATUS_OK : NT_STATUS_OBJECT_NAME_NOT_FOUND;
+
+do_query:
+
+ /* If its not in cache and we are offline, then fail */
+
+ if (is_domain_offline(domain)) {
+ DBG_DEBUG("resolve_alias_to_username: rejecting query "
+ "in offline mode\n");
+ return NT_STATUS_NOT_FOUND;
+ }
+
+ /* an alias cannot contain a domain prefix or '@' */
+
+ if (strchr(alias, '\\') || strchr(alias, '@')) {
+ DBG_DEBUG("resolve_alias_to_username: skipping fully "
+ "qualified name %s\n", alias);
+ return NT_STATUS_OBJECT_NAME_INVALID;
+ }
+
+ status = nss_map_from_alias( mem_ctx, domain->name, alias, name );
+
+ if ( NT_STATUS_IS_OK( status ) ) {
+ wcache_save_alias_username( domain, status, alias, *name );
+ }
+
+ if (NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED)) {
+ wcache_save_alias_username(domain, status, alias, "(NULL)");
+ }
+
+ DBG_INFO("resolve_alias_to_username: backend query returned %s\n",
+ nt_errstr(status));
+
+ if ( NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) ) {
+ set_domain_offline( domain );
+ }
+
+ return status;
+}
+
+NTSTATUS wcache_cached_creds_exist(struct winbindd_domain *domain, const struct dom_sid *sid)
+{
+ struct winbind_cache *cache = get_cache(domain);
+ int ret;
+ struct dom_sid_buf tmp;
+ fstring key_str;
+ uint32_t rid;
+
+ if (!cache->tdb) {
+ return NT_STATUS_INTERNAL_DB_ERROR;
+ }
+
+ if (is_null_sid(sid)) {
+ return NT_STATUS_INVALID_SID;
+ }
+
+ if (!(sid_peek_rid(sid, &rid)) || (rid == 0)) {
+ return NT_STATUS_INVALID_SID;
+ }
+
+ fstr_sprintf(key_str, "CRED/%s", dom_sid_str_buf(sid, &tmp));
+
+ ret = tdb_exists(cache->tdb, string_tdb_data(key_str));
+ if (ret != 1) {
+ return NT_STATUS_OBJECT_NAME_NOT_FOUND;
+ }
+
+ return NT_STATUS_OK;
+}
+
+/* Lookup creds for a SID - copes with old (unsalted) creds as well
+ as new salted ones. */
+
+NTSTATUS wcache_get_creds(struct winbindd_domain *domain,
+ TALLOC_CTX *mem_ctx,
+ const struct dom_sid *sid,
+ const uint8_t **cached_nt_pass,
+ const uint8_t **cached_salt)
+{
+ struct winbind_cache *cache = get_cache(domain);
+ struct cache_entry *centry = NULL;
+ NTSTATUS status;
+ uint32_t rid;
+ struct dom_sid_buf sidstr;
+
+ if (!cache->tdb) {
+ return NT_STATUS_INTERNAL_DB_ERROR;
+ }
+
+ if (is_null_sid(sid)) {
+ return NT_STATUS_INVALID_SID;
+ }
+
+ if (!(sid_peek_rid(sid, &rid)) || (rid == 0)) {
+ return NT_STATUS_INVALID_SID;
+ }
+
+ /* Try and get a salted cred first. If we can't
+ fall back to an unsalted cred. */
+
+ centry = wcache_fetch(cache, domain, "CRED/%s",
+ dom_sid_str_buf(sid, &sidstr));
+ if (!centry) {
+ DBG_DEBUG("wcache_get_creds: entry for [CRED/%s] not found\n",
+ dom_sid_str_buf(sid, &sidstr));
+ return NT_STATUS_OBJECT_NAME_NOT_FOUND;
+ }
+
+ /*
+ * We don't use the time element at this moment,
+ * but we have to consume it, so that we don't
+ * need to change the disk format of the cache.
+ */
+ (void)centry_time(centry);
+
+ /* In the salted case this isn't actually the nt_hash itself,
+ but the MD5 of the salt + nt_hash. Let the caller
+ sort this out. It can tell as we only return the cached_salt
+ if we are returning a salted cred. */
+
+ *cached_nt_pass = (const uint8_t *)centry_hash16(centry, mem_ctx);
+ if (*cached_nt_pass == NULL) {
+
+ dom_sid_str_buf(sid, &sidstr);
+
+ /* Bad (old) cred cache. Delete and pretend we
+ don't have it. */
+ DBG_WARNING("wcache_get_creds: bad entry for [CRED/%s] - deleting\n",
+ sidstr.buf);
+ wcache_delete("CRED/%s", sidstr.buf);
+ centry_free(centry);
+ return NT_STATUS_OBJECT_NAME_NOT_FOUND;
+ }
+
+ /* We only have 17 bytes more data in the salted cred case. */
+ if (centry->len - centry->ofs == 17) {
+ *cached_salt = (const uint8_t *)centry_hash16(centry, mem_ctx);
+ } else {
+ *cached_salt = NULL;
+ }
+
+ dump_data_pw("cached_nt_pass", *cached_nt_pass, NT_HASH_LEN);
+ if (*cached_salt) {
+ dump_data_pw("cached_salt", *cached_salt, NT_HASH_LEN);
+ }
+
+ status = centry->status;
+
+ DBG_DEBUG("wcache_get_creds: [Cached] - cached creds for user %s status: %s\n",
+ dom_sid_str_buf(sid, &sidstr),
+ nt_errstr(status) );
+
+ centry_free(centry);
+ return status;
+}
+
+/* Store creds for a SID - only writes out new salted ones. */
+
+NTSTATUS wcache_save_creds(struct winbindd_domain *domain,
+ const struct dom_sid *sid,
+ const uint8_t nt_pass[NT_HASH_LEN])
+{
+ struct cache_entry *centry;
+ struct dom_sid_buf sid_str;
+ uint32_t rid;
+ uint8_t cred_salt[NT_HASH_LEN];
+ uint8_t salted_hash[NT_HASH_LEN];
+ gnutls_hash_hd_t hash_hnd = NULL;
+ int rc;
+
+ if (is_null_sid(sid)) {
+ return NT_STATUS_INVALID_SID;
+ }
+
+ if (!(sid_peek_rid(sid, &rid)) || (rid == 0)) {
+ return NT_STATUS_INVALID_SID;
+ }
+
+ centry = centry_start(domain, NT_STATUS_OK);
+ if (!centry) {
+ return NT_STATUS_INTERNAL_DB_ERROR;
+ }
+
+ dump_data_pw("nt_pass", nt_pass, NT_HASH_LEN);
+
+ centry_put_time(centry, time(NULL));
+
+ /* Create a salt and then salt the hash. */
+ generate_random_buffer(cred_salt, NT_HASH_LEN);
+
+ rc = gnutls_hash_init(&hash_hnd, GNUTLS_DIG_MD5);
+ if (rc < 0) {
+ centry_free(centry);
+ return gnutls_error_to_ntstatus(rc, NT_STATUS_HASH_NOT_SUPPORTED);
+ }
+
+ rc = gnutls_hash(hash_hnd, cred_salt, 16);
+ if (rc < 0) {
+ gnutls_hash_deinit(hash_hnd, NULL);
+ centry_free(centry);
+ return gnutls_error_to_ntstatus(rc, NT_STATUS_HASH_NOT_SUPPORTED);
+ }
+ rc = gnutls_hash(hash_hnd, nt_pass, 16);
+ if (rc < 0) {
+ gnutls_hash_deinit(hash_hnd, NULL);
+ centry_free(centry);
+ return gnutls_error_to_ntstatus(rc, NT_STATUS_HASH_NOT_SUPPORTED);
+ }
+ gnutls_hash_deinit(hash_hnd, salted_hash);
+
+ centry_put_hash16(centry, salted_hash);
+ centry_put_hash16(centry, cred_salt);
+ centry_end(centry, "CRED/%s", dom_sid_str_buf(sid, &sid_str));
+
+ DBG_DEBUG("wcache_save_creds: %s\n", sid_str.buf);
+
+ centry_free(centry);
+
+ return NT_STATUS_OK;
+}
+
+
+/* Query display info. This is the basic user list fn */
+NTSTATUS wb_cache_query_user_list(struct winbindd_domain *domain,
+ TALLOC_CTX *mem_ctx,
+ uint32_t **prids)
+{
+ struct winbind_cache *cache = get_cache(domain);
+ struct cache_entry *centry = NULL;
+ uint32_t num_rids = 0;
+ uint32_t *rids = NULL;
+ NTSTATUS status;
+ unsigned int i, retry;
+ bool old_status = domain->online;
+
+ *prids = NULL;
+
+ if (!cache->tdb)
+ goto do_query;
+
+ centry = wcache_fetch(cache, domain, "UL/%s", domain->name);
+ if (!centry)
+ goto do_query;
+
+do_fetch_cache:
+ num_rids = centry_uint32(centry);
+
+ if (num_rids == 0) {
+ goto do_cached;
+ }
+
+ rids = talloc_array(mem_ctx, uint32_t, num_rids);
+ if (rids == NULL) {
+ centry_free(centry);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ for (i=0; i<num_rids; i++) {
+ rids[i] = centry_uint32(centry);
+ }
+
+do_cached:
+ status = centry->status;
+
+ DBG_DEBUG("query_user_list: [Cached] - cached list for domain %s status: %s\n",
+ domain->name, nt_errstr(status) );
+
+ centry_free(centry);
+ return status;
+
+do_query:
+
+ /* Put the query_user_list() in a retry loop. There appears to be
+ * some bug either with Windows 2000 or Samba's handling of large
+ * rpc replies. This manifests itself as sudden disconnection
+ * at a random point in the enumeration of a large (60k) user list.
+ * The retry loop simply tries the operation again. )-: It's not
+ * pretty but an acceptable workaround until we work out what the
+ * real problem is. */
+
+ retry = 0;
+ do {
+
+ DBG_DEBUG("query_user_list: [Cached] - doing backend query for list for domain %s\n",
+ domain->name );
+
+ rids = NULL;
+ status = domain->backend->query_user_list(domain, mem_ctx,
+ &rids);
+ num_rids = talloc_array_length(rids);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ DBG_NOTICE("query_user_list: returned 0x%08x, "
+ "retrying\n", NT_STATUS_V(status));
+ }
+ reset_cm_connection_on_error(domain, NULL, status);
+ if (NT_STATUS_EQUAL(status, NT_STATUS_UNSUCCESSFUL)) {
+ DBG_NOTICE("query_user_list: flushing "
+ "connection cache\n");
+ invalidate_cm_connection(domain);
+ }
+ if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
+ NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
+ if (!domain->internal && old_status) {
+ set_domain_offline(domain);
+ }
+ /* store partial response. */
+ if (num_rids > 0) {
+ /*
+ * humm, what about the status used for cache?
+ * Should it be NT_STATUS_OK?
+ */
+ break;
+ }
+ /*
+ * domain is offline now, and there is no user entries,
+ * try to fetch from cache again.
+ */
+ if (cache->tdb && !domain->online && !domain->internal && old_status) {
+ centry = wcache_fetch(cache, domain, "UL/%s", domain->name);
+ /* partial response... */
+ if (!centry) {
+ goto skip_save;
+ } else {
+ goto do_fetch_cache;
+ }
+ } else {
+ goto skip_save;
+ }
+ }
+
+ } while (NT_STATUS_V(status) == NT_STATUS_V(NT_STATUS_UNSUCCESSFUL) &&
+ (retry++ < 5));
+
+ /* and save it */
+ refresh_sequence_number(domain);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ centry = centry_start(domain, status);
+ if (!centry)
+ goto skip_save;
+ centry_put_uint32(centry, num_rids);
+ for (i=0; i<num_rids; i++) {
+ centry_put_uint32(centry, rids[i]);
+ }
+ centry_end(centry, "UL/%s", domain->name);
+ centry_free(centry);
+
+ *prids = rids;
+
+skip_save:
+ return status;
+}
+
+/* list all domain groups */
+NTSTATUS wb_cache_enum_dom_groups(struct winbindd_domain *domain,
+ TALLOC_CTX *mem_ctx,
+ uint32_t *num_entries,
+ struct wb_acct_info **info)
+{
+ struct winbind_cache *cache = get_cache(domain);
+ struct cache_entry *centry = NULL;
+ NTSTATUS status;
+ unsigned int i;
+ bool old_status;
+
+ old_status = domain->online;
+ if (!cache->tdb)
+ goto do_query;
+
+ centry = wcache_fetch(cache, domain, "GL/%s/domain", domain->name);
+ if (!centry)
+ goto do_query;
+
+do_fetch_cache:
+ *num_entries = centry_uint32(centry);
+
+ if (*num_entries == 0)
+ goto do_cached;
+
+ (*info) = talloc_array(mem_ctx, struct wb_acct_info, *num_entries);
+ if (! (*info)) {
+ smb_panic_fn("enum_dom_groups out of memory");
+ }
+ for (i=0; i<(*num_entries); i++) {
+ (*info)[i].acct_name = centry_string(centry, (*info));
+ (*info)[i].acct_desc = centry_string(centry, (*info));
+ (*info)[i].rid = centry_uint32(centry);
+ }
+
+do_cached:
+ status = centry->status;
+
+ DBG_DEBUG("enum_dom_groups: [Cached] - cached list for domain %s status: %s\n",
+ domain->name, nt_errstr(status) );
+
+ centry_free(centry);
+ return status;
+
+do_query:
+ *num_entries = 0;
+ *info = NULL;
+
+ DBG_DEBUG("enum_dom_groups: [Cached] - doing backend query for list for domain %s\n",
+ domain->name );
+
+ status = domain->backend->enum_dom_groups(domain, mem_ctx, num_entries, info);
+
+ if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
+ NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
+ if (!domain->internal && old_status) {
+ set_domain_offline(domain);
+ }
+ if (cache->tdb &&
+ !domain->online &&
+ !domain->internal &&
+ old_status) {
+ centry = wcache_fetch(cache, domain, "GL/%s/domain", domain->name);
+ if (centry) {
+ goto do_fetch_cache;
+ }
+ }
+ }
+ /* and save it */
+ refresh_sequence_number(domain);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ centry = centry_start(domain, status);
+ if (!centry)
+ goto skip_save;
+ centry_put_uint32(centry, *num_entries);
+ for (i=0; i<(*num_entries); i++) {
+ centry_put_string(centry, (*info)[i].acct_name);
+ centry_put_string(centry, (*info)[i].acct_desc);
+ centry_put_uint32(centry, (*info)[i].rid);
+ }
+ centry_end(centry, "GL/%s/domain", domain->name);
+ centry_free(centry);
+
+skip_save:
+ return status;
+}
+
+/* list all domain groups */
+NTSTATUS wb_cache_enum_local_groups(struct winbindd_domain *domain,
+ TALLOC_CTX *mem_ctx,
+ uint32_t *num_entries,
+ struct wb_acct_info **info)
+{
+ struct winbind_cache *cache = get_cache(domain);
+ struct cache_entry *centry = NULL;
+ NTSTATUS status;
+ unsigned int i;
+ bool old_status;
+
+ old_status = domain->online;
+ if (!cache->tdb)
+ goto do_query;
+
+ centry = wcache_fetch(cache, domain, "GL/%s/local", domain->name);
+ if (!centry)
+ goto do_query;
+
+do_fetch_cache:
+ *num_entries = centry_uint32(centry);
+
+ if (*num_entries == 0)
+ goto do_cached;
+
+ (*info) = talloc_array(mem_ctx, struct wb_acct_info, *num_entries);
+ if (! (*info)) {
+ smb_panic_fn("enum_dom_groups out of memory");
+ }
+ for (i=0; i<(*num_entries); i++) {
+ (*info)[i].acct_name = centry_string(centry, (*info));
+ (*info)[i].acct_desc = centry_string(centry, (*info));
+ (*info)[i].rid = centry_uint32(centry);
+ }
+
+do_cached:
+
+ /* If we are returning cached data and the domain controller
+ is down then we don't know whether the data is up to date
+ or not. Return NT_STATUS_MORE_PROCESSING_REQUIRED to
+ indicate this. */
+
+ if (wcache_server_down(domain)) {
+ DBG_DEBUG("enum_local_groups: returning cached user list and server was down\n");
+ status = NT_STATUS_MORE_PROCESSING_REQUIRED;
+ } else
+ status = centry->status;
+
+ DBG_DEBUG("enum_local_groups: [Cached] - cached list for domain %s status: %s\n",
+ domain->name, nt_errstr(status) );
+
+ centry_free(centry);
+ return status;
+
+do_query:
+ *num_entries = 0;
+ *info = NULL;
+
+ DBG_DEBUG("enum_local_groups: [Cached] - doing backend query for list for domain %s\n",
+ domain->name );
+
+ status = domain->backend->enum_local_groups(domain, mem_ctx, num_entries, info);
+
+ if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
+ NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
+ if (!domain->internal && old_status) {
+ set_domain_offline(domain);
+ }
+ if (cache->tdb &&
+ !domain->internal &&
+ !domain->online &&
+ old_status) {
+ centry = wcache_fetch(cache, domain, "GL/%s/local", domain->name);
+ if (centry) {
+ goto do_fetch_cache;
+ }
+ }
+ }
+ /* and save it */
+ refresh_sequence_number(domain);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ centry = centry_start(domain, status);
+ if (!centry)
+ goto skip_save;
+ centry_put_uint32(centry, *num_entries);
+ for (i=0; i<(*num_entries); i++) {
+ centry_put_string(centry, (*info)[i].acct_name);
+ centry_put_string(centry, (*info)[i].acct_desc);
+ centry_put_uint32(centry, (*info)[i].rid);
+ }
+ centry_end(centry, "GL/%s/local", domain->name);
+ centry_free(centry);
+
+skip_save:
+ return status;
+}
+
+struct wcache_name_to_sid_state {
+ struct dom_sid *sid;
+ enum lsa_SidType *type;
+ bool offline;
+ bool found;
+};
+
+static void wcache_name_to_sid_fn(const struct dom_sid *sid,
+ enum lsa_SidType type,
+ bool expired,
+ void *private_data)
+{
+ struct wcache_name_to_sid_state *state = private_data;
+
+ *state->sid = *sid;
+ *state->type = type;
+ state->found = (!expired || state->offline);
+}
+
+static NTSTATUS wcache_name_to_sid(struct winbindd_domain *domain,
+ const char *domain_name,
+ const char *name,
+ struct dom_sid *sid,
+ enum lsa_SidType *type)
+{
+ struct wcache_name_to_sid_state state = {
+ .sid = sid, .type = type, .found = false,
+ .offline = is_domain_offline(domain),
+ };
+ bool ok;
+
+ ok = namemap_cache_find_name(domain_name, name, wcache_name_to_sid_fn,
+ &state);
+ if (!ok) {
+ DBG_DEBUG("namemap_cache_find_name failed\n");
+ return NT_STATUS_NOT_FOUND;
+ }
+ if (!state.found) {
+ DBG_DEBUG("cache entry not found\n");
+ return NT_STATUS_NOT_FOUND;
+ }
+ if (*type == SID_NAME_UNKNOWN) {
+ return NT_STATUS_NONE_MAPPED;
+ }
+
+ return NT_STATUS_OK;
+}
+
+/* convert a single name to a sid in a domain */
+NTSTATUS wb_cache_name_to_sid(struct winbindd_domain *domain,
+ TALLOC_CTX *mem_ctx,
+ const char *domain_name,
+ const char *name,
+ uint32_t flags,
+ struct dom_sid *sid,
+ enum lsa_SidType *type)
+{
+ NTSTATUS status;
+ bool old_status;
+ const char *dom_name;
+
+ old_status = domain->online;
+
+ status = wcache_name_to_sid(domain, domain_name, name, sid, type);
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
+ return status;
+ }
+
+ ZERO_STRUCTP(sid);
+
+ DBG_DEBUG("name_to_sid: [Cached] - doing backend query for name for domain %s\n",
+ domain->name );
+
+ winbindd_domain_init_backend(domain);
+ status = domain->backend->name_to_sid(domain, mem_ctx, domain_name,
+ name, flags, &dom_name, sid, type);
+
+ if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
+ NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
+ if (!domain->internal && old_status) {
+ set_domain_offline(domain);
+ }
+ if (!domain->internal &&
+ !domain->online &&
+ old_status) {
+ NTSTATUS cache_status;
+ cache_status = wcache_name_to_sid(domain, domain_name, name, sid, type);
+ return cache_status;
+ }
+ }
+ /* and save it */
+
+ if (domain->online &&
+ (NT_STATUS_IS_OK(status) || NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED))) {
+ enum lsa_SidType save_type = *type;
+
+ if (NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED)) {
+ save_type = SID_NAME_UNKNOWN;
+ }
+
+ wcache_save_name_to_sid(domain, status, domain_name, name, sid,
+ save_type);
+
+ /* Only save the reverse mapping if this was not a UPN */
+ if (!strchr(name, '@')) {
+ if (!strupper_m(discard_const_p(char, domain_name))) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+ (void)strlower_m(discard_const_p(char, name));
+ wcache_save_sid_to_name(domain, status, sid,
+ dom_name, name, save_type);
+ }
+ }
+
+ return status;
+}
+
+struct wcache_sid_to_name_state {
+ TALLOC_CTX *mem_ctx;
+ char **domain_name;
+ char **name;
+ enum lsa_SidType *type;
+ bool offline;
+ bool found;
+};
+
+static void wcache_sid_to_name_fn(const char *domain,
+ const char *name,
+ enum lsa_SidType type,
+ bool expired,
+ void *private_data)
+{
+ struct wcache_sid_to_name_state *state = private_data;
+
+ *state->domain_name = talloc_strdup(state->mem_ctx, domain);
+ if (*state->domain_name == NULL) {
+ return;
+ }
+ *state->name = talloc_strdup(state->mem_ctx, name);
+ if (*state->name == NULL) {
+ return;
+ }
+ *state->type = type;
+ state->found = (!expired || state->offline);
+}
+
+static NTSTATUS wcache_sid_to_name(struct winbindd_domain *domain,
+ const struct dom_sid *sid,
+ TALLOC_CTX *mem_ctx,
+ char **domain_name,
+ char **name,
+ enum lsa_SidType *type)
+{
+ struct wcache_sid_to_name_state state = {
+ .mem_ctx = mem_ctx, .found = false,
+ .domain_name = domain_name, .name = name, .type = type,
+ .offline = is_domain_offline(domain)
+ };
+ bool ok;
+
+ ok = namemap_cache_find_sid(sid, wcache_sid_to_name_fn, &state);
+ if (!ok) {
+ DBG_DEBUG("namemap_cache_find_name failed\n");
+ return NT_STATUS_NOT_FOUND;
+ }
+ if (!state.found) {
+ DBG_DEBUG("cache entry not found\n");
+ return NT_STATUS_NOT_FOUND;
+ }
+ if (*type == SID_NAME_UNKNOWN) {
+ return NT_STATUS_NONE_MAPPED;
+ }
+
+ return NT_STATUS_OK;
+}
+
+/* convert a sid to a user or group name. The sid is guaranteed to be in the domain
+ given */
+NTSTATUS wb_cache_sid_to_name(struct winbindd_domain *domain,
+ TALLOC_CTX *mem_ctx,
+ const struct dom_sid *sid,
+ char **domain_name,
+ char **name,
+ enum lsa_SidType *type)
+{
+ NTSTATUS status;
+ bool old_status;
+
+ old_status = domain->online;
+ status = wcache_sid_to_name(domain, sid, mem_ctx, domain_name, name,
+ type);
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
+ return status;
+ }
+
+ *name = NULL;
+ *domain_name = NULL;
+
+ DBG_DEBUG("sid_to_name: [Cached] - doing backend query for name for domain %s\n",
+ domain->name );
+
+ winbindd_domain_init_backend(domain);
+
+ status = domain->backend->sid_to_name(domain, mem_ctx, sid, domain_name, name, type);
+
+ if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
+ NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
+ if (!domain->internal && old_status) {
+ set_domain_offline(domain);
+ }
+ if (!domain->internal &&
+ !domain->online &&
+ old_status) {
+ NTSTATUS cache_status;
+ cache_status = wcache_sid_to_name(domain, sid, mem_ctx,
+ domain_name, name, type);
+ return cache_status;
+ }
+ }
+ /* and save it */
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ wcache_save_sid_to_name(domain, status, sid, *domain_name, *name, *type);
+
+ /* We can't save the name to sid mapping here, as with sid history a
+ * later name2sid would give the wrong sid. */
+
+ return status;
+}
+
+NTSTATUS wb_cache_rids_to_names(struct winbindd_domain *domain,
+ TALLOC_CTX *mem_ctx,
+ const struct dom_sid *domain_sid,
+ uint32_t *rids,
+ size_t num_rids,
+ char **domain_name,
+ char ***names,
+ enum lsa_SidType **types)
+{
+ struct winbind_cache *cache = get_cache(domain);
+ size_t i;
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+ bool have_mapped;
+ bool have_unmapped;
+ bool old_status;
+
+ old_status = domain->online;
+ *domain_name = NULL;
+ *names = NULL;
+ *types = NULL;
+
+ if (!cache->tdb) {
+ goto do_query;
+ }
+
+ if (num_rids == 0) {
+ return NT_STATUS_OK;
+ }
+
+ *names = talloc_array(mem_ctx, char *, num_rids);
+ *types = talloc_array(mem_ctx, enum lsa_SidType, num_rids);
+
+ if ((*names == NULL) || (*types == NULL)) {
+ result = NT_STATUS_NO_MEMORY;
+ goto error;
+ }
+
+ have_mapped = have_unmapped = false;
+
+ for (i=0; i<num_rids; i++) {
+ struct dom_sid sid;
+ NTSTATUS status;
+ enum lsa_SidType type;
+ char *dom, *name;
+
+ if (!sid_compose(&sid, domain_sid, rids[i])) {
+ result = NT_STATUS_INTERNAL_ERROR;
+ goto error;
+ }
+
+ status = wcache_sid_to_name(domain, &sid, *names, &dom,
+ &name, &type);
+
+ (*types)[i] = SID_NAME_UNKNOWN;
+ (*names)[i] = talloc_strdup(*names, "");
+
+ if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
+ /* not cached */
+ goto do_query;
+ }
+
+ if (NT_STATUS_IS_OK(status)) {
+ have_mapped = true;
+ (*types)[i] = type;
+
+ if (*domain_name == NULL) {
+ *domain_name = dom;
+ } else {
+ TALLOC_FREE(dom);
+ }
+
+ (*names)[i] = name;
+
+ } else if (NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED)) {
+ have_unmapped = true;
+ } else {
+ /* something's definitely wrong */
+ result = status;
+ goto error;
+ }
+ }
+
+ if (!have_mapped) {
+ return NT_STATUS_NONE_MAPPED;
+ }
+ if (!have_unmapped) {
+ return NT_STATUS_OK;
+ }
+ return STATUS_SOME_UNMAPPED;
+
+ do_query:
+
+ TALLOC_FREE(*names);
+ TALLOC_FREE(*types);
+
+ result = domain->backend->rids_to_names(domain, mem_ctx, domain_sid,
+ rids, num_rids, domain_name,
+ names, types);
+
+ if (NT_STATUS_EQUAL(result, NT_STATUS_IO_TIMEOUT) ||
+ NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
+ if (!domain->internal && old_status) {
+ set_domain_offline(domain);
+ }
+ if (cache->tdb &&
+ !domain->internal &&
+ !domain->online &&
+ old_status) {
+ have_mapped = have_unmapped = false;
+
+ *names = talloc_array(mem_ctx, char *, num_rids);
+ if (*names == NULL) {
+ result = NT_STATUS_NO_MEMORY;
+ goto error;
+ }
+
+ *types = talloc_array(mem_ctx, enum lsa_SidType,
+ num_rids);
+ if (*types == NULL) {
+ result = NT_STATUS_NO_MEMORY;
+ goto error;
+ }
+
+ for (i=0; i<num_rids; i++) {
+ struct dom_sid sid;
+ NTSTATUS status;
+ enum lsa_SidType type;
+ char *dom, *name;
+
+ if (!sid_compose(&sid, domain_sid, rids[i])) {
+ result = NT_STATUS_INTERNAL_ERROR;
+ goto error;
+ }
+
+ status = wcache_sid_to_name(domain, &sid,
+ *names, &dom,
+ &name, &type);
+
+ (*types)[i] = SID_NAME_UNKNOWN;
+ (*names)[i] = talloc_strdup(*names, "");
+
+ if (NT_STATUS_IS_OK(status)) {
+ have_mapped = true;
+ (*types)[i] = type;
+
+ if (*domain_name == NULL) {
+ *domain_name = dom;
+ } else {
+ TALLOC_FREE(dom);
+ }
+
+ (*names)[i] = name;
+
+ } else if (NT_STATUS_EQUAL(
+ status,
+ NT_STATUS_NONE_MAPPED)) {
+ have_unmapped = true;
+ } else {
+ /* something's definitely wrong */
+ result = status;
+ goto error;
+ }
+ }
+
+ if (!have_mapped) {
+ return NT_STATUS_NONE_MAPPED;
+ }
+ if (!have_unmapped) {
+ return NT_STATUS_OK;
+ }
+ return STATUS_SOME_UNMAPPED;
+ }
+ }
+ /*
+ None of the queried rids has been found so save all negative entries
+ */
+ if (NT_STATUS_EQUAL(result, NT_STATUS_NONE_MAPPED)) {
+ for (i = 0; i < num_rids; i++) {
+ struct dom_sid sid;
+ const char *name = "";
+ const enum lsa_SidType type = SID_NAME_UNKNOWN;
+ NTSTATUS status = NT_STATUS_NONE_MAPPED;
+
+ if (!sid_compose(&sid, domain_sid, rids[i])) {
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+
+ wcache_save_sid_to_name(domain, status, &sid, *domain_name,
+ name, type);
+ }
+
+ return result;
+ }
+
+ /*
+ Some or all of the queried rids have been found.
+ */
+ if (!NT_STATUS_IS_OK(result) &&
+ !NT_STATUS_EQUAL(result, STATUS_SOME_UNMAPPED)) {
+ return result;
+ }
+
+ refresh_sequence_number(domain);
+
+ for (i=0; i<num_rids; i++) {
+ struct dom_sid sid;
+ NTSTATUS status;
+
+ if (!sid_compose(&sid, domain_sid, rids[i])) {
+ result = NT_STATUS_INTERNAL_ERROR;
+ goto error;
+ }
+
+ status = (*types)[i] == SID_NAME_UNKNOWN ?
+ NT_STATUS_NONE_MAPPED : NT_STATUS_OK;
+
+ wcache_save_sid_to_name(domain, status, &sid, *domain_name,
+ (*names)[i], (*types)[i]);
+ }
+
+ return result;
+
+ error:
+ TALLOC_FREE(*names);
+ TALLOC_FREE(*types);
+ return result;
+}
+
+static NTSTATUS wcache_query_user(struct winbindd_domain *domain,
+ TALLOC_CTX *mem_ctx,
+ const struct dom_sid *user_sid,
+ struct wbint_userinfo *info)
+{
+ struct winbind_cache *cache = get_cache(domain);
+ struct cache_entry *centry = NULL;
+ NTSTATUS status;
+ struct dom_sid_buf sid_string;
+
+ if (cache->tdb == NULL) {
+ return NT_STATUS_NOT_FOUND;
+ }
+
+ centry = wcache_fetch(
+ cache, domain, "U/%s", dom_sid_str_buf(user_sid, &sid_string));
+ if (centry == NULL) {
+ return NT_STATUS_NOT_FOUND;
+ }
+
+ /* if status is not ok then this is a negative hit
+ and the rest of the data doesn't matter */
+ status = centry->status;
+ if (NT_STATUS_IS_OK(status)) {
+ info->domain_name = centry_string(centry, mem_ctx);
+ info->acct_name = centry_string(centry, mem_ctx);
+ info->full_name = centry_string(centry, mem_ctx);
+ info->homedir = centry_string(centry, mem_ctx);
+ info->shell = centry_string(centry, mem_ctx);
+ info->uid = centry_uint32(centry);
+ info->primary_gid = centry_uint32(centry);
+ info->primary_group_name = centry_string(centry, mem_ctx);
+ centry_sid(centry, &info->user_sid);
+ centry_sid(centry, &info->group_sid);
+ }
+
+ DBG_DEBUG("query_user: [Cached] - cached info for domain %s status: "
+ "%s\n", domain->name, nt_errstr(status) );
+
+ centry_free(centry);
+ return status;
+}
+
+
+/**
+* @brief Query a fullname from the username cache (for further gecos processing)
+*
+* @param domain A pointer to the winbindd_domain struct.
+* @param mem_ctx The talloc context.
+* @param user_sid The user sid.
+* @param full_name A pointer to the full_name string.
+*
+* @return NTSTATUS code
+*/
+NTSTATUS wcache_query_user_fullname(struct winbindd_domain *domain,
+ TALLOC_CTX *mem_ctx,
+ const struct dom_sid *user_sid,
+ const char **full_name)
+{
+ NTSTATUS status;
+ struct wbint_userinfo info;
+
+ status = wcache_query_user(domain, mem_ctx, user_sid, &info);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ if (info.full_name != NULL) {
+ *full_name = talloc_strdup(mem_ctx, info.full_name);
+ if (*full_name == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ }
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS wcache_lookup_usergroups(struct winbindd_domain *domain,
+ TALLOC_CTX *mem_ctx,
+ const struct dom_sid *user_sid,
+ uint32_t *pnum_sids,
+ struct dom_sid **psids)
+{
+ struct winbind_cache *cache = get_cache(domain);
+ struct cache_entry *centry = NULL;
+ NTSTATUS status;
+ uint32_t i, num_sids;
+ struct dom_sid *sids;
+ struct dom_sid_buf sid_string;
+
+ if (cache->tdb == NULL) {
+ return NT_STATUS_NOT_FOUND;
+ }
+
+ centry = wcache_fetch(
+ cache,
+ domain,
+ "UG/%s",
+ dom_sid_str_buf(user_sid, &sid_string));
+ if (centry == NULL) {
+ return NT_STATUS_NOT_FOUND;
+ }
+
+ num_sids = centry_uint32(centry);
+ sids = talloc_array(mem_ctx, struct dom_sid, num_sids);
+ if (sids == NULL) {
+ centry_free(centry);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ for (i=0; i<num_sids; i++) {
+ centry_sid(centry, &sids[i]);
+ }
+
+ status = centry->status;
+
+ DBG_DEBUG("lookup_usergroups: [Cached] - cached info for domain %s "
+ "status: %s\n", domain->name, nt_errstr(status));
+
+ centry_free(centry);
+
+ *pnum_sids = num_sids;
+ *psids = sids;
+ return status;
+}
+
+/* Lookup groups a user is a member of. */
+NTSTATUS wb_cache_lookup_usergroups(struct winbindd_domain *domain,
+ TALLOC_CTX *mem_ctx,
+ const struct dom_sid *user_sid,
+ uint32_t *num_groups,
+ struct dom_sid **user_gids)
+{
+ struct cache_entry *centry = NULL;
+ NTSTATUS status;
+ unsigned int i;
+ struct dom_sid_buf sid_string;
+ bool old_status;
+
+ old_status = domain->online;
+ status = wcache_lookup_usergroups(domain, mem_ctx, user_sid,
+ num_groups, user_gids);
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
+ return status;
+ }
+
+ (*num_groups) = 0;
+ (*user_gids) = NULL;
+
+ DBG_DEBUG("lookup_usergroups: [Cached] - doing backend query for info for domain %s\n",
+ domain->name );
+
+ status = domain->backend->lookup_usergroups(domain, mem_ctx, user_sid, num_groups, user_gids);
+
+ if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
+ NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
+ if (!domain->internal && old_status) {
+ set_domain_offline(domain);
+ }
+ if (!domain->internal &&
+ !domain->online &&
+ old_status) {
+ NTSTATUS cache_status;
+ cache_status = wcache_lookup_usergroups(domain, mem_ctx, user_sid,
+ num_groups, user_gids);
+ return cache_status;
+ }
+ }
+ if ( NT_STATUS_EQUAL(status, NT_STATUS_SYNCHRONIZATION_REQUIRED) )
+ goto skip_save;
+
+ /* and save it */
+ refresh_sequence_number(domain);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ centry = centry_start(domain, status);
+ if (!centry)
+ goto skip_save;
+
+ centry_put_uint32(centry, *num_groups);
+ for (i=0; i<(*num_groups); i++) {
+ centry_put_sid(centry, &(*user_gids)[i]);
+ }
+
+ centry_end(centry, "UG/%s", dom_sid_str_buf(user_sid, &sid_string));
+ centry_free(centry);
+
+skip_save:
+ return status;
+}
+
+static char *wcache_make_sidlist(TALLOC_CTX *mem_ctx, uint32_t num_sids,
+ const struct dom_sid *sids)
+{
+ uint32_t i;
+ char *sidlist;
+
+ sidlist = talloc_strdup(mem_ctx, "");
+ if (sidlist == NULL) {
+ return NULL;
+ }
+ for (i=0; i<num_sids; i++) {
+ struct dom_sid_buf tmp;
+ sidlist = talloc_asprintf_append_buffer(
+ sidlist,
+ "/%s",
+ dom_sid_str_buf(&sids[i], &tmp));
+ if (sidlist == NULL) {
+ return NULL;
+ }
+ }
+ return sidlist;
+}
+
+static NTSTATUS wcache_lookup_useraliases(struct winbindd_domain *domain,
+ TALLOC_CTX *mem_ctx,
+ uint32_t num_sids,
+ const struct dom_sid *sids,
+ uint32_t *pnum_aliases,
+ uint32_t **paliases)
+{
+ struct winbind_cache *cache = get_cache(domain);
+ struct cache_entry *centry = NULL;
+ uint32_t i, num_aliases;
+ uint32_t *aliases;
+ NTSTATUS status;
+ char *sidlist;
+
+ if (cache->tdb == NULL) {
+ return NT_STATUS_NOT_FOUND;
+ }
+
+ if (num_sids == 0) {
+ *pnum_aliases = 0;
+ *paliases = NULL;
+ return NT_STATUS_OK;
+ }
+
+ /* We need to cache indexed by the whole list of SIDs, the aliases
+ * resulting might come from any of the SIDs. */
+
+ sidlist = wcache_make_sidlist(talloc_tos(), num_sids, sids);
+ if (sidlist == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ centry = wcache_fetch(cache, domain, "UA%s", sidlist);
+ TALLOC_FREE(sidlist);
+ if (centry == NULL) {
+ return NT_STATUS_NOT_FOUND;
+ }
+
+ num_aliases = centry_uint32(centry);
+ aliases = talloc_array(mem_ctx, uint32_t, num_aliases);
+ if (aliases == NULL) {
+ centry_free(centry);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ for (i=0; i<num_aliases; i++) {
+ aliases[i] = centry_uint32(centry);
+ }
+
+ status = centry->status;
+
+ DBG_DEBUG("lookup_useraliases: [Cached] - cached info for domain: %s "
+ "status %s\n", domain->name, nt_errstr(status));
+
+ centry_free(centry);
+
+ *pnum_aliases = num_aliases;
+ *paliases = aliases;
+
+ return status;
+}
+
+NTSTATUS wb_cache_lookup_useraliases(struct winbindd_domain *domain,
+ TALLOC_CTX *mem_ctx,
+ uint32_t num_sids,
+ const struct dom_sid *sids,
+ uint32_t *num_aliases,
+ uint32_t **alias_rids)
+{
+ struct cache_entry *centry = NULL;
+ NTSTATUS status;
+ char *sidlist;
+ uint32_t i;
+ bool old_status;
+
+ old_status = domain->online;
+ status = wcache_lookup_useraliases(domain, mem_ctx, num_sids, sids,
+ num_aliases, alias_rids);
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
+ return status;
+ }
+
+ (*num_aliases) = 0;
+ (*alias_rids) = NULL;
+
+ DBG_DEBUG("lookup_usergroups: [Cached] - doing backend query for info "
+ "for domain %s\n", domain->name );
+
+ sidlist = wcache_make_sidlist(talloc_tos(), num_sids, sids);
+ if (sidlist == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ status = domain->backend->lookup_useraliases(domain, mem_ctx,
+ num_sids, sids,
+ num_aliases, alias_rids);
+
+ if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
+ NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
+ if (!domain->internal && old_status) {
+ set_domain_offline(domain);
+ }
+ if (!domain->internal &&
+ !domain->online &&
+ old_status) {
+ NTSTATUS cache_status;
+ cache_status = wcache_lookup_useraliases(domain, mem_ctx, num_sids,
+ sids, num_aliases, alias_rids);
+ return cache_status;
+ }
+ }
+ /* and save it */
+ refresh_sequence_number(domain);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ centry = centry_start(domain, status);
+ if (!centry)
+ goto skip_save;
+ centry_put_uint32(centry, *num_aliases);
+ for (i=0; i<(*num_aliases); i++)
+ centry_put_uint32(centry, (*alias_rids)[i]);
+ centry_end(centry, "UA%s", sidlist);
+ centry_free(centry);
+
+ skip_save:
+ return status;
+}
+
+static NTSTATUS wcache_lookup_aliasmem(struct winbindd_domain *domain,
+ TALLOC_CTX *mem_ctx,
+ const struct dom_sid *group_sid,
+ uint32_t *num_names,
+ struct dom_sid **sid_mem)
+{
+ struct winbind_cache *cache = get_cache(domain);
+ struct cache_entry *centry = NULL;
+ NTSTATUS status;
+ unsigned int i;
+ struct dom_sid_buf sid_string;
+
+ if (cache->tdb == NULL) {
+ return NT_STATUS_NOT_FOUND;
+ }
+
+ centry = wcache_fetch(cache,
+ domain,
+ "AM/%s",
+ dom_sid_str_buf(group_sid, &sid_string));
+ if (centry == NULL) {
+ return NT_STATUS_NOT_FOUND;
+ }
+
+ *sid_mem = NULL;
+
+ *num_names = centry_uint32(centry);
+ if (*num_names == 0) {
+ centry_free(centry);
+ return NT_STATUS_OK;
+ }
+
+ *sid_mem = talloc_array(mem_ctx, struct dom_sid, *num_names);
+ if (*sid_mem == NULL) {
+ TALLOC_FREE(*sid_mem);
+ centry_free(centry);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ for (i = 0; i < (*num_names); i++) {
+ centry_sid(centry, &(*sid_mem)[i]);
+ }
+
+ status = centry->status;
+
+ D_DEBUG("[Cached] - cached info for domain %s "
+ "status: %s\n",
+ domain->name,
+ nt_errstr(status));
+
+ centry_free(centry);
+ return status;
+}
+
+NTSTATUS wb_cache_lookup_aliasmem(struct winbindd_domain *domain,
+ TALLOC_CTX *mem_ctx,
+ const struct dom_sid *group_sid,
+ enum lsa_SidType type,
+ uint32_t *num_sids,
+ struct dom_sid **sid_mem)
+{
+ struct cache_entry *centry = NULL;
+ NTSTATUS status;
+ unsigned int i;
+ struct dom_sid_buf sid_string;
+ bool old_status;
+
+ old_status = domain->online;
+ status = wcache_lookup_aliasmem(domain,
+ mem_ctx,
+ group_sid,
+ num_sids,
+ sid_mem);
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
+ return status;
+ }
+
+ (*num_sids) = 0;
+ (*sid_mem) = NULL;
+
+ D_DEBUG("[Cached] - doing backend query for info for domain %s\n",
+ domain->name);
+
+ status = domain->backend->lookup_aliasmem(domain,
+ mem_ctx,
+ group_sid,
+ type,
+ num_sids,
+ sid_mem);
+
+ if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
+ NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
+ if (!domain->internal && old_status) {
+ set_domain_offline(domain);
+ }
+ if (!domain->internal && !domain->online && old_status) {
+ NTSTATUS cache_status;
+ cache_status = wcache_lookup_aliasmem(domain,
+ mem_ctx,
+ group_sid,
+ num_sids,
+ sid_mem);
+ return cache_status;
+ }
+ }
+ /* and save it */
+ refresh_sequence_number(domain);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ centry = centry_start(domain, status);
+ if (!centry)
+ goto skip_save;
+ centry_put_uint32(centry, *num_sids);
+ for (i = 0; i < (*num_sids); i++) {
+ centry_put_sid(centry, &(*sid_mem)[i]);
+ }
+ centry_end(centry, "AM/%s", dom_sid_str_buf(group_sid, &sid_string));
+ centry_free(centry);
+
+skip_save:
+ return status;
+}
+
+static NTSTATUS wcache_lookup_groupmem(struct winbindd_domain *domain,
+ TALLOC_CTX *mem_ctx,
+ const struct dom_sid *group_sid,
+ uint32_t *num_names,
+ struct dom_sid **sid_mem, char ***names,
+ uint32_t **name_types)
+{
+ struct winbind_cache *cache = get_cache(domain);
+ struct cache_entry *centry = NULL;
+ NTSTATUS status;
+ unsigned int i;
+ struct dom_sid_buf sid_string;
+
+ if (cache->tdb == NULL) {
+ return NT_STATUS_NOT_FOUND;
+ }
+
+ centry = wcache_fetch(
+ cache,
+ domain,
+ "GM/%s",
+ dom_sid_str_buf(group_sid, &sid_string));
+ if (centry == NULL) {
+ return NT_STATUS_NOT_FOUND;
+ }
+
+ *sid_mem = NULL;
+ *names = NULL;
+ *name_types = NULL;
+
+ *num_names = centry_uint32(centry);
+ if (*num_names == 0) {
+ centry_free(centry);
+ return NT_STATUS_OK;
+ }
+
+ *sid_mem = talloc_array(mem_ctx, struct dom_sid, *num_names);
+ *names = talloc_array(mem_ctx, char *, *num_names);
+ *name_types = talloc_array(mem_ctx, uint32_t, *num_names);
+
+ if ((*sid_mem == NULL) || (*names == NULL) || (*name_types == NULL)) {
+ TALLOC_FREE(*sid_mem);
+ TALLOC_FREE(*names);
+ TALLOC_FREE(*name_types);
+ centry_free(centry);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ for (i=0; i<(*num_names); i++) {
+ centry_sid(centry, &(*sid_mem)[i]);
+ (*names)[i] = centry_string(centry, mem_ctx);
+ (*name_types)[i] = centry_uint32(centry);
+ }
+
+ status = centry->status;
+
+ DBG_DEBUG("lookup_groupmem: [Cached] - cached info for domain %s "
+ "status: %s\n", domain->name, nt_errstr(status));
+
+ centry_free(centry);
+ return status;
+}
+
+NTSTATUS wb_cache_lookup_groupmem(struct winbindd_domain *domain,
+ TALLOC_CTX *mem_ctx,
+ const struct dom_sid *group_sid,
+ enum lsa_SidType type,
+ uint32_t *num_names,
+ struct dom_sid **sid_mem,
+ char ***names,
+ uint32_t **name_types)
+{
+ struct cache_entry *centry = NULL;
+ NTSTATUS status;
+ unsigned int i;
+ struct dom_sid_buf sid_string;
+ bool old_status;
+
+ old_status = domain->online;
+ status = wcache_lookup_groupmem(domain, mem_ctx, group_sid, num_names,
+ sid_mem, names, name_types);
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
+ return status;
+ }
+
+ (*num_names) = 0;
+ (*sid_mem) = NULL;
+ (*names) = NULL;
+ (*name_types) = NULL;
+
+ DBG_DEBUG("lookup_groupmem: [Cached] - doing backend query for info for domain %s\n",
+ domain->name );
+
+ status = domain->backend->lookup_groupmem(domain, mem_ctx, group_sid,
+ type, num_names,
+ sid_mem, names, name_types);
+
+ if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
+ NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
+ if (!domain->internal && old_status) {
+ set_domain_offline(domain);
+ }
+ if (!domain->internal &&
+ !domain->online &&
+ old_status) {
+ NTSTATUS cache_status;
+ cache_status = wcache_lookup_groupmem(domain, mem_ctx, group_sid,
+ num_names, sid_mem, names,
+ name_types);
+ return cache_status;
+ }
+ }
+ /* and save it */
+ refresh_sequence_number(domain);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ centry = centry_start(domain, status);
+ if (!centry)
+ goto skip_save;
+ centry_put_uint32(centry, *num_names);
+ for (i=0; i<(*num_names); i++) {
+ centry_put_sid(centry, &(*sid_mem)[i]);
+ centry_put_string(centry, (*names)[i]);
+ centry_put_uint32(centry, (*name_types)[i]);
+ }
+ centry_end(centry,
+ "GM/%s",
+ dom_sid_str_buf(group_sid, &sid_string));
+ centry_free(centry);
+
+skip_save:
+ return status;
+}
+
+/* find the sequence number for a domain */
+NTSTATUS wb_cache_sequence_number(struct winbindd_domain *domain,
+ uint32_t *seq)
+{
+ refresh_sequence_number(domain);
+
+ *seq = domain->sequence_number;
+
+ return NT_STATUS_OK;
+}
+
+/* enumerate trusted domains
+ * (we need to have the list of trustdoms in the cache when we go offline) -
+ * Guenther */
+NTSTATUS wb_cache_trusted_domains(struct winbindd_domain *domain,
+ TALLOC_CTX *mem_ctx,
+ struct netr_DomainTrustList *trusts)
+{
+ NTSTATUS status;
+ struct winbind_cache *cache;
+ struct winbindd_tdc_domain *dom_list = NULL;
+ size_t num_domains = 0;
+ bool retval = false;
+ size_t i;
+ bool old_status;
+
+ old_status = domain->online;
+ trusts->count = 0;
+ trusts->array = NULL;
+
+ cache = get_cache(domain);
+ if (!cache || !cache->tdb) {
+ goto do_query;
+ }
+
+ if (domain->online) {
+ goto do_query;
+ }
+
+ retval = wcache_tdc_fetch_list(&dom_list, &num_domains);
+ if (!retval || !num_domains || !dom_list) {
+ TALLOC_FREE(dom_list);
+ goto do_query;
+ }
+
+do_fetch_cache:
+ trusts->array = talloc_zero_array(mem_ctx, struct netr_DomainTrust, num_domains);
+ if (!trusts->array) {
+ TALLOC_FREE(dom_list);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ for (i = 0; i < num_domains; i++) {
+ struct netr_DomainTrust *trust;
+ struct dom_sid *sid;
+ struct winbindd_domain *dom;
+
+ dom = find_domain_from_name_noinit(dom_list[i].domain_name);
+ if (dom && dom->internal) {
+ continue;
+ }
+
+ trust = &trusts->array[trusts->count];
+ trust->netbios_name = talloc_strdup(trusts->array, dom_list[i].domain_name);
+ trust->dns_name = talloc_strdup(trusts->array, dom_list[i].dns_name);
+ sid = talloc(trusts->array, struct dom_sid);
+ if (!trust->netbios_name || !trust->dns_name ||
+ !sid) {
+ TALLOC_FREE(dom_list);
+ TALLOC_FREE(trusts->array);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ trust->trust_flags = dom_list[i].trust_flags;
+ trust->trust_attributes = dom_list[i].trust_attribs;
+ trust->trust_type = dom_list[i].trust_type;
+ sid_copy(sid, &dom_list[i].sid);
+ trust->sid = sid;
+ trusts->count++;
+ }
+
+ TALLOC_FREE(dom_list);
+ return NT_STATUS_OK;
+
+do_query:
+ DBG_DEBUG("trusted_domains: [Cached] - doing backend query for info for domain %s\n",
+ domain->name );
+
+ status = domain->backend->trusted_domains(domain, mem_ctx, trusts);
+
+ if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
+ NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
+ if (!domain->internal && old_status) {
+ set_domain_offline(domain);
+ }
+ if (!domain->internal &&
+ !domain->online &&
+ old_status) {
+ retval = wcache_tdc_fetch_list(&dom_list, &num_domains);
+ if (retval && num_domains && dom_list) {
+ TALLOC_FREE(trusts->array);
+ trusts->count = 0;
+ goto do_fetch_cache;
+ }
+ }
+ }
+ /* no trusts gives NT_STATUS_NO_MORE_ENTRIES resetting to NT_STATUS_OK
+ * so that the generic centry handling still applies correctly -
+ * Guenther*/
+
+ if (!NT_STATUS_IS_ERR(status)) {
+ status = NT_STATUS_OK;
+ }
+ return status;
+}
+
+/* get lockout policy */
+NTSTATUS wb_cache_lockout_policy(struct winbindd_domain *domain,
+ TALLOC_CTX *mem_ctx,
+ struct samr_DomInfo12 *policy)
+{
+ struct winbind_cache *cache = get_cache(domain);
+ struct cache_entry *centry = NULL;
+ NTSTATUS status;
+ bool old_status;
+
+ old_status = domain->online;
+ if (!cache->tdb)
+ goto do_query;
+
+ centry = wcache_fetch(cache, domain, "LOC_POL/%s", domain->name);
+
+ if (!centry)
+ goto do_query;
+
+do_fetch_cache:
+ policy->lockout_duration = centry_nttime(centry);
+ policy->lockout_window = centry_nttime(centry);
+ policy->lockout_threshold = centry_uint16(centry);
+
+ status = centry->status;
+
+ DBG_DEBUG("lockout_policy: [Cached] - cached info for domain %s status: %s\n",
+ domain->name, nt_errstr(status) );
+
+ centry_free(centry);
+ return status;
+
+do_query:
+ ZERO_STRUCTP(policy);
+
+ DBG_DEBUG("lockout_policy: [Cached] - doing backend query for info for domain %s\n",
+ domain->name );
+
+ status = domain->backend->lockout_policy(domain, mem_ctx, policy);
+
+ if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
+ NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
+ if (!domain->internal && old_status) {
+ set_domain_offline(domain);
+ }
+ if (cache->tdb &&
+ !domain->internal &&
+ !domain->online &&
+ old_status) {
+ centry = wcache_fetch(cache, domain, "LOC_POL/%s", domain->name);
+ if (centry) {
+ goto do_fetch_cache;
+ }
+ }
+ }
+ /* and save it */
+ refresh_sequence_number(domain);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ wcache_save_lockout_policy(domain, status, policy);
+
+ return status;
+}
+
+/* get password policy */
+NTSTATUS wb_cache_password_policy(struct winbindd_domain *domain,
+ TALLOC_CTX *mem_ctx,
+ struct samr_DomInfo1 *policy)
+{
+ struct winbind_cache *cache = get_cache(domain);
+ struct cache_entry *centry = NULL;
+ NTSTATUS status;
+ bool old_status;
+
+ old_status = domain->online;
+ if (!cache->tdb)
+ goto do_query;
+
+ centry = wcache_fetch(cache, domain, "PWD_POL/%s", domain->name);
+
+ if (!centry)
+ goto do_query;
+
+do_fetch_cache:
+ policy->min_password_length = centry_uint16(centry);
+ policy->password_history_length = centry_uint16(centry);
+ policy->password_properties = centry_uint32(centry);
+ policy->max_password_age = centry_nttime(centry);
+ policy->min_password_age = centry_nttime(centry);
+
+ status = centry->status;
+
+ DBG_DEBUG("lockout_policy: [Cached] - cached info for domain %s status: %s\n",
+ domain->name, nt_errstr(status) );
+
+ centry_free(centry);
+ return status;
+
+do_query:
+ ZERO_STRUCTP(policy);
+
+ DBG_DEBUG("password_policy: [Cached] - doing backend query for info for domain %s\n",
+ domain->name );
+
+ status = domain->backend->password_policy(domain, mem_ctx, policy);
+
+ if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
+ NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
+ if (!domain->internal && old_status) {
+ set_domain_offline(domain);
+ }
+ if (cache->tdb &&
+ !domain->internal &&
+ !domain->online &&
+ old_status) {
+ centry = wcache_fetch(cache, domain, "PWD_POL/%s", domain->name);
+ if (centry) {
+ goto do_fetch_cache;
+ }
+ }
+ }
+ /* and save it */
+ refresh_sequence_number(domain);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ wcache_save_password_policy(domain, status, policy);
+
+ return status;
+}
+
+
+/* Invalidate cached user and group lists coherently */
+
+static int traverse_fn(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf,
+ void *state)
+{
+ if (strncmp((const char *)kbuf.dptr, "UL/", 3) == 0 ||
+ strncmp((const char *)kbuf.dptr, "GL/", 3) == 0)
+ tdb_delete(the_tdb, kbuf);
+
+ return 0;
+}
+
+/* Invalidate the getpwnam and getgroups entries for a winbindd domain */
+
+void wcache_invalidate_samlogon(struct winbindd_domain *domain,
+ const struct dom_sid *sid)
+{
+ fstring key_str;
+ struct dom_sid_buf sid_string;
+ struct winbind_cache *cache;
+
+ /* don't clear cached U/SID and UG/SID entries when we want to logon
+ * offline - gd */
+
+ if (lp_winbind_offline_logon()) {
+ return;
+ }
+
+ if (!domain)
+ return;
+
+ cache = get_cache(domain);
+
+ if (!cache->tdb) {
+ return;
+ }
+
+ /* Clear U/SID cache entry */
+ fstr_sprintf(key_str, "U/%s", dom_sid_str_buf(sid, &sid_string));
+ DBG_DEBUG("wcache_invalidate_samlogon: clearing %s\n", key_str);
+ tdb_delete(cache->tdb, string_tdb_data(key_str));
+
+ /* Clear UG/SID cache entry */
+ fstr_sprintf(key_str, "UG/%s", dom_sid_str_buf(sid, &sid_string));
+ DBG_DEBUG("wcache_invalidate_samlogon: clearing %s\n", key_str);
+ tdb_delete(cache->tdb, string_tdb_data(key_str));
+
+ /* Samba/winbindd never needs this. */
+ netsamlogon_clear_cached_user(sid);
+}
+
+bool wcache_invalidate_cache(void)
+{
+ struct winbindd_domain *domain;
+
+ for (domain = domain_list(); domain; domain = domain->next) {
+ struct winbind_cache *cache = get_cache(domain);
+
+ DBG_DEBUG("wcache_invalidate_cache: invalidating cache "
+ "entries for %s\n", domain->name);
+ if (cache) {
+ if (cache->tdb) {
+ tdb_traverse(cache->tdb, traverse_fn, NULL);
+ } else {
+ return false;
+ }
+ }
+ }
+ return true;
+}
+
+bool wcache_invalidate_cache_noinit(void)
+{
+ struct winbindd_domain *domain;
+
+ for (domain = domain_list(); domain; domain = domain->next) {
+ struct winbind_cache *cache;
+
+ /* Skip uninitialized domains. */
+ if (!domain->initialized && !domain->internal) {
+ continue;
+ }
+
+ cache = get_cache(domain);
+
+ DBG_DEBUG("wcache_invalidate_cache: invalidating cache "
+ "entries for %s\n", domain->name);
+ if (cache) {
+ if (cache->tdb) {
+ tdb_traverse(cache->tdb, traverse_fn, NULL);
+ /*
+ * Flushing cache has nothing to with domains.
+ * return here if we successfully flushed once.
+ * To avoid unnecessary traversing the cache.
+ */
+ return true;
+ } else {
+ return false;
+ }
+ }
+ }
+ return true;
+}
+
+static bool init_wcache(void)
+{
+ char *db_path;
+
+ if (wcache == NULL) {
+ wcache = SMB_XMALLOC_P(struct winbind_cache);
+ ZERO_STRUCTP(wcache);
+ }
+
+ if (wcache->tdb != NULL)
+ return true;
+
+ db_path = wcache_path();
+ if (db_path == NULL) {
+ return false;
+ }
+
+ /* when working offline we must not clear the cache on restart */
+ wcache->tdb = tdb_open_log(db_path,
+ WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE,
+ TDB_INCOMPATIBLE_HASH |
+ (lp_winbind_offline_logon() ? TDB_DEFAULT : (TDB_DEFAULT | TDB_CLEAR_IF_FIRST)),
+ O_RDWR|O_CREAT, 0600);
+ TALLOC_FREE(db_path);
+ if (wcache->tdb == NULL) {
+ DBG_ERR("Failed to open winbindd_cache.tdb!\n");
+ return false;
+ }
+
+ return true;
+}
+
+/************************************************************************
+ This is called by the parent to initialize the cache file.
+ We don't need sophisticated locking here as we know we're the
+ only opener.
+************************************************************************/
+
+bool initialize_winbindd_cache(void)
+{
+ bool cache_bad = false;
+ uint32_t vers = 0;
+ bool ok;
+
+ if (!init_wcache()) {
+ DBG_ERR("initialize_winbindd_cache: init_wcache failed.\n");
+ return false;
+ }
+
+ /* Check version number. */
+ ok = tdb_fetch_uint32(wcache->tdb, WINBINDD_CACHE_VERSION_KEYSTR, &vers);
+ if (!ok) {
+ DBG_DEBUG("Failed to get cache version\n");
+ cache_bad = true;
+ }
+ if (vers != WINBINDD_CACHE_VERSION) {
+ DBG_DEBUG("Invalid cache version %u != %u\n",
+ vers,
+ WINBINDD_CACHE_VERSION);
+ cache_bad = true;
+ }
+
+ if (cache_bad) {
+ char *db_path;
+
+ DBG_NOTICE("initialize_winbindd_cache: clearing cache "
+ "and re-creating with version number %d\n",
+ WINBINDD_CACHE_VERSION);
+
+ tdb_close(wcache->tdb);
+ wcache->tdb = NULL;
+
+ db_path = wcache_path();
+ if (db_path == NULL) {
+ return false;
+ }
+
+ if (unlink(db_path) == -1) {
+ DBG_ERR("initialize_winbindd_cache: unlink %s failed %s\n",
+ db_path,
+ strerror(errno) );
+ TALLOC_FREE(db_path);
+ return false;
+ }
+ TALLOC_FREE(db_path);
+ if (!init_wcache()) {
+ DBG_ERR("initialize_winbindd_cache: re-initialization "
+ "init_wcache failed.\n");
+ return false;
+ }
+
+ /* Write the version. */
+ if (!tdb_store_uint32(wcache->tdb, WINBINDD_CACHE_VERSION_KEYSTR, WINBINDD_CACHE_VERSION)) {
+ DBG_ERR("initialize_winbindd_cache: version number store failed %s\n",
+ tdb_errorstr(wcache->tdb) );
+ return false;
+ }
+ }
+
+ tdb_close(wcache->tdb);
+ wcache->tdb = NULL;
+ return true;
+}
+
+void close_winbindd_cache(void)
+{
+ if (!wcache) {
+ return;
+ }
+ if (wcache->tdb) {
+ tdb_close(wcache->tdb);
+ wcache->tdb = NULL;
+ }
+}
+
+bool lookup_cached_sid(TALLOC_CTX *mem_ctx, const struct dom_sid *sid,
+ char **domain_name, char **name,
+ enum lsa_SidType *type)
+{
+ struct winbindd_domain *domain;
+ NTSTATUS status;
+
+ domain = find_lookup_domain_from_sid(sid);
+ if (domain == NULL) {
+ return false;
+ }
+ status = wcache_sid_to_name(domain, sid, mem_ctx, domain_name, name,
+ type);
+ return NT_STATUS_IS_OK(status);
+}
+
+bool lookup_cached_name(const char *namespace,
+ const char *domain_name,
+ const char *name,
+ struct dom_sid *sid,
+ enum lsa_SidType *type)
+{
+ struct winbindd_domain *domain;
+ NTSTATUS status;
+ bool original_online_state;
+
+ domain = find_lookup_domain_from_name(namespace);
+ if (domain == NULL) {
+ return false;
+ }
+
+ /* If we are doing a cached logon, temporarily set the domain
+ offline so the cache won't expire the entry */
+
+ original_online_state = domain->online;
+ domain->online = false;
+ status = wcache_name_to_sid(domain, domain_name, name, sid, type);
+ domain->online = original_online_state;
+
+ return NT_STATUS_IS_OK(status);
+}
+
+/*
+ * Cache a name to sid without checking the sequence number.
+ * Used when caching from a trusted PAC.
+ */
+
+void cache_name2sid_trusted(struct winbindd_domain *domain,
+ const char *domain_name,
+ const char *name,
+ enum lsa_SidType type,
+ const struct dom_sid *sid)
+{
+ /*
+ * Ensure we store the mapping with the
+ * existing sequence number from the cache.
+ */
+ get_cache(domain);
+ (void)fetch_cache_seqnum(domain, time(NULL));
+ wcache_save_name_to_sid(domain,
+ NT_STATUS_OK,
+ domain_name,
+ name,
+ sid,
+ type);
+}
+
+void cache_name2sid(struct winbindd_domain *domain,
+ const char *domain_name, const char *name,
+ enum lsa_SidType type, const struct dom_sid *sid)
+{
+ refresh_sequence_number(domain);
+ wcache_save_name_to_sid(domain, NT_STATUS_OK, domain_name, name,
+ sid, type);
+}
+
+/*
+ * The original idea that this cache only contains centries has
+ * been blurred - now other stuff gets put in here. Ensure we
+ * ignore these things on cleanup.
+ */
+
+static int traverse_fn_cleanup(TDB_CONTEXT *the_tdb, TDB_DATA kbuf,
+ TDB_DATA dbuf, void *state)
+{
+ struct cache_entry *centry;
+
+ if (is_non_centry_key(kbuf)) {
+ return 0;
+ }
+
+ centry = wcache_fetch_raw((char *)kbuf.dptr);
+ if (!centry) {
+ return 0;
+ }
+
+ if (!NT_STATUS_IS_OK(centry->status)) {
+ DBG_DEBUG("deleting centry %s\n", (const char *)kbuf.dptr);
+ tdb_delete(the_tdb, kbuf);
+ }
+
+ centry_free(centry);
+ return 0;
+}
+
+/* flush the cache */
+static void wcache_flush_cache(void)
+{
+ char *db_path;
+
+ if (!wcache)
+ return;
+ if (wcache->tdb) {
+ tdb_close(wcache->tdb);
+ wcache->tdb = NULL;
+ }
+ if (!winbindd_use_cache()) {
+ return;
+ }
+
+ db_path = wcache_path();
+ if (db_path == NULL) {
+ return;
+ }
+
+ /* when working offline we must not clear the cache on restart */
+ wcache->tdb = tdb_open_log(db_path,
+ WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE,
+ TDB_INCOMPATIBLE_HASH |
+ (lp_winbind_offline_logon() ? TDB_DEFAULT : (TDB_DEFAULT | TDB_CLEAR_IF_FIRST)),
+ O_RDWR|O_CREAT, 0600);
+ TALLOC_FREE(db_path);
+ if (!wcache->tdb) {
+ DBG_ERR("Failed to open winbindd_cache.tdb!\n");
+ return;
+ }
+
+ tdb_traverse(wcache->tdb, traverse_fn_cleanup, NULL);
+
+ DBG_DEBUG("wcache_flush_cache success\n");
+}
+
+/* Count cached creds */
+
+static int traverse_fn_cached_creds(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf,
+ void *state)
+{
+ int *cred_count = (int*)state;
+
+ if (strncmp((const char *)kbuf.dptr, "CRED/", 5) == 0) {
+ (*cred_count)++;
+ }
+ return 0;
+}
+
+NTSTATUS wcache_count_cached_creds(struct winbindd_domain *domain, int *count)
+{
+ struct winbind_cache *cache = get_cache(domain);
+
+ *count = 0;
+
+ if (!cache->tdb) {
+ return NT_STATUS_INTERNAL_DB_ERROR;
+ }
+
+ tdb_traverse(cache->tdb, traverse_fn_cached_creds, (void *)count);
+
+ return NT_STATUS_OK;
+}
+
+struct cred_list {
+ struct cred_list *prev, *next;
+ TDB_DATA key;
+ fstring name;
+ time_t created;
+};
+static struct cred_list *wcache_cred_list;
+
+static int traverse_fn_get_credlist(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf,
+ void *state)
+{
+ struct cred_list *cred;
+
+ if (strncmp((const char *)kbuf.dptr, "CRED/", 5) == 0) {
+
+ cred = SMB_MALLOC_P(struct cred_list);
+ if (cred == NULL) {
+ DBG_ERR("traverse_fn_remove_first_creds: failed to malloc new entry for list\n");
+ return -1;
+ }
+
+ ZERO_STRUCTP(cred);
+
+ /* save a copy of the key */
+
+ fstrcpy(cred->name, (const char *)kbuf.dptr);
+ DLIST_ADD(wcache_cred_list, cred);
+ }
+
+ return 0;
+}
+
+NTSTATUS wcache_remove_oldest_cached_creds(struct winbindd_domain *domain, const struct dom_sid *sid)
+{
+ struct winbind_cache *cache = get_cache(domain);
+ NTSTATUS status;
+ int ret;
+ struct cred_list *cred, *next, *oldest = NULL;
+
+ if (!cache->tdb) {
+ return NT_STATUS_INTERNAL_DB_ERROR;
+ }
+
+ /* we possibly already have an entry */
+ if (sid && NT_STATUS_IS_OK(wcache_cached_creds_exist(domain, sid))) {
+
+ fstring key_str;
+ struct dom_sid_buf tmp;
+
+ DBG_DEBUG("we already have an entry, deleting that\n");
+
+ fstr_sprintf(key_str, "CRED/%s", dom_sid_str_buf(sid, &tmp));
+
+ tdb_delete(cache->tdb, string_tdb_data(key_str));
+
+ return NT_STATUS_OK;
+ }
+
+ ret = tdb_traverse(cache->tdb, traverse_fn_get_credlist, NULL);
+ if (ret == 0) {
+ return NT_STATUS_OK;
+ } else if ((ret < 0) || (wcache_cred_list == NULL)) {
+ return NT_STATUS_OBJECT_NAME_NOT_FOUND;
+ }
+
+ ZERO_STRUCTP(oldest);
+
+ for (cred = wcache_cred_list; cred; cred = cred->next) {
+
+ TDB_DATA data;
+ time_t t;
+
+ data = tdb_fetch(cache->tdb, string_tdb_data(cred->name));
+ if (!data.dptr) {
+ DBG_DEBUG("wcache_remove_oldest_cached_creds: entry for [%s] not found\n",
+ cred->name);
+ status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
+ goto done;
+ }
+
+ t = IVAL(data.dptr, 0);
+ SAFE_FREE(data.dptr);
+
+ if (!oldest) {
+ oldest = SMB_MALLOC_P(struct cred_list);
+ if (oldest == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ goto done;
+ }
+
+ fstrcpy(oldest->name, cred->name);
+ oldest->created = t;
+ continue;
+ }
+
+ if (t < oldest->created) {
+ fstrcpy(oldest->name, cred->name);
+ oldest->created = t;
+ }
+ }
+
+ if (tdb_delete(cache->tdb, string_tdb_data(oldest->name)) == 0) {
+ status = NT_STATUS_OK;
+ } else {
+ status = NT_STATUS_UNSUCCESSFUL;
+ }
+done:
+ for (cred = wcache_cred_list; cred; cred = next) {
+ next = cred->next;
+ DLIST_REMOVE(wcache_cred_list, cred);
+ SAFE_FREE(cred);
+ }
+ SAFE_FREE(oldest);
+
+ return status;
+}
+
+/* Change the global online/offline state. */
+bool set_global_winbindd_state_offline(void)
+{
+ bool ok;
+ uint8_t buf[4] = {0};
+ TDB_DATA data = {
+ .dptr = buf,
+ .dsize = sizeof(buf)
+ };
+ int rc;
+
+ DBG_NOTICE("Offline requested\n");
+
+ if (wcache == NULL || wcache->tdb == NULL) {
+ DBG_NOTICE("Winbind cache doesn't exist yet\n");
+ return false;
+ }
+
+ if (!lp_winbind_offline_logon()) {
+ DBG_DEBUG("Rejecting request to set winbind offline, "
+ "offline logons are disabled in smb.conf\n");
+ return false;
+ }
+
+ ok = get_global_winbindd_state_offline();
+ if (ok) {
+ return true;
+ }
+
+ PUSH_LE_U32(buf, 0, time(NULL));
+
+ rc = tdb_store_bystring(wcache->tdb,
+ "WINBINDD_OFFLINE",
+ data,
+ TDB_INSERT);
+ if (rc != 0) {
+ return false;
+ }
+
+ return true;
+
+}
+
+void set_global_winbindd_state_online(void)
+{
+ DBG_DEBUG("set_global_winbindd_state_online: online requested.\n");
+
+ if (!lp_winbind_offline_logon()) {
+ DBG_DEBUG("Rejecting request to set winbind online, "
+ "offline logons are disabled in smb.conf.\n");
+ return;
+ }
+
+ if (!wcache->tdb) {
+ return;
+ }
+
+ /* Ensure there is no key "WINBINDD_OFFLINE" in the cache tdb. */
+ tdb_delete_bystring(wcache->tdb, "WINBINDD_OFFLINE");
+}
+
+bool get_global_winbindd_state_offline(void)
+{
+ TDB_DATA data;
+
+ data = tdb_fetch_bystring(wcache->tdb, "WINBINDD_OFFLINE");
+ if (data.dptr == NULL || data.dsize != 4) {
+ DBG_DEBUG("Offline state not set.\n");
+ SAFE_FREE(data.dptr);
+ return false;
+ }
+
+ return true;
+}
+
+/***********************************************************************
+ Validate functions for all possible cache tdb keys.
+***********************************************************************/
+
+static struct cache_entry *create_centry_validate(const char *kstr, TDB_DATA data,
+ struct tdb_validation_status *state)
+{
+ struct cache_entry *centry;
+
+ centry = SMB_XMALLOC_P(struct cache_entry);
+ centry->data = (unsigned char *)smb_memdup(data.dptr, data.dsize);
+ if (!centry->data) {
+ SAFE_FREE(centry);
+ return NULL;
+ }
+ centry->len = data.dsize;
+ centry->ofs = 0;
+
+ if (centry->len < 16) {
+ /* huh? corrupt cache? */
+ DBG_ERR("create_centry_validate: Corrupt cache for key %s "
+ "(len < 16) ?\n", kstr);
+ centry_free(centry);
+ state->bad_entry = true;
+ state->success = false;
+ return NULL;
+ }
+
+ centry->status = NT_STATUS(centry_uint32(centry));
+ centry->sequence_number = centry_uint32(centry);
+ centry->timeout = centry_uint64_t(centry);
+ return centry;
+}
+
+static int validate_seqnum(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
+ struct tdb_validation_status *state)
+{
+ if (dbuf.dsize != 8) {
+ DBG_ERR("validate_seqnum: Corrupt cache for key %s (len %u != 8) ?\n",
+ keystr, (unsigned int)dbuf.dsize );
+ state->bad_entry = true;
+ return 1;
+ }
+ return 0;
+}
+
+static int validate_u(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
+ struct tdb_validation_status *state)
+{
+ struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
+ struct dom_sid sid;
+
+ if (!centry) {
+ return 1;
+ }
+
+ (void)centry_string(centry, mem_ctx);
+ (void)centry_string(centry, mem_ctx);
+ (void)centry_string(centry, mem_ctx);
+ (void)centry_string(centry, mem_ctx);
+ (void)centry_string(centry, mem_ctx);
+ (void)centry_uint32(centry);
+ (void)centry_uint32(centry);
+ (void)centry_string(centry, mem_ctx);
+ (void)centry_sid(centry, &sid);
+ (void)centry_sid(centry, &sid);
+
+ centry_free(centry);
+
+ if (!(state->success)) {
+ return 1;
+ }
+ DBG_DEBUG("validate_u: %s ok\n", keystr);
+ return 0;
+}
+
+static int validate_loc_pol(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
+ struct tdb_validation_status *state)
+{
+ struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
+
+ if (!centry) {
+ return 1;
+ }
+
+ (void)centry_nttime(centry);
+ (void)centry_nttime(centry);
+ (void)centry_uint16(centry);
+
+ centry_free(centry);
+
+ if (!(state->success)) {
+ return 1;
+ }
+ DBG_DEBUG("validate_loc_pol: %s ok\n", keystr);
+ return 0;
+}
+
+static int validate_pwd_pol(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
+ struct tdb_validation_status *state)
+{
+ struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
+
+ if (!centry) {
+ return 1;
+ }
+
+ (void)centry_uint16(centry);
+ (void)centry_uint16(centry);
+ (void)centry_uint32(centry);
+ (void)centry_nttime(centry);
+ (void)centry_nttime(centry);
+
+ centry_free(centry);
+
+ if (!(state->success)) {
+ return 1;
+ }
+ DBG_DEBUG("validate_pwd_pol: %s ok\n", keystr);
+ return 0;
+}
+
+static int validate_cred(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
+ struct tdb_validation_status *state)
+{
+ struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
+
+ if (!centry) {
+ return 1;
+ }
+
+ (void)centry_time(centry);
+ (void)centry_hash16(centry, mem_ctx);
+
+ /* We only have 17 bytes more data in the salted cred case. */
+ if (centry->len - centry->ofs == 17) {
+ (void)centry_hash16(centry, mem_ctx);
+ }
+
+ centry_free(centry);
+
+ if (!(state->success)) {
+ return 1;
+ }
+ DBG_DEBUG("validate_cred: %s ok\n", keystr);
+ return 0;
+}
+
+static int validate_ul(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
+ struct tdb_validation_status *state)
+{
+ struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
+ int32_t num_entries, i;
+
+ if (!centry) {
+ return 1;
+ }
+
+ num_entries = (int32_t)centry_uint32(centry);
+
+ for (i=0; i< num_entries; i++) {
+ (void)centry_uint32(centry);
+ }
+
+ centry_free(centry);
+
+ if (!(state->success)) {
+ return 1;
+ }
+ DBG_DEBUG("validate_ul: %s ok\n", keystr);
+ return 0;
+}
+
+static int validate_gl(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
+ struct tdb_validation_status *state)
+{
+ struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
+ int32_t num_entries, i;
+
+ if (!centry) {
+ return 1;
+ }
+
+ num_entries = centry_uint32(centry);
+
+ for (i=0; i< num_entries; i++) {
+ (void)centry_string(centry, mem_ctx);
+ (void)centry_string(centry, mem_ctx);
+ (void)centry_uint32(centry);
+ }
+
+ centry_free(centry);
+
+ if (!(state->success)) {
+ return 1;
+ }
+ DBG_DEBUG("validate_gl: %s ok\n", keystr);
+ return 0;
+}
+
+static int validate_ug(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
+ struct tdb_validation_status *state)
+{
+ struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
+ int32_t num_groups, i;
+
+ if (!centry) {
+ return 1;
+ }
+
+ num_groups = centry_uint32(centry);
+
+ for (i=0; i< num_groups; i++) {
+ struct dom_sid sid;
+ centry_sid(centry, &sid);
+ }
+
+ centry_free(centry);
+
+ if (!(state->success)) {
+ return 1;
+ }
+ DBG_DEBUG("validate_ug: %s ok\n", keystr);
+ return 0;
+}
+
+static int validate_ua(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
+ struct tdb_validation_status *state)
+{
+ struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
+ int32_t num_aliases, i;
+
+ if (!centry) {
+ return 1;
+ }
+
+ num_aliases = centry_uint32(centry);
+
+ for (i=0; i < num_aliases; i++) {
+ (void)centry_uint32(centry);
+ }
+
+ centry_free(centry);
+
+ if (!(state->success)) {
+ return 1;
+ }
+ DBG_DEBUG("validate_ua: %s ok\n", keystr);
+ return 0;
+}
+
+static int validate_gm(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
+ struct tdb_validation_status *state)
+{
+ struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
+ int32_t num_names, i;
+
+ if (!centry) {
+ return 1;
+ }
+
+ num_names = centry_uint32(centry);
+
+ for (i=0; i< num_names; i++) {
+ struct dom_sid sid;
+ centry_sid(centry, &sid);
+ (void)centry_string(centry, mem_ctx);
+ (void)centry_uint32(centry);
+ }
+
+ centry_free(centry);
+
+ if (!(state->success)) {
+ return 1;
+ }
+ DBG_DEBUG("validate_gm: %s ok\n", keystr);
+ return 0;
+}
+
+static int validate_dr(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
+ struct tdb_validation_status *state)
+{
+ /* Can't say anything about this other than must be nonzero. */
+ if (dbuf.dsize == 0) {
+ DBG_ERR("validate_dr: Corrupt cache for key %s (len == 0) ?\n",
+ keystr);
+ state->bad_entry = true;
+ state->success = false;
+ return 1;
+ }
+
+ DBG_DEBUG("validate_dr: %s ok\n", keystr);
+ return 0;
+}
+
+static int validate_de(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
+ struct tdb_validation_status *state)
+{
+ /* Can't say anything about this other than must be nonzero. */
+ if (dbuf.dsize == 0) {
+ DBG_ERR("validate_de: Corrupt cache for key %s (len == 0) ?\n",
+ keystr);
+ state->bad_entry = true;
+ state->success = false;
+ return 1;
+ }
+
+ DBG_DEBUG("validate_de: %s ok\n", keystr);
+ return 0;
+}
+
+static int validate_nss_an(TALLOC_CTX *mem_ctx, const char *keystr,
+ TDB_DATA dbuf,
+ struct tdb_validation_status *state)
+{
+ struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
+
+ if (!centry) {
+ return 1;
+ }
+
+ (void)centry_string( centry, mem_ctx );
+
+ centry_free(centry);
+
+ if (!(state->success)) {
+ return 1;
+ }
+ DBG_DEBUG("validate_pwinfo: %s ok\n", keystr);
+ return 0;
+}
+
+static int validate_nss_na(TALLOC_CTX *mem_ctx, const char *keystr,
+ TDB_DATA dbuf,
+ struct tdb_validation_status *state)
+{
+ struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
+
+ if (!centry) {
+ return 1;
+ }
+
+ (void)centry_string( centry, mem_ctx );
+
+ centry_free(centry);
+
+ if (!(state->success)) {
+ return 1;
+ }
+ DBG_DEBUG("%s ok\n", keystr);
+ return 0;
+}
+
+static int validate_trustdomcache(TALLOC_CTX *mem_ctx, const char *keystr,
+ TDB_DATA dbuf,
+ struct tdb_validation_status *state)
+{
+ if (dbuf.dsize == 0) {
+ DBG_ERR("validate_trustdomcache: Corrupt cache for "
+ "key %s (len ==0) ?\n", keystr);
+ state->bad_entry = true;
+ state->success = false;
+ return 1;
+ }
+
+ DBG_DEBUG("validate_trustdomcache: %s ok\n"
+ " Don't trust me, I am a DUMMY!\n",
+ keystr);
+ return 0;
+}
+
+static int validate_offline(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
+ struct tdb_validation_status *state)
+{
+ if (dbuf.dsize != 4) {
+ DBG_ERR("validate_offline: Corrupt cache for key %s (len %u != 4) ?\n",
+ keystr, (unsigned int)dbuf.dsize );
+ state->bad_entry = true;
+ state->success = false;
+ return 1;
+ }
+ DBG_DEBUG("validate_offline: %s ok\n", keystr);
+ return 0;
+}
+
+static int validate_ndr(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
+ struct tdb_validation_status *state)
+{
+ /*
+ * Ignore validation for now. The proper way to do this is with a
+ * checksum. Just pure parsing does not really catch much.
+ */
+ return 0;
+}
+
+static int validate_cache_version(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
+ struct tdb_validation_status *state)
+{
+ if (dbuf.dsize != 4) {
+ DBG_ERR("validate_cache_version: Corrupt cache for "
+ "key %s (len %u != 4) ?\n",
+ keystr, (unsigned int)dbuf.dsize);
+ state->bad_entry = true;
+ state->success = false;
+ return 1;
+ }
+
+ DBG_DEBUG("validate_cache_version: %s ok\n", keystr);
+ return 0;
+}
+
+/***********************************************************************
+ A list of all possible cache tdb keys with associated validation
+ functions.
+***********************************************************************/
+
+struct key_val_struct {
+ const char *keyname;
+ int (*validate_data_fn)(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf, struct tdb_validation_status* state);
+} key_val[] = {
+ {"SEQNUM/", validate_seqnum},
+ {"U/", validate_u},
+ {"LOC_POL/", validate_loc_pol},
+ {"PWD_POL/", validate_pwd_pol},
+ {"CRED/", validate_cred},
+ {"UL/", validate_ul},
+ {"GL/", validate_gl},
+ {"UG/", validate_ug},
+ {"UA", validate_ua},
+ {"GM/", validate_gm},
+ {"DR/", validate_dr},
+ {"DE/", validate_de},
+ {"TRUSTDOMCACHE/", validate_trustdomcache},
+ {"NSS/NA/", validate_nss_na},
+ {"NSS/AN/", validate_nss_an},
+ {"WINBINDD_OFFLINE", validate_offline},
+ {"NDR/", validate_ndr},
+ {WINBINDD_CACHE_VERSION_KEYSTR, validate_cache_version},
+ {NULL, NULL}
+};
+
+/***********************************************************************
+ Function to look at every entry in the tdb and validate it as far as
+ possible.
+***********************************************************************/
+
+static int cache_traverse_validate_fn(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf, void *state)
+{
+ int i;
+ unsigned int max_key_len = 1024;
+ struct tdb_validation_status *v_state = (struct tdb_validation_status *)state;
+
+ /* Paranoia check. */
+ if (strncmp("UA/", (const char *)kbuf.dptr, 3) == 0 ||
+ strncmp("NDR/", (const char *)kbuf.dptr, 4) == 0) {
+ max_key_len = 1024 * 1024;
+ }
+ if (kbuf.dsize > max_key_len) {
+ DBG_ERR("cache_traverse_validate_fn: key length too large: "
+ "(%u) > (%u)\n\n",
+ (unsigned int)kbuf.dsize, (unsigned int)max_key_len);
+ return 1;
+ }
+
+ for (i = 0; key_val[i].keyname; i++) {
+ size_t namelen = strlen(key_val[i].keyname);
+ if (kbuf.dsize >= namelen && (
+ strncmp(key_val[i].keyname, (const char *)kbuf.dptr, namelen)) == 0) {
+ TALLOC_CTX *mem_ctx;
+ char *keystr;
+ int ret;
+
+ keystr = SMB_MALLOC_ARRAY(char, kbuf.dsize+1);
+ if (!keystr) {
+ return 1;
+ }
+ memcpy(keystr, kbuf.dptr, kbuf.dsize);
+ keystr[kbuf.dsize] = '\0';
+
+ mem_ctx = talloc_init("validate_ctx");
+ if (!mem_ctx) {
+ SAFE_FREE(keystr);
+ return 1;
+ }
+
+ ret = key_val[i].validate_data_fn(mem_ctx, keystr, dbuf,
+ v_state);
+
+ SAFE_FREE(keystr);
+ talloc_destroy(mem_ctx);
+ return ret;
+ }
+ }
+
+ DBG_ERR("cache_traverse_validate_fn: unknown cache entry\nkey :\n");
+ dump_data(0, (uint8_t *)kbuf.dptr, kbuf.dsize);
+ DBG_ERR("data :\n");
+ dump_data(0, (uint8_t *)dbuf.dptr, dbuf.dsize);
+ v_state->unknown_key = true;
+ v_state->success = false;
+ return 1; /* terminate. */
+}
+
+static void validate_panic(const char *const why)
+{
+ DBG_ERR("validating cache: would panic %s\n"
+ "exiting instead (cache validation mode)\n", why );
+ exit(47);
+}
+
+static int wbcache_update_centry_fn(TDB_CONTEXT *tdb,
+ TDB_DATA key,
+ TDB_DATA data,
+ void *state)
+{
+ uint64_t ctimeout;
+ TDB_DATA blob;
+
+ if (is_non_centry_key(key)) {
+ return 0;
+ }
+
+ if (data.dptr == NULL || data.dsize == 0) {
+ if (tdb_delete(tdb, key) < 0) {
+ DBG_ERR("tdb_delete for [%s] failed!\n",
+ key.dptr);
+ return 1;
+ }
+ }
+
+ /* add timeout to blob (uint64_t) */
+ blob.dsize = data.dsize + 8;
+
+ blob.dptr = SMB_XMALLOC_ARRAY(uint8_t, blob.dsize);
+ if (blob.dptr == NULL) {
+ return 1;
+ }
+ memset(blob.dptr, 0, blob.dsize);
+
+ /* copy status and seqnum */
+ memcpy(blob.dptr, data.dptr, 8);
+
+ /* add timeout */
+ ctimeout = lp_winbind_cache_time() + time(NULL);
+ SBVAL(blob.dptr, 8, ctimeout);
+
+ /* copy the rest */
+ memcpy(blob.dptr + 16, data.dptr + 8, data.dsize - 8);
+
+ if (tdb_store(tdb, key, blob, TDB_REPLACE) < 0) {
+ DBG_ERR("tdb_store to update [%s] failed!\n",
+ key.dptr);
+ SAFE_FREE(blob.dptr);
+ return 1;
+ }
+
+ SAFE_FREE(blob.dptr);
+ return 0;
+}
+
+static bool wbcache_upgrade_v1_to_v2(TDB_CONTEXT *tdb)
+{
+ int rc;
+
+ DBG_NOTICE("Upgrade to version 2 of the winbindd_cache.tdb\n");
+
+ rc = tdb_traverse(tdb, wbcache_update_centry_fn, NULL);
+ if (rc < 0) {
+ return false;
+ }
+
+ return true;
+}
+
+/***********************************************************************
+ Try and validate every entry in the winbindd cache. If we fail here,
+ delete the cache tdb and return non-zero.
+***********************************************************************/
+
+int winbindd_validate_cache(void)
+{
+ int ret = -1;
+ char *tdb_path = NULL;
+ TDB_CONTEXT *tdb = NULL;
+ uint32_t vers_id;
+ bool ok;
+
+ DBG_DEBUG("winbindd_validate_cache: replacing panic function\n");
+ smb_panic_fn = validate_panic;
+
+ tdb_path = wcache_path();
+ if (tdb_path == NULL) {
+ goto done;
+ }
+
+ tdb = tdb_open_log(tdb_path,
+ WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE,
+ TDB_INCOMPATIBLE_HASH |
+ ( lp_winbind_offline_logon()
+ ? TDB_DEFAULT
+ : TDB_DEFAULT | TDB_CLEAR_IF_FIRST ),
+ O_RDWR|O_CREAT,
+ 0600);
+ if (!tdb) {
+ DBG_ERR("winbindd_validate_cache: "
+ "error opening/initializing tdb\n");
+ goto done;
+ }
+
+ /* Version check and upgrade code. */
+ if (!tdb_fetch_uint32(tdb, WINBINDD_CACHE_VERSION_KEYSTR, &vers_id)) {
+ DBG_DEBUG("Fresh database\n");
+ tdb_store_uint32(tdb, WINBINDD_CACHE_VERSION_KEYSTR, WINBINDD_CACHE_VERSION);
+ vers_id = WINBINDD_CACHE_VERSION;
+ }
+
+ if (vers_id != WINBINDD_CACHE_VERSION) {
+ if (vers_id == WINBINDD_CACHE_VER1) {
+ ok = wbcache_upgrade_v1_to_v2(tdb);
+ if (!ok) {
+ DBG_DEBUG("winbindd_validate_cache: upgrade to version 2 failed.\n");
+ unlink(tdb_path);
+ goto done;
+ }
+
+ tdb_store_uint32(tdb,
+ WINBINDD_CACHE_VERSION_KEYSTR,
+ WINBINDD_CACHE_VERSION);
+ vers_id = WINBINDD_CACHE_VER2;
+ }
+ }
+
+ tdb_close(tdb);
+
+ ret = tdb_validate_and_backup(tdb_path, cache_traverse_validate_fn);
+
+ if (ret != 0) {
+ DBG_DEBUG("winbindd_validate_cache: validation not successful.\n"
+ "removing tdb %s.\n", tdb_path);
+ unlink(tdb_path);
+ }
+
+done:
+ TALLOC_FREE(tdb_path);
+ DBG_DEBUG("winbindd_validate_cache: restoring panic function\n");
+ smb_panic_fn = smb_panic;
+ return ret;
+}
+
+/***********************************************************************
+ Try and validate every entry in the winbindd cache.
+***********************************************************************/
+
+int winbindd_validate_cache_nobackup(void)
+{
+ int ret = -1;
+ char *tdb_path;
+
+ DBG_DEBUG("winbindd_validate_cache: replacing panic function\n");
+ smb_panic_fn = validate_panic;
+
+ tdb_path = wcache_path();
+ if (tdb_path == NULL) {
+ goto err_panic_restore;
+ }
+
+ if (wcache == NULL || wcache->tdb == NULL) {
+ ret = tdb_validate_open(tdb_path, cache_traverse_validate_fn);
+ } else {
+ ret = tdb_validate(wcache->tdb, cache_traverse_validate_fn);
+ }
+
+ if (ret != 0) {
+ DBG_DEBUG("winbindd_validate_cache_nobackup: validation not "
+ "successful.\n");
+ }
+
+ TALLOC_FREE(tdb_path);
+err_panic_restore:
+ DBG_DEBUG("winbindd_validate_cache_nobackup: restoring panic "
+ "function\n");
+ smb_panic_fn = smb_panic;
+ return ret;
+}
+
+bool winbindd_cache_validate_and_initialize(void)
+{
+ close_winbindd_cache();
+
+ if (lp_winbind_offline_logon()) {
+ if (winbindd_validate_cache() < 0) {
+ DBG_ERR("winbindd cache tdb corrupt and no backup "
+ "could be restored.\n");
+ }
+ }
+
+ return initialize_winbindd_cache();
+}
+
+/*********************************************************************
+ ********************************************************************/
+
+static bool add_wbdomain_to_tdc_array( struct winbindd_domain *new_dom,
+ struct winbindd_tdc_domain **domains,
+ size_t *num_domains )
+{
+ struct winbindd_tdc_domain *list = NULL;
+ size_t i, idx;
+ bool set_only = false;
+
+ /* don't allow duplicates */
+
+ idx = *num_domains;
+ list = *domains;
+
+ for ( i=0; i< (*num_domains); i++ ) {
+ if ( strequal( new_dom->name, list[i].domain_name ) ) {
+ DBG_DEBUG("add_wbdomain_to_tdc_array: Found existing record for %s\n",
+ new_dom->name);
+ idx = i;
+ set_only = true;
+
+ break;
+ }
+ }
+
+ if ( !set_only ) {
+ if ( !*domains ) {
+ list = talloc_array( NULL, struct winbindd_tdc_domain, 1 );
+ idx = 0;
+ } else {
+ list = talloc_realloc( *domains, *domains,
+ struct winbindd_tdc_domain,
+ (*num_domains)+1);
+ idx = *num_domains;
+ }
+
+ ZERO_STRUCT( list[idx] );
+ }
+
+ if ( !list )
+ return false;
+
+ list[idx].domain_name = talloc_strdup(list, new_dom->name);
+ if (list[idx].domain_name == NULL) {
+ return false;
+ }
+ if (new_dom->alt_name != NULL) {
+ list[idx].dns_name = talloc_strdup(list, new_dom->alt_name);
+ if (list[idx].dns_name == NULL) {
+ return false;
+ }
+ }
+
+ if ( !is_null_sid( &new_dom->sid ) ) {
+ sid_copy( &list[idx].sid, &new_dom->sid );
+ } else {
+ sid_copy(&list[idx].sid, &global_sid_NULL);
+ }
+
+ if ( new_dom->domain_flags != 0x0 )
+ list[idx].trust_flags = new_dom->domain_flags;
+
+ if ( new_dom->domain_type != 0x0 )
+ list[idx].trust_type = new_dom->domain_type;
+
+ if ( new_dom->domain_trust_attribs != 0x0 )
+ list[idx].trust_attribs = new_dom->domain_trust_attribs;
+
+ if ( !set_only ) {
+ *domains = list;
+ *num_domains = idx + 1;
+ }
+
+ return true;
+}
+
+/*********************************************************************
+ ********************************************************************/
+
+static TDB_DATA make_tdc_key( const char *domain_name )
+{
+ char *keystr = NULL;
+ TDB_DATA key = { NULL, 0 };
+
+ if ( !domain_name ) {
+ DBG_INFO("make_tdc_key: Keyname workgroup is NULL!\n");
+ return key;
+ }
+
+ if (asprintf( &keystr, "TRUSTDOMCACHE/%s", domain_name ) == -1) {
+ return key;
+ }
+ key = string_term_tdb_data(keystr);
+
+ return key;
+}
+
+/*********************************************************************
+ ********************************************************************/
+
+static int pack_tdc_domains( struct winbindd_tdc_domain *domains,
+ size_t num_domains,
+ unsigned char **buf )
+{
+ unsigned char *buffer = NULL;
+ int len = 0;
+ int buflen = 0;
+ size_t i = 0;
+
+ DBG_DEBUG("pack_tdc_domains: Packing %d trusted domains\n",
+ (int)num_domains);
+
+ buflen = 0;
+
+ again:
+ len = 0;
+
+ /* Store the number of array items first */
+ len += tdb_pack( buffer ? buffer+len : NULL,
+ buffer ? buflen-len : 0, "d",
+ num_domains );
+
+ /* now pack each domain trust record */
+ for ( i=0; i<num_domains; i++ ) {
+
+ struct dom_sid_buf tmp;
+
+ if ( buflen > 0 ) {
+ DBG_DEBUG("pack_tdc_domains: Packing domain %s (%s)\n",
+ domains[i].domain_name,
+ domains[i].dns_name ? domains[i].dns_name : "UNKNOWN" );
+ }
+
+ len += tdb_pack( buffer ? buffer+len : NULL,
+ buffer ? buflen-len : 0, "fffddd",
+ domains[i].domain_name,
+ domains[i].dns_name ? domains[i].dns_name : "",
+ dom_sid_str_buf(&domains[i].sid, &tmp),
+ domains[i].trust_flags,
+ domains[i].trust_attribs,
+ domains[i].trust_type );
+ }
+
+ if ( buflen < len ) {
+ SAFE_FREE(buffer);
+ if ( (buffer = SMB_MALLOC_ARRAY(unsigned char, len)) == NULL ) {
+ DBG_ERR("pack_tdc_domains: failed to alloc buffer!\n");
+ buflen = -1;
+ goto done;
+ }
+ buflen = len;
+ goto again;
+ }
+
+ *buf = buffer;
+
+ done:
+ return buflen;
+}
+
+/*********************************************************************
+ ********************************************************************/
+
+static size_t unpack_tdc_domains( unsigned char *buf, int buflen,
+ struct winbindd_tdc_domain **domains )
+{
+ fstring domain_name, dns_name, sid_string;
+ uint32_t type, attribs, flags;
+ int num_domains;
+ int len = 0;
+ int i;
+ struct winbindd_tdc_domain *list = NULL;
+
+ /* get the number of domains */
+ len += tdb_unpack( buf+len, buflen-len, "d", &num_domains);
+ if ( len == -1 ) {
+ DBG_INFO("unpack_tdc_domains: Failed to unpack domain array\n");
+ return 0;
+ }
+
+ list = talloc_array( NULL, struct winbindd_tdc_domain, num_domains );
+ if ( !list ) {
+ DBG_ERR("unpack_tdc_domains: Failed to talloc() domain list!\n");
+ return 0;
+ }
+
+ for ( i=0; i<num_domains; i++ ) {
+ int this_len;
+
+ this_len = tdb_unpack( buf+len, buflen-len, "fffddd",
+ domain_name,
+ dns_name,
+ sid_string,
+ &flags,
+ &attribs,
+ &type );
+
+ if ( this_len == -1 ) {
+ DBG_INFO("unpack_tdc_domains: Failed to unpack domain array\n");
+ TALLOC_FREE( list );
+ return 0;
+ }
+ len += this_len;
+
+ DBG_DEBUG("unpack_tdc_domains: Unpacking domain %s (%s) "
+ "SID %s, flags = 0x%x, attribs = 0x%x, type = 0x%x\n",
+ domain_name, dns_name, sid_string,
+ flags, attribs, type);
+
+ list[i].domain_name = talloc_strdup( list, domain_name );
+ list[i].dns_name = NULL;
+ if (dns_name[0] != '\0') {
+ list[i].dns_name = talloc_strdup(list, dns_name);
+ }
+ if ( !string_to_sid( &(list[i].sid), sid_string ) ) {
+ DBG_DEBUG("unpack_tdc_domains: no SID for domain %s\n",
+ domain_name);
+ }
+ list[i].trust_flags = flags;
+ list[i].trust_attribs = attribs;
+ list[i].trust_type = type;
+ }
+
+ *domains = list;
+
+ return num_domains;
+}
+
+/*********************************************************************
+ ********************************************************************/
+
+static bool wcache_tdc_store_list( struct winbindd_tdc_domain *domains, size_t num_domains )
+{
+ TDB_DATA key = make_tdc_key( lp_workgroup() );
+ TDB_DATA data = { NULL, 0 };
+ int ret;
+
+ if ( !key.dptr )
+ return false;
+
+ /* See if we were asked to delete the cache entry */
+
+ if ( !domains ) {
+ ret = tdb_delete( wcache->tdb, key );
+ goto done;
+ }
+
+ data.dsize = pack_tdc_domains( domains, num_domains, &data.dptr );
+
+ if ( !data.dptr ) {
+ ret = -1;
+ goto done;
+ }
+
+ ret = tdb_store( wcache->tdb, key, data, 0 );
+
+ done:
+ SAFE_FREE( data.dptr );
+ SAFE_FREE( key.dptr );
+
+ return ( ret == 0 );
+}
+
+/*********************************************************************
+ ********************************************************************/
+
+bool wcache_tdc_fetch_list( struct winbindd_tdc_domain **domains, size_t *num_domains )
+{
+ TDB_DATA key = make_tdc_key( lp_workgroup() );
+ TDB_DATA data = { NULL, 0 };
+
+ *domains = NULL;
+ *num_domains = 0;
+
+ if ( !key.dptr )
+ return false;
+
+ data = tdb_fetch( wcache->tdb, key );
+
+ SAFE_FREE( key.dptr );
+
+ if ( !data.dptr )
+ return false;
+
+ *num_domains = unpack_tdc_domains( data.dptr, data.dsize, domains );
+
+ SAFE_FREE( data.dptr );
+
+ if ( !*domains )
+ return false;
+
+ return true;
+}
+
+/*********************************************************************
+ ********************************************************************/
+
+bool wcache_tdc_add_domain( struct winbindd_domain *domain )
+{
+ struct winbindd_tdc_domain *dom_list = NULL;
+ size_t num_domains = 0;
+ bool ret = false;
+ struct dom_sid_buf buf;
+
+ DBG_DEBUG("wcache_tdc_add_domain: Adding domain %s (%s), SID %s, "
+ "flags = 0x%x, attributes = 0x%x, type = 0x%x\n",
+ domain->name, domain->alt_name,
+ dom_sid_str_buf(&domain->sid, &buf),
+ domain->domain_flags,
+ domain->domain_trust_attribs,
+ domain->domain_type);
+
+ if ( !init_wcache() ) {
+ return false;
+ }
+
+ /* fetch the list */
+
+ wcache_tdc_fetch_list( &dom_list, &num_domains );
+
+ /* add the new domain */
+
+ if ( !add_wbdomain_to_tdc_array( domain, &dom_list, &num_domains ) ) {
+ goto done;
+ }
+
+ /* pack the domain */
+
+ if ( !wcache_tdc_store_list( dom_list, num_domains ) ) {
+ goto done;
+ }
+
+ /* Success */
+
+ ret = true;
+ done:
+ TALLOC_FREE( dom_list );
+
+ return ret;
+}
+
+static struct winbindd_tdc_domain *wcache_tdc_dup_domain(
+ TALLOC_CTX *mem_ctx, const struct winbindd_tdc_domain *src)
+{
+ struct winbindd_tdc_domain *dst;
+
+ dst = talloc(mem_ctx, struct winbindd_tdc_domain);
+ if (dst == NULL) {
+ goto fail;
+ }
+ dst->domain_name = talloc_strdup(dst, src->domain_name);
+ if (dst->domain_name == NULL) {
+ goto fail;
+ }
+
+ dst->dns_name = NULL;
+ if (src->dns_name != NULL) {
+ dst->dns_name = talloc_strdup(dst, src->dns_name);
+ if (dst->dns_name == NULL) {
+ goto fail;
+ }
+ }
+
+ sid_copy(&dst->sid, &src->sid);
+ dst->trust_flags = src->trust_flags;
+ dst->trust_type = src->trust_type;
+ dst->trust_attribs = src->trust_attribs;
+ return dst;
+fail:
+ TALLOC_FREE(dst);
+ return NULL;
+}
+
+/*********************************************************************
+ ********************************************************************/
+
+struct winbindd_tdc_domain * wcache_tdc_fetch_domain( TALLOC_CTX *ctx, const char *name )
+{
+ struct winbindd_tdc_domain *dom_list = NULL;
+ size_t num_domains = 0;
+ size_t i;
+ struct winbindd_tdc_domain *d = NULL;
+
+ DBG_DEBUG("wcache_tdc_fetch_domain: Searching for domain %s\n", name);
+
+ if ( !init_wcache() ) {
+ return NULL;
+ }
+
+ /* fetch the list */
+
+ wcache_tdc_fetch_list( &dom_list, &num_domains );
+
+ for ( i=0; i<num_domains; i++ ) {
+ if ( strequal(name, dom_list[i].domain_name) ||
+ strequal(name, dom_list[i].dns_name) )
+ {
+ DBG_DEBUG("wcache_tdc_fetch_domain: Found domain %s\n",
+ name);
+
+ d = wcache_tdc_dup_domain(ctx, &dom_list[i]);
+ break;
+ }
+ }
+
+ TALLOC_FREE( dom_list );
+
+ return d;
+}
+
+/*********************************************************************
+ ********************************************************************/
+
+void wcache_tdc_clear( void )
+{
+ if ( !init_wcache() )
+ return;
+
+ wcache_tdc_store_list( NULL, 0 );
+
+ return;
+}
+
+static bool wcache_ndr_key(TALLOC_CTX *mem_ctx, const char *domain_name,
+ uint32_t opnum, const DATA_BLOB *req,
+ TDB_DATA *pkey)
+{
+ char *key;
+ size_t keylen;
+
+ key = talloc_asprintf(mem_ctx, "NDR/%s/%d/", domain_name, (int)opnum);
+ if (key == NULL) {
+ return false;
+ }
+ keylen = talloc_get_size(key) - 1;
+
+ key = talloc_realloc(mem_ctx, key, char, keylen + req->length);
+ if (key == NULL) {
+ return false;
+ }
+ memcpy(key + keylen, req->data, req->length);
+
+ pkey->dptr = (uint8_t *)key;
+ pkey->dsize = talloc_get_size(key);
+ return true;
+}
+
+static bool wcache_opnum_cacheable(uint32_t opnum)
+{
+ switch (opnum) {
+ case NDR_WBINT_LOOKUPSID:
+ case NDR_WBINT_LOOKUPSIDS:
+ case NDR_WBINT_LOOKUPNAME:
+ case NDR_WBINT_SIDS2UNIXIDS:
+ case NDR_WBINT_UNIXIDS2SIDS:
+ case NDR_WBINT_GETNSSINFO:
+ case NDR_WBINT_LOOKUPUSERALIASES:
+ case NDR_WBINT_LOOKUPUSERGROUPS:
+ case NDR_WBINT_LOOKUPGROUPMEMBERS:
+ case NDR_WBINT_QUERYGROUPLIST:
+ case NDR_WBINT_QUERYUSERRIDLIST:
+ case NDR_WBINT_DSGETDCNAME:
+ case NDR_WBINT_LOOKUPRIDS:
+ return true;
+ }
+ return false;
+}
+
+bool wcache_fetch_ndr(TALLOC_CTX *mem_ctx, struct winbindd_domain *domain,
+ uint32_t opnum, const DATA_BLOB *req, DATA_BLOB *resp)
+{
+ TDB_DATA key, data;
+ bool ret = false;
+
+ if (!wcache_opnum_cacheable(opnum) ||
+ is_my_own_sam_domain(domain) ||
+ is_builtin_domain(domain)) {
+ return false;
+ }
+
+ if (wcache->tdb == NULL) {
+ return false;
+ }
+
+ if (!wcache_ndr_key(talloc_tos(), domain->name, opnum, req, &key)) {
+ return false;
+ }
+ data = tdb_fetch(wcache->tdb, key);
+ TALLOC_FREE(key.dptr);
+
+ if (data.dptr == NULL) {
+ return false;
+ }
+ if (data.dsize < 12) {
+ goto fail;
+ }
+
+ if (is_domain_online(domain)) {
+ uint32_t entry_seqnum, dom_seqnum, last_check;
+ uint64_t entry_timeout;
+
+ if (!wcache_fetch_seqnum(domain->name, &dom_seqnum,
+ &last_check)) {
+ goto fail;
+ }
+ entry_seqnum = IVAL(data.dptr, 0);
+ if (entry_seqnum != dom_seqnum) {
+ DBG_DEBUG("Entry has wrong sequence number: %d\n",
+ (int)entry_seqnum);
+ goto fail;
+ }
+ entry_timeout = BVAL(data.dptr, 4);
+ if (time(NULL) > (time_t)entry_timeout) {
+ DBG_DEBUG("Entry has timed out\n");
+ goto fail;
+ }
+ }
+
+ resp->data = (uint8_t *)talloc_memdup(mem_ctx, data.dptr + 12,
+ data.dsize - 12);
+ if (resp->data == NULL) {
+ DBG_DEBUG("talloc failed\n");
+ goto fail;
+ }
+ resp->length = data.dsize - 12;
+
+ ret = true;
+fail:
+ SAFE_FREE(data.dptr);
+ return ret;
+}
+
+void wcache_store_ndr(struct winbindd_domain *domain, uint32_t opnum,
+ const DATA_BLOB *req, const DATA_BLOB *resp)
+{
+ TDB_DATA key, data;
+ uint32_t dom_seqnum, last_check;
+ uint64_t timeout;
+
+ if (!wcache_opnum_cacheable(opnum) ||
+ is_my_own_sam_domain(domain) ||
+ is_builtin_domain(domain)) {
+ return;
+ }
+
+ if (wcache->tdb == NULL) {
+ return;
+ }
+
+ if (!wcache_fetch_seqnum(domain->name, &dom_seqnum, &last_check)) {
+ DBG_DEBUG("could not fetch seqnum for domain %s\n",
+ domain->name);
+ return;
+ }
+
+ if (!wcache_ndr_key(talloc_tos(), domain->name, opnum, req, &key)) {
+ return;
+ }
+
+ timeout = time(NULL) + lp_winbind_cache_time();
+
+ data.dsize = resp->length + 12;
+ data.dptr = talloc_array(key.dptr, uint8_t, data.dsize);
+ if (data.dptr == NULL) {
+ goto done;
+ }
+
+ SIVAL(data.dptr, 0, dom_seqnum);
+ SBVAL(data.dptr, 4, timeout);
+ memcpy(data.dptr + 12, resp->data, resp->length);
+
+ tdb_store(wcache->tdb, key, data, 0);
+
+done:
+ TALLOC_FREE(key.dptr);
+ return;
+}
diff --git a/source3/winbindd/winbindd_ccache_access.c b/source3/winbindd/winbindd_ccache_access.c
new file mode 100644
index 0000000..cc395ad
--- /dev/null
+++ b/source3/winbindd/winbindd_ccache_access.c
@@ -0,0 +1,397 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Winbind daemon - cached credentials functions
+
+ Copyright (C) Robert O'Callahan 2006
+ Copyright (C) Jeremy Allison 2006 (minor fixes to fit into Samba and
+ protect against integer wrap).
+ Copyright (C) Andrew Bartlett 2011
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "winbindd.h"
+#include "auth/gensec/gensec.h"
+#include "auth_generic.h"
+#include "lib/util/string_wrappers.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_WINBIND
+
+static bool client_can_access_ccache_entry(uid_t client_uid,
+ struct WINBINDD_MEMORY_CREDS *entry)
+{
+ if (client_uid == entry->uid || client_uid == 0) {
+ DEBUG(10, ("Access granted to uid %u\n", (unsigned int)client_uid));
+ return True;
+ }
+
+ DEBUG(1, ("Access denied to uid %u (expected %u)\n",
+ (unsigned int)client_uid, (unsigned int)entry->uid));
+ return False;
+}
+
+static NTSTATUS do_ntlm_auth_with_stored_pw(const char *namespace,
+ const char *domain,
+ const char *username,
+ const char *password,
+ const DATA_BLOB initial_msg,
+ const DATA_BLOB challenge_msg,
+ TALLOC_CTX *mem_ctx,
+ DATA_BLOB *auth_msg,
+ uint8_t session_key[16],
+ uint8_t *new_spnego)
+{
+ NTSTATUS status;
+ struct auth_generic_state *auth_generic_state = NULL;
+ DATA_BLOB reply, session_key_blob;
+
+ status = auth_generic_client_prepare(mem_ctx, &auth_generic_state);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(1, ("Could not start NTLMSSP client: %s\n",
+ nt_errstr(status)));
+ goto done;
+ }
+
+ status = auth_generic_set_username(auth_generic_state, username);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(1, ("Could not set username: %s\n",
+ nt_errstr(status)));
+ goto done;
+ }
+
+ status = auth_generic_set_domain(auth_generic_state, domain);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(1, ("Could not set domain: %s\n",
+ nt_errstr(status)));
+ goto done;
+ }
+
+ status = auth_generic_set_password(auth_generic_state, password);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(1, ("Could not set password: %s\n",
+ nt_errstr(status)));
+ goto done;
+ }
+
+ if (initial_msg.length == 0) {
+ gensec_want_feature(auth_generic_state->gensec_security,
+ GENSEC_FEATURE_SESSION_KEY);
+ }
+
+ status = auth_generic_client_start_by_name(auth_generic_state,
+ "ntlmssp_resume_ccache");
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(1, ("Could not start NTLMSSP resume mech: %s\n",
+ nt_errstr(status)));
+ goto done;
+ }
+
+ /*
+ * We inject the initial NEGOTIATE message our caller used
+ * in order to get the state machine into the correct position.
+ */
+ reply = data_blob_null;
+ status = gensec_update(auth_generic_state->gensec_security,
+ talloc_tos(), initial_msg, &reply);
+ data_blob_free(&reply);
+
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
+ DEBUG(1, ("Failed to create initial message! [%s]\n",
+ nt_errstr(status)));
+ goto done;
+ }
+
+ /* Now we are ready to handle the server's actual response. */
+ status = gensec_update(auth_generic_state->gensec_security,
+ mem_ctx, challenge_msg, &reply);
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_OK)) {
+ DEBUG(1, ("We didn't get a response to the challenge! [%s]\n",
+ nt_errstr(status)));
+ data_blob_free(&reply);
+ goto done;
+ }
+
+ status = gensec_session_key(auth_generic_state->gensec_security,
+ talloc_tos(), &session_key_blob);
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_OK)) {
+ DEBUG(1, ("We didn't get the session key we requested! [%s]\n",
+ nt_errstr(status)));
+ data_blob_free(&reply);
+ goto done;
+ }
+
+ if (session_key_blob.length != 16) {
+ DEBUG(1, ("invalid session key length %d\n",
+ (int)session_key_blob.length));
+ data_blob_free(&reply);
+ goto done;
+ }
+ memcpy(session_key, session_key_blob.data, 16);
+ data_blob_free(&session_key_blob);
+ *auth_msg = reply;
+ *new_spnego = gensec_have_feature(auth_generic_state->gensec_security,
+ GENSEC_FEATURE_NEW_SPNEGO);
+ status = NT_STATUS_OK;
+
+done:
+ TALLOC_FREE(auth_generic_state);
+ return status;
+}
+
+static bool check_client_uid(struct winbindd_cli_state *state, uid_t uid)
+{
+ int ret;
+ uid_t ret_uid;
+ gid_t ret_gid;
+
+ ret_uid = (uid_t)-1;
+
+ ret = getpeereid(state->sock, &ret_uid, &ret_gid);
+ if (ret != 0) {
+ DEBUG(1, ("check_client_uid: Could not get socket peer uid: %s; "
+ "denying access\n", strerror(errno)));
+ return False;
+ }
+
+ if (uid != ret_uid && ret_uid != sec_initial_uid()) {
+ DEBUG(1, ("check_client_uid: Client lied about its uid: said %u, "
+ "actually was %u; denying access\n",
+ (unsigned int)uid, (unsigned int)ret_uid));
+ return False;
+ }
+
+ return True;
+}
+
+bool winbindd_ccache_ntlm_auth(struct winbindd_cli_state *state)
+{
+ struct winbindd_domain *domain;
+ char *name_namespace = NULL;
+ char *name_domain = NULL;
+ char *name_user = NULL;
+ char *auth_user = NULL;
+ NTSTATUS result = NT_STATUS_NOT_SUPPORTED;
+ struct WINBINDD_MEMORY_CREDS *entry;
+ DATA_BLOB initial, challenge, auth;
+ uint32_t initial_blob_len, challenge_blob_len, extra_len;
+ bool ok;
+
+ /* Ensure null termination */
+ state->request->data.ccache_ntlm_auth.user[
+ sizeof(state->request->data.ccache_ntlm_auth.user)-1]='\0';
+
+ DEBUG(3, ("[%5lu]: perform NTLM auth on behalf of user %s\n", (unsigned long)state->pid,
+ state->request->data.ccache_ntlm_auth.user));
+
+ /* Parse domain and username */
+
+ auth_user = state->request->data.ccache_ntlm_auth.user;
+ ok = canonicalize_username(state,
+ &auth_user,
+ &name_namespace,
+ &name_domain,
+ &name_user);
+ if (!ok) {
+ DEBUG(5,("winbindd_ccache_ntlm_auth: cannot parse domain and user from name [%s]\n",
+ state->request->data.ccache_ntlm_auth.user));
+ return false;
+ }
+
+ fstrcpy(state->request->data.ccache_ntlm_auth.user, auth_user);
+ TALLOC_FREE(auth_user);
+
+ domain = find_auth_domain(state->request->flags, name_domain);
+
+ if (domain == NULL) {
+ DEBUG(5,("winbindd_ccache_ntlm_auth: can't get domain [%s]\n",
+ name_domain));
+ return false;
+ }
+
+ if (!check_client_uid(state, state->request->data.ccache_ntlm_auth.uid)) {
+ return false;
+ }
+
+ /* validate blob lengths */
+ initial_blob_len = state->request->data.ccache_ntlm_auth.initial_blob_len;
+ challenge_blob_len = state->request->data.ccache_ntlm_auth.challenge_blob_len;
+ extra_len = state->request->extra_len;
+
+ if (initial_blob_len > extra_len || challenge_blob_len > extra_len ||
+ initial_blob_len + challenge_blob_len > extra_len ||
+ initial_blob_len + challenge_blob_len < initial_blob_len ||
+ initial_blob_len + challenge_blob_len < challenge_blob_len) {
+
+ DEBUG(10,("winbindd_dual_ccache_ntlm_auth: blob lengths overrun "
+ "or wrap. Buffer [%d+%d > %d]\n",
+ initial_blob_len,
+ challenge_blob_len,
+ extra_len));
+ goto process_result;
+ }
+
+ TALLOC_FREE(name_namespace);
+ TALLOC_FREE(name_domain);
+ TALLOC_FREE(name_user);
+ /* Parse domain and username */
+ ok = parse_domain_user(state,
+ state->request->data.ccache_ntlm_auth.user,
+ &name_namespace,
+ &name_domain,
+ &name_user);
+ if (!ok) {
+ DEBUG(10,("winbindd_dual_ccache_ntlm_auth: cannot parse "
+ "domain and user from name [%s]\n",
+ state->request->data.ccache_ntlm_auth.user));
+ goto process_result;
+ }
+
+ entry = find_memory_creds_by_name(state->request->data.ccache_ntlm_auth.user);
+ if (entry == NULL || entry->nt_hash == NULL || entry->lm_hash == NULL) {
+ DEBUG(10,("winbindd_dual_ccache_ntlm_auth: could not find "
+ "credentials for user %s\n",
+ state->request->data.ccache_ntlm_auth.user));
+ goto process_result;
+ }
+
+ DEBUG(10,("winbindd_dual_ccache_ntlm_auth: found ccache [%s]\n", entry->username));
+
+ if (!client_can_access_ccache_entry(state->request->data.ccache_ntlm_auth.uid, entry)) {
+ goto process_result;
+ }
+
+ if (initial_blob_len == 0 && challenge_blob_len == 0) {
+ /* this is just a probe to see if credentials are available. */
+ result = NT_STATUS_OK;
+ state->response->data.ccache_ntlm_auth.auth_blob_len = 0;
+ goto process_result;
+ }
+
+ initial = data_blob_const(state->request->extra_data.data,
+ initial_blob_len);
+ challenge = data_blob_const(
+ state->request->extra_data.data + initial_blob_len,
+ state->request->data.ccache_ntlm_auth.challenge_blob_len);
+
+ result = do_ntlm_auth_with_stored_pw(
+ name_namespace,
+ name_domain,
+ name_user,
+ entry->pass,
+ initial,
+ challenge,
+ talloc_tos(),
+ &auth,
+ state->response->data.ccache_ntlm_auth.session_key,
+ &state->response->data.ccache_ntlm_auth.new_spnego);
+
+ if (!NT_STATUS_IS_OK(result)) {
+ goto process_result;
+ }
+
+ state->response->extra_data.data = talloc_memdup(
+ state->mem_ctx, auth.data, auth.length);
+ if (!state->response->extra_data.data) {
+ result = NT_STATUS_NO_MEMORY;
+ goto process_result;
+ }
+ state->response->length += auth.length;
+ state->response->data.ccache_ntlm_auth.auth_blob_len = auth.length;
+
+ data_blob_free(&auth);
+
+ process_result:
+ TALLOC_FREE(name_namespace);
+ TALLOC_FREE(name_domain);
+ TALLOC_FREE(name_user);
+ return NT_STATUS_IS_OK(result);
+}
+
+bool winbindd_ccache_save(struct winbindd_cli_state *state)
+{
+ struct winbindd_domain *domain;
+ char *name_namespace = NULL;
+ char *name_domain = NULL;
+ char *name_user = NULL;
+ char *save_user = NULL;
+ NTSTATUS status;
+ bool ok;
+
+ /* Ensure null termination */
+ state->request->data.ccache_save.user[
+ sizeof(state->request->data.ccache_save.user)-1]='\0';
+ state->request->data.ccache_save.pass[
+ sizeof(state->request->data.ccache_save.pass)-1]='\0';
+
+ DEBUG(3, ("[%5lu]: save password of user %s\n",
+ (unsigned long)state->pid,
+ state->request->data.ccache_save.user));
+
+ /* Parse domain and username */
+
+
+ save_user = state->request->data.ccache_save.user;
+ ok = canonicalize_username(state,
+ &save_user,
+ &name_namespace,
+ &name_domain,
+ &name_user);
+ if (!ok) {
+ DEBUG(5,("winbindd_ccache_save: cannot parse domain and user "
+ "from name [%s]\n",
+ state->request->data.ccache_save.user));
+ return false;
+ }
+
+ fstrcpy(state->request->data.ccache_save.user, save_user);
+
+ /*
+ * The domain is checked here only for compatibility
+ * reasons. We used to do the winbindd memory ccache for
+ * ntlm_auth in the domain child. With that code, we had to
+ * make sure that we do have a domain around to send this
+ * to. Now we do the memory cache in the parent winbindd,
+ * where it would not matter if we have a domain or not.
+ */
+
+ domain = find_auth_domain(state->request->flags, name_domain);
+ if (domain == NULL) {
+ DEBUG(5, ("winbindd_ccache_save: can't get domain [%s]\n",
+ name_domain));
+ return false;
+ }
+
+ if (!check_client_uid(state, state->request->data.ccache_save.uid)) {
+ return false;
+ }
+
+ status = winbindd_add_memory_creds(
+ state->request->data.ccache_save.user,
+ state->request->data.ccache_save.uid,
+ state->request->data.ccache_save.pass);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(1, ("winbindd_add_memory_creds failed %s\n",
+ nt_errstr(status)));
+ return false;
+ }
+ return true;
+}
diff --git a/source3/winbindd/winbindd_change_machine_acct.c b/source3/winbindd/winbindd_change_machine_acct.c
new file mode 100644
index 0000000..fe5b9bf
--- /dev/null
+++ b/source3/winbindd/winbindd_change_machine_acct.c
@@ -0,0 +1,99 @@
+/*
+ Unix SMB/CIFS implementation.
+ async implementation of WINBINDD_CHANGE_MACHINE_ACCT
+ Copyright (C) Volker Lendecke 2009
+ Copyright (C) Guenther Deschner 2009
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "winbindd.h"
+#include "librpc/gen_ndr/ndr_winbind_c.h"
+
+struct winbindd_change_machine_acct_state {
+ uint8_t dummy;
+};
+
+static void winbindd_change_machine_acct_done(struct tevent_req *subreq);
+
+struct tevent_req *winbindd_change_machine_acct_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct winbindd_cli_state *cli,
+ struct winbindd_request *request)
+{
+ struct tevent_req *req, *subreq;
+ struct winbindd_change_machine_acct_state *state;
+ struct winbindd_domain *domain;
+ const char *dcname = NULL;
+
+ req = tevent_req_create(mem_ctx, &state,
+ struct winbindd_change_machine_acct_state);
+ if (req == NULL) {
+ return NULL;
+ }
+
+ if (request->data.init_conn.dcname[0] != '\0') {
+ dcname = request->data.init_conn.dcname;
+ }
+
+ domain = find_domain_from_name(request->domain_name);
+ if (domain == NULL) {
+ tevent_req_nterror(req, NT_STATUS_NO_SUCH_DOMAIN);
+ return tevent_req_post(req, ev);
+ }
+ if (domain->internal) {
+ /*
+ * Internal domains are passdb based, we can always
+ * contact them.
+ *
+ * This also protects us from changing the password on
+ * the AD DC without updating all the right databases.
+ * Do not remove this until that code is fixed.
+ */
+ tevent_req_done(req);
+ return tevent_req_post(req, ev);
+ }
+
+ subreq = dcerpc_wbint_ChangeMachineAccount_send(state, ev,
+ dom_child_handle(domain),
+ dcname);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, winbindd_change_machine_acct_done, req);
+ return req;
+}
+
+static void winbindd_change_machine_acct_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct winbindd_change_machine_acct_state *state = tevent_req_data(
+ req, struct winbindd_change_machine_acct_state);
+ NTSTATUS status, result;
+
+ status = dcerpc_wbint_ChangeMachineAccount_recv(subreq, state, &result);
+ if (any_nt_status_not_ok(status, result, &status)) {
+ tevent_req_nterror(req, status);
+ return;
+ }
+ tevent_req_done(req);
+}
+
+NTSTATUS winbindd_change_machine_acct_recv(struct tevent_req *req,
+ struct winbindd_response *presp)
+{
+ return tevent_req_simple_recv_ntstatus(req);
+}
diff --git a/source3/winbindd/winbindd_check_machine_acct.c b/source3/winbindd/winbindd_check_machine_acct.c
new file mode 100644
index 0000000..c657374
--- /dev/null
+++ b/source3/winbindd/winbindd_check_machine_acct.c
@@ -0,0 +1,96 @@
+/*
+ Unix SMB/CIFS implementation.
+ async implementation of WINBINDD_CHECK_MACHINE_ACCT
+ Copyright (C) Volker Lendecke 2009
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "winbindd.h"
+#include "librpc/gen_ndr/ndr_winbind_c.h"
+
+struct winbindd_check_machine_acct_state {
+ uint8_t dummy;
+};
+
+static void winbindd_check_machine_acct_done(struct tevent_req *subreq);
+
+struct tevent_req *winbindd_check_machine_acct_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct winbindd_cli_state *cli,
+ struct winbindd_request *request)
+{
+ struct tevent_req *req, *subreq;
+ struct winbindd_check_machine_acct_state *state;
+ struct winbindd_domain *domain;
+
+ req = tevent_req_create(mem_ctx, &state,
+ struct winbindd_check_machine_acct_state);
+ if (req == NULL) {
+ return NULL;
+ }
+
+ if (request->domain_name[0] == '\0') {
+ /* preserve old behavior, when no domain name is given */
+ domain = find_our_domain();
+ } else {
+ domain = find_domain_from_name(request->domain_name);
+ }
+ if (domain == NULL) {
+ tevent_req_nterror(req, NT_STATUS_NO_SUCH_DOMAIN);
+ return tevent_req_post(req, ev);
+ }
+ if (domain->internal) {
+ /*
+ * Internal domains are passdb based, we can always
+ * contact them.
+ */
+ tevent_req_done(req);
+ return tevent_req_post(req, ev);
+ }
+
+ subreq = dcerpc_wbint_CheckMachineAccount_send(state, ev,
+ dom_child_handle(domain));
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, winbindd_check_machine_acct_done, req);
+ return req;
+}
+
+static void winbindd_check_machine_acct_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct winbindd_check_machine_acct_state *state = tevent_req_data(
+ req, struct winbindd_check_machine_acct_state);
+ NTSTATUS status, result;
+
+ status = dcerpc_wbint_CheckMachineAccount_recv(subreq, state, &result);
+ if (any_nt_status_not_ok(status, result, &status)) {
+ tevent_req_nterror(req, status);
+ return;
+ }
+ tevent_req_done(req);
+}
+
+NTSTATUS winbindd_check_machine_acct_recv(struct tevent_req *req,
+ struct winbindd_response *presp)
+{
+ NTSTATUS status = tevent_req_simple_recv_ntstatus(req);
+
+ set_auth_errors(presp, status);
+ return status;
+}
diff --git a/source3/winbindd/winbindd_cm.c b/source3/winbindd/winbindd_cm.c
new file mode 100644
index 0000000..1685edb
--- /dev/null
+++ b/source3/winbindd/winbindd_cm.c
@@ -0,0 +1,3434 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Winbind daemon connection manager
+
+ Copyright (C) Tim Potter 2001
+ Copyright (C) Andrew Bartlett 2002
+ Copyright (C) Gerald (Jerry) Carter 2003-2005.
+ Copyright (C) Volker Lendecke 2004-2005
+ Copyright (C) Jeremy Allison 2006
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+/*
+ We need to manage connections to domain controllers without having to
+ mess up the main winbindd code with other issues. The aim of the
+ connection manager is to:
+
+ - make connections to domain controllers and cache them
+ - re-establish connections when networks or servers go down
+ - centralise the policy on connection timeouts, domain controller
+ selection etc
+ - manage re-entrancy for when winbindd becomes able to handle
+ multiple outstanding rpc requests
+
+ Why not have connection management as part of the rpc layer like tng?
+ Good question. This code may morph into libsmb/rpc_cache.c or something
+ like that but at the moment it's simply staying as part of winbind. I
+ think the TNG architecture of forcing every user of the rpc layer to use
+ the connection caching system is a bad idea. It should be an optional
+ method of using the routines.
+
+ The TNG design is quite good but I disagree with some aspects of the
+ implementation. -tpot
+
+ */
+
+/*
+ TODO:
+
+ - I'm pretty annoyed by all the make_nmb_name() stuff. It should be
+ moved down into another function.
+
+ - Take care when destroying cli_structs as they can be shared between
+ various sam handles.
+
+ */
+
+#include "includes.h"
+#include "winbindd.h"
+#include "libsmb/namequery.h"
+#include "../libcli/auth/libcli_auth.h"
+#include "../librpc/gen_ndr/ndr_netlogon_c.h"
+#include "rpc_client/cli_pipe.h"
+#include "rpc_client/cli_netlogon.h"
+#include "../librpc/gen_ndr/ndr_samr_c.h"
+#include "../librpc/gen_ndr/ndr_lsa_c.h"
+#include "rpc_client/cli_lsarpc.h"
+#include "../librpc/gen_ndr/ndr_dssetup_c.h"
+#include "libads/sitename_cache.h"
+#include "libsmb/libsmb.h"
+#include "libsmb/clidgram.h"
+#include "ads.h"
+#include "secrets.h"
+#include "../libcli/security/security.h"
+#include "passdb.h"
+#include "messages.h"
+#include "auth/gensec/gensec.h"
+#include "../libcli/smb/smbXcli_base.h"
+#include "libcli/auth/netlogon_creds_cli.h"
+#include "auth.h"
+#include "rpc_server/rpc_ncacn_np.h"
+#include "auth/credentials/credentials.h"
+#include "lib/param/param.h"
+#include "lib/gencache.h"
+#include "lib/util/string_wrappers.h"
+#include "lib/global_contexts.h"
+#include "librpc/gen_ndr/ndr_winbind_c.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_WINBIND
+
+struct dc_name_ip {
+ fstring name;
+ struct sockaddr_storage ss;
+};
+
+extern struct winbindd_methods reconnect_methods;
+
+static NTSTATUS init_dc_connection_network(struct winbindd_domain *domain, bool need_rw_dc);
+static void set_dc_type_and_flags( struct winbindd_domain *domain );
+static bool set_dc_type_and_flags_trustinfo( struct winbindd_domain *domain );
+static bool get_dcs(TALLOC_CTX *mem_ctx, struct winbindd_domain *domain,
+ struct dc_name_ip **dcs, int *num_dcs,
+ uint32_t request_flags);
+
+void winbind_msg_domain_offline(struct messaging_context *msg_ctx,
+ void *private_data,
+ uint32_t msg_type,
+ struct server_id server_id,
+ DATA_BLOB *data)
+{
+ const char *domain_name = (const char *)data->data;
+ struct winbindd_domain *domain;
+
+ domain = find_domain_from_name_noinit(domain_name);
+ if (domain == NULL) {
+ DBG_DEBUG("Domain %s not found!\n", domain_name);
+ return;
+ }
+
+ DBG_DEBUG("Domain %s was %s, change to offline now.\n",
+ domain_name,
+ domain->online ? "online" : "offline");
+
+ domain->online = false;
+}
+
+void winbind_msg_domain_online(struct messaging_context *msg_ctx,
+ void *private_data,
+ uint32_t msg_type,
+ struct server_id server_id,
+ DATA_BLOB *data)
+{
+ const char *domain_name = (const char *)data->data;
+ struct winbindd_domain *domain;
+
+ domain = find_domain_from_name_noinit(domain_name);
+ if (domain == NULL) {
+ return;
+ }
+
+ SMB_ASSERT(wb_child_domain() == NULL);
+
+ DBG_DEBUG("Domain %s was %s, marking as online now!\n",
+ domain_name,
+ domain->online ? "online" : "offline");
+
+ domain->online = true;
+}
+
+/****************************************************************
+ Set domain offline and also add handler to put us back online
+ if we detect a DC.
+****************************************************************/
+
+void set_domain_offline(struct winbindd_domain *domain)
+{
+ pid_t parent_pid = getppid();
+
+ DEBUG(10,("set_domain_offline: called for domain %s\n",
+ domain->name ));
+
+ if (domain->internal) {
+ DEBUG(3,("set_domain_offline: domain %s is internal - logic error.\n",
+ domain->name ));
+ return;
+ }
+
+ domain->online = False;
+
+ /* Offline domains are always initialized. They're
+ re-initialized when they go back online. */
+
+ domain->initialized = True;
+
+ /* Send a message to the parent that the domain is offline. */
+ if (parent_pid > 1 && !domain->internal) {
+ messaging_send_buf(global_messaging_context(),
+ pid_to_procid(parent_pid),
+ MSG_WINBIND_DOMAIN_OFFLINE,
+ (uint8_t *)domain->name,
+ strlen(domain->name) + 1);
+ }
+
+ /* Send an offline message to the idmap child when our
+ primary domain goes offline */
+ if ( domain->primary ) {
+ pid_t idmap_pid = idmap_child_pid();
+
+ if (idmap_pid != 0) {
+ messaging_send_buf(global_messaging_context(),
+ pid_to_procid(idmap_pid),
+ MSG_WINBIND_OFFLINE,
+ (const uint8_t *)domain->name,
+ strlen(domain->name)+1);
+ }
+ }
+
+ return;
+}
+
+/****************************************************************
+ Set domain online - if allowed.
+****************************************************************/
+
+static void set_domain_online(struct winbindd_domain *domain)
+{
+ pid_t parent_pid = getppid();
+
+ DEBUG(10,("set_domain_online: called for domain %s\n",
+ domain->name ));
+
+ if (domain->internal) {
+ DEBUG(3,("set_domain_online: domain %s is internal - logic error.\n",
+ domain->name ));
+ return;
+ }
+
+ if (get_global_winbindd_state_offline()) {
+ DEBUG(10,("set_domain_online: domain %s remaining globally offline\n",
+ domain->name ));
+ return;
+ }
+
+ winbindd_set_locator_kdc_envs(domain);
+
+ /* If we are waiting to get a krb5 ticket, trigger immediately. */
+ ccache_regain_all_now();
+
+ /* Ok, we're out of any startup mode now... */
+ domain->startup = False;
+
+ if (domain->online == False) {
+ /* We were offline - now we're online. We default to
+ using the MS-RPC backend if we started offline,
+ and if we're going online for the first time we
+ should really re-initialize the backends and the
+ checks to see if we're talking to an AD or NT domain.
+ */
+
+ domain->initialized = False;
+
+ /* 'reconnect_methods' is the MS-RPC backend. */
+ if (domain->backend == &reconnect_methods) {
+ domain->backend = NULL;
+ }
+ }
+
+ domain->online = True;
+
+ /* Send a message to the parent that the domain is online. */
+ if (parent_pid > 1 && !domain->internal) {
+ messaging_send_buf(global_messaging_context(),
+ pid_to_procid(parent_pid),
+ MSG_WINBIND_DOMAIN_ONLINE,
+ (uint8_t *)domain->name,
+ strlen(domain->name) + 1);
+ }
+
+ /* Send an online message to the idmap child when our
+ primary domain comes online */
+
+ if ( domain->primary ) {
+ pid_t idmap_pid = idmap_child_pid();
+
+ if (idmap_pid != 0) {
+ messaging_send_buf(global_messaging_context(),
+ pid_to_procid(idmap_pid),
+ MSG_WINBIND_ONLINE,
+ (const uint8_t *)domain->name,
+ strlen(domain->name)+1);
+ }
+ }
+
+ return;
+}
+
+/****************************************************************
+ Requested to set a domain online.
+****************************************************************/
+
+void set_domain_online_request(struct winbindd_domain *domain)
+{
+ NTSTATUS status;
+
+ SMB_ASSERT(wb_child_domain() || idmap_child());
+
+ DEBUG(10,("set_domain_online_request: called for domain %s\n",
+ domain->name ));
+
+ if (get_global_winbindd_state_offline()) {
+ DEBUG(10,("set_domain_online_request: domain %s remaining globally offline\n",
+ domain->name ));
+ return;
+ }
+
+ if (domain->internal) {
+ DEBUG(10, ("set_domain_online_request: Internal domains are "
+ "always online\n"));
+ return;
+ }
+
+ /*
+ * This call takes care of setting the online flag to true if we
+ * connected, or tell the parent to ping us back if false. Bypasses
+ * online check so always does network calls.
+ */
+ status = init_dc_connection_network(domain, true);
+ DBG_DEBUG("init_dc_connection_network(), returned %s, called for "
+ "domain %s (online = %s)\n",
+ nt_errstr(status),
+ domain->name,
+ domain->online ? "true" : "false");
+}
+
+/****************************************************************
+ Add -ve connection cache entries for domain and realm.
+****************************************************************/
+
+static void winbind_add_failed_connection_entry(
+ const struct winbindd_domain *domain,
+ const char *server,
+ NTSTATUS result)
+{
+ add_failed_connection_entry(domain->name, server, result);
+ /* If this was the saf name for the last thing we talked to,
+ remove it. */
+ saf_delete(domain->name);
+ if (domain->alt_name != NULL) {
+ add_failed_connection_entry(domain->alt_name, server, result);
+ saf_delete(domain->alt_name);
+ }
+ winbindd_unset_locator_kdc_env(domain);
+}
+
+/* Choose between anonymous or authenticated connections. We need to use
+ an authenticated connection if DCs have the RestrictAnonymous registry
+ entry set > 0, or the "Additional restrictions for anonymous
+ connections" set in the win2k Local Security Policy.
+
+ Caller to free() result in domain, username, password
+*/
+
+static void cm_get_ipc_userpass(char **username, char **domain, char **password)
+{
+ *username = (char *)secrets_fetch(SECRETS_AUTH_USER, NULL);
+ *domain = (char *)secrets_fetch(SECRETS_AUTH_DOMAIN, NULL);
+ *password = (char *)secrets_fetch(SECRETS_AUTH_PASSWORD, NULL);
+
+ if (*username && **username) {
+
+ if (!*domain || !**domain)
+ *domain = smb_xstrdup(lp_workgroup());
+
+ if (!*password || !**password)
+ *password = smb_xstrdup("");
+
+ DEBUG(3, ("cm_get_ipc_userpass: Retrieved auth-user from secrets.tdb [%s\\%s]\n",
+ *domain, *username));
+
+ } else {
+ DEBUG(3, ("cm_get_ipc_userpass: No auth-user defined\n"));
+ *username = smb_xstrdup("");
+ *domain = smb_xstrdup("");
+ *password = smb_xstrdup("");
+ }
+}
+
+static NTSTATUS cm_get_ipc_credentials(TALLOC_CTX *mem_ctx,
+ struct cli_credentials **_creds)
+{
+
+ TALLOC_CTX *frame = talloc_stackframe();
+ NTSTATUS status = NT_STATUS_INTERNAL_ERROR;
+ struct loadparm_context *lp_ctx;
+ char *username = NULL;
+ char *netbios_domain = NULL;
+ char *password = NULL;
+ struct cli_credentials *creds = NULL;
+ bool ok;
+
+ cm_get_ipc_userpass(&username, &netbios_domain, &password);
+
+ lp_ctx = loadparm_init_s3(frame, loadparm_s3_helpers());
+ if (lp_ctx == NULL) {
+ DEBUG(1, ("loadparm_init_s3 failed\n"));
+ status = NT_STATUS_INTERNAL_ERROR;
+ goto fail;
+ }
+
+ creds = cli_credentials_init(mem_ctx);
+ if (creds == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ goto fail;
+ }
+
+ ok = cli_credentials_set_conf(creds, lp_ctx);
+ if (!ok) {
+ status = NT_STATUS_INTERNAL_ERROR;
+ goto fail;
+ }
+
+ cli_credentials_set_kerberos_state(creds,
+ CRED_USE_KERBEROS_DISABLED,
+ CRED_SPECIFIED);
+
+ ok = cli_credentials_set_domain(creds, netbios_domain, CRED_SPECIFIED);
+ if (!ok) {
+ status = NT_STATUS_NO_MEMORY;
+ goto fail;
+ }
+
+ ok = cli_credentials_set_username(creds, username, CRED_SPECIFIED);
+ if (!ok) {
+ status = NT_STATUS_NO_MEMORY;
+ goto fail;
+ }
+
+ ok = cli_credentials_set_password(creds, password, CRED_SPECIFIED);
+ if (!ok) {
+ status = NT_STATUS_NO_MEMORY;
+ goto fail;
+ }
+
+ *_creds = creds;
+ creds = NULL;
+ status = NT_STATUS_OK;
+ fail:
+ TALLOC_FREE(creds);
+ SAFE_FREE(username);
+ SAFE_FREE(netbios_domain);
+ SAFE_FREE(password);
+ TALLOC_FREE(frame);
+ return status;
+}
+
+static bool cm_is_ipc_credentials(struct cli_credentials *creds)
+{
+ TALLOC_CTX *frame = talloc_stackframe();
+ char *ipc_account = NULL;
+ char *ipc_domain = NULL;
+ char *ipc_password = NULL;
+ const char *creds_account = NULL;
+ const char *creds_domain = NULL;
+ const char *creds_password = NULL;
+ bool ret = false;
+
+ cm_get_ipc_userpass(&ipc_account, &ipc_domain, &ipc_password);
+
+ creds_account = cli_credentials_get_username(creds);
+ creds_domain = cli_credentials_get_domain(creds);
+ creds_password = cli_credentials_get_password(creds);
+
+ if (!strequal(ipc_domain, creds_domain)) {
+ goto done;
+ }
+
+ if (!strequal(ipc_account, creds_account)) {
+ goto done;
+ }
+
+ if (!strcsequal(ipc_password, creds_password)) {
+ goto done;
+ }
+
+ ret = true;
+ done:
+ SAFE_FREE(ipc_account);
+ SAFE_FREE(ipc_domain);
+ SAFE_FREE(ipc_password);
+ TALLOC_FREE(frame);
+ return ret;
+}
+
+static bool get_dc_name_via_netlogon(struct winbindd_domain *domain,
+ fstring dcname,
+ struct sockaddr_storage *dc_ss,
+ uint32_t request_flags)
+{
+ struct winbindd_domain *our_domain = NULL;
+ struct rpc_pipe_client *netlogon_pipe = NULL;
+ NTSTATUS result;
+ WERROR werr;
+ TALLOC_CTX *mem_ctx;
+ unsigned int orig_timeout;
+ const char *tmp = NULL;
+ const char *p;
+ struct dcerpc_binding_handle *b;
+
+ /* Hmmmm. We can only open one connection to the NETLOGON pipe at the
+ * moment.... */
+
+ if (IS_DC) {
+ return False;
+ }
+
+ if (domain->primary) {
+ return False;
+ }
+
+ our_domain = find_our_domain();
+
+ if ((mem_ctx = talloc_init("get_dc_name_via_netlogon")) == NULL) {
+ return False;
+ }
+
+ result = cm_connect_netlogon(our_domain, &netlogon_pipe);
+ if (!NT_STATUS_IS_OK(result)) {
+ talloc_destroy(mem_ctx);
+ return False;
+ }
+
+ b = netlogon_pipe->binding_handle;
+
+ /* This call can take a long time - allow the server to time out.
+ 35 seconds should do it. */
+
+ orig_timeout = rpccli_set_timeout(netlogon_pipe, 35000);
+
+ if (our_domain->active_directory) {
+ struct netr_DsRGetDCNameInfo *domain_info = NULL;
+
+ /*
+ * TODO request flags are not respected in the server
+ * (and in some cases, like REQUIRE_PDC, causes an error)
+ */
+ result = dcerpc_netr_DsRGetDCName(b,
+ mem_ctx,
+ our_domain->dcname,
+ domain->name,
+ NULL,
+ NULL,
+ request_flags|DS_RETURN_DNS_NAME,
+ &domain_info,
+ &werr);
+ if (NT_STATUS_IS_OK(result) && W_ERROR_IS_OK(werr)) {
+ tmp = talloc_strdup(
+ mem_ctx, domain_info->dc_unc);
+ if (tmp == NULL) {
+ DEBUG(0, ("talloc_strdup failed\n"));
+ talloc_destroy(mem_ctx);
+ return false;
+ }
+ if (domain->alt_name == NULL) {
+ domain->alt_name = talloc_strdup(domain,
+ domain_info->domain_name);
+ if (domain->alt_name == NULL) {
+ DEBUG(0, ("talloc_strdup failed\n"));
+ talloc_destroy(mem_ctx);
+ return false;
+ }
+ }
+ if (domain->forest_name == NULL) {
+ domain->forest_name = talloc_strdup(domain,
+ domain_info->forest_name);
+ if (domain->forest_name == NULL) {
+ DEBUG(0, ("talloc_strdup failed\n"));
+ talloc_destroy(mem_ctx);
+ return false;
+ }
+ }
+ }
+ } else {
+ result = dcerpc_netr_GetAnyDCName(b, mem_ctx,
+ our_domain->dcname,
+ domain->name,
+ &tmp,
+ &werr);
+ }
+
+ /* And restore our original timeout. */
+ rpccli_set_timeout(netlogon_pipe, orig_timeout);
+
+ if (!NT_STATUS_IS_OK(result)) {
+ DEBUG(10,("dcerpc_netr_GetAnyDCName failed: %s\n",
+ nt_errstr(result)));
+ talloc_destroy(mem_ctx);
+ return false;
+ }
+
+ if (!W_ERROR_IS_OK(werr)) {
+ DEBUG(10,("dcerpc_netr_GetAnyDCName failed: %s\n",
+ win_errstr(werr)));
+ talloc_destroy(mem_ctx);
+ return false;
+ }
+
+ /* dcerpc_netr_GetAnyDCName gives us a name with \\ */
+ p = strip_hostname(tmp);
+
+ fstrcpy(dcname, p);
+
+ talloc_destroy(mem_ctx);
+
+ DEBUG(10,("dcerpc_netr_GetAnyDCName returned %s\n", dcname));
+
+ if (!resolve_name(dcname, dc_ss, 0x20, true)) {
+ return False;
+ }
+
+ return True;
+}
+
+/**
+ * Helper function to assemble trust password and account name
+ */
+static NTSTATUS get_trust_credentials(struct winbindd_domain *domain,
+ TALLOC_CTX *mem_ctx,
+ bool netlogon,
+ struct cli_credentials **_creds)
+{
+ const struct winbindd_domain *creds_domain = NULL;
+ struct cli_credentials *creds;
+ NTSTATUS status;
+ bool force_machine_account = false;
+
+ /* If we are a DC and this is not our own domain */
+
+ if (!domain->active_directory) {
+ if (!netlogon) {
+ /*
+ * For non active directory domains
+ * we can only use NTLMSSP for SMB.
+ *
+ * But the trust account is not allowed
+ * to use SMB with NTLMSSP.
+ */
+ force_machine_account = true;
+ }
+ }
+
+ if (IS_DC && !force_machine_account) {
+ creds_domain = domain;
+ } else {
+ creds_domain = find_our_domain();
+ if (creds_domain == NULL) {
+ return NT_STATUS_INVALID_SERVER_STATE;
+ }
+ }
+
+ status = pdb_get_trust_credentials(creds_domain->name,
+ creds_domain->alt_name,
+ mem_ctx,
+ &creds);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto ipc_fallback;
+ }
+
+ if (creds_domain != domain) {
+ /*
+ * We can only use schannel against a direct trust
+ */
+ cli_credentials_set_secure_channel_type(creds,
+ SEC_CHAN_NULL);
+ }
+
+ *_creds = creds;
+ return NT_STATUS_OK;
+
+ ipc_fallback:
+ if (netlogon) {
+ return NT_STATUS_CANT_ACCESS_DOMAIN_INFO;
+ }
+
+ status = cm_get_ipc_credentials(mem_ctx, &creds);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ *_creds = creds;
+ return NT_STATUS_OK;
+}
+
+/************************************************************************
+ Given a fd with a just-connected TCP connection to a DC, open a connection
+ to the pipe.
+************************************************************************/
+
+static NTSTATUS cm_prepare_connection(struct winbindd_domain *domain,
+ const int sockfd,
+ const char *controller,
+ struct cli_state **cli,
+ bool *retry)
+{
+ bool try_ipc_auth = false;
+ const char *machine_principal = NULL;
+ const char *machine_realm = NULL;
+ const char *machine_account = NULL;
+ const char *machine_domain = NULL;
+ int flags = 0;
+ struct cli_credentials *creds = NULL;
+
+ struct named_mutex *mutex;
+
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+ NTSTATUS tmp_status;
+ NTSTATUS tcon_status = NT_STATUS_NETWORK_NAME_DELETED;
+
+ enum smb_signing_setting smb_sign_client_connections = lp_client_ipc_signing();
+
+ if (IS_DC) {
+ if (domain->secure_channel_type == SEC_CHAN_NULL) {
+ /*
+ * Make sure we don't even try to
+ * connect to a foreign domain
+ * without a direct outbound trust.
+ */
+ close(sockfd);
+ return NT_STATUS_NO_TRUST_LSA_SECRET;
+ }
+
+ /*
+ * As AD DC we only use netlogon and lsa
+ * using schannel over an anonymous transport
+ * (ncacn_ip_tcp or ncacn_np).
+ *
+ * Currently we always establish the SMB connection,
+ * even if we don't use it, because we later use ncacn_ip_tcp.
+ *
+ * As we won't use the SMB connection there's no
+ * need to try kerberos. And NT4 domains expect
+ * an anonymous IPC$ connection anyway.
+ */
+ smb_sign_client_connections = SMB_SIGNING_OFF;
+ }
+
+ if (smb_sign_client_connections == SMB_SIGNING_DEFAULT) {
+ /*
+ * If we are connecting to our own AD domain, require
+ * smb signing to disrupt MITM attacks
+ */
+ if (domain->primary && lp_security() == SEC_ADS) {
+ smb_sign_client_connections = SMB_SIGNING_REQUIRED;
+ /*
+ * If we are in or are an AD domain and connecting to another
+ * AD domain in our forest
+ * then require smb signing to disrupt MITM attacks
+ */
+ } else if ((lp_security() == SEC_ADS)
+ && domain->active_directory
+ && (domain->domain_trust_attribs
+ & LSA_TRUST_ATTRIBUTE_WITHIN_FOREST)) {
+ smb_sign_client_connections = SMB_SIGNING_REQUIRED;
+ }
+ }
+
+ DEBUG(10,("cm_prepare_connection: connecting to DC %s for domain %s\n",
+ controller, domain->name ));
+
+ *retry = True;
+
+ mutex = grab_named_mutex(talloc_tos(), controller,
+ WINBIND_SERVER_MUTEX_WAIT_TIME);
+ if (mutex == NULL) {
+ close(sockfd);
+ DEBUG(0,("cm_prepare_connection: mutex grab failed for %s\n",
+ controller));
+ result = NT_STATUS_POSSIBLE_DEADLOCK;
+ goto done;
+ }
+
+ /*
+ * cm_prepare_connection() is responsible that sockfd does not leak.
+ * Once cli_state_create() returns with success, the
+ * smbXcli_conn_destructor() makes sure that close(sockfd) is finally
+ * called. Till that, close(sockfd) must be called on every unsuccessful
+ * return.
+ */
+ *cli = cli_state_create(NULL, sockfd, controller,
+ smb_sign_client_connections, flags);
+ if (*cli == NULL) {
+ close(sockfd);
+ DEBUG(1, ("Could not cli_initialize\n"));
+ result = NT_STATUS_NO_MEMORY;
+ goto done;
+ }
+
+ cli_set_timeout(*cli, 10000); /* 10 seconds */
+
+ set_socket_options(sockfd, lp_socket_options());
+
+ result = smbXcli_negprot((*cli)->conn,
+ (*cli)->timeout,
+ lp_client_ipc_min_protocol(),
+ lp_client_ipc_max_protocol(),
+ NULL,
+ NULL,
+ NULL);
+
+ if (!NT_STATUS_IS_OK(result)) {
+ DEBUG(1, ("cli_negprot failed: %s\n", nt_errstr(result)));
+ goto done;
+ }
+
+ if (smbXcli_conn_protocol((*cli)->conn) >= PROTOCOL_NT1 &&
+ smb1cli_conn_capabilities((*cli)->conn) & CAP_EXTENDED_SECURITY) {
+ try_ipc_auth = true;
+ } else if (smbXcli_conn_protocol((*cli)->conn) >= PROTOCOL_SMB2_02) {
+ try_ipc_auth = true;
+ } else if (smb_sign_client_connections == SMB_SIGNING_REQUIRED) {
+ /*
+ * If we are forcing on SMB signing, then we must
+ * require authentication unless this is a one-way
+ * trust, and we have no stored user/password
+ */
+ try_ipc_auth = true;
+ }
+
+ if (IS_DC) {
+ /*
+ * As AD DC we only use netlogon and lsa
+ * using schannel over an anonymous transport
+ * (ncacn_ip_tcp or ncacn_np).
+ *
+ * Currently we always establish the SMB connection,
+ * even if we don't use it, because we later use ncacn_ip_tcp.
+ *
+ * As we won't use the SMB connection there's no
+ * need to try kerberos. And NT4 domains expect
+ * an anonymous IPC$ connection anyway.
+ */
+ try_ipc_auth = false;
+ }
+
+ if (try_ipc_auth) {
+ result = get_trust_credentials(domain, talloc_tos(), false, &creds);
+ if (!NT_STATUS_IS_OK(result)) {
+ DEBUG(1, ("get_trust_credentials(%s) failed: %s\n",
+ domain->name, nt_errstr(result)));
+ goto done;
+ }
+ } else {
+ /*
+ * Without SPNEGO or NTLMSSP (perhaps via SMB2) we
+ * would try and authentication with our machine
+ * account password and fail. This is very rare in
+ * the modern world however
+ */
+ creds = cli_credentials_init_anon(talloc_tos());
+ if (creds == NULL) {
+ result = NT_STATUS_NO_MEMORY;
+ DEBUG(1, ("cli_credentials_init_anon(%s) failed: %s\n",
+ domain->name, nt_errstr(result)));
+ goto done;
+ }
+ }
+
+ machine_principal = cli_credentials_get_principal(creds,
+ talloc_tos());
+ machine_realm = cli_credentials_get_realm(creds);
+ machine_account = cli_credentials_get_username(creds);
+ machine_domain = cli_credentials_get_domain(creds);
+
+ DEBUG(5, ("connecting to %s (%s, %s) with account [%s\\%s] principal "
+ "[%s] and realm [%s]\n",
+ controller, domain->name, domain->alt_name,
+ machine_domain, machine_account,
+ machine_principal, machine_realm));
+
+ if (cli_credentials_is_anonymous(creds)) {
+ goto anon_fallback;
+ }
+
+ winbindd_set_locator_kdc_envs(domain);
+
+ result = cli_session_setup_creds(*cli, creds);
+ if (NT_STATUS_IS_OK(result)) {
+ goto session_setup_done;
+ }
+
+ DEBUG(1, ("authenticated session setup to %s using %s failed with %s\n",
+ controller,
+ cli_credentials_get_unparsed_name(creds, talloc_tos()),
+ nt_errstr(result)));
+
+ /*
+ * If we are not going to validate the connection
+ * with SMB signing, then allow us to fall back to
+ * anonymous
+ */
+ if (NT_STATUS_EQUAL(result, NT_STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT)
+ || NT_STATUS_EQUAL(result, NT_STATUS_TRUSTED_DOMAIN_FAILURE)
+ || NT_STATUS_EQUAL(result, NT_STATUS_INVALID_ACCOUNT_NAME)
+ || NT_STATUS_EQUAL(result, NT_STATUS_INVALID_COMPUTER_NAME)
+ || NT_STATUS_EQUAL(result, NT_STATUS_NO_SUCH_DOMAIN)
+ || NT_STATUS_EQUAL(result, NT_STATUS_NO_LOGON_SERVERS)
+ || NT_STATUS_EQUAL(result, NT_STATUS_LOGON_FAILURE))
+ {
+ if (!cm_is_ipc_credentials(creds)) {
+ goto ipc_fallback;
+ }
+
+ if (smb_sign_client_connections == SMB_SIGNING_REQUIRED) {
+ goto done;
+ }
+
+ goto anon_fallback;
+ }
+
+ goto done;
+
+ ipc_fallback:
+ TALLOC_FREE(creds);
+ tmp_status = cm_get_ipc_credentials(talloc_tos(), &creds);
+ if (!NT_STATUS_IS_OK(tmp_status)) {
+ result = tmp_status;
+ goto done;
+ }
+
+ if (cli_credentials_is_anonymous(creds)) {
+ goto anon_fallback;
+ }
+
+ machine_account = cli_credentials_get_username(creds);
+ machine_domain = cli_credentials_get_domain(creds);
+
+ DEBUG(5, ("connecting to %s from %s using NTLMSSP with username "
+ "[%s]\\[%s]\n", controller, lp_netbios_name(),
+ machine_domain, machine_account));
+
+ result = cli_session_setup_creds(*cli, creds);
+ if (NT_STATUS_IS_OK(result)) {
+ goto session_setup_done;
+ }
+
+ DEBUG(1, ("authenticated session setup to %s using %s failed with %s\n",
+ controller,
+ cli_credentials_get_unparsed_name(creds, talloc_tos()),
+ nt_errstr(result)));
+
+ /*
+ * If we are not going to validate the connection
+ * with SMB signing, then allow us to fall back to
+ * anonymous
+ */
+ if (NT_STATUS_EQUAL(result, NT_STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT)
+ || NT_STATUS_EQUAL(result, NT_STATUS_TRUSTED_DOMAIN_FAILURE)
+ || NT_STATUS_EQUAL(result, NT_STATUS_INVALID_ACCOUNT_NAME)
+ || NT_STATUS_EQUAL(result, NT_STATUS_INVALID_COMPUTER_NAME)
+ || NT_STATUS_EQUAL(result, NT_STATUS_NO_SUCH_DOMAIN)
+ || NT_STATUS_EQUAL(result, NT_STATUS_NO_LOGON_SERVERS)
+ || NT_STATUS_EQUAL(result, NT_STATUS_LOGON_FAILURE))
+ {
+ goto anon_fallback;
+ }
+
+ goto done;
+
+ anon_fallback:
+ TALLOC_FREE(creds);
+
+ if (smb_sign_client_connections == SMB_SIGNING_REQUIRED) {
+ goto done;
+ }
+
+ /* Fall back to anonymous connection, this might fail later */
+ DEBUG(5,("cm_prepare_connection: falling back to anonymous "
+ "connection for DC %s\n",
+ controller ));
+
+ result = cli_session_setup_anon(*cli);
+ if (NT_STATUS_IS_OK(result)) {
+ DEBUG(5, ("Connected anonymously\n"));
+ goto session_setup_done;
+ }
+
+ DEBUG(1, ("anonymous session setup to %s failed with %s\n",
+ controller, nt_errstr(result)));
+
+ /* We can't session setup */
+ goto done;
+
+ session_setup_done:
+ TALLOC_FREE(creds);
+
+ /*
+ * This should be a short term hack until
+ * dynamic re-authentication is implemented.
+ *
+ * See Bug 9175 - winbindd doesn't recover from
+ * NT_STATUS_NETWORK_SESSION_EXPIRED
+ */
+ if (smbXcli_conn_protocol((*cli)->conn) >= PROTOCOL_SMB2_02) {
+ smbXcli_session_set_disconnect_expired((*cli)->smb2.session);
+ }
+
+ result = cli_tree_connect(*cli, "IPC$", "IPC", NULL);
+ if (!NT_STATUS_IS_OK(result)) {
+ DEBUG(1,("failed tcon_X with %s\n", nt_errstr(result)));
+ goto done;
+ }
+ tcon_status = result;
+
+ /* cache the server name for later connections */
+
+ saf_store(domain->name, controller);
+ if (domain->alt_name) {
+ saf_store(domain->alt_name, controller);
+ }
+
+ winbindd_set_locator_kdc_envs(domain);
+
+ TALLOC_FREE(mutex);
+ *retry = False;
+
+ result = NT_STATUS_OK;
+
+ done:
+ TALLOC_FREE(mutex);
+ TALLOC_FREE(creds);
+
+ if (NT_STATUS_IS_OK(result)) {
+ result = tcon_status;
+ }
+
+ if (!NT_STATUS_IS_OK(result)) {
+ DEBUG(1, ("Failed to prepare SMB connection to %s: %s\n",
+ controller, nt_errstr(result)));
+ winbind_add_failed_connection_entry(domain, controller, result);
+ if ((*cli) != NULL) {
+ cli_shutdown(*cli);
+ *cli = NULL;
+ }
+ }
+
+ return result;
+}
+
+/*******************************************************************
+ Add a dcname and sockaddr_storage pair to the end of a dc_name_ip
+ array.
+
+ Keeps the list unique by not adding duplicate entries.
+
+ @param[in] mem_ctx talloc memory context to allocate from
+ @param[in] domain_name domain of the DC
+ @param[in] dcname name of the DC to add to the list
+ @param[in] pss Internet address and port pair to add to the list
+ @param[in,out] dcs array of dc_name_ip structures to add to
+ @param[in,out] num_dcs number of dcs returned in the dcs array
+ @return true if the list was added to, false otherwise
+*******************************************************************/
+
+static bool add_one_dc_unique(TALLOC_CTX *mem_ctx, const char *domain_name,
+ const char *dcname, struct sockaddr_storage *pss,
+ struct dc_name_ip **dcs, int *num)
+{
+ int i = 0;
+
+ if (!NT_STATUS_IS_OK(check_negative_conn_cache(domain_name, dcname))) {
+ DEBUG(10, ("DC %s was in the negative conn cache\n", dcname));
+ return False;
+ }
+
+ /* Make sure there's no duplicates in the list */
+ for (i=0; i<*num; i++)
+ if (sockaddr_equal(
+ (struct sockaddr *)(void *)&(*dcs)[i].ss,
+ (struct sockaddr *)(void *)pss))
+ return False;
+
+ *dcs = talloc_realloc(mem_ctx, *dcs, struct dc_name_ip, (*num)+1);
+
+ if (*dcs == NULL)
+ return False;
+
+ fstrcpy((*dcs)[*num].name, dcname);
+ (*dcs)[*num].ss = *pss;
+ *num += 1;
+ return True;
+}
+
+static bool add_sockaddr_to_array(TALLOC_CTX *mem_ctx,
+ struct sockaddr_storage *pss, uint16_t port,
+ struct sockaddr_storage **addrs, int *num)
+{
+ *addrs = talloc_realloc(mem_ctx, *addrs, struct sockaddr_storage, (*num)+1);
+
+ if (*addrs == NULL) {
+ *num = 0;
+ return False;
+ }
+
+ (*addrs)[*num] = *pss;
+ set_sockaddr_port((struct sockaddr *)&(*addrs)[*num], port);
+
+ *num += 1;
+ return True;
+}
+
+#ifdef HAVE_ADS
+static bool dcip_check_name_ads(const struct winbindd_domain *domain,
+ struct samba_sockaddr *sa,
+ uint32_t request_flags,
+ TALLOC_CTX *mem_ctx,
+ char **namep)
+{
+ TALLOC_CTX *tmp_ctx = talloc_stackframe();
+ char *name = NULL;
+ ADS_STRUCT *ads = NULL;
+ ADS_STATUS ads_status;
+ char addr[INET6_ADDRSTRLEN];
+
+ print_sockaddr(addr, sizeof(addr), &sa->u.ss);
+ D_DEBUG("Trying to figure out the DC name for domain '%s' at IP '%s'.\n",
+ domain->name,
+ addr);
+
+ ads = ads_init(tmp_ctx,
+ domain->alt_name,
+ domain->name,
+ addr,
+ ADS_SASL_PLAIN);
+ if (ads == NULL) {
+ ads_status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
+ goto out;
+ }
+ ads->auth.flags |= ADS_AUTH_NO_BIND;
+ ads->config.flags |= request_flags;
+ ads->server.no_fallback = true;
+
+ ads_status = ads_connect(ads);
+ if (!ADS_ERR_OK(ads_status)) {
+ goto out;
+ }
+
+ /* We got a cldap packet. */
+ name = talloc_strdup(tmp_ctx, ads->config.ldap_server_name);
+ if (name == NULL) {
+ ads_status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
+ goto out;
+ }
+ namecache_store(name, 0x20, 1, sa);
+
+ DBG_DEBUG("CLDAP flags = 0x%"PRIx32"\n", ads->config.flags);
+
+ if (domain->primary && (ads->config.flags & NBT_SERVER_KDC)) {
+ if (ads_closest_dc(ads)) {
+ char *sitename = sitename_fetch(tmp_ctx,
+ ads->config.realm);
+
+ /* We're going to use this KDC for this realm/domain.
+ If we are using sites, then force the krb5 libs
+ to use this KDC. */
+
+ create_local_private_krb5_conf_for_domain(domain->alt_name,
+ domain->name,
+ sitename,
+ &sa->u.ss);
+
+ TALLOC_FREE(sitename);
+ } else {
+ /* use an off site KDC */
+ create_local_private_krb5_conf_for_domain(domain->alt_name,
+ domain->name,
+ NULL,
+ &sa->u.ss);
+ }
+ winbindd_set_locator_kdc_envs(domain);
+
+ /* Ensure we contact this DC also. */
+ saf_store(domain->name, name);
+ saf_store(domain->alt_name, name);
+ }
+
+ D_DEBUG("DC name for domain '%s' at IP '%s' is '%s'\n",
+ domain->name,
+ addr,
+ name);
+ *namep = talloc_move(mem_ctx, &name);
+
+out:
+ TALLOC_FREE(tmp_ctx);
+
+ return ADS_ERR_OK(ads_status) ? true : false;
+}
+#endif
+
+/*******************************************************************
+ convert an ip to a name
+ For an AD Domain, it checks the requirements of the request flags.
+*******************************************************************/
+
+static bool dcip_check_name(TALLOC_CTX *mem_ctx,
+ const struct winbindd_domain *domain,
+ struct sockaddr_storage *pss,
+ char **name, uint32_t request_flags)
+{
+ struct samba_sockaddr sa = {0};
+ uint32_t nt_version = NETLOGON_NT_VERSION_1;
+ NTSTATUS status;
+ const char *dc_name;
+ fstring nbtname;
+#ifdef HAVE_ADS
+ bool is_ad_domain = false;
+#endif
+ bool ok = sockaddr_storage_to_samba_sockaddr(&sa, pss);
+ if (!ok) {
+ return false;
+ }
+
+#ifdef HAVE_ADS
+ /* For active directory servers, try to get the ldap server name.
+ None of these failures should be considered critical for now */
+
+ if ((lp_security() == SEC_ADS) && (domain->alt_name != NULL)) {
+ is_ad_domain = true;
+ } else if (lp_server_role() == ROLE_ACTIVE_DIRECTORY_DC) {
+ is_ad_domain = domain->active_directory;
+ }
+
+ if (is_ad_domain) {
+ return dcip_check_name_ads(domain,
+ &sa,
+ request_flags,
+ mem_ctx,
+ name);
+ }
+#endif
+
+ {
+ size_t len = strlen(lp_netbios_name());
+ char my_acct_name[len+2];
+
+ snprintf(my_acct_name,
+ sizeof(my_acct_name),
+ "%s$",
+ lp_netbios_name());
+
+ status = nbt_getdc(global_messaging_context(), 10, &sa.u.ss,
+ domain->name, &domain->sid,
+ my_acct_name, ACB_WSTRUST,
+ nt_version, mem_ctx, &nt_version,
+ &dc_name, NULL);
+ }
+ if (NT_STATUS_IS_OK(status)) {
+ *name = talloc_strdup(mem_ctx, dc_name);
+ if (*name == NULL) {
+ return false;
+ }
+ namecache_store(*name, 0x20, 1, &sa);
+ return True;
+ }
+
+ /* try node status request */
+
+ if (name_status_find(domain->name, 0x1c, 0x20, &sa.u.ss, nbtname) ) {
+ namecache_store(nbtname, 0x20, 1, &sa);
+
+ if (name != NULL) {
+ *name = talloc_strdup(mem_ctx, nbtname);
+ if (*name == NULL) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+ return False;
+}
+
+/*******************************************************************
+ Retrieve a list of IP addresses for domain controllers.
+
+ The array is sorted in the preferred connection order.
+
+ @param[in] mem_ctx talloc memory context to allocate from
+ @param[in] domain domain to retrieve DCs for
+ @param[out] dcs array of dcs that will be returned
+ @param[out] num_dcs number of dcs returned in the dcs array
+ @return always true
+*******************************************************************/
+
+static bool get_dcs(TALLOC_CTX *mem_ctx, struct winbindd_domain *domain,
+ struct dc_name_ip **dcs, int *num_dcs,
+ uint32_t request_flags)
+{
+ fstring dcname;
+ struct sockaddr_storage ss;
+ struct samba_sockaddr *sa_list = NULL;
+ size_t salist_size = 0;
+ size_t i;
+ bool is_our_domain;
+ enum security_types sec = (enum security_types)lp_security();
+
+ is_our_domain = strequal(domain->name, lp_workgroup());
+
+ /* If not our domain, get the preferred DC, by asking our primary DC */
+ if ( !is_our_domain
+ && get_dc_name_via_netlogon(domain, dcname, &ss, request_flags)
+ && add_one_dc_unique(mem_ctx, domain->name, dcname, &ss, dcs,
+ num_dcs) )
+ {
+ char addr[INET6_ADDRSTRLEN];
+ print_sockaddr(addr, sizeof(addr), &ss);
+ DEBUG(10, ("Retrieved DC %s at %s via netlogon\n",
+ dcname, addr));
+ return True;
+ }
+
+ if ((sec == SEC_ADS) && (domain->alt_name != NULL)) {
+ char *sitename = NULL;
+
+ /* We need to make sure we know the local site before
+ doing any DNS queries, as this will restrict the
+ get_sorted_dc_list() call below to only fetching
+ DNS records for the correct site. */
+
+ /* Find any DC to get the site record.
+ We deliberately don't care about the
+ return here. */
+
+ get_dc_name(domain->name, domain->alt_name, dcname, &ss);
+
+ sitename = sitename_fetch(mem_ctx, domain->alt_name);
+ if (sitename) {
+
+ /* Do the site-specific AD dns lookup first. */
+ (void)get_sorted_dc_list(mem_ctx,
+ domain->alt_name,
+ sitename,
+ &sa_list,
+ &salist_size,
+ true);
+
+ /* Add ips to the DC array. We don't look up the name
+ of the DC in this function, but we fill in the char*
+ of the ip now to make the failed connection cache
+ work */
+ for ( i=0; i<salist_size; i++ ) {
+ char addr[INET6_ADDRSTRLEN];
+ print_sockaddr(addr, sizeof(addr),
+ &sa_list[i].u.ss);
+ add_one_dc_unique(mem_ctx,
+ domain->name,
+ addr,
+ &sa_list[i].u.ss,
+ dcs,
+ num_dcs);
+ }
+
+ TALLOC_FREE(sa_list);
+ TALLOC_FREE(sitename);
+ salist_size = 0;
+ }
+
+ /* Now we add DCs from the main AD DNS lookup. */
+ (void)get_sorted_dc_list(mem_ctx,
+ domain->alt_name,
+ NULL,
+ &sa_list,
+ &salist_size,
+ true);
+
+ for ( i=0; i<salist_size; i++ ) {
+ char addr[INET6_ADDRSTRLEN];
+ print_sockaddr(addr, sizeof(addr),
+ &sa_list[i].u.ss);
+ add_one_dc_unique(mem_ctx,
+ domain->name,
+ addr,
+ &sa_list[i].u.ss,
+ dcs,
+ num_dcs);
+ }
+
+ TALLOC_FREE(sa_list);
+ salist_size = 0;
+ }
+
+ /* Try standard netbios queries if no ADS and fall back to DNS queries
+ * if alt_name is available */
+ if (*num_dcs == 0) {
+ (void)get_sorted_dc_list(mem_ctx,
+ domain->name,
+ NULL,
+ &sa_list,
+ &salist_size,
+ false);
+ if (salist_size == 0) {
+ if (domain->alt_name != NULL) {
+ (void)get_sorted_dc_list(mem_ctx,
+ domain->alt_name,
+ NULL,
+ &sa_list,
+ &salist_size,
+ true);
+ }
+ }
+
+ for ( i=0; i<salist_size; i++ ) {
+ char addr[INET6_ADDRSTRLEN];
+ print_sockaddr(addr, sizeof(addr),
+ &sa_list[i].u.ss);
+ add_one_dc_unique(mem_ctx,
+ domain->name,
+ addr,
+ &sa_list[i].u.ss,
+ dcs,
+ num_dcs);
+ }
+
+ TALLOC_FREE(sa_list);
+ salist_size = 0;
+ }
+
+ return True;
+}
+
+static bool connect_preferred_dc(TALLOC_CTX *mem_ctx,
+ struct winbindd_domain *domain,
+ uint32_t request_flags,
+ int *fd)
+{
+ char *saf_servername = NULL;
+ NTSTATUS status;
+ bool ok;
+
+ /*
+ * We have to check the server affinity cache here since later we select
+ * a DC based on response time and not preference.
+ */
+ if (domain->force_dc) {
+ saf_servername = domain->dcname;
+ } else {
+ saf_servername = saf_fetch(mem_ctx, domain->name);
+ }
+
+ /*
+ * Check the negative connection cache before talking to it. It going
+ * down may have triggered the reconnection.
+ */
+ if (saf_servername != NULL) {
+ status = check_negative_conn_cache(domain->name,
+ saf_servername);
+ if (!NT_STATUS_IS_OK(status)) {
+ saf_servername = NULL;
+ }
+ }
+
+ if (saf_servername != NULL) {
+ DBG_DEBUG("saf_servername is '%s' for domain %s\n",
+ saf_servername, domain->name);
+
+ /* convert an ip address to a name */
+ if (is_ipaddress(saf_servername)) {
+ ok = interpret_string_addr(&domain->dcaddr,
+ saf_servername,
+ AI_NUMERICHOST);
+ if (!ok) {
+ return false;
+ }
+ } else {
+ ok = resolve_name(saf_servername,
+ &domain->dcaddr,
+ 0x20,
+ true);
+ if (!ok) {
+ goto fail;
+ }
+ }
+
+ TALLOC_FREE(domain->dcname);
+ ok = dcip_check_name(domain,
+ domain,
+ &domain->dcaddr,
+ &domain->dcname,
+ request_flags);
+ if (!ok) {
+ goto fail;
+ }
+ }
+
+ if (domain->dcname == NULL) {
+ return false;
+ }
+
+ status = check_negative_conn_cache(domain->name, domain->dcname);
+ if (!NT_STATUS_IS_OK(status)) {
+ return false;
+ }
+
+ status = smbsock_connect(&domain->dcaddr, 0,
+ NULL, -1, NULL, -1,
+ fd, NULL, 10);
+ if (!NT_STATUS_IS_OK(status)) {
+ winbind_add_failed_connection_entry(domain,
+ domain->dcname,
+ NT_STATUS_UNSUCCESSFUL);
+ return false;
+ }
+ return true;
+
+fail:
+ winbind_add_failed_connection_entry(domain,
+ saf_servername,
+ NT_STATUS_UNSUCCESSFUL);
+ return false;
+
+}
+
+/*******************************************************************
+ Find and make a connection to a DC in the given domain.
+
+ @param[in] mem_ctx talloc memory context to allocate from
+ @param[in] domain domain to find a dc in
+ @param[out] fd fd of the open socket connected to the newly found dc
+ @return true when a DC connection is made, false otherwise
+*******************************************************************/
+
+static bool find_dc(TALLOC_CTX *mem_ctx,
+ struct winbindd_domain *domain,
+ uint32_t request_flags,
+ int *fd)
+{
+ struct dc_name_ip *dcs = NULL;
+ int num_dcs = 0;
+
+ const char **dcnames = NULL;
+ size_t num_dcnames = 0;
+
+ struct sockaddr_storage *addrs = NULL;
+ int num_addrs = 0;
+
+ int i;
+ size_t fd_index;
+
+ NTSTATUS status;
+ bool ok;
+
+ *fd = -1;
+
+ D_NOTICE("First try to connect to the closest DC (using server "
+ "affinity cache). If this fails, try to lookup the DC using "
+ "DNS afterwards.\n");
+ ok = connect_preferred_dc(mem_ctx, domain, request_flags, fd);
+ if (ok) {
+ return true;
+ }
+
+ if (domain->force_dc) {
+ return false;
+ }
+
+ again:
+ D_DEBUG("Retrieving a list of IP addresses for DCs.\n");
+ if (!get_dcs(mem_ctx, domain, &dcs, &num_dcs, request_flags) || (num_dcs == 0))
+ return False;
+
+ D_DEBUG("Retrieved IP addresses for %d DCs.\n", num_dcs);
+ for (i=0; i<num_dcs; i++) {
+
+ if (!add_string_to_array(mem_ctx, dcs[i].name,
+ &dcnames, &num_dcnames)) {
+ return False;
+ }
+ if (!add_sockaddr_to_array(mem_ctx, &dcs[i].ss, TCP_SMB_PORT,
+ &addrs, &num_addrs)) {
+ return False;
+ }
+ }
+
+ if ((num_dcnames == 0) || (num_dcnames != num_addrs))
+ return False;
+
+ if ((addrs == NULL) || (dcnames == NULL))
+ return False;
+
+ D_DEBUG("Trying to establish a connection to one of the %d DCs "
+ "(timeout of 10 sec for each DC).\n",
+ num_dcs);
+ status = smbsock_any_connect(addrs, dcnames, NULL, NULL, NULL,
+ num_addrs, 0, 10, fd, &fd_index, NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ for (i=0; i<num_dcs; i++) {
+ char ab[INET6_ADDRSTRLEN];
+ print_sockaddr(ab, sizeof(ab), &dcs[i].ss);
+ DBG_DEBUG("smbsock_any_connect failed for "
+ "domain %s address %s. Error was %s\n",
+ domain->name, ab, nt_errstr(status));
+ winbind_add_failed_connection_entry(domain,
+ dcs[i].name, NT_STATUS_UNSUCCESSFUL);
+ }
+ return False;
+ }
+ D_NOTICE("Successfully connected to DC '%s'.\n", dcs[fd_index].name);
+
+ domain->dcaddr = addrs[fd_index];
+
+ if (*dcnames[fd_index] != '\0' && !is_ipaddress(dcnames[fd_index])) {
+ /* Ok, we've got a name for the DC */
+ TALLOC_FREE(domain->dcname);
+ domain->dcname = talloc_strdup(domain, dcnames[fd_index]);
+ if (domain->dcname == NULL) {
+ return false;
+ }
+ return true;
+ }
+
+ /* Try to figure out the name */
+ TALLOC_FREE(domain->dcname);
+ ok = dcip_check_name(domain,
+ domain,
+ &domain->dcaddr,
+ &domain->dcname,
+ request_flags);
+ if (ok) {
+ return true;
+ }
+
+ /* We can not continue without the DC's name */
+ winbind_add_failed_connection_entry(domain, dcs[fd_index].name,
+ NT_STATUS_UNSUCCESSFUL);
+
+ /* Throw away all arrays as we're doing this again. */
+ TALLOC_FREE(dcs);
+ num_dcs = 0;
+
+ TALLOC_FREE(dcnames);
+ num_dcnames = 0;
+
+ TALLOC_FREE(addrs);
+ num_addrs = 0;
+
+ if (*fd != -1) {
+ close(*fd);
+ *fd = -1;
+ }
+
+ /*
+ * This should not be an infinite loop, since get_dcs() will not return
+ * the DC added to the negative connection cache in the above
+ * winbind_add_failed_connection_entry() call.
+ */
+ goto again;
+}
+
+static char *current_dc_key(TALLOC_CTX *mem_ctx, const char *domain_name)
+{
+ return talloc_asprintf_strupper_m(mem_ctx, "CURRENT_DCNAME/%s",
+ domain_name);
+}
+
+static void store_current_dc_in_gencache(const char *domain_name,
+ const char *dc_name,
+ struct cli_state *cli)
+{
+ char addr[INET6_ADDRSTRLEN];
+ char *key = NULL;
+ char *value = NULL;
+
+ if (!cli_state_is_connected(cli)) {
+ return;
+ }
+
+ print_sockaddr(addr, sizeof(addr),
+ smbXcli_conn_remote_sockaddr(cli->conn));
+
+ key = current_dc_key(talloc_tos(), domain_name);
+ if (key == NULL) {
+ goto done;
+ }
+
+ value = talloc_asprintf(talloc_tos(), "%s %s", addr, dc_name);
+ if (value == NULL) {
+ goto done;
+ }
+
+ gencache_set(key, value, 0x7fffffff);
+done:
+ TALLOC_FREE(value);
+ TALLOC_FREE(key);
+}
+
+bool fetch_current_dc_from_gencache(TALLOC_CTX *mem_ctx,
+ const char *domain_name,
+ char **p_dc_name, char **p_dc_ip)
+{
+ char *key, *p;
+ char *value = NULL;
+ bool ret = false;
+ char *dc_name = NULL;
+ char *dc_ip = NULL;
+
+ key = current_dc_key(talloc_tos(), domain_name);
+ if (key == NULL) {
+ goto done;
+ }
+ if (!gencache_get(key, mem_ctx, &value, NULL)) {
+ goto done;
+ }
+ p = strchr(value, ' ');
+ if (p == NULL) {
+ goto done;
+ }
+ dc_ip = talloc_strndup(mem_ctx, value, p - value);
+ if (dc_ip == NULL) {
+ goto done;
+ }
+ dc_name = talloc_strdup(mem_ctx, p+1);
+ if (dc_name == NULL) {
+ goto done;
+ }
+
+ if (p_dc_ip != NULL) {
+ *p_dc_ip = dc_ip;
+ dc_ip = NULL;
+ }
+ if (p_dc_name != NULL) {
+ *p_dc_name = dc_name;
+ dc_name = NULL;
+ }
+ ret = true;
+done:
+ TALLOC_FREE(dc_name);
+ TALLOC_FREE(dc_ip);
+ TALLOC_FREE(key);
+ TALLOC_FREE(value);
+ return ret;
+}
+
+NTSTATUS wb_open_internal_pipe(TALLOC_CTX *mem_ctx,
+ const struct ndr_interface_table *table,
+ struct rpc_pipe_client **ret_pipe)
+{
+ struct rpc_pipe_client *cli = NULL;
+ const struct auth_session_info *session_info = NULL;
+ NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
+
+
+ session_info = get_session_info_system();
+ SMB_ASSERT(session_info != NULL);
+
+ status = rpc_pipe_open_local_np(
+ mem_ctx, table, NULL, NULL, NULL, NULL, session_info, &cli);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ if (ret_pipe) {
+ *ret_pipe = cli;
+ }
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS cm_open_connection(struct winbindd_domain *domain,
+ struct winbindd_cm_conn *new_conn,
+ bool need_rw_dc)
+{
+ TALLOC_CTX *mem_ctx;
+ NTSTATUS result = NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND;
+ int retries;
+ uint32_t request_flags = need_rw_dc ? DS_WRITABLE_REQUIRED : 0;
+ int fd = -1;
+ bool retry = false;
+ bool seal_pipes = true;
+
+ if ((mem_ctx = talloc_init("cm_open_connection")) == NULL) {
+ set_domain_offline(domain);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ D_NOTICE("Creating connection to domain controller. This is a start of "
+ "a new connection or a DC failover. The failover only happens "
+ "if the domain has more than one DC. We will try to connect 3 "
+ "times at most.\n");
+ for (retries = 0; retries < 3; retries++) {
+ bool found_dc;
+
+ D_DEBUG("Attempt %d/3: DC '%s' of domain '%s'.\n",
+ retries,
+ domain->dcname ? domain->dcname : "",
+ domain->name);
+
+ found_dc = find_dc(mem_ctx, domain, request_flags, &fd);
+ if (!found_dc) {
+ /* This is the one place where we will
+ set the global winbindd offline state
+ to true, if a "WINBINDD_OFFLINE" entry
+ is found in the winbindd cache. */
+ set_global_winbindd_state_offline();
+ result = NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND;
+ break;
+ }
+
+ new_conn->cli = NULL;
+
+ result = cm_prepare_connection(domain, fd, domain->dcname,
+ &new_conn->cli, &retry);
+ if (NT_STATUS_IS_OK(result)) {
+ break;
+ }
+ if (!retry) {
+ break;
+ }
+ }
+
+ if (!NT_STATUS_IS_OK(result)) {
+ /* Ensure we setup the retry handler. */
+ set_domain_offline(domain);
+ goto out;
+ }
+
+ winbindd_set_locator_kdc_envs(domain);
+
+ if (domain->online == False) {
+ /* We're changing state from offline to online. */
+ set_global_winbindd_state_online();
+ }
+ set_domain_online(domain);
+
+ /*
+ * Much as I hate global state, this seems to be the point
+ * where we can be certain that we have a proper connection to
+ * a DC. wbinfo --dc-info needs that information, store it in
+ * gencache with a looong timeout. This will need revisiting
+ * once we start to connect to multiple DCs, wbcDcInfo is
+ * already prepared for that.
+ */
+ store_current_dc_in_gencache(domain->name, domain->dcname,
+ new_conn->cli);
+
+ seal_pipes = lp_winbind_sealed_pipes();
+ seal_pipes = lp_parm_bool(-1, "winbind sealed pipes",
+ domain->name,
+ seal_pipes);
+
+ if (seal_pipes) {
+ new_conn->auth_level = DCERPC_AUTH_LEVEL_PRIVACY;
+ } else {
+ new_conn->auth_level = DCERPC_AUTH_LEVEL_INTEGRITY;
+ }
+
+out:
+ talloc_destroy(mem_ctx);
+ return result;
+}
+
+/* Close down all open pipes on a connection. */
+
+void invalidate_cm_connection(struct winbindd_domain *domain)
+{
+ NTSTATUS result;
+ struct winbindd_cm_conn *conn = &domain->conn;
+
+ domain->sequence_number = DOM_SEQUENCE_NONE;
+ domain->last_seq_check = 0;
+ domain->last_status = NT_STATUS_SERVER_DISABLED;
+
+ /* We're closing down a possibly dead
+ connection. Don't have impossibly long (10s) timeouts. */
+
+ if (conn->cli) {
+ cli_set_timeout(conn->cli, 1000); /* 1 second. */
+ }
+
+ if (conn->samr_pipe != NULL) {
+ if (is_valid_policy_hnd(&conn->sam_connect_handle)) {
+ dcerpc_samr_Close(conn->samr_pipe->binding_handle,
+ talloc_tos(),
+ &conn->sam_connect_handle,
+ &result);
+ }
+ TALLOC_FREE(conn->samr_pipe);
+ /* Ok, it must be dead. Drop timeout to 0.5 sec. */
+ if (conn->cli) {
+ cli_set_timeout(conn->cli, 500);
+ }
+ }
+
+ if (conn->lsa_pipe != NULL) {
+ if (is_valid_policy_hnd(&conn->lsa_policy)) {
+ dcerpc_lsa_Close(conn->lsa_pipe->binding_handle,
+ talloc_tos(),
+ &conn->lsa_policy,
+ &result);
+ }
+ TALLOC_FREE(conn->lsa_pipe);
+ /* Ok, it must be dead. Drop timeout to 0.5 sec. */
+ if (conn->cli) {
+ cli_set_timeout(conn->cli, 500);
+ }
+ }
+
+ if (conn->lsa_pipe_tcp != NULL) {
+ if (is_valid_policy_hnd(&conn->lsa_policy)) {
+ dcerpc_lsa_Close(conn->lsa_pipe_tcp->binding_handle,
+ talloc_tos(),
+ &conn->lsa_policy,
+ &result);
+ }
+ TALLOC_FREE(conn->lsa_pipe_tcp);
+ /* Ok, it must be dead. Drop timeout to 0.5 sec. */
+ if (conn->cli) {
+ cli_set_timeout(conn->cli, 500);
+ }
+ }
+
+ if (conn->netlogon_pipe != NULL) {
+ TALLOC_FREE(conn->netlogon_pipe);
+ /* Ok, it must be dead. Drop timeout to 0.5 sec. */
+ if (conn->cli) {
+ cli_set_timeout(conn->cli, 500);
+ }
+ }
+
+ conn->auth_level = DCERPC_AUTH_LEVEL_PRIVACY;
+ TALLOC_FREE(conn->netlogon_creds_ctx);
+
+ if (conn->cli) {
+ cli_shutdown(conn->cli);
+ }
+
+ conn->cli = NULL;
+}
+
+void close_conns_after_fork(void)
+{
+ struct winbindd_domain *domain;
+ struct winbindd_cli_state *cli_state;
+
+ for (domain = domain_list(); domain; domain = domain->next) {
+ /*
+ * first close the low level SMB TCP connection
+ * so that we don't generate any SMBclose
+ * requests in invalidate_cm_connection()
+ */
+ if (cli_state_is_connected(domain->conn.cli)) {
+ smbXcli_conn_disconnect(domain->conn.cli->conn, NT_STATUS_OK);
+ }
+
+ invalidate_cm_connection(domain);
+ }
+
+ for (cli_state = winbindd_client_list();
+ cli_state != NULL;
+ cli_state = cli_state->next) {
+ if (cli_state->sock >= 0) {
+ close(cli_state->sock);
+ cli_state->sock = -1;
+ }
+ }
+}
+
+static bool connection_ok(struct winbindd_domain *domain)
+{
+ bool ok;
+
+ ok = cli_state_is_connected(domain->conn.cli);
+ if (!ok) {
+ DEBUG(3, ("connection_ok: Connection to %s for domain %s is not connected\n",
+ domain->dcname, domain->name));
+ return False;
+ }
+
+ if (!domain->online) {
+ DEBUG(3, ("connection_ok: Domain %s is offline\n", domain->name));
+ return False;
+ }
+
+ return True;
+}
+
+/* Initialize a new connection up to the RPC BIND.
+ Bypass online status check so always does network calls. */
+
+static NTSTATUS init_dc_connection_network(struct winbindd_domain *domain, bool need_rw_dc)
+{
+ NTSTATUS result;
+ bool skip_connection = domain->internal;
+ if (need_rw_dc && domain->rodc) {
+ skip_connection = false;
+ }
+
+ /* Internal connections never use the network. */
+ if (dom_sid_equal(&domain->sid, &global_sid_Builtin)) {
+ return NT_STATUS_CANT_ACCESS_DOMAIN_INFO;
+ }
+
+ /* Still ask the internal LSA and SAMR server about the local domain */
+ if (skip_connection || connection_ok(domain)) {
+ if (!domain->initialized) {
+ set_dc_type_and_flags(domain);
+ }
+ return NT_STATUS_OK;
+ }
+
+ invalidate_cm_connection(domain);
+
+ if (!domain->primary && !domain->initialized) {
+ /*
+ * Before we connect to a trust, work out if it is an
+ * AD domain by asking our own domain.
+ */
+ set_dc_type_and_flags_trustinfo(domain);
+ }
+
+ result = cm_open_connection(domain, &domain->conn, need_rw_dc);
+
+ if (NT_STATUS_IS_OK(result) && !domain->initialized) {
+ set_dc_type_and_flags(domain);
+ }
+
+ return result;
+}
+
+NTSTATUS init_dc_connection(struct winbindd_domain *domain, bool need_rw_dc)
+{
+ if (dom_sid_equal(&domain->sid, &global_sid_Builtin)) {
+ return NT_STATUS_CANT_ACCESS_DOMAIN_INFO;
+ }
+
+ SMB_ASSERT(wb_child_domain() || idmap_child());
+
+ return init_dc_connection_network(domain, need_rw_dc);
+}
+
+static NTSTATUS init_dc_connection_rpc(struct winbindd_domain *domain, bool need_rw_dc)
+{
+ NTSTATUS status;
+
+ status = init_dc_connection(domain, need_rw_dc);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ if (!domain->internal && domain->conn.cli == NULL) {
+ /* happens for trusted domains without inbound trust */
+ return NT_STATUS_TRUSTED_DOMAIN_FAILURE;
+ }
+
+ return NT_STATUS_OK;
+}
+
+/******************************************************************************
+ Set the trust flags (direction and forest location) for a domain
+******************************************************************************/
+
+static bool set_dc_type_and_flags_trustinfo( struct winbindd_domain *domain )
+{
+ struct winbindd_domain *our_domain;
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+ WERROR werr;
+ struct netr_DomainTrustList trusts;
+ int i;
+ uint32_t flags = (NETR_TRUST_FLAG_IN_FOREST |
+ NETR_TRUST_FLAG_OUTBOUND |
+ NETR_TRUST_FLAG_INBOUND);
+ struct rpc_pipe_client *cli;
+ TALLOC_CTX *mem_ctx = NULL;
+ struct dcerpc_binding_handle *b;
+
+ if (IS_DC) {
+ /*
+ * On a DC we loaded all trusts
+ * from configuration and never learn
+ * new domains.
+ */
+ return true;
+ }
+
+ DEBUG(5, ("set_dc_type_and_flags_trustinfo: domain %s\n", domain->name ));
+
+ /* Our primary domain doesn't need to worry about trust flags.
+ Force it to go through the network setup */
+ if ( domain->primary ) {
+ return False;
+ }
+
+ mem_ctx = talloc_stackframe();
+ our_domain = find_our_domain();
+ if (our_domain->internal) {
+ result = init_dc_connection(our_domain, false);
+ if (!NT_STATUS_IS_OK(result)) {
+ DEBUG(3,("set_dc_type_and_flags_trustinfo: "
+ "Not able to make a connection to our domain: %s\n",
+ nt_errstr(result)));
+ TALLOC_FREE(mem_ctx);
+ return false;
+ }
+ }
+
+ /* This won't work unless our domain is AD */
+ if ( !our_domain->active_directory ) {
+ TALLOC_FREE(mem_ctx);
+ return False;
+ }
+
+ if (our_domain->internal) {
+ result = wb_open_internal_pipe(mem_ctx, &ndr_table_netlogon, &cli);
+ } else if (!connection_ok(our_domain)) {
+ DEBUG(3,("set_dc_type_and_flags_trustinfo: "
+ "No connection to our domain!\n"));
+ TALLOC_FREE(mem_ctx);
+ return False;
+ } else {
+ result = cm_connect_netlogon(our_domain, &cli);
+ }
+
+ if (!NT_STATUS_IS_OK(result)) {
+ DEBUG(5, ("set_dc_type_and_flags_trustinfo: Could not open "
+ "a connection to %s for PIPE_NETLOGON (%s)\n",
+ domain->name, nt_errstr(result)));
+ TALLOC_FREE(mem_ctx);
+ return False;
+ }
+ b = cli->binding_handle;
+
+ /* Use DsEnumerateDomainTrusts to get us the trust direction and type. */
+ result = dcerpc_netr_DsrEnumerateDomainTrusts(b, mem_ctx,
+ cli->desthost,
+ flags,
+ &trusts,
+ &werr);
+ if (!NT_STATUS_IS_OK(result)) {
+ DEBUG(0,("set_dc_type_and_flags_trustinfo: "
+ "failed to query trusted domain list: %s\n",
+ nt_errstr(result)));
+ TALLOC_FREE(mem_ctx);
+ return false;
+ }
+ if (!W_ERROR_IS_OK(werr)) {
+ DEBUG(0,("set_dc_type_and_flags_trustinfo: "
+ "failed to query trusted domain list: %s\n",
+ win_errstr(werr)));
+ TALLOC_FREE(mem_ctx);
+ return false;
+ }
+
+ /* Now find the domain name and get the flags */
+
+ for ( i=0; i<trusts.count; i++ ) {
+ if ( strequal( domain->name, trusts.array[i].netbios_name) ) {
+ domain->domain_flags = trusts.array[i].trust_flags;
+ domain->domain_type = trusts.array[i].trust_type;
+ domain->domain_trust_attribs = trusts.array[i].trust_attributes;
+
+ if ( domain->domain_type == LSA_TRUST_TYPE_UPLEVEL )
+ domain->active_directory = True;
+
+ /* This flag is only set if the domain is *our*
+ primary domain and the primary domain is in
+ native mode */
+
+ domain->native_mode = (domain->domain_flags & NETR_TRUST_FLAG_NATIVE);
+
+ DEBUG(5, ("set_dc_type_and_flags_trustinfo: domain %s is %sin "
+ "native mode.\n", domain->name,
+ domain->native_mode ? "" : "NOT "));
+
+ DEBUG(5,("set_dc_type_and_flags_trustinfo: domain %s is %s"
+ "running active directory.\n", domain->name,
+ domain->active_directory ? "" : "NOT "));
+
+ domain->can_do_ncacn_ip_tcp = domain->active_directory;
+
+ domain->initialized = True;
+
+ break;
+ }
+ }
+
+ TALLOC_FREE(mem_ctx);
+
+ return domain->initialized;
+}
+
+/******************************************************************************
+ We can 'sense' certain things about the DC by it's replies to certain
+ questions.
+
+ This tells us if this particular remote server is Active Directory, and if it
+ is native mode.
+******************************************************************************/
+
+static void set_dc_type_and_flags_connect( struct winbindd_domain *domain )
+{
+ NTSTATUS status, result;
+ NTSTATUS close_status = NT_STATUS_UNSUCCESSFUL;
+ WERROR werr;
+ TALLOC_CTX *mem_ctx = NULL;
+ struct rpc_pipe_client *cli = NULL;
+ struct policy_handle pol = { .handle_type = 0 };
+ union dssetup_DsRoleInfo info;
+ union lsa_PolicyInformation *lsa_info = NULL;
+ union lsa_revision_info out_revision_info = {
+ .info1 = {
+ .revision = 0,
+ },
+ };
+ uint32_t out_version = 0;
+
+ if (!domain->internal && !connection_ok(domain)) {
+ return;
+ }
+
+ mem_ctx = talloc_init("set_dc_type_and_flags on domain %s\n",
+ domain->name);
+ if (!mem_ctx) {
+ DEBUG(1, ("set_dc_type_and_flags_connect: talloc_init() failed\n"));
+ return;
+ }
+
+ DEBUG(5, ("set_dc_type_and_flags_connect: domain %s\n", domain->name ));
+
+ if (domain->internal) {
+ status = wb_open_internal_pipe(mem_ctx,
+ &ndr_table_dssetup,
+ &cli);
+ } else {
+ status = cli_rpc_pipe_open_noauth(domain->conn.cli,
+ &ndr_table_dssetup,
+ &cli);
+ }
+
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(5, ("set_dc_type_and_flags_connect: Could not bind to "
+ "PI_DSSETUP on domain %s: (%s)\n",
+ domain->name, nt_errstr(status)));
+
+ /* if this is just a non-AD domain we need to continue
+ * identifying so that we can in the end return with
+ * domain->initialized = True - gd */
+
+ goto no_dssetup;
+ }
+
+ status = dcerpc_dssetup_DsRoleGetPrimaryDomainInformation(cli->binding_handle, mem_ctx,
+ DS_ROLE_BASIC_INFORMATION,
+ &info,
+ &werr);
+ TALLOC_FREE(cli);
+
+ if (NT_STATUS_IS_OK(status)) {
+ result = werror_to_ntstatus(werr);
+ }
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(5, ("set_dc_type_and_flags_connect: rpccli_ds_getprimarydominfo "
+ "on domain %s failed: (%s)\n",
+ domain->name, nt_errstr(status)));
+
+ /* older samba3 DCs will return DCERPC_FAULT_OP_RNG_ERROR for
+ * every opcode on the DSSETUP pipe, continue with
+ * no_dssetup mode here as well to get domain->initialized
+ * set - gd */
+
+ if (NT_STATUS_EQUAL(status, NT_STATUS_RPC_PROCNUM_OUT_OF_RANGE)) {
+ goto no_dssetup;
+ }
+
+ TALLOC_FREE(mem_ctx);
+ return;
+ }
+
+ if ((info.basic.flags & DS_ROLE_PRIMARY_DS_RUNNING) &&
+ !(info.basic.flags & DS_ROLE_PRIMARY_DS_MIXED_MODE)) {
+ domain->native_mode = True;
+ } else {
+ domain->native_mode = False;
+ }
+
+no_dssetup:
+ if (domain->internal) {
+ status = wb_open_internal_pipe(mem_ctx,
+ &ndr_table_lsarpc,
+ &cli);
+ } else {
+ status = cli_rpc_pipe_open_noauth(domain->conn.cli,
+ &ndr_table_lsarpc, &cli);
+ }
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(5, ("set_dc_type_and_flags_connect: Could not bind to "
+ "PI_LSARPC on domain %s: (%s)\n",
+ domain->name, nt_errstr(status)));
+ TALLOC_FREE(cli);
+ TALLOC_FREE(mem_ctx);
+ return;
+ }
+
+ status = dcerpc_lsa_open_policy_fallback(cli->binding_handle,
+ mem_ctx,
+ cli->srv_name_slash,
+ true,
+ SEC_FLAG_MAXIMUM_ALLOWED,
+ &out_version,
+ &out_revision_info,
+ &pol,
+ &result);
+
+ if (NT_STATUS_IS_OK(status) && NT_STATUS_IS_OK(result)) {
+ /* This particular query is exactly what Win2k clients use
+ to determine that the DC is active directory */
+ status = dcerpc_lsa_QueryInfoPolicy2(cli->binding_handle, mem_ctx,
+ &pol,
+ LSA_POLICY_INFO_DNS,
+ &lsa_info,
+ &result);
+ }
+
+ /*
+ * If the status and result will not be OK we will fallback to
+ * OpenPolicy.
+ */
+ if (NT_STATUS_IS_OK(status) && NT_STATUS_IS_OK(result)) {
+ domain->active_directory = True;
+
+ if (lsa_info->dns.name.string) {
+ if (!strequal(domain->name, lsa_info->dns.name.string))
+ {
+ DEBUG(1, ("set_dc_type_and_flags_connect: DC "
+ "for domain %s claimed it was a DC "
+ "for domain %s, refusing to "
+ "initialize\n",
+ domain->name,
+ lsa_info->dns.name.string));
+ TALLOC_FREE(cli);
+ TALLOC_FREE(mem_ctx);
+ return;
+ }
+ talloc_free(domain->name);
+ domain->name = talloc_strdup(domain,
+ lsa_info->dns.name.string);
+ if (domain->name == NULL) {
+ goto done;
+ }
+ }
+
+ if (lsa_info->dns.dns_domain.string) {
+ if (domain->alt_name != NULL &&
+ !strequal(domain->alt_name,
+ lsa_info->dns.dns_domain.string))
+ {
+ DEBUG(1, ("set_dc_type_and_flags_connect: DC "
+ "for domain %s (%s) claimed it was "
+ "a DC for domain %s, refusing to "
+ "initialize\n",
+ domain->alt_name, domain->name,
+ lsa_info->dns.dns_domain.string));
+ TALLOC_FREE(cli);
+ TALLOC_FREE(mem_ctx);
+ return;
+ }
+ talloc_free(domain->alt_name);
+ domain->alt_name =
+ talloc_strdup(domain,
+ lsa_info->dns.dns_domain.string);
+ if (domain->alt_name == NULL) {
+ goto done;
+ }
+ }
+
+ /* See if we can set some domain trust flags about
+ ourself */
+
+ if (lsa_info->dns.dns_forest.string) {
+ talloc_free(domain->forest_name);
+ domain->forest_name =
+ talloc_strdup(domain,
+ lsa_info->dns.dns_forest.string);
+ if (domain->forest_name == NULL) {
+ goto done;
+ }
+
+ if (strequal(domain->forest_name, domain->alt_name)) {
+ domain->domain_flags |= NETR_TRUST_FLAG_TREEROOT;
+ }
+ }
+
+ if (lsa_info->dns.sid) {
+ if (!is_null_sid(&domain->sid) &&
+ !dom_sid_equal(&domain->sid,
+ lsa_info->dns.sid))
+ {
+ struct dom_sid_buf buf1, buf2;
+ DEBUG(1, ("set_dc_type_and_flags_connect: DC "
+ "for domain %s (%s) claimed it was "
+ "a DC for domain %s, refusing to "
+ "initialize\n",
+ dom_sid_str_buf(&domain->sid, &buf1),
+ domain->name,
+ dom_sid_str_buf(lsa_info->dns.sid,
+ &buf2)));
+ TALLOC_FREE(cli);
+ TALLOC_FREE(mem_ctx);
+ return;
+ }
+ sid_copy(&domain->sid, lsa_info->dns.sid);
+ }
+ } else {
+ domain->active_directory = False;
+
+ status = rpccli_lsa_open_policy(cli, mem_ctx, True,
+ SEC_FLAG_MAXIMUM_ALLOWED,
+ &pol);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ goto done;
+ }
+
+ status = dcerpc_lsa_QueryInfoPolicy(cli->binding_handle, mem_ctx,
+ &pol,
+ LSA_POLICY_INFO_ACCOUNT_DOMAIN,
+ &lsa_info,
+ &result);
+ if (NT_STATUS_IS_OK(status) && NT_STATUS_IS_OK(result)) {
+
+ if (lsa_info->account_domain.name.string) {
+ if (!strequal(domain->name,
+ lsa_info->account_domain.name.string))
+ {
+ DEBUG(1,
+ ("set_dc_type_and_flags_connect: "
+ "DC for domain %s claimed it was"
+ " a DC for domain %s, refusing "
+ "to initialize\n", domain->name,
+ lsa_info->
+ account_domain.name.string));
+ TALLOC_FREE(cli);
+ TALLOC_FREE(mem_ctx);
+ return;
+ }
+ talloc_free(domain->name);
+ domain->name =
+ talloc_strdup(domain,
+ lsa_info->account_domain.name.string);
+ }
+
+ if (lsa_info->account_domain.sid) {
+ if (!is_null_sid(&domain->sid) &&
+ !dom_sid_equal(&domain->sid,
+ lsa_info->account_domain.sid))
+ {
+ struct dom_sid_buf buf1, buf2;
+ DEBUG(1,
+ ("set_dc_type_and_flags_connect: "
+ "DC for domain %s (%s) claimed "
+ "it was a DC for domain %s, "
+ "refusing to initialize\n",
+ dom_sid_str_buf(
+ &domain->sid, &buf1),
+ domain->name,
+ dom_sid_str_buf(
+ lsa_info->account_domain.sid,
+ &buf2)));
+ TALLOC_FREE(cli);
+ TALLOC_FREE(mem_ctx);
+ return;
+ }
+ sid_copy(&domain->sid, lsa_info->account_domain.sid);
+ }
+ }
+ }
+done:
+ if (is_valid_policy_hnd(&pol)) {
+ dcerpc_lsa_Close(cli->binding_handle,
+ mem_ctx,
+ &pol,
+ &close_status);
+ }
+
+ DEBUG(5, ("set_dc_type_and_flags_connect: domain %s is %sin native mode.\n",
+ domain->name, domain->native_mode ? "" : "NOT "));
+
+ DEBUG(5,("set_dc_type_and_flags_connect: domain %s is %srunning active directory.\n",
+ domain->name, domain->active_directory ? "" : "NOT "));
+
+ domain->can_do_ncacn_ip_tcp = domain->active_directory;
+
+ TALLOC_FREE(cli);
+
+ TALLOC_FREE(mem_ctx);
+
+ domain->initialized = True;
+}
+
+/**********************************************************************
+ Set the domain_flags (trust attributes, domain operating modes, etc...
+***********************************************************************/
+
+static void set_dc_type_and_flags( struct winbindd_domain *domain )
+{
+ if (IS_DC) {
+ /*
+ * On a DC we loaded all trusts
+ * from configuration and never learn
+ * new domains.
+ */
+ return;
+ }
+
+ /* we always have to contact our primary domain */
+
+ if ( domain->primary || domain->internal) {
+ DEBUG(10,("set_dc_type_and_flags: setting up flags for "
+ "primary or internal domain\n"));
+ set_dc_type_and_flags_connect( domain );
+ return;
+ }
+
+ /* Use our DC to get the information if possible */
+
+ if ( !set_dc_type_and_flags_trustinfo( domain ) ) {
+ /* Otherwise, fallback to contacting the
+ domain directly */
+ set_dc_type_and_flags_connect( domain );
+ }
+
+ return;
+}
+
+
+
+/**********************************************************************
+***********************************************************************/
+
+static NTSTATUS cm_get_schannel_creds(struct winbindd_domain *domain,
+ struct netlogon_creds_cli_context **ppdc)
+{
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+ struct rpc_pipe_client *netlogon_pipe;
+
+ *ppdc = NULL;
+
+ if ((!IS_DC) && (!domain->primary)) {
+ return NT_STATUS_TRUSTED_DOMAIN_FAILURE;
+ }
+
+ if (domain->conn.netlogon_creds_ctx != NULL) {
+ *ppdc = domain->conn.netlogon_creds_ctx;
+ return NT_STATUS_OK;
+ }
+
+ result = cm_connect_netlogon_secure(domain, &netlogon_pipe, ppdc);
+ if (!NT_STATUS_IS_OK(result)) {
+ return result;
+ }
+
+ return NT_STATUS_OK;
+}
+
+NTSTATUS cm_connect_sam(struct winbindd_domain *domain, TALLOC_CTX *mem_ctx,
+ bool need_rw_dc,
+ struct rpc_pipe_client **cli, struct policy_handle *sam_handle)
+{
+ struct winbindd_cm_conn *conn;
+ NTSTATUS status, result;
+ struct netlogon_creds_cli_context *p_creds;
+ struct cli_credentials *creds = NULL;
+ bool retry = false; /* allow one retry attempt for expired session */
+ const char *remote_name = NULL;
+ const struct sockaddr_storage *remote_sockaddr = NULL;
+ bool sealed_pipes = true;
+ bool strong_key = true;
+
+ if (sid_check_is_our_sam(&domain->sid)) {
+ if (domain->rodc == false || need_rw_dc == false) {
+ return open_internal_samr_conn(mem_ctx, domain, cli, sam_handle);
+ }
+ }
+
+ if (IS_AD_DC) {
+ /*
+ * In theory we should not use SAMR within
+ * winbindd at all, but that's a larger task to
+ * remove this and avoid breaking existing
+ * setups.
+ *
+ * At least as AD DC we have the restriction
+ * to avoid SAMR against trusted domains,
+ * as there're no existing setups.
+ */
+ return NT_STATUS_REQUEST_NOT_ACCEPTED;
+ }
+
+retry:
+ status = init_dc_connection_rpc(domain, need_rw_dc);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ conn = &domain->conn;
+
+ if (rpccli_is_connected(conn->samr_pipe)) {
+ goto done;
+ }
+
+ TALLOC_FREE(conn->samr_pipe);
+
+ /*
+ * No SAMR pipe yet. Attempt to get an NTLMSSP SPNEGO authenticated
+ * sign and sealed pipe using the machine account password by
+ * preference. If we can't - try schannel, if that fails, try
+ * anonymous.
+ */
+
+ result = get_trust_credentials(domain, talloc_tos(), false, &creds);
+ if (!NT_STATUS_IS_OK(result)) {
+ DEBUG(10, ("cm_connect_sam: No user available for "
+ "domain %s, trying schannel\n", domain->name));
+ goto schannel;
+ }
+
+ if (cli_credentials_is_anonymous(creds)) {
+ goto anonymous;
+ }
+
+ remote_name = smbXcli_conn_remote_name(conn->cli->conn);
+ remote_sockaddr = smbXcli_conn_remote_sockaddr(conn->cli->conn);
+
+ /*
+ * We have an authenticated connection. Use a SPNEGO
+ * authenticated SAMR pipe with sign & seal.
+ */
+ status = cli_rpc_pipe_open_with_creds(conn->cli,
+ &ndr_table_samr,
+ NCACN_NP,
+ DCERPC_AUTH_TYPE_SPNEGO,
+ conn->auth_level,
+ remote_name,
+ remote_sockaddr,
+ creds,
+ &conn->samr_pipe);
+
+ if (NT_STATUS_EQUAL(status, NT_STATUS_NETWORK_SESSION_EXPIRED)
+ && !retry) {
+ invalidate_cm_connection(domain);
+ retry = true;
+ goto retry;
+ }
+
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(10,("cm_connect_sam: failed to connect to SAMR "
+ "pipe for domain %s using NTLMSSP "
+ "authenticated pipe: user %s. Error was "
+ "%s\n", domain->name,
+ cli_credentials_get_unparsed_name(creds, talloc_tos()),
+ nt_errstr(status)));
+ goto schannel;
+ }
+
+ DEBUG(10,("cm_connect_sam: connected to SAMR pipe for "
+ "domain %s using NTLMSSP authenticated "
+ "pipe: user %s\n", domain->name,
+ cli_credentials_get_unparsed_name(creds, talloc_tos())));
+
+ status = dcerpc_samr_Connect2(conn->samr_pipe->binding_handle, mem_ctx,
+ conn->samr_pipe->desthost,
+ SEC_FLAG_MAXIMUM_ALLOWED,
+ &conn->sam_connect_handle,
+ &result);
+
+ if (NT_STATUS_EQUAL(status, NT_STATUS_IO_DEVICE_ERROR) && !retry) {
+ invalidate_cm_connection(domain);
+ TALLOC_FREE(conn->samr_pipe);
+ retry = true;
+ goto retry;
+ }
+
+ if (NT_STATUS_IS_OK(status) && NT_STATUS_IS_OK(result)) {
+ goto open_domain;
+ }
+ if (NT_STATUS_IS_OK(status)) {
+ status = result;
+ }
+
+ DEBUG(10,("cm_connect_sam: ntlmssp-sealed dcerpc_samr_Connect2 "
+ "failed for domain %s, error was %s. Trying schannel\n",
+ domain->name, nt_errstr(status) ));
+ TALLOC_FREE(conn->samr_pipe);
+
+ schannel:
+
+ /* Fall back to schannel if it's a W2K pre-SP1 box. */
+
+ status = cm_get_schannel_creds(domain, &p_creds);
+ if (!NT_STATUS_IS_OK(status)) {
+ /* If this call fails - conn->cli can now be NULL ! */
+ DEBUG(10, ("cm_connect_sam: Could not get schannel auth info "
+ "for domain %s (error %s), trying anon\n",
+ domain->name,
+ nt_errstr(status) ));
+ goto anonymous;
+ }
+ TALLOC_FREE(creds);
+ status = cli_rpc_pipe_open_schannel_with_creds(
+ conn->cli, &ndr_table_samr, NCACN_NP, p_creds,
+ remote_name,
+ remote_sockaddr,
+ &conn->samr_pipe);
+
+ if (NT_STATUS_EQUAL(status, NT_STATUS_NETWORK_SESSION_EXPIRED)
+ && !retry) {
+ invalidate_cm_connection(domain);
+ retry = true;
+ goto retry;
+ }
+
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(10,("cm_connect_sam: failed to connect to SAMR pipe for "
+ "domain %s using schannel. Error was %s\n",
+ domain->name, nt_errstr(status) ));
+ goto anonymous;
+ }
+ DEBUG(10,("cm_connect_sam: connected to SAMR pipe for domain %s using "
+ "schannel.\n", domain->name ));
+
+ status = dcerpc_samr_Connect2(conn->samr_pipe->binding_handle, mem_ctx,
+ conn->samr_pipe->desthost,
+ SEC_FLAG_MAXIMUM_ALLOWED,
+ &conn->sam_connect_handle,
+ &result);
+
+ if (NT_STATUS_EQUAL(status, NT_STATUS_IO_DEVICE_ERROR) && !retry) {
+ invalidate_cm_connection(domain);
+ TALLOC_FREE(conn->samr_pipe);
+ retry = true;
+ goto retry;
+ }
+
+ if (NT_STATUS_IS_OK(status) && NT_STATUS_IS_OK(result)) {
+ goto open_domain;
+ }
+ if (NT_STATUS_IS_OK(status)) {
+ status = result;
+ }
+ DEBUG(10,("cm_connect_sam: schannel-sealed dcerpc_samr_Connect2 failed "
+ "for domain %s, error was %s. Trying anonymous\n",
+ domain->name, nt_errstr(status) ));
+ TALLOC_FREE(conn->samr_pipe);
+
+ anonymous:
+
+ sealed_pipes = lp_winbind_sealed_pipes();
+ sealed_pipes = lp_parm_bool(-1, "winbind sealed pipes",
+ domain->name,
+ sealed_pipes);
+ strong_key = lp_require_strong_key();
+ strong_key = lp_parm_bool(-1, "require strong key",
+ domain->name,
+ strong_key);
+
+ /* Finally fall back to anonymous. */
+ if (sealed_pipes || strong_key) {
+ status = NT_STATUS_DOWNGRADE_DETECTED;
+ DEBUG(1, ("Unwilling to make SAMR connection to domain %s "
+ "without connection level security, "
+ "must set 'winbind sealed pipes:%s = false' and "
+ "'require strong key:%s = false' to proceed: %s\n",
+ domain->name, domain->name, domain->name,
+ nt_errstr(status)));
+ goto done;
+ }
+ status = cli_rpc_pipe_open_noauth(conn->cli, &ndr_table_samr,
+ &conn->samr_pipe);
+
+ if (NT_STATUS_EQUAL(status, NT_STATUS_NETWORK_SESSION_EXPIRED)
+ && !retry) {
+ invalidate_cm_connection(domain);
+ retry = true;
+ goto retry;
+ }
+
+ if (!NT_STATUS_IS_OK(status)) {
+ goto done;
+ }
+
+ status = dcerpc_samr_Connect2(conn->samr_pipe->binding_handle, mem_ctx,
+ conn->samr_pipe->desthost,
+ SEC_FLAG_MAXIMUM_ALLOWED,
+ &conn->sam_connect_handle,
+ &result);
+
+ if (NT_STATUS_EQUAL(status, NT_STATUS_IO_DEVICE_ERROR) && !retry) {
+ invalidate_cm_connection(domain);
+ TALLOC_FREE(conn->samr_pipe);
+ retry = true;
+ goto retry;
+ }
+
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(10,("cm_connect_sam: rpccli_samr_Connect2 failed "
+ "for domain %s Error was %s\n",
+ domain->name, nt_errstr(status) ));
+ goto done;
+ }
+ if (!NT_STATUS_IS_OK(result)) {
+ status = result;
+ DEBUG(10,("cm_connect_sam: dcerpc_samr_Connect2 failed "
+ "for domain %s Error was %s\n",
+ domain->name, nt_errstr(result)));
+ goto done;
+ }
+
+ open_domain:
+ status = dcerpc_samr_OpenDomain(conn->samr_pipe->binding_handle,
+ mem_ctx,
+ &conn->sam_connect_handle,
+ SEC_FLAG_MAXIMUM_ALLOWED,
+ &domain->sid,
+ &conn->sam_domain_handle,
+ &result);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto done;
+ }
+
+ status = result;
+ done:
+
+ if (NT_STATUS_EQUAL(status, NT_STATUS_ACCESS_DENIED)) {
+ /*
+ * if we got access denied, we might just have no access rights
+ * to talk to the remote samr server server (e.g. when we are a
+ * PDC and we are connecting a w2k8 pdc via an interdomain
+ * trust). In that case do not invalidate the whole connection
+ * stack
+ */
+ TALLOC_FREE(conn->samr_pipe);
+ ZERO_STRUCT(conn->sam_domain_handle);
+ return status;
+ } else if (!NT_STATUS_IS_OK(status)) {
+ invalidate_cm_connection(domain);
+ return status;
+ }
+
+ *cli = conn->samr_pipe;
+ *sam_handle = conn->sam_domain_handle;
+ return status;
+}
+
+/**********************************************************************
+ open an schanneld ncacn_ip_tcp connection to LSA
+***********************************************************************/
+
+static NTSTATUS cm_connect_lsa_tcp(struct winbindd_domain *domain,
+ TALLOC_CTX *mem_ctx,
+ struct rpc_pipe_client **cli)
+{
+ struct winbindd_cm_conn *conn;
+ struct netlogon_creds_cli_context *p_creds = NULL;
+ NTSTATUS status;
+ const char *remote_name = NULL;
+ const struct sockaddr_storage *remote_sockaddr = NULL;
+
+ DEBUG(10,("cm_connect_lsa_tcp\n"));
+
+ status = init_dc_connection_rpc(domain, false);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ conn = &domain->conn;
+
+ /*
+ * rpccli_is_connected handles more error cases
+ */
+ if (rpccli_is_connected(conn->lsa_pipe_tcp) &&
+ conn->lsa_pipe_tcp->transport->transport == NCACN_IP_TCP &&
+ conn->lsa_pipe_tcp->auth->auth_level >= DCERPC_AUTH_LEVEL_INTEGRITY) {
+ goto done;
+ }
+
+ TALLOC_FREE(conn->lsa_pipe_tcp);
+
+ status = cm_get_schannel_creds(domain, &p_creds);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto done;
+ }
+
+ remote_name = smbXcli_conn_remote_name(conn->cli->conn);
+ remote_sockaddr = smbXcli_conn_remote_sockaddr(conn->cli->conn);
+
+ status = cli_rpc_pipe_open_schannel_with_creds(
+ conn->cli,
+ &ndr_table_lsarpc,
+ NCACN_IP_TCP,
+ p_creds,
+ remote_name,
+ remote_sockaddr,
+ &conn->lsa_pipe_tcp);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(10,("cli_rpc_pipe_open_schannel_with_key failed: %s\n",
+ nt_errstr(status)));
+ goto done;
+ }
+
+ done:
+ if (!NT_STATUS_IS_OK(status)) {
+ TALLOC_FREE(conn->lsa_pipe_tcp);
+ return status;
+ }
+
+ *cli = conn->lsa_pipe_tcp;
+
+ return status;
+}
+
+NTSTATUS cm_connect_lsa(struct winbindd_domain *domain, TALLOC_CTX *mem_ctx,
+ struct rpc_pipe_client **cli, struct policy_handle *lsa_policy)
+{
+ struct winbindd_cm_conn *conn;
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+ struct netlogon_creds_cli_context *p_creds;
+ struct cli_credentials *creds = NULL;
+ bool retry = false; /* allow one retry attempt for expired session */
+ const char *remote_name = NULL;
+ const struct sockaddr_storage *remote_sockaddr = NULL;
+ bool sealed_pipes = true;
+ bool strong_key = true;
+
+retry:
+ result = init_dc_connection_rpc(domain, false);
+ if (!NT_STATUS_IS_OK(result))
+ return result;
+
+ conn = &domain->conn;
+
+ if (rpccli_is_connected(conn->lsa_pipe)) {
+ goto done;
+ }
+
+ TALLOC_FREE(conn->lsa_pipe);
+
+ if (IS_DC) {
+ /*
+ * Make sure we only use schannel as AD DC.
+ */
+ goto schannel;
+ }
+
+ result = get_trust_credentials(domain, talloc_tos(), false, &creds);
+ if (!NT_STATUS_IS_OK(result)) {
+ DEBUG(10, ("cm_connect_lsa: No user available for "
+ "domain %s, trying schannel\n", domain->name));
+ goto schannel;
+ }
+
+ if (cli_credentials_is_anonymous(creds)) {
+ goto anonymous;
+ }
+
+ remote_name = smbXcli_conn_remote_name(conn->cli->conn);
+ remote_sockaddr = smbXcli_conn_remote_sockaddr(conn->cli->conn);
+
+ /*
+ * We have an authenticated connection. Use a SPNEGO
+ * authenticated LSA pipe with sign & seal.
+ */
+ result = cli_rpc_pipe_open_with_creds
+ (conn->cli, &ndr_table_lsarpc, NCACN_NP,
+ DCERPC_AUTH_TYPE_SPNEGO,
+ conn->auth_level,
+ remote_name,
+ remote_sockaddr,
+ creds,
+ &conn->lsa_pipe);
+
+ if (NT_STATUS_EQUAL(result, NT_STATUS_NETWORK_SESSION_EXPIRED)
+ && !retry) {
+ invalidate_cm_connection(domain);
+ retry = true;
+ goto retry;
+ }
+
+ if (!NT_STATUS_IS_OK(result)) {
+ DEBUG(10,("cm_connect_lsa: failed to connect to LSA pipe for "
+ "domain %s using NTLMSSP authenticated pipe: user "
+ "%s. Error was %s. Trying schannel.\n",
+ domain->name,
+ cli_credentials_get_unparsed_name(creds, talloc_tos()),
+ nt_errstr(result)));
+ goto schannel;
+ }
+
+ DEBUG(10,("cm_connect_lsa: connected to LSA pipe for domain %s using "
+ "NTLMSSP authenticated pipe: user %s\n",
+ domain->name, cli_credentials_get_unparsed_name(creds, talloc_tos())));
+
+ result = rpccli_lsa_open_policy(conn->lsa_pipe, mem_ctx, True,
+ SEC_FLAG_MAXIMUM_ALLOWED,
+ &conn->lsa_policy);
+ if (NT_STATUS_EQUAL(result, NT_STATUS_IO_DEVICE_ERROR) && !retry) {
+ invalidate_cm_connection(domain);
+ TALLOC_FREE(conn->lsa_pipe);
+ retry = true;
+ goto retry;
+ }
+
+ if (NT_STATUS_IS_OK(result)) {
+ goto done;
+ }
+
+ DEBUG(10,("cm_connect_lsa: rpccli_lsa_open_policy failed, trying "
+ "schannel\n"));
+
+ TALLOC_FREE(conn->lsa_pipe);
+
+ schannel:
+
+ /* Fall back to schannel if it's a W2K pre-SP1 box. */
+
+ result = cm_get_schannel_creds(domain, &p_creds);
+ if (!NT_STATUS_IS_OK(result)) {
+ /* If this call fails - conn->cli can now be NULL ! */
+ DEBUG(10, ("cm_connect_lsa: Could not get schannel auth info "
+ "for domain %s (error %s), trying anon\n",
+ domain->name,
+ nt_errstr(result) ));
+ goto anonymous;
+ }
+
+ TALLOC_FREE(creds);
+ result = cli_rpc_pipe_open_schannel_with_creds(
+ conn->cli, &ndr_table_lsarpc, NCACN_NP, p_creds,
+ remote_name,
+ remote_sockaddr,
+ &conn->lsa_pipe);
+
+ if (NT_STATUS_EQUAL(result, NT_STATUS_NETWORK_SESSION_EXPIRED)
+ && !retry) {
+ invalidate_cm_connection(domain);
+ retry = true;
+ goto retry;
+ }
+
+ if (!NT_STATUS_IS_OK(result)) {
+ DEBUG(10,("cm_connect_lsa: failed to connect to LSA pipe for "
+ "domain %s using schannel. Error was %s\n",
+ domain->name, nt_errstr(result) ));
+ goto anonymous;
+ }
+ DEBUG(10,("cm_connect_lsa: connected to LSA pipe for domain %s using "
+ "schannel.\n", domain->name ));
+
+ result = rpccli_lsa_open_policy(conn->lsa_pipe, mem_ctx, True,
+ SEC_FLAG_MAXIMUM_ALLOWED,
+ &conn->lsa_policy);
+
+ if (NT_STATUS_EQUAL(result, NT_STATUS_IO_DEVICE_ERROR) && !retry) {
+ invalidate_cm_connection(domain);
+ TALLOC_FREE(conn->lsa_pipe);
+ retry = true;
+ goto retry;
+ }
+
+ if (NT_STATUS_IS_OK(result)) {
+ goto done;
+ }
+
+ if (IS_DC) {
+ /*
+ * Make sure we only use schannel as AD DC.
+ */
+ goto done;
+ }
+
+ DEBUG(10,("cm_connect_lsa: rpccli_lsa_open_policy failed, trying "
+ "anonymous\n"));
+
+ TALLOC_FREE(conn->lsa_pipe);
+
+ anonymous:
+
+ if (IS_DC) {
+ /*
+ * Make sure we only use schannel as AD DC.
+ */
+ goto done;
+ }
+
+ sealed_pipes = lp_winbind_sealed_pipes();
+ sealed_pipes = lp_parm_bool(-1, "winbind sealed pipes",
+ domain->name,
+ sealed_pipes);
+ strong_key = lp_require_strong_key();
+ strong_key = lp_parm_bool(-1, "require strong key",
+ domain->name,
+ strong_key);
+
+ /* Finally fall back to anonymous. */
+ if (sealed_pipes || strong_key) {
+ result = NT_STATUS_DOWNGRADE_DETECTED;
+ DEBUG(1, ("Unwilling to make LSA connection to domain %s "
+ "without connection level security, "
+ "must set 'winbind sealed pipes:%s = false' and "
+ "'require strong key:%s = false' to proceed: %s\n",
+ domain->name, domain->name, domain->name,
+ nt_errstr(result)));
+ goto done;
+ }
+
+ result = cli_rpc_pipe_open_noauth(conn->cli,
+ &ndr_table_lsarpc,
+ &conn->lsa_pipe);
+
+ if (NT_STATUS_EQUAL(result, NT_STATUS_NETWORK_SESSION_EXPIRED)
+ && !retry) {
+ invalidate_cm_connection(domain);
+ retry = true;
+ goto retry;
+ }
+
+ if (!NT_STATUS_IS_OK(result)) {
+ goto done;
+ }
+
+ result = rpccli_lsa_open_policy(conn->lsa_pipe, mem_ctx, True,
+ SEC_FLAG_MAXIMUM_ALLOWED,
+ &conn->lsa_policy);
+
+ if (NT_STATUS_EQUAL(result, NT_STATUS_IO_DEVICE_ERROR) && !retry) {
+ invalidate_cm_connection(domain);
+ TALLOC_FREE(conn->lsa_pipe);
+ retry = true;
+ goto retry;
+ }
+
+ done:
+ if (!NT_STATUS_IS_OK(result)) {
+ invalidate_cm_connection(domain);
+ return result;
+ }
+
+ *cli = conn->lsa_pipe;
+ *lsa_policy = conn->lsa_policy;
+ return result;
+}
+
+/****************************************************************************
+Open a LSA connection to a DC, suitable for LSA lookup calls.
+****************************************************************************/
+
+NTSTATUS cm_connect_lsat(struct winbindd_domain *domain,
+ TALLOC_CTX *mem_ctx,
+ struct rpc_pipe_client **cli,
+ struct policy_handle *lsa_policy)
+{
+ NTSTATUS status;
+
+ if (domain->can_do_ncacn_ip_tcp) {
+ status = cm_connect_lsa_tcp(domain, mem_ctx, cli);
+ if (NT_STATUS_EQUAL(status, NT_STATUS_ACCESS_DENIED) ||
+ NT_STATUS_EQUAL(status, NT_STATUS_RPC_SEC_PKG_ERROR) ||
+ NT_STATUS_EQUAL(status, NT_STATUS_NETWORK_ACCESS_DENIED)) {
+ invalidate_cm_connection(domain);
+ status = cm_connect_lsa_tcp(domain, mem_ctx, cli);
+ }
+ if (NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ /*
+ * we tried twice to connect via ncan_ip_tcp and schannel and
+ * failed - maybe it is a trusted domain we can't connect to ?
+ * do not try tcp next time - gd
+ *
+ * This also prevents NETLOGON over TCP
+ */
+ domain->can_do_ncacn_ip_tcp = false;
+ }
+
+ status = cm_connect_lsa(domain, mem_ctx, cli, lsa_policy);
+
+ return status;
+}
+
+/****************************************************************************
+ Open the netlogon pipe to this DC.
+****************************************************************************/
+
+static NTSTATUS cm_connect_netlogon_transport(struct winbindd_domain *domain,
+ enum dcerpc_transport_t transport,
+ struct rpc_pipe_client **cli)
+{
+ struct messaging_context *msg_ctx = global_messaging_context();
+ struct winbindd_cm_conn *conn;
+ NTSTATUS result;
+ enum netr_SchannelType sec_chan_type;
+ struct cli_credentials *creds = NULL;
+
+ *cli = NULL;
+
+ if (IS_DC) {
+ if (domain->secure_channel_type == SEC_CHAN_NULL) {
+ /*
+ * Make sure we don't even try to
+ * connect to a foreign domain
+ * without a direct outbound trust.
+ */
+ return NT_STATUS_NO_TRUST_LSA_SECRET;
+ }
+ }
+
+ result = init_dc_connection_rpc(domain, domain->rodc);
+ if (!NT_STATUS_IS_OK(result)) {
+ return result;
+ }
+
+ conn = &domain->conn;
+
+ if (rpccli_is_connected(conn->netlogon_pipe)) {
+ *cli = conn->netlogon_pipe;
+ return NT_STATUS_OK;
+ }
+
+ TALLOC_FREE(conn->netlogon_pipe);
+ TALLOC_FREE(conn->netlogon_creds_ctx);
+
+ result = get_trust_credentials(domain, talloc_tos(), true, &creds);
+ if (!NT_STATUS_IS_OK(result)) {
+ DBG_DEBUG("No user available for domain %s when trying "
+ "schannel\n", domain->name);
+ return NT_STATUS_CANT_ACCESS_DOMAIN_INFO;
+ }
+
+ if (cli_credentials_is_anonymous(creds)) {
+ DBG_WARNING("get_trust_credential only gave anonymous for %s, "
+ "unable to make get NETLOGON credentials\n",
+ domain->name);
+ return NT_STATUS_CANT_ACCESS_DOMAIN_INFO;
+ }
+
+ sec_chan_type = cli_credentials_get_secure_channel_type(creds);
+ if (sec_chan_type == SEC_CHAN_NULL) {
+ const char *remote_name =
+ smbXcli_conn_remote_name(conn->cli->conn);
+ const struct sockaddr_storage *remote_sockaddr =
+ smbXcli_conn_remote_sockaddr(conn->cli->conn);
+
+ if (transport == NCACN_IP_TCP) {
+ DBG_NOTICE("get_secure_channel_type gave SEC_CHAN_NULL "
+ "for %s, deny NCACN_IP_TCP and let the "
+ "caller fallback to NCACN_NP.\n",
+ domain->name);
+ return NT_STATUS_CANT_ACCESS_DOMAIN_INFO;
+ }
+
+ DBG_NOTICE("get_secure_channel_type gave SEC_CHAN_NULL for %s, "
+ "fallback to noauth on NCACN_NP.\n",
+ domain->name);
+
+ result = cli_rpc_pipe_open_noauth_transport(
+ conn->cli,
+ transport,
+ &ndr_table_netlogon,
+ remote_name,
+ remote_sockaddr,
+ &conn->netlogon_pipe);
+ if (!NT_STATUS_IS_OK(result)) {
+ invalidate_cm_connection(domain);
+ return result;
+ }
+
+ *cli = conn->netlogon_pipe;
+ return NT_STATUS_OK;
+ }
+
+ result = rpccli_create_netlogon_creds_ctx(creds,
+ domain->dcname,
+ msg_ctx,
+ domain,
+ &conn->netlogon_creds_ctx);
+ if (!NT_STATUS_IS_OK(result)) {
+ DEBUG(1, ("rpccli_create_netlogon_creds failed for %s, "
+ "unable to create NETLOGON credentials: %s\n",
+ domain->name, nt_errstr(result)));
+ return result;
+ }
+
+ result = rpccli_connect_netlogon(
+ conn->cli, transport,
+ conn->netlogon_creds_ctx, conn->netlogon_force_reauth, creds,
+ &conn->netlogon_pipe);
+ conn->netlogon_force_reauth = false;
+ if (!NT_STATUS_IS_OK(result)) {
+ DBG_DEBUG("rpccli_connect_netlogon failed: %s\n",
+ nt_errstr(result));
+ return result;
+ }
+
+ *cli = conn->netlogon_pipe;
+ return NT_STATUS_OK;
+}
+
+/****************************************************************************
+Open a NETLOGON connection to a DC, suitable for SamLogon calls.
+****************************************************************************/
+
+NTSTATUS cm_connect_netlogon(struct winbindd_domain *domain,
+ struct rpc_pipe_client **cli)
+{
+ NTSTATUS status;
+
+ status = init_dc_connection_rpc(domain, domain->rodc);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ if (domain->active_directory && domain->can_do_ncacn_ip_tcp) {
+ status = cm_connect_netlogon_transport(domain, NCACN_IP_TCP, cli);
+ if (NT_STATUS_EQUAL(status, NT_STATUS_ACCESS_DENIED) ||
+ NT_STATUS_EQUAL(status, NT_STATUS_RPC_SEC_PKG_ERROR) ||
+ NT_STATUS_EQUAL(status, NT_STATUS_NETWORK_ACCESS_DENIED)) {
+ invalidate_cm_connection(domain);
+ status = cm_connect_netlogon_transport(domain, NCACN_IP_TCP, cli);
+ }
+ if (NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ /*
+ * we tried twice to connect via ncan_ip_tcp and schannel and
+ * failed - maybe it is a trusted domain we can't connect to ?
+ * do not try tcp next time - gd
+ *
+ * This also prevents LSA over TCP
+ */
+ domain->can_do_ncacn_ip_tcp = false;
+ }
+
+ status = cm_connect_netlogon_transport(domain, NCACN_NP, cli);
+ if (NT_STATUS_EQUAL(status, NT_STATUS_NETWORK_SESSION_EXPIRED)) {
+ /*
+ * SMB2 session expired, needs reauthentication. Drop
+ * connection and retry.
+ */
+ invalidate_cm_connection(domain);
+ status = cm_connect_netlogon_transport(domain, NCACN_NP, cli);
+ }
+
+ return status;
+}
+
+NTSTATUS cm_connect_netlogon_secure(struct winbindd_domain *domain,
+ struct rpc_pipe_client **cli,
+ struct netlogon_creds_cli_context **ppdc)
+{
+ NTSTATUS status;
+
+ if (domain->secure_channel_type == SEC_CHAN_NULL) {
+ return NT_STATUS_CANT_ACCESS_DOMAIN_INFO;
+ }
+
+ status = cm_connect_netlogon(domain, cli);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ if (domain->conn.netlogon_creds_ctx == NULL) {
+ return NT_STATUS_TRUSTED_DOMAIN_FAILURE;
+ }
+
+ *ppdc = domain->conn.netlogon_creds_ctx;
+ return NT_STATUS_OK;
+}
+
+void winbind_msg_ip_dropped(struct messaging_context *msg_ctx,
+ void *private_data,
+ uint32_t msg_type,
+ struct server_id server_id,
+ DATA_BLOB *data)
+{
+ struct winbindd_domain *domain;
+ char *freeit = NULL;
+ char *addr;
+
+ if ((data == NULL)
+ || (data->data == NULL)
+ || (data->length == 0)
+ || (data->data[data->length-1] != '\0')) {
+ DEBUG(1, ("invalid msg_ip_dropped message: not a valid "
+ "string\n"));
+ return;
+ }
+
+ addr = (char *)data->data;
+ DEBUG(10, ("IP %s dropped\n", addr));
+
+ if (!is_ipaddress(addr)) {
+ char *slash;
+ /*
+ * Some code sends us ip addresses with the /netmask
+ * suffix
+ */
+ slash = strchr(addr, '/');
+ if (slash == NULL) {
+ DEBUG(1, ("invalid msg_ip_dropped message: %s\n",
+ addr));
+ return;
+ }
+ freeit = talloc_strndup(talloc_tos(), addr, slash-addr);
+ if (freeit == NULL) {
+ DEBUG(1, ("talloc failed\n"));
+ return;
+ }
+ addr = freeit;
+ DEBUG(10, ("Stripped /netmask to IP %s\n", addr));
+ }
+
+ for (domain = domain_list(); domain != NULL; domain = domain->next) {
+ char sockaddr[INET6_ADDRSTRLEN];
+
+ if (!cli_state_is_connected(domain->conn.cli)) {
+ continue;
+ }
+
+ print_sockaddr(sockaddr, sizeof(sockaddr),
+ smbXcli_conn_local_sockaddr(domain->conn.cli->conn));
+
+ if (strequal(sockaddr, addr)) {
+ smbXcli_conn_disconnect(domain->conn.cli->conn, NT_STATUS_OK);
+ }
+ }
+ TALLOC_FREE(freeit);
+}
+
+void winbind_msg_disconnect_dc(struct messaging_context *msg_ctx,
+ void *private_data,
+ uint32_t msg_type,
+ struct server_id server_id,
+ DATA_BLOB *data)
+{
+ struct winbindd_domain *domain;
+
+ for (domain = domain_list(); domain; domain = domain->next) {
+ if (domain->internal) {
+ continue;
+ }
+ invalidate_cm_connection(domain);
+ }
+}
diff --git a/source3/winbindd/winbindd_cred_cache.c b/source3/winbindd/winbindd_cred_cache.c
new file mode 100644
index 0000000..59daaff
--- /dev/null
+++ b/source3/winbindd/winbindd_cred_cache.c
@@ -0,0 +1,1061 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Winbind daemon - krb5 credential cache functions
+ and in-memory cache functions.
+
+ Copyright (C) Guenther Deschner 2005-2006
+ Copyright (C) Jeremy Allison 2006
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "winbindd.h"
+#include "../libcli/auth/libcli_auth.h"
+#include "smb_krb5.h"
+#include "libads/kerberos_proto.h"
+#include "lib/global_contexts.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_WINBIND
+
+/* uncomment this to do fast debugging on the krb5 ticket renewal event */
+#ifdef DEBUG_KRB5_TKT_RENEWAL
+#undef DEBUG_KRB5_TKT_RENEWAL
+#endif
+
+#define MAX_CCACHES 100
+
+static struct WINBINDD_CCACHE_ENTRY *ccache_list;
+static void krb5_ticket_gain_handler(struct tevent_context *,
+ struct tevent_timer *,
+ struct timeval,
+ void *);
+static void add_krb5_ticket_gain_handler_event(struct WINBINDD_CCACHE_ENTRY *,
+ struct timeval);
+
+/* The Krb5 ticket refresh handler should be scheduled
+ at one-half of the period from now till the tkt
+ expiration */
+
+static time_t krb5_event_refresh_time(time_t end_time)
+{
+ time_t rest = end_time - time(NULL);
+ return end_time - rest/2;
+}
+
+/****************************************************************
+ Find an entry by name.
+****************************************************************/
+
+static struct WINBINDD_CCACHE_ENTRY *get_ccache_by_username(const char *username)
+{
+ struct WINBINDD_CCACHE_ENTRY *entry;
+
+ for (entry = ccache_list; entry; entry = entry->next) {
+ if (strequal(entry->username, username)) {
+ return entry;
+ }
+ }
+ return NULL;
+}
+
+/****************************************************************
+ How many do we have ?
+****************************************************************/
+
+static int ccache_entry_count(void)
+{
+ struct WINBINDD_CCACHE_ENTRY *entry;
+ int i = 0;
+
+ for (entry = ccache_list; entry; entry = entry->next) {
+ i++;
+ }
+ return i;
+}
+
+void ccache_remove_all_after_fork(void)
+{
+ struct WINBINDD_CCACHE_ENTRY *cur, *next;
+
+ for (cur = ccache_list; cur; cur = next) {
+ next = cur->next;
+ DLIST_REMOVE(ccache_list, cur);
+ TALLOC_FREE(cur->event);
+ TALLOC_FREE(cur);
+ }
+
+ return;
+}
+
+/****************************************************************
+ Do the work of refreshing the ticket.
+****************************************************************/
+
+static void krb5_ticket_refresh_handler(struct tevent_context *event_ctx,
+ struct tevent_timer *te,
+ struct timeval now,
+ void *private_data)
+{
+ struct WINBINDD_CCACHE_ENTRY *entry =
+ talloc_get_type_abort(private_data, struct WINBINDD_CCACHE_ENTRY);
+#ifdef HAVE_KRB5
+ int ret;
+ time_t new_start;
+ time_t expire_time = 0;
+ struct WINBINDD_MEMORY_CREDS *cred_ptr = entry->cred_ptr;
+#endif
+
+ DBG_DEBUG("event called for: %s, %s\n",
+ entry->ccname, entry->username);
+
+ TALLOC_FREE(entry->event);
+
+#ifdef HAVE_KRB5
+
+ /* Kinit again if we have the user password and we can't renew the old
+ * tgt anymore
+ * NB
+ * This happens when machine are put to sleep for a very long time. */
+
+ if (entry->renew_until < time(NULL)) {
+rekinit:
+ if (cred_ptr && cred_ptr->pass) {
+
+ set_effective_uid(entry->uid);
+
+ ret = kerberos_kinit_password_ext(entry->principal_name,
+ cred_ptr->pass,
+ 0, /* hm, can we do time correction here ? */
+ &entry->refresh_time,
+ &entry->renew_until,
+ entry->ccname,
+ False, /* no PAC required anymore */
+ True,
+ WINBINDD_PAM_AUTH_KRB5_RENEW_TIME,
+ NULL,
+ NULL,
+ NULL,
+ NULL);
+ gain_root_privilege();
+
+ if (ret) {
+ DEBUG(3,("krb5_ticket_refresh_handler: "
+ "could not re-kinit: %s\n",
+ error_message(ret)));
+ /* destroy the ticket because we cannot rekinit
+ * it, ignore error here */
+ ads_kdestroy(entry->ccname);
+
+ /* Don't break the ticket refresh chain: retry
+ * refreshing ticket sometime later when KDC is
+ * unreachable -- BoYang. More error code handling
+ * here?
+ * */
+
+ if ((ret == KRB5_KDC_UNREACH)
+ || (ret == KRB5_REALM_CANT_RESOLVE)) {
+#if defined(DEBUG_KRB5_TKT_RENEWAL)
+ new_start = time(NULL) + 30;
+#else
+ new_start = time(NULL) +
+ MAX(30, lp_winbind_cache_time());
+#endif
+ add_krb5_ticket_gain_handler_event(entry,
+ timeval_set(new_start, 0));
+ return;
+ }
+ TALLOC_FREE(entry->event);
+ return;
+ }
+
+ DEBUG(10,("krb5_ticket_refresh_handler: successful re-kinit "
+ "for: %s in ccache: %s\n",
+ entry->principal_name, entry->ccname));
+
+#if defined(DEBUG_KRB5_TKT_RENEWAL)
+ new_start = time(NULL) + 30;
+#else
+ /* The tkt should be refreshed at one-half the period
+ from now to the expiration time */
+ expire_time = entry->refresh_time;
+ new_start = krb5_event_refresh_time(entry->refresh_time);
+#endif
+ goto done;
+ } else {
+ /* can this happen?
+ * No cached credentials
+ * destroy ticket and refresh chain
+ * */
+ ads_kdestroy(entry->ccname);
+ TALLOC_FREE(entry->event);
+ return;
+ }
+ }
+
+ set_effective_uid(entry->uid);
+
+ ret = smb_krb5_renew_ticket(entry->ccname,
+ entry->canon_principal,
+ entry->service,
+ &new_start);
+#if defined(DEBUG_KRB5_TKT_RENEWAL)
+ new_start = time(NULL) + 30;
+#else
+ expire_time = new_start;
+ new_start = krb5_event_refresh_time(new_start);
+#endif
+
+ gain_root_privilege();
+
+ if (ret) {
+ DEBUG(3,("krb5_ticket_refresh_handler: "
+ "could not renew tickets: %s\n",
+ error_message(ret)));
+ /* maybe we are beyond the renewing window */
+
+ /* evil rises here, we refresh ticket failed,
+ * but the ticket might be expired. Therefore,
+ * When we refresh ticket failed, destroy the
+ * ticket */
+
+ ads_kdestroy(entry->ccname);
+
+ /* avoid breaking the renewal chain: retry in
+ * lp_winbind_cache_time() seconds when the KDC was not
+ * available right now.
+ * the return code can be KRB5_REALM_CANT_RESOLVE.
+ * More error code handling here? */
+
+ if ((ret == KRB5_KDC_UNREACH)
+ || (ret == KRB5_REALM_CANT_RESOLVE)) {
+#if defined(DEBUG_KRB5_TKT_RENEWAL)
+ new_start = time(NULL) + 30;
+#else
+ new_start = time(NULL) +
+ MAX(30, lp_winbind_cache_time());
+#endif
+ /* ticket is destroyed here, we have to regain it
+ * if it is possible */
+ add_krb5_ticket_gain_handler_event(entry,
+ timeval_set(new_start, 0));
+ return;
+ }
+
+ /* This is evil, if the ticket was already expired.
+ * renew ticket function returns KRB5KRB_AP_ERR_TKT_EXPIRED.
+ * But there is still a chance that we can rekinit it.
+ *
+ * This happens when user login in online mode, and then network
+ * down or something cause winbind goes offline for a very long time,
+ * and then goes online again. ticket expired, renew failed.
+ * This happens when machine are put to sleep for a long time,
+ * but shorter than entry->renew_util.
+ * NB
+ * Looks like the KDC is reachable, we want to rekinit as soon as
+ * possible instead of waiting some time later. */
+ if ((ret == KRB5KRB_AP_ERR_TKT_EXPIRED)
+ || (ret == KRB5_FCC_NOFILE)) goto rekinit;
+
+ return;
+ }
+
+done:
+ /* in cases that ticket will be unrenewable soon, we don't try to renew ticket
+ * but try to regain ticket if it is possible */
+ if (entry->renew_until && expire_time
+ && (entry->renew_until <= expire_time)) {
+ /* try to regain ticket 10 seconds before expiration */
+ expire_time -= 10;
+ add_krb5_ticket_gain_handler_event(entry,
+ timeval_set(expire_time, 0));
+ return;
+ }
+
+ if (entry->refresh_time == 0) {
+ entry->refresh_time = new_start;
+ }
+ entry->event = tevent_add_timer(global_event_context(), entry,
+ timeval_set(new_start, 0),
+ krb5_ticket_refresh_handler,
+ entry);
+
+#endif
+}
+
+/****************************************************************
+ Do the work of regaining a ticket when coming from offline auth.
+****************************************************************/
+
+static void krb5_ticket_gain_handler(struct tevent_context *event_ctx,
+ struct tevent_timer *te,
+ struct timeval now,
+ void *private_data)
+{
+ struct WINBINDD_CCACHE_ENTRY *entry =
+ talloc_get_type_abort(private_data, struct WINBINDD_CCACHE_ENTRY);
+#ifdef HAVE_KRB5
+ int ret;
+ struct timeval t;
+ struct WINBINDD_MEMORY_CREDS *cred_ptr = entry->cred_ptr;
+ struct winbindd_domain *domain = NULL;
+#endif
+
+ DBG_DEBUG("event called for: %s, %s\n",
+ entry->ccname, entry->username);
+
+ TALLOC_FREE(entry->event);
+
+#ifdef HAVE_KRB5
+
+ if (!cred_ptr || !cred_ptr->pass) {
+ DEBUG(10,("krb5_ticket_gain_handler: no memory creds\n"));
+ return;
+ }
+
+ if ((domain = find_domain_from_name(entry->realm)) == NULL) {
+ DEBUG(0,("krb5_ticket_gain_handler: unknown domain\n"));
+ return;
+ }
+
+ if (!domain->online) {
+ goto retry_later;
+ }
+
+ set_effective_uid(entry->uid);
+
+ ret = kerberos_kinit_password_ext(entry->principal_name,
+ cred_ptr->pass,
+ 0, /* hm, can we do time correction here ? */
+ &entry->refresh_time,
+ &entry->renew_until,
+ entry->ccname,
+ False, /* no PAC required anymore */
+ True,
+ WINBINDD_PAM_AUTH_KRB5_RENEW_TIME,
+ NULL,
+ NULL,
+ NULL,
+ NULL);
+ gain_root_privilege();
+
+ if (ret) {
+ DEBUG(3,("krb5_ticket_gain_handler: "
+ "could not kinit: %s\n",
+ error_message(ret)));
+ /* evil. If we cannot do it, destroy any the __maybe__
+ * __existing__ ticket */
+ ads_kdestroy(entry->ccname);
+ goto retry_later;
+ }
+
+ DEBUG(10,("krb5_ticket_gain_handler: "
+ "successful kinit for: %s in ccache: %s\n",
+ entry->principal_name, entry->ccname));
+
+ goto got_ticket;
+
+ retry_later:
+
+#if defined(DEBUG_KRB5_TKT_RENEWAL)
+ t = timeval_set(time(NULL) + 30, 0);
+#else
+ t = timeval_current_ofs(MAX(30, lp_winbind_cache_time()), 0);
+#endif
+
+ add_krb5_ticket_gain_handler_event(entry, t);
+ return;
+
+ got_ticket:
+
+#if defined(DEBUG_KRB5_TKT_RENEWAL)
+ t = timeval_set(time(NULL) + 30, 0);
+#else
+ t = timeval_set(krb5_event_refresh_time(entry->refresh_time), 0);
+#endif
+
+ if (entry->refresh_time == 0) {
+ entry->refresh_time = t.tv_sec;
+ }
+ entry->event = tevent_add_timer(global_event_context(),
+ entry,
+ t,
+ krb5_ticket_refresh_handler,
+ entry);
+
+ return;
+#endif
+}
+
+/**************************************************************
+ The gain initial ticket case is recognised as entry->refresh_time
+ is always zero.
+**************************************************************/
+
+static void add_krb5_ticket_gain_handler_event(struct WINBINDD_CCACHE_ENTRY *entry,
+ struct timeval t)
+{
+ entry->refresh_time = 0;
+ entry->event = tevent_add_timer(global_event_context(),
+ entry,
+ t,
+ krb5_ticket_gain_handler,
+ entry);
+}
+
+void ccache_regain_all_now(void)
+{
+ struct WINBINDD_CCACHE_ENTRY *cur;
+ struct timeval t = timeval_current();
+
+ for (cur = ccache_list; cur; cur = cur->next) {
+ struct tevent_timer *new_event;
+
+ /*
+ * if refresh_time is 0, we know that the
+ * the event has the krb5_ticket_gain_handler
+ */
+ if (cur->refresh_time == 0) {
+ new_event = tevent_add_timer(global_event_context(),
+ cur,
+ t,
+ krb5_ticket_gain_handler,
+ cur);
+ } else {
+ new_event = tevent_add_timer(global_event_context(),
+ cur,
+ t,
+ krb5_ticket_refresh_handler,
+ cur);
+ }
+
+ if (!new_event) {
+ continue;
+ }
+
+ TALLOC_FREE(cur->event);
+ cur->event = new_event;
+ }
+
+ return;
+}
+
+/****************************************************************
+ Check if an ccache entry exists.
+****************************************************************/
+
+bool ccache_entry_exists(const char *username)
+{
+ struct WINBINDD_CCACHE_ENTRY *entry = get_ccache_by_username(username);
+ return (entry != NULL);
+}
+
+/****************************************************************
+ Ensure we're changing the correct entry.
+****************************************************************/
+
+bool ccache_entry_identical(const char *username,
+ uid_t uid,
+ const char *ccname)
+{
+ struct WINBINDD_CCACHE_ENTRY *entry = get_ccache_by_username(username);
+
+ if (!entry) {
+ return False;
+ }
+
+ if (entry->uid != uid) {
+ DEBUG(0,("cache_entry_identical: uid's differ: %u != %u\n",
+ (unsigned int)entry->uid, (unsigned int)uid));
+ return False;
+ }
+ if (!strcsequal(entry->ccname, ccname)) {
+ DEBUG(0,("cache_entry_identical: "
+ "ccnames differ: (cache) %s != (client) %s\n",
+ entry->ccname, ccname));
+ return False;
+ }
+ return True;
+}
+
+NTSTATUS add_ccache_to_list(const char *princ_name,
+ const char *ccname,
+ const char *username,
+ const char *pass,
+ const char *realm,
+ uid_t uid,
+ time_t create_time,
+ time_t ticket_end,
+ time_t renew_until,
+ bool postponed_request,
+ const char *canon_principal,
+ const char *canon_realm)
+{
+ struct WINBINDD_CCACHE_ENTRY *entry = NULL;
+ struct timeval t;
+ NTSTATUS ntret;
+
+ if ((username == NULL && princ_name == NULL) ||
+ ccname == NULL || uid == (uid_t)-1) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ if (ccache_entry_count() + 1 > MAX_CCACHES) {
+ DEBUG(10,("add_ccache_to_list: "
+ "max number of ccaches reached\n"));
+ return NT_STATUS_NO_MORE_ENTRIES;
+ }
+
+ /* Reference count old entries */
+ entry = get_ccache_by_username(username);
+ if (entry) {
+ /* Check cached entries are identical. */
+ if (!ccache_entry_identical(username, uid, ccname)) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+ entry->ref_count++;
+ DEBUG(10,("add_ccache_to_list: "
+ "ref count on entry %s is now %d\n",
+ username, entry->ref_count));
+ /* FIXME: in this case we still might want to have a krb5 cred
+ * event handler created - gd
+ * Add ticket refresh handler here */
+
+ if (!lp_winbind_refresh_tickets() || renew_until <= 0) {
+ return NT_STATUS_OK;
+ }
+
+ if (!entry->event) {
+ if (postponed_request) {
+ t = timeval_current_ofs(MAX(30, lp_winbind_cache_time()), 0);
+ add_krb5_ticket_gain_handler_event(entry, t);
+ } else {
+ /* Renew at 1/2 the ticket expiration time */
+#if defined(DEBUG_KRB5_TKT_RENEWAL)
+ t = timeval_set(time(NULL)+30, 0);
+#else
+ t = timeval_set(krb5_event_refresh_time(ticket_end),
+ 0);
+#endif
+ if (!entry->refresh_time) {
+ entry->refresh_time = t.tv_sec;
+ }
+ entry->event = tevent_add_timer(global_event_context(),
+ entry,
+ t,
+ krb5_ticket_refresh_handler,
+ entry);
+ }
+
+ if (!entry->event) {
+ ntret = remove_ccache(username);
+ if (!NT_STATUS_IS_OK(ntret)) {
+ DEBUG(0, ("add_ccache_to_list: Failed to remove krb5 "
+ "ccache %s for user %s\n", entry->ccname,
+ entry->username));
+ DEBUG(0, ("add_ccache_to_list: error is %s\n",
+ nt_errstr(ntret)));
+ return ntret;
+ }
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ DEBUG(10,("add_ccache_to_list: added krb5_ticket handler\n"));
+
+ }
+
+ /*
+ * If we're set up to renew our krb5 tickets, we must
+ * cache the credentials in memory for the ticket
+ * renew function (or increase the reference count
+ * if we're logging in more than once). Fix inspired
+ * by patch from Ian Gordon <ian.gordon@strath.ac.uk>
+ * for bugid #9098.
+ */
+
+ ntret = winbindd_add_memory_creds(username, uid, pass);
+ DEBUG(10, ("winbindd_add_memory_creds returned: %s\n",
+ nt_errstr(ntret)));
+
+ return NT_STATUS_OK;
+ }
+
+ entry = talloc(NULL, struct WINBINDD_CCACHE_ENTRY);
+ if (!entry) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ ZERO_STRUCTP(entry);
+
+ if (username) {
+ entry->username = talloc_strdup(entry, username);
+ if (!entry->username) {
+ goto no_mem;
+ }
+ }
+ if (princ_name) {
+ entry->principal_name = talloc_strdup(entry, princ_name);
+ if (!entry->principal_name) {
+ goto no_mem;
+ }
+ }
+ if (canon_principal != NULL) {
+ entry->canon_principal = talloc_strdup(entry, canon_principal);
+ if (entry->canon_principal == NULL) {
+ goto no_mem;
+ }
+ }
+ if (canon_realm != NULL) {
+ entry->canon_realm = talloc_strdup(entry, canon_realm);
+ if (entry->canon_realm == NULL) {
+ goto no_mem;
+ }
+ }
+
+ entry->ccname = talloc_strdup(entry, ccname);
+ if (!entry->ccname) {
+ goto no_mem;
+ }
+
+ entry->realm = talloc_strdup(entry, realm);
+ if (!entry->realm) {
+ goto no_mem;
+ }
+
+ entry->service = talloc_asprintf(entry,
+ "%s/%s@%s",
+ KRB5_TGS_NAME,
+ canon_realm,
+ canon_realm);
+ if (entry->service == NULL) {
+ goto no_mem;
+ }
+
+ entry->create_time = create_time;
+ entry->renew_until = renew_until;
+ entry->uid = uid;
+ entry->ref_count = 1;
+
+ if (!lp_winbind_refresh_tickets() || renew_until <= 0) {
+ goto add_entry;
+ }
+
+ if (postponed_request) {
+ t = timeval_current_ofs(MAX(30, lp_winbind_cache_time()), 0);
+ add_krb5_ticket_gain_handler_event(entry, t);
+ } else {
+ /* Renew at 1/2 the ticket expiration time */
+#if defined(DEBUG_KRB5_TKT_RENEWAL)
+ t = timeval_set(time(NULL)+30, 0);
+#else
+ t = timeval_set(krb5_event_refresh_time(ticket_end), 0);
+#endif
+ if (entry->refresh_time == 0) {
+ entry->refresh_time = t.tv_sec;
+ }
+ entry->event = tevent_add_timer(global_event_context(),
+ entry,
+ t,
+ krb5_ticket_refresh_handler,
+ entry);
+ }
+
+ if (!entry->event) {
+ goto no_mem;
+ }
+
+ DEBUG(10,("add_ccache_to_list: added krb5_ticket handler\n"));
+
+ add_entry:
+
+ DLIST_ADD(ccache_list, entry);
+
+ DBG_DEBUG("Added ccache [%s] for user [%s] and service [%s]\n",
+ entry->ccname, entry->username, entry->service);
+
+ if (entry->event) {
+ /*
+ * If we're set up to renew our krb5 tickets, we must
+ * cache the credentials in memory for the ticket
+ * renew function. Fix inspired by patch from
+ * Ian Gordon <ian.gordon@strath.ac.uk> for
+ * bugid #9098.
+ */
+
+ ntret = winbindd_add_memory_creds(username, uid, pass);
+ DEBUG(10, ("winbindd_add_memory_creds returned: %s\n",
+ nt_errstr(ntret)));
+ }
+
+ return NT_STATUS_OK;
+
+ no_mem:
+
+ TALLOC_FREE(entry);
+ return NT_STATUS_NO_MEMORY;
+}
+
+/*******************************************************************
+ Remove a WINBINDD_CCACHE_ENTRY entry and the krb5 ccache if no longer
+ referenced.
+ *******************************************************************/
+
+NTSTATUS remove_ccache(const char *username)
+{
+ struct WINBINDD_CCACHE_ENTRY *entry = get_ccache_by_username(username);
+ NTSTATUS status = NT_STATUS_OK;
+#ifdef HAVE_KRB5
+ krb5_error_code ret;
+#endif
+
+ if (!entry) {
+ return NT_STATUS_OBJECT_NAME_NOT_FOUND;
+ }
+
+ if (entry->ref_count <= 0) {
+ DEBUG(0,("remove_ccache: logic error. "
+ "ref count for user %s = %d\n",
+ username, entry->ref_count));
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+
+ entry->ref_count--;
+
+ if (entry->ref_count > 0) {
+ DEBUG(10,("remove_ccache: entry %s ref count now %d\n",
+ username, entry->ref_count));
+ return NT_STATUS_OK;
+ }
+
+ /* no references any more */
+
+ DLIST_REMOVE(ccache_list, entry);
+ TALLOC_FREE(entry->event); /* unregisters events */
+
+#ifdef HAVE_KRB5
+ ret = ads_kdestroy(entry->ccname);
+
+ /* we ignore the error when there has been no credential cache */
+ if (ret == KRB5_FCC_NOFILE) {
+ ret = 0;
+ } else if (ret) {
+ DEBUG(0,("remove_ccache: "
+ "failed to destroy user krb5 ccache %s with: %s\n",
+ entry->ccname, error_message(ret)));
+ } else {
+ DEBUG(10,("remove_ccache: "
+ "successfully destroyed krb5 ccache %s for user %s\n",
+ entry->ccname, username));
+ }
+ status = krb5_to_nt_status(ret);
+#endif
+
+ TALLOC_FREE(entry);
+ DEBUG(10,("remove_ccache: removed ccache for user %s\n", username));
+
+ return status;
+}
+
+/*******************************************************************
+ In memory credentials cache code.
+*******************************************************************/
+
+static struct WINBINDD_MEMORY_CREDS *memory_creds_list;
+
+/***********************************************************
+ Find an entry on the list by name.
+***********************************************************/
+
+struct WINBINDD_MEMORY_CREDS *find_memory_creds_by_name(const char *username)
+{
+ struct WINBINDD_MEMORY_CREDS *p;
+
+ for (p = memory_creds_list; p; p = p->next) {
+ if (strequal(p->username, username)) {
+ return p;
+ }
+ }
+ return NULL;
+}
+
+/***********************************************************
+ Store the required creds and mlock them.
+***********************************************************/
+
+static NTSTATUS store_memory_creds(struct WINBINDD_MEMORY_CREDS *memcredp,
+ const char *pass)
+{
+#if !defined(HAVE_MLOCK)
+ return NT_STATUS_OK;
+#else
+ /* new_entry->nt_hash is the base pointer for the block
+ of memory pointed into by new_entry->lm_hash and
+ new_entry->pass (if we're storing plaintext). */
+
+ memcredp->len = NT_HASH_LEN + LM_HASH_LEN;
+ if (pass) {
+ memcredp->len += strlen(pass)+1;
+ }
+
+
+#if defined(LINUX)
+ /* aligning the memory on on x86_64 and compiling
+ with gcc 4.1 using -O2 causes a segv in the
+ next memset() --jerry */
+ memcredp->nt_hash = SMB_MALLOC_ARRAY(unsigned char, memcredp->len);
+#else
+ /* On non-linux platforms, mlock()'d memory must be aligned */
+ memcredp->nt_hash = SMB_MEMALIGN_ARRAY(unsigned char,
+ getpagesize(), memcredp->len);
+#endif
+ if (!memcredp->nt_hash) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ memset(memcredp->nt_hash, 0x0, memcredp->len);
+
+ memcredp->lm_hash = memcredp->nt_hash + NT_HASH_LEN;
+
+#ifdef DEBUG_PASSWORD
+ DEBUG(10,("mlocking memory: %p\n", memcredp->nt_hash));
+#endif
+ if ((mlock(memcredp->nt_hash, memcredp->len)) == -1) {
+ DEBUG(0,("failed to mlock memory: %s (%d)\n",
+ strerror(errno), errno));
+ SAFE_FREE(memcredp->nt_hash);
+ return map_nt_error_from_unix(errno);
+ }
+
+#ifdef DEBUG_PASSWORD
+ DEBUG(10,("mlocked memory: %p\n", memcredp->nt_hash));
+#endif
+
+ if (pass) {
+ /* Create and store the password hashes. */
+ E_md4hash(pass, memcredp->nt_hash);
+ E_deshash(pass, memcredp->lm_hash);
+
+ memcredp->pass = (char *)memcredp->lm_hash + LM_HASH_LEN;
+ memcpy(memcredp->pass, pass,
+ memcredp->len - NT_HASH_LEN - LM_HASH_LEN);
+ }
+
+ return NT_STATUS_OK;
+#endif
+}
+
+/***********************************************************
+ Destroy existing creds.
+***********************************************************/
+
+static NTSTATUS delete_memory_creds(struct WINBINDD_MEMORY_CREDS *memcredp)
+{
+#if !defined(HAVE_MUNLOCK)
+ return NT_STATUS_OK;
+#else
+ if (munlock(memcredp->nt_hash, memcredp->len) == -1) {
+ DEBUG(0,("failed to munlock memory: %s (%d)\n",
+ strerror(errno), errno));
+ return map_nt_error_from_unix(errno);
+ }
+ memset(memcredp->nt_hash, '\0', memcredp->len);
+ SAFE_FREE(memcredp->nt_hash);
+ memcredp->nt_hash = NULL;
+ memcredp->lm_hash = NULL;
+ memcredp->pass = NULL;
+ memcredp->len = 0;
+ return NT_STATUS_OK;
+#endif
+}
+
+/***********************************************************
+ Replace the required creds with new ones (password change).
+***********************************************************/
+
+static NTSTATUS winbindd_replace_memory_creds_internal(struct WINBINDD_MEMORY_CREDS *memcredp,
+ const char *pass)
+{
+ NTSTATUS status = delete_memory_creds(memcredp);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ return store_memory_creds(memcredp, pass);
+}
+
+/*************************************************************
+ Store credentials in memory in a list.
+*************************************************************/
+
+static NTSTATUS winbindd_add_memory_creds_internal(const char *username,
+ uid_t uid,
+ const char *pass)
+{
+ /* Shortcut to ensure we don't store if no mlock. */
+#if !defined(HAVE_MLOCK) || !defined(HAVE_MUNLOCK)
+ return NT_STATUS_OK;
+#else
+ NTSTATUS status;
+ struct WINBINDD_MEMORY_CREDS *memcredp = NULL;
+
+ memcredp = find_memory_creds_by_name(username);
+ if (uid == (uid_t)-1) {
+ DEBUG(0,("winbindd_add_memory_creds_internal: "
+ "invalid uid for user %s.\n", username));
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ if (memcredp) {
+ /* Already exists. Increment the reference count and replace stored creds. */
+ if (uid != memcredp->uid) {
+ DEBUG(0,("winbindd_add_memory_creds_internal: "
+ "uid %u for user %s doesn't "
+ "match stored uid %u. Replacing.\n",
+ (unsigned int)uid, username,
+ (unsigned int)memcredp->uid));
+ memcredp->uid = uid;
+ }
+ memcredp->ref_count++;
+ DEBUG(10,("winbindd_add_memory_creds_internal: "
+ "ref count for user %s is now %d\n",
+ username, memcredp->ref_count));
+ return winbindd_replace_memory_creds_internal(memcredp, pass);
+ }
+
+ memcredp = talloc_zero(NULL, struct WINBINDD_MEMORY_CREDS);
+ if (!memcredp) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ memcredp->username = talloc_strdup(memcredp, username);
+ if (!memcredp->username) {
+ talloc_destroy(memcredp);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ status = store_memory_creds(memcredp, pass);
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_destroy(memcredp);
+ return status;
+ }
+
+ memcredp->uid = uid;
+ memcredp->ref_count = 1;
+ DLIST_ADD(memory_creds_list, memcredp);
+
+ DEBUG(10,("winbindd_add_memory_creds_internal: "
+ "added entry for user %s\n", username));
+
+ return NT_STATUS_OK;
+#endif
+}
+
+/*************************************************************
+ Store users credentials in memory. If we also have a
+ struct WINBINDD_CCACHE_ENTRY for this username with a
+ refresh timer, then store the plaintext of the password
+ and associate the new credentials with the struct WINBINDD_CCACHE_ENTRY.
+*************************************************************/
+
+NTSTATUS winbindd_add_memory_creds(const char *username,
+ uid_t uid,
+ const char *pass)
+{
+ struct WINBINDD_CCACHE_ENTRY *entry = get_ccache_by_username(username);
+ NTSTATUS status;
+
+ status = winbindd_add_memory_creds_internal(username, uid, pass);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ if (entry) {
+ struct WINBINDD_MEMORY_CREDS *memcredp = NULL;
+ memcredp = find_memory_creds_by_name(username);
+ if (memcredp) {
+ entry->cred_ptr = memcredp;
+ }
+ }
+
+ return status;
+}
+
+/*************************************************************
+ Decrement the in-memory ref count - delete if zero.
+*************************************************************/
+
+NTSTATUS winbindd_delete_memory_creds(const char *username)
+{
+ struct WINBINDD_MEMORY_CREDS *memcredp = NULL;
+ struct WINBINDD_CCACHE_ENTRY *entry = NULL;
+ NTSTATUS status = NT_STATUS_OK;
+
+ memcredp = find_memory_creds_by_name(username);
+ entry = get_ccache_by_username(username);
+
+ if (!memcredp) {
+ DEBUG(10,("winbindd_delete_memory_creds: unknown user %s\n",
+ username));
+ return NT_STATUS_OBJECT_NAME_NOT_FOUND;
+ }
+
+ if (memcredp->ref_count <= 0) {
+ DEBUG(0,("winbindd_delete_memory_creds: logic error. "
+ "ref count for user %s = %d\n",
+ username, memcredp->ref_count));
+ status = NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+
+ memcredp->ref_count--;
+ if (memcredp->ref_count <= 0) {
+ delete_memory_creds(memcredp);
+ DLIST_REMOVE(memory_creds_list, memcredp);
+ talloc_destroy(memcredp);
+ DEBUG(10,("winbindd_delete_memory_creds: "
+ "deleted entry for user %s\n",
+ username));
+ } else {
+ DEBUG(10,("winbindd_delete_memory_creds: "
+ "entry for user %s ref_count now %d\n",
+ username, memcredp->ref_count));
+ }
+
+ if (entry) {
+ /* Ensure we have no dangling references to this. */
+ entry->cred_ptr = NULL;
+ }
+
+ return status;
+}
+
+/***********************************************************
+ Replace the required creds with new ones (password change).
+***********************************************************/
+
+NTSTATUS winbindd_replace_memory_creds(const char *username,
+ const char *pass)
+{
+ struct WINBINDD_MEMORY_CREDS *memcredp = NULL;
+
+ memcredp = find_memory_creds_by_name(username);
+ if (!memcredp) {
+ DEBUG(10,("winbindd_replace_memory_creds: unknown user %s\n",
+ username));
+ return NT_STATUS_OBJECT_NAME_NOT_FOUND;
+ }
+
+ DEBUG(10,("winbindd_replace_memory_creds: replaced creds for user %s\n",
+ username));
+
+ return winbindd_replace_memory_creds_internal(memcredp, pass);
+}
diff --git a/source3/winbindd/winbindd_creds.c b/source3/winbindd/winbindd_creds.c
new file mode 100644
index 0000000..a0cce7e
--- /dev/null
+++ b/source3/winbindd/winbindd_creds.c
@@ -0,0 +1,147 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Winbind daemon - cached credentials functions
+
+ Copyright (C) Guenther Deschner 2005
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "winbindd.h"
+#include "../libcli/auth/libcli_auth.h"
+#include "../libcli/security/security.h"
+#include "libsmb/samlogon_cache.h"
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_WINBIND
+
+#define MAX_CACHED_LOGINS 10
+
+NTSTATUS winbindd_get_creds(struct winbindd_domain *domain,
+ TALLOC_CTX *mem_ctx,
+ const struct dom_sid *sid,
+ struct netr_SamInfo3 **info3,
+ const uint8_t **cached_nt_pass,
+ const uint8_t **cred_salt)
+{
+ struct netr_SamInfo3 *info;
+ NTSTATUS status;
+
+ status = wcache_get_creds(domain, mem_ctx, sid, cached_nt_pass, cred_salt);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ info = netsamlogon_cache_get(mem_ctx, sid);
+ if (info == NULL) {
+ return NT_STATUS_OBJECT_NAME_NOT_FOUND;
+ }
+
+ *info3 = info;
+
+ return NT_STATUS_OK;
+}
+
+
+NTSTATUS winbindd_store_creds(struct winbindd_domain *domain,
+ const char *user,
+ const char *pass,
+ struct netr_SamInfo3 *info3)
+{
+ NTSTATUS status;
+ uchar nt_pass[NT_HASH_LEN];
+ struct dom_sid cred_sid;
+
+ if (info3 != NULL) {
+
+ sid_compose(&cred_sid, info3->base.domain_sid,
+ info3->base.rid);
+ info3->base.user_flags |= NETLOGON_CACHED_ACCOUNT;
+
+ } else if (user != NULL) {
+
+ /* do lookup ourself */
+
+ enum lsa_SidType type;
+
+ if (!lookup_cached_name(domain->name, /* namespace */
+ domain->name,
+ user,
+ &cred_sid,
+ &type)) {
+ return NT_STATUS_NO_SUCH_USER;
+ }
+ } else {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ if (pass) {
+
+ int count = 0;
+
+ status = wcache_count_cached_creds(domain, &count);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ DEBUG(11,("we have %d cached creds\n", count));
+
+ if (count + 1 > MAX_CACHED_LOGINS) {
+
+ DEBUG(10,("need to delete the oldest cached login\n"));
+
+ status = wcache_remove_oldest_cached_creds(domain, &cred_sid);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(10,("failed to remove oldest cached cred: %s\n",
+ nt_errstr(status)));
+ return status;
+ }
+ }
+
+ E_md4hash(pass, nt_pass);
+
+ dump_data_pw("nt_pass", nt_pass, NT_HASH_LEN);
+
+ status = wcache_save_creds(domain, &cred_sid, nt_pass);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ }
+
+ if (info3 != NULL && user != NULL) {
+ if (!netsamlogon_cache_store(user, info3)) {
+ return NT_STATUS_ACCESS_DENIED;
+ }
+ }
+
+ return NT_STATUS_OK;
+}
+
+NTSTATUS winbindd_update_creds_by_info3(struct winbindd_domain *domain,
+ const char *user,
+ const char *pass,
+ struct netr_SamInfo3 *info3)
+{
+ return winbindd_store_creds(domain, user, pass, info3);
+}
+
+NTSTATUS winbindd_update_creds_by_name(struct winbindd_domain *domain,
+ const char *user,
+ const char *pass)
+{
+ return winbindd_store_creds(domain, user, pass, NULL);
+}
+
+
diff --git a/source3/winbindd/winbindd_domain.c b/source3/winbindd/winbindd_domain.c
new file mode 100644
index 0000000..4c8aa9a
--- /dev/null
+++ b/source3/winbindd/winbindd_domain.c
@@ -0,0 +1,36 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Winbind domain child functions
+
+ Copyright (C) Stefan Metzmacher 2007
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "winbindd.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_WINBIND
+
+void setup_domain_child(struct winbindd_domain *domain)
+{
+ int i;
+
+ for (i=0; i<talloc_array_length(domain->children); i++) {
+ setup_child(domain, &domain->children[i],
+ "log.wb", domain->name);
+ }
+}
diff --git a/source3/winbindd/winbindd_domain_info.c b/source3/winbindd/winbindd_domain_info.c
new file mode 100644
index 0000000..c4364d9
--- /dev/null
+++ b/source3/winbindd/winbindd_domain_info.c
@@ -0,0 +1,141 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * async implementation of WINBINDD_DOMAIN_INFO
+ * Copyright (C) Volker Lendecke 2018
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "includes.h"
+#include "winbindd.h"
+#include "lib/util/string_wrappers.h"
+#include "lib/global_contexts.h"
+#include "librpc/gen_ndr/ndr_winbind_c.h"
+
+struct winbindd_domain_info_state {
+ struct winbindd_domain *domain;
+ uint32_t in;
+ uint32_t out;
+};
+
+static void winbindd_domain_info_done(struct tevent_req *subreq);
+
+struct tevent_req *winbindd_domain_info_send(
+ TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct winbindd_cli_state *cli,
+ struct winbindd_request *request)
+{
+ struct tevent_req *req, *subreq;
+ struct winbindd_domain_info_state *state;
+
+ req = tevent_req_create(mem_ctx, &state,
+ struct winbindd_domain_info_state);
+ if (req == NULL) {
+ return NULL;
+ }
+
+ DEBUG(3, ("[%5lu]: domain_info [%s]\n", (unsigned long)cli->pid,
+ cli->request->domain_name));
+
+ state->domain = find_domain_from_name_noinit(
+ cli->request->domain_name);
+
+ if (state->domain == NULL) {
+ DEBUG(3, ("Did not find domain [%s]\n",
+ cli->request->domain_name));
+ tevent_req_nterror(req, NT_STATUS_NO_SUCH_DOMAIN);
+ return tevent_req_post(req, ev);
+ }
+
+ if (state->domain->initialized) {
+ tevent_req_done(req);
+ return tevent_req_post(req, ev);
+ }
+
+ /*
+ * Send a ping down. This implicitly initializes the domain.
+ */
+
+ state->in = cli->pid;
+ state->out = 0;
+ subreq = dcerpc_wbint_Ping_send(state,
+ global_event_context(),
+ dom_child_handle(state->domain),
+ state->in,
+ &state->out);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, winbindd_domain_info_done, req);
+
+ return req;
+}
+
+static void winbindd_domain_info_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct winbindd_domain_info_state *state = tevent_req_data(
+ req, struct winbindd_domain_info_state);
+ NTSTATUS status, result;
+
+ status = dcerpc_wbint_Ping_recv(subreq, state, &result);
+ TALLOC_FREE(subreq);
+ if (tevent_req_nterror(req, status)) {
+ DBG_NOTICE("dcerpc_wbint_Ping call failed: %s\n",
+ nt_errstr(status));
+ return;
+ }
+
+ if (tevent_req_nterror(req, result)) {
+ DBG_NOTICE("dcerpc_wbint_Ping failed: %s\n",
+ nt_errstr(result));
+ return;
+ }
+
+ if (!state->domain->initialized) {
+ DBG_INFO("dcerpc_wbint_Ping did not initialize domain %s\n",
+ state->domain->name);
+ tevent_req_nterror(req, NT_STATUS_INTERNAL_ERROR);
+ return;
+ }
+
+ tevent_req_done(req);
+}
+
+NTSTATUS winbindd_domain_info_recv(struct tevent_req *req,
+ struct winbindd_response *response)
+{
+ struct winbindd_domain_info_state *state = tevent_req_data(
+ req, struct winbindd_domain_info_state);
+ struct winbindd_domain *domain = state->domain;
+ NTSTATUS status;
+
+ if (tevent_req_is_nterror(req, &status)) {
+ DBG_NOTICE("winbindd_domain_info failed: %s\n",
+ nt_errstr(status));
+ return status;
+ }
+
+ fstrcpy(response->data.domain_info.name, domain->name);
+ fstrcpy(response->data.domain_info.alt_name, domain->alt_name);
+ sid_to_fstring(response->data.domain_info.sid, &domain->sid);
+
+ response->data.domain_info.native_mode = domain->native_mode;
+ response->data.domain_info.active_directory = domain->active_directory;
+ response->data.domain_info.primary = domain->primary;
+
+ return NT_STATUS_OK;
+}
diff --git a/source3/winbindd/winbindd_dsgetdcname.c b/source3/winbindd/winbindd_dsgetdcname.c
new file mode 100644
index 0000000..9dadeab
--- /dev/null
+++ b/source3/winbindd/winbindd_dsgetdcname.c
@@ -0,0 +1,200 @@
+/*
+ Unix SMB/CIFS implementation.
+ async implementation of WINBINDD_DSGETDCNAME
+ Copyright (C) Volker Lendecke 2009
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "util/debug.h"
+#include "winbindd.h"
+#include "librpc/gen_ndr/ndr_winbind_c.h"
+#include "lib/util/string_wrappers.h"
+
+struct winbindd_dsgetdcname_state {
+ struct GUID guid;
+ struct netr_DsRGetDCNameInfo *dc_info;
+};
+
+static uint32_t get_dsgetdc_flags(uint32_t wbc_flags);
+static void winbindd_dsgetdcname_done(struct tevent_req *subreq);
+
+struct tevent_req *winbindd_dsgetdcname_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct winbindd_cli_state *cli,
+ struct winbindd_request *request)
+{
+ struct tevent_req *req, *subreq;
+ struct dcerpc_binding_handle *child_binding_handle = NULL;
+ struct winbindd_dsgetdcname_state *state;
+ struct GUID *guid_ptr = NULL;
+ uint32_t ds_flags = 0;
+ NTSTATUS status;
+
+ req = tevent_req_create(mem_ctx, &state,
+ struct winbindd_dsgetdcname_state);
+ if (req == NULL) {
+ return NULL;
+ }
+
+ D_NOTICE("[%s (%u)] Winbind external command DSGETDCNAME start.\n",
+ cli->client_name,
+ (unsigned int)cli->pid);
+
+ request->data.dsgetdcname.domain_name
+ [sizeof(request->data.dsgetdcname.domain_name)-1] = '\0';
+ request->data.dsgetdcname.site_name
+ [sizeof(request->data.dsgetdcname.site_name)-1] = '\0';
+ request->data.dsgetdcname.domain_guid
+ [sizeof(request->data.dsgetdcname.domain_guid)-1] = '\0';
+
+ D_NOTICE("Calling DsGetDcName for domain '%s'.\n",
+ request->data.dsgetdcname.domain_name);
+
+ ds_flags = get_dsgetdc_flags(request->data.dsgetdcname.flags);
+
+ status = GUID_from_string(request->data.dsgetdcname.domain_guid,
+ &state->guid);
+ if (NT_STATUS_IS_OK(status) && !GUID_all_zero(&state->guid)) {
+ guid_ptr = &state->guid;
+ }
+
+ child_binding_handle = locator_child_handle();
+
+ subreq = dcerpc_wbint_DsGetDcName_send(
+ state, ev, child_binding_handle,
+ request->data.dsgetdcname.domain_name, guid_ptr,
+ request->data.dsgetdcname.site_name,
+ ds_flags, &state->dc_info);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, winbindd_dsgetdcname_done, req);
+ return req;
+}
+
+static void winbindd_dsgetdcname_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct winbindd_dsgetdcname_state *state = tevent_req_data(
+ req, struct winbindd_dsgetdcname_state);
+ NTSTATUS status, result;
+
+ status = dcerpc_wbint_DsGetDcName_recv(subreq, state, &result);
+ TALLOC_FREE(subreq);
+ if (any_nt_status_not_ok(status, result, &status)) {
+ tevent_req_nterror(req, status);
+ return;
+ }
+ tevent_req_done(req);
+}
+
+NTSTATUS winbindd_dsgetdcname_recv(struct tevent_req *req,
+ struct winbindd_response *response)
+{
+ struct winbindd_dsgetdcname_state *state = tevent_req_data(
+ req, struct winbindd_dsgetdcname_state);
+ struct GUID_txt_buf guid_str_buf;
+ char *guid_str;
+ NTSTATUS status;
+
+ D_NOTICE("Winbind external command DSGETDCNAME end.\n");
+ if (tevent_req_is_nterror(req, &status)) {
+ D_WARNING("Failed with: %s\n", nt_errstr(status));
+ return status;
+ }
+
+ fstrcpy(response->data.dsgetdcname.dc_unc,
+ state->dc_info->dc_unc);
+ fstrcpy(response->data.dsgetdcname.dc_address,
+ state->dc_info->dc_address);
+ response->data.dsgetdcname.dc_address_type =
+ state->dc_info->dc_address_type;
+
+ guid_str = GUID_buf_string(&state->dc_info->domain_guid,
+ &guid_str_buf);
+ fstrcpy(response->data.dsgetdcname.domain_guid, guid_str);
+
+ fstrcpy(response->data.dsgetdcname.domain_name,
+ state->dc_info->domain_name);
+ fstrcpy(response->data.dsgetdcname.forest_name,
+ state->dc_info->forest_name);
+ response->data.dsgetdcname.dc_flags = state->dc_info->dc_flags;
+ fstrcpy(response->data.dsgetdcname.dc_site_name,
+ state->dc_info->dc_site_name);
+ fstrcpy(response->data.dsgetdcname.client_site_name,
+ state->dc_info->client_site_name);
+
+ return NT_STATUS_OK;
+}
+
+static uint32_t get_dsgetdc_flags(uint32_t wbc_flags)
+{
+ struct wbc_flag_map {
+ uint32_t wbc_dc_flag;
+ uint32_t ds_dc_flags;
+ } lookup_dc_flags[] = {
+ { WBC_LOOKUP_DC_FORCE_REDISCOVERY,
+ DS_FORCE_REDISCOVERY },
+ { WBC_LOOKUP_DC_DS_REQUIRED,
+ DS_DIRECTORY_SERVICE_REQUIRED },
+ { WBC_LOOKUP_DC_DS_PREFERRED,
+ DS_DIRECTORY_SERVICE_PREFERRED},
+ { WBC_LOOKUP_DC_GC_SERVER_REQUIRED,
+ DS_GC_SERVER_REQUIRED },
+ { WBC_LOOKUP_DC_PDC_REQUIRED,
+ DS_PDC_REQUIRED},
+ { WBC_LOOKUP_DC_BACKGROUND_ONLY,
+ DS_BACKGROUND_ONLY },
+ { WBC_LOOKUP_DC_IP_REQUIRED,
+ DS_IP_REQUIRED },
+ { WBC_LOOKUP_DC_KDC_REQUIRED,
+ DS_KDC_REQUIRED },
+ { WBC_LOOKUP_DC_TIMESERV_REQUIRED,
+ DS_TIMESERV_REQUIRED },
+ { WBC_LOOKUP_DC_WRITABLE_REQUIRED,
+ DS_WRITABLE_REQUIRED },
+ { WBC_LOOKUP_DC_GOOD_TIMESERV_PREFERRED,
+ DS_GOOD_TIMESERV_PREFERRED },
+ { WBC_LOOKUP_DC_AVOID_SELF,
+ DS_AVOID_SELF },
+ { WBC_LOOKUP_DC_ONLY_LDAP_NEEDED,
+ DS_ONLY_LDAP_NEEDED },
+ { WBC_LOOKUP_DC_IS_FLAT_NAME,
+ DS_IS_FLAT_NAME },
+ { WBC_LOOKUP_DC_IS_DNS_NAME,
+ DS_IS_DNS_NAME },
+ { WBC_LOOKUP_DC_TRY_NEXTCLOSEST_SITE,
+ DS_TRY_NEXTCLOSEST_SITE },
+ { WBC_LOOKUP_DC_DS_6_REQUIRED,
+ DS_DIRECTORY_SERVICE_6_REQUIRED },
+ { WBC_LOOKUP_DC_RETURN_DNS_NAME,
+ DS_RETURN_DNS_NAME },
+ { WBC_LOOKUP_DC_RETURN_FLAT_NAME,
+ DS_RETURN_FLAT_NAME }
+ };
+
+ uint32_t ds_flags = 0;
+ size_t i = 0;
+
+ for (i=0; i<ARRAY_SIZE(lookup_dc_flags); i++) {
+ if (wbc_flags & lookup_dc_flags[i].wbc_dc_flag) {
+ ds_flags |= lookup_dc_flags[i].ds_dc_flags;
+ }
+ }
+
+ return ds_flags;
+}
diff --git a/source3/winbindd/winbindd_dual.c b/source3/winbindd/winbindd_dual.c
new file mode 100644
index 0000000..e63b405
--- /dev/null
+++ b/source3/winbindd/winbindd_dual.c
@@ -0,0 +1,2093 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Winbind child daemons
+
+ Copyright (C) Andrew Tridgell 2002
+ Copyright (C) Volker Lendecke 2004,2005
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+/*
+ * We fork a child per domain to be able to act non-blocking in the main
+ * winbind daemon. A domain controller thousands of miles away being being
+ * slow replying with a 10.000 user list should not hold up netlogon calls
+ * that can be handled locally.
+ */
+
+#include "includes.h"
+#include "winbindd.h"
+#include "rpc_client/rpc_client.h"
+#include "nsswitch/wb_reqtrans.h"
+#include "secrets.h"
+#include "../lib/util/select.h"
+#include "winbindd_traceid.h"
+#include "../libcli/security/security.h"
+#include "system/select.h"
+#include "messages.h"
+#include "../lib/util/tevent_unix.h"
+#include "lib/param/loadparm.h"
+#include "lib/util/sys_rw.h"
+#include "lib/util/sys_rw_data.h"
+#include "passdb.h"
+#include "lib/util/string_wrappers.h"
+#include "lib/global_contexts.h"
+#include "idmap.h"
+#include "libcli/auth/netlogon_creds_cli.h"
+#include "../lib/util/pidfile.h"
+#include "librpc/gen_ndr/ndr_winbind_c.h"
+#include "lib/util/util_process.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_WINBIND
+
+static void forall_domain_children(bool (*fn)(struct winbindd_child *c,
+ void *private_data),
+ void *private_data)
+{
+ struct winbindd_domain *d;
+
+ for (d = domain_list(); d != NULL; d = d->next) {
+ int i;
+
+ for (i = 0; i < talloc_array_length(d->children); i++) {
+ struct winbindd_child *c = &d->children[i];
+ bool ok;
+
+ if (c->pid == 0) {
+ continue;
+ }
+
+ ok = fn(c, private_data);
+ if (!ok) {
+ return;
+ }
+ }
+ }
+}
+
+static void forall_children(bool (*fn)(struct winbindd_child *c,
+ void *private_data),
+ void *private_data)
+{
+ struct winbindd_child *c;
+ bool ok;
+
+ c = idmap_child();
+ if (c->pid != 0) {
+ ok = fn(c, private_data);
+ if (!ok) {
+ return;
+ }
+ }
+
+ c = locator_child();
+ if (c->pid != 0) {
+ ok = fn(c, private_data);
+ if (!ok) {
+ return;
+ }
+ }
+
+ forall_domain_children(fn, private_data);
+}
+
+/* Read some data from a client connection */
+
+static NTSTATUS child_read_request(int sock, struct winbindd_request *wreq)
+{
+ NTSTATUS status;
+
+ status = read_data_ntstatus(sock, (char *)wreq, sizeof(*wreq));
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(3, ("child_read_request: read_data failed: %s\n",
+ nt_errstr(status)));
+ return status;
+ }
+
+ if (wreq->extra_len == 0) {
+ wreq->extra_data.data = NULL;
+ return NT_STATUS_OK;
+ }
+
+ DEBUG(10, ("Need to read %d extra bytes\n", (int)wreq->extra_len));
+
+ wreq->extra_data.data = SMB_MALLOC_ARRAY(char, wreq->extra_len + 1);
+ if (wreq->extra_data.data == NULL) {
+ DEBUG(0, ("malloc failed\n"));
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ /* Ensure null termination */
+ wreq->extra_data.data[wreq->extra_len] = '\0';
+
+ status = read_data_ntstatus(sock, wreq->extra_data.data,
+ wreq->extra_len);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0, ("Could not read extra data: %s\n",
+ nt_errstr(status)));
+ }
+ return status;
+}
+
+static NTSTATUS child_write_response(int sock, struct winbindd_response *wrsp)
+{
+ struct iovec iov[2];
+ int iov_count;
+
+ iov[0].iov_base = (void *)wrsp;
+ iov[0].iov_len = sizeof(struct winbindd_response);
+ iov_count = 1;
+
+ if (wrsp->length > sizeof(struct winbindd_response)) {
+ iov[1].iov_base = (void *)wrsp->extra_data.data;
+ iov[1].iov_len = wrsp->length-iov[0].iov_len;
+ iov_count = 2;
+ }
+
+ DEBUG(10, ("Writing %d bytes to parent\n", (int)wrsp->length));
+
+ if (write_data_iov(sock, iov, iov_count) != wrsp->length) {
+ DEBUG(0, ("Could not write result\n"));
+ return NT_STATUS_INVALID_HANDLE;
+ }
+
+ return NT_STATUS_OK;
+}
+
+/*
+ * Do winbind child async request. This is not simply wb_simple_trans. We have
+ * to do the queueing ourselves because while a request is queued, the child
+ * might have crashed, and we have to re-fork it in the _trigger function.
+ */
+
+struct wb_child_request_state {
+ struct tevent_context *ev;
+ struct tevent_req *queue_subreq;
+ struct tevent_req *subreq;
+ struct winbindd_child *child;
+ struct winbindd_request *request;
+ struct winbindd_response *response;
+};
+
+static bool fork_domain_child(struct winbindd_child *child);
+
+static void wb_child_request_waited(struct tevent_req *subreq);
+static void wb_child_request_done(struct tevent_req *subreq);
+static void wb_child_request_orphaned(struct tevent_req *subreq);
+
+static void wb_child_request_cleanup(struct tevent_req *req,
+ enum tevent_req_state req_state);
+
+struct tevent_req *wb_child_request_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct winbindd_child *child,
+ struct winbindd_request *request)
+{
+ struct tevent_req *req;
+ struct wb_child_request_state *state;
+ struct tevent_req *subreq;
+
+ req = tevent_req_create(mem_ctx, &state,
+ struct wb_child_request_state);
+ if (req == NULL) {
+ return NULL;
+ }
+
+ state->ev = ev;
+ state->child = child;
+
+ /*
+ * We have to make a copy of "request", because our caller
+ * might drop us via talloc_free().
+ *
+ * The talloc_move() magic in wb_child_request_cleanup() keeps
+ * all the requests, but if we are sitting deep within
+ * writev_send() down to the client, we have given it the
+ * pointer to "request". As our caller lost interest, it will
+ * just free "request", while writev_send still references it.
+ */
+
+ state->request = talloc_memdup(state, request, sizeof(*request));
+ if (tevent_req_nomem(state->request, req)) {
+ return tevent_req_post(req, ev);
+ }
+
+ state->request->traceid = debug_traceid_get();
+
+ if (request->extra_data.data != NULL) {
+ state->request->extra_data.data = talloc_memdup(
+ state->request,
+ request->extra_data.data,
+ request->extra_len);
+ if (tevent_req_nomem(state->request->extra_data.data, req)) {
+ return tevent_req_post(req, ev);
+ }
+ }
+
+ subreq = tevent_queue_wait_send(state, ev, child->queue);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, wb_child_request_waited, req);
+ state->queue_subreq = subreq;
+
+ tevent_req_set_cleanup_fn(req, wb_child_request_cleanup);
+
+ return req;
+}
+
+static void wb_child_request_waited(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct wb_child_request_state *state = tevent_req_data(
+ req, struct wb_child_request_state);
+ bool ok;
+
+ ok = tevent_queue_wait_recv(subreq);
+ if (!ok) {
+ tevent_req_oom(req);
+ return;
+ }
+ /*
+ * We need to keep state->queue_subreq
+ * in order to block the queue.
+ */
+ subreq = NULL;
+
+ if ((state->child->sock == -1) && (!fork_domain_child(state->child))) {
+ tevent_req_error(req, errno);
+ return;
+ }
+
+ tevent_fd_set_flags(state->child->monitor_fde, 0);
+
+ subreq = wb_simple_trans_send(state, global_event_context(), NULL,
+ state->child->sock, state->request);
+ if (tevent_req_nomem(subreq, req)) {
+ return;
+ }
+
+ state->subreq = subreq;
+ tevent_req_set_callback(subreq, wb_child_request_done, req);
+ tevent_req_set_endtime(req, state->ev, timeval_current_ofs(300, 0));
+}
+
+static void wb_child_request_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct wb_child_request_state *state = tevent_req_data(
+ req, struct wb_child_request_state);
+ int ret, err;
+
+ ret = wb_simple_trans_recv(subreq, state, &state->response, &err);
+ /* Freeing the subrequest is deferred until the cleanup function,
+ * which has to know whether a subrequest exists, and consequently
+ * decide whether to shut down the pipe to the child process.
+ */
+ if (ret == -1) {
+ tevent_req_error(req, err);
+ return;
+ }
+ tevent_req_done(req);
+}
+
+static void wb_child_request_orphaned(struct tevent_req *subreq)
+{
+ struct winbindd_child *child =
+ (struct winbindd_child *)tevent_req_callback_data_void(subreq);
+
+ DBG_WARNING("cleanup orphaned subreq[%p]\n", subreq);
+ TALLOC_FREE(subreq);
+
+ if (child->domain != NULL) {
+ /*
+ * If the child is attached to a domain,
+ * we need to make sure the domain queue
+ * can move forward, after the orphaned
+ * request is done.
+ */
+ tevent_queue_start(child->domain->queue);
+ }
+}
+
+int wb_child_request_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx,
+ struct winbindd_response **presponse, int *err)
+{
+ struct wb_child_request_state *state = tevent_req_data(
+ req, struct wb_child_request_state);
+
+ if (tevent_req_is_unix_error(req, err)) {
+ return -1;
+ }
+ *presponse = talloc_move(mem_ctx, &state->response);
+ return 0;
+}
+
+static void wb_child_request_cleanup(struct tevent_req *req,
+ enum tevent_req_state req_state)
+{
+ struct wb_child_request_state *state =
+ tevent_req_data(req, struct wb_child_request_state);
+
+ if (state->subreq == NULL) {
+ /* nothing to cleanup */
+ return;
+ }
+
+ if (req_state == TEVENT_REQ_RECEIVED) {
+ struct tevent_req *subreq = NULL;
+
+ /*
+ * Our caller gave up, but we need to keep
+ * the low level request (wb_simple_trans)
+ * in order to maintain the parent child protocol.
+ *
+ * We also need to keep the child queue blocked
+ * until we got the response from the child.
+ */
+
+ subreq = talloc_move(state->child->queue, &state->subreq);
+ talloc_move(subreq, &state->queue_subreq);
+ talloc_move(subreq, &state->request);
+ tevent_req_set_callback(subreq,
+ wb_child_request_orphaned,
+ state->child);
+
+ DBG_WARNING("keep orphaned subreq[%p]\n", subreq);
+ return;
+ }
+
+ TALLOC_FREE(state->subreq);
+ TALLOC_FREE(state->queue_subreq);
+
+ tevent_fd_set_flags(state->child->monitor_fde, TEVENT_FD_READ);
+
+ if (state->child->domain != NULL) {
+ /*
+ * If the child is attached to a domain,
+ * we need to make sure the domain queue
+ * can move forward, after the request
+ * is done.
+ */
+ tevent_queue_start(state->child->domain->queue);
+ }
+
+ if (req_state == TEVENT_REQ_DONE) {
+ /* transmitted request and got response */
+ return;
+ }
+
+ /*
+ * Failed to transmit and receive response, or request
+ * cancelled while being serviced.
+ * The basic parent/child communication broke, close
+ * our socket
+ */
+ TALLOC_FREE(state->child->monitor_fde);
+ close(state->child->sock);
+ state->child->sock = -1;
+}
+
+static void child_socket_readable(struct tevent_context *ev,
+ struct tevent_fd *fde,
+ uint16_t flags,
+ void *private_data)
+{
+ struct winbindd_child *child = private_data;
+
+ if ((flags & TEVENT_FD_READ) == 0) {
+ return;
+ }
+
+ TALLOC_FREE(child->monitor_fde);
+
+ /*
+ * We're only active when there is no outstanding child
+ * request. Arriving here means the child closed its socket,
+ * it died. Do the same here.
+ */
+
+ SMB_ASSERT(child->sock != -1);
+
+ close(child->sock);
+ child->sock = -1;
+}
+
+static struct winbindd_child *choose_domain_child(struct winbindd_domain *domain)
+{
+ struct winbindd_child *shortest = &domain->children[0];
+ struct winbindd_child *current;
+ int i;
+
+ for (i=0; i<talloc_array_length(domain->children); i++) {
+ size_t shortest_len, current_len;
+
+ current = &domain->children[i];
+ current_len = tevent_queue_length(current->queue);
+
+ if (current_len == 0) {
+ /* idle child */
+ return current;
+ }
+
+ shortest_len = tevent_queue_length(shortest->queue);
+
+ if (current_len < shortest_len) {
+ shortest = current;
+ }
+ }
+
+ return shortest;
+}
+
+struct dcerpc_binding_handle *dom_child_handle(struct winbindd_domain *domain)
+{
+ return domain->binding_handle;
+}
+
+struct wb_domain_request_state {
+ struct tevent_context *ev;
+ struct tevent_queue_entry *queue_entry;
+ struct winbindd_domain *domain;
+ struct winbindd_child *child;
+ struct winbindd_request *request;
+ struct winbindd_request *init_req;
+ struct winbindd_response *response;
+ struct tevent_req *pending_subreq;
+ struct wbint_InitConnection r;
+};
+
+static void wb_domain_request_cleanup(struct tevent_req *req,
+ enum tevent_req_state req_state)
+{
+ struct wb_domain_request_state *state = tevent_req_data(
+ req, struct wb_domain_request_state);
+
+ /*
+ * If we're completely done or got a failure.
+ * we should remove ourself from the domain queue,
+ * after removing the child subreq from the child queue
+ * and give the next one in the queue the chance
+ * to check for an idle child.
+ */
+ TALLOC_FREE(state->pending_subreq);
+ TALLOC_FREE(state->queue_entry);
+ tevent_queue_start(state->domain->queue);
+}
+
+static void wb_domain_request_trigger(struct tevent_req *req,
+ void *private_data);
+static void wb_domain_request_gotdc(struct tevent_req *subreq);
+static void wb_domain_request_initialized(struct tevent_req *subreq);
+static void wb_domain_request_done(struct tevent_req *subreq);
+
+struct tevent_req *wb_domain_request_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct winbindd_domain *domain,
+ struct winbindd_request *request)
+{
+ struct tevent_req *req;
+ struct wb_domain_request_state *state;
+
+ req = tevent_req_create(mem_ctx, &state,
+ struct wb_domain_request_state);
+ if (req == NULL) {
+ return NULL;
+ }
+
+ state->domain = domain;
+ state->ev = ev;
+ state->request = request;
+
+ tevent_req_set_cleanup_fn(req, wb_domain_request_cleanup);
+
+ state->queue_entry = tevent_queue_add_entry(
+ domain->queue, state->ev, req,
+ wb_domain_request_trigger, NULL);
+ if (tevent_req_nomem(state->queue_entry, req)) {
+ return tevent_req_post(req, ev);
+ }
+
+ return req;
+}
+
+static void wb_domain_request_trigger(struct tevent_req *req,
+ void *private_data)
+{
+ struct wb_domain_request_state *state = tevent_req_data(
+ req, struct wb_domain_request_state);
+ struct winbindd_domain *domain = state->domain;
+ struct tevent_req *subreq = NULL;
+ size_t shortest_queue_length;
+
+ state->child = choose_domain_child(domain);
+ shortest_queue_length = tevent_queue_length(state->child->queue);
+ if (shortest_queue_length > 0) {
+ /*
+ * All children are busy, we need to stop
+ * the queue and untrigger our own queue
+ * entry. Once a pending request
+ * is done it calls tevent_queue_start
+ * and we get retriggered.
+ */
+ state->child = NULL;
+ tevent_queue_stop(state->domain->queue);
+ tevent_queue_entry_untrigger(state->queue_entry);
+ return;
+ }
+
+ if (domain->initialized) {
+ subreq = wb_child_request_send(state, state->ev, state->child,
+ state->request);
+ if (tevent_req_nomem(subreq, req)) {
+ return;
+ }
+ tevent_req_set_callback(subreq, wb_domain_request_done, req);
+ state->pending_subreq = subreq;
+
+ /*
+ * Once the domain is initialized and
+ * once we placed our real request into the child queue,
+ * we can remove ourself from the domain queue
+ * and give the next one in the queue the chance
+ * to check for an idle child.
+ */
+ TALLOC_FREE(state->queue_entry);
+ return;
+ }
+
+ state->init_req = talloc_zero(state, struct winbindd_request);
+ if (tevent_req_nomem(state->init_req, req)) {
+ return;
+ }
+
+ if (IS_DC || domain->primary || domain->internal) {
+ /* The primary domain has to find the DC name itself */
+ state->r.in.dcname = talloc_strdup(state, "");
+ if (tevent_req_nomem(state->r.in.dcname, req)) {
+ return;
+ }
+
+ subreq = dcerpc_wbint_InitConnection_r_send(state,
+ state->ev,
+ state->child->binding_handle,
+ &state->r);
+ if (tevent_req_nomem(subreq, req)) {
+ return;
+ }
+ tevent_req_set_callback(subreq, wb_domain_request_initialized,
+ req);
+ state->pending_subreq = subreq;
+ return;
+ }
+
+ /*
+ * This is *not* the primary domain,
+ * let's ask our DC about a DC name.
+ *
+ * We prefer getting a dns name in dc_unc,
+ * which is indicated by DS_RETURN_DNS_NAME.
+ * For NT4 domains we still get the netbios name.
+ */
+ subreq = wb_dsgetdcname_send(state, state->ev,
+ state->domain->name,
+ NULL, /* domain_guid */
+ NULL, /* site_name */
+ DS_RETURN_DNS_NAME); /* flags */
+ if (tevent_req_nomem(subreq, req)) {
+ return;
+ }
+ tevent_req_set_callback(subreq, wb_domain_request_gotdc, req);
+ state->pending_subreq = subreq;
+ return;
+}
+
+static void wb_domain_request_gotdc(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct wb_domain_request_state *state = tevent_req_data(
+ req, struct wb_domain_request_state);
+ struct netr_DsRGetDCNameInfo *dcinfo = NULL;
+ NTSTATUS status;
+ const char *dcname = NULL;
+
+ state->pending_subreq = NULL;
+
+ status = wb_dsgetdcname_recv(subreq, state, &dcinfo);
+ TALLOC_FREE(subreq);
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+ dcname = dcinfo->dc_unc;
+ while (dcname != NULL && *dcname == '\\') {
+ dcname++;
+ }
+
+ state->r.in.dcname = talloc_strdup(state, dcname);
+ if (tevent_req_nomem(state->r.in.dcname, req)) {
+ return;
+ }
+
+ TALLOC_FREE(dcinfo);
+
+ subreq = dcerpc_wbint_InitConnection_r_send(state,
+ state->ev,
+ state->child->binding_handle,
+ &state->r);
+ if (tevent_req_nomem(subreq, req)) {
+ return;
+ }
+ tevent_req_set_callback(subreq, wb_domain_request_initialized, req);
+ state->pending_subreq = subreq;
+}
+
+static void wb_domain_request_initialized(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct wb_domain_request_state *state = tevent_req_data(
+ req, struct wb_domain_request_state);
+ NTSTATUS status;
+
+ state->pending_subreq = NULL;
+
+ status = dcerpc_wbint_InitConnection_r_recv(subreq, state);
+ TALLOC_FREE(subreq);
+ if (NT_STATUS_IS_ERR(status)) {
+ tevent_req_error(req, map_errno_from_nt_status(status));
+ return;
+ }
+
+ status = state->r.out.result;
+ if (NT_STATUS_IS_ERR(status)) {
+ tevent_req_error(req, map_errno_from_nt_status(status));
+ return;
+ }
+
+ state->domain->sid = *state->r.out.sid;
+
+ talloc_free(state->domain->name);
+ state->domain->name = talloc_strdup(state->domain, *state->r.out.name);
+ if (state->domain->name == NULL) {
+ tevent_req_error(req, ENOMEM);
+ return;
+ }
+
+ if (*state->r.out.alt_name != NULL &&
+ strlen(*state->r.out.alt_name) > 0) {
+ talloc_free(state->domain->alt_name);
+
+ state->domain->alt_name = talloc_strdup(state->domain,
+ *state->r.out.alt_name);
+ if (state->domain->alt_name == NULL) {
+ tevent_req_error(req, ENOMEM);
+ return;
+ }
+ }
+
+ state->domain->native_mode =
+ (*state->r.out.flags & WB_DOMINFO_DOMAIN_NATIVE);
+ state->domain->active_directory =
+ (*state->r.out.flags & WB_DOMINFO_DOMAIN_AD);
+ state->domain->initialized = true;
+
+ subreq = wb_child_request_send(state, state->ev, state->child,
+ state->request);
+ if (tevent_req_nomem(subreq, req)) {
+ return;
+ }
+ tevent_req_set_callback(subreq, wb_domain_request_done, req);
+ state->pending_subreq = subreq;
+
+ /*
+ * Once the domain is initialized and
+ * once we placed our real request into the child queue,
+ * we can remove ourself from the domain queue
+ * and give the next one in the queue the chance
+ * to check for an idle child.
+ */
+ TALLOC_FREE(state->queue_entry);
+}
+
+static void wb_domain_request_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct wb_domain_request_state *state = tevent_req_data(
+ req, struct wb_domain_request_state);
+ int ret, err;
+
+ state->pending_subreq = NULL;
+
+ ret = wb_child_request_recv(subreq, talloc_tos(), &state->response,
+ &err);
+ TALLOC_FREE(subreq);
+ if (ret == -1) {
+ tevent_req_error(req, err);
+ return;
+ }
+ tevent_req_done(req);
+}
+
+int wb_domain_request_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx,
+ struct winbindd_response **presponse, int *err)
+{
+ struct wb_domain_request_state *state = tevent_req_data(
+ req, struct wb_domain_request_state);
+
+ if (tevent_req_is_unix_error(req, err)) {
+ return -1;
+ }
+ *presponse = talloc_move(mem_ctx, &state->response);
+ return 0;
+}
+
+static void child_process_request(struct winbindd_child *child,
+ struct winbindd_cli_state *state)
+{
+ struct winbindd_domain *domain = child->domain;
+
+ /* Free response data - we may be interrupted and receive another
+ command before being able to send this data off. */
+
+ state->response->result = WINBINDD_ERROR;
+ state->response->length = sizeof(struct winbindd_response);
+
+ /* as all requests in the child are sync, we can use talloc_tos() */
+ state->mem_ctx = talloc_tos();
+
+ /* Process command */
+ state->response->result = winbindd_dual_ndrcmd(domain, state);
+}
+
+void setup_child(struct winbindd_domain *domain, struct winbindd_child *child,
+ const char *logprefix,
+ const char *logname)
+{
+ const struct loadparm_substitution *lp_sub =
+ loadparm_s3_global_substitution();
+
+ if (logprefix && logname) {
+ char *logbase = NULL;
+
+ if (*lp_logfile(talloc_tos(), lp_sub)) {
+ char *end = NULL;
+
+ if (asprintf(&logbase, "%s", lp_logfile(talloc_tos(), lp_sub)) < 0) {
+ smb_panic("Internal error: asprintf failed");
+ }
+
+ if ((end = strrchr_m(logbase, '/'))) {
+ *end = '\0';
+ }
+ } else {
+ if (asprintf(&logbase, "%s", get_dyn_LOGFILEBASE()) < 0) {
+ smb_panic("Internal error: asprintf failed");
+ }
+ }
+
+ if (asprintf(&child->logfilename, "%s/%s-%s",
+ logbase, logprefix, logname) < 0) {
+ SAFE_FREE(logbase);
+ smb_panic("Internal error: asprintf failed");
+ }
+
+ SAFE_FREE(logbase);
+ } else {
+ smb_panic("Internal error: logprefix == NULL && "
+ "logname == NULL");
+ }
+
+ child->pid = 0;
+ child->sock = -1;
+ child->domain = domain;
+ child->queue = tevent_queue_create(NULL, "winbind_child");
+ SMB_ASSERT(child->queue != NULL);
+
+ child->binding_handle = wbint_binding_handle(NULL, NULL, child);
+ SMB_ASSERT(child->binding_handle != NULL);
+}
+
+struct winbind_child_died_state {
+ pid_t pid;
+ struct winbindd_child *child;
+};
+
+static bool winbind_child_died_fn(struct winbindd_child *child,
+ void *private_data)
+{
+ struct winbind_child_died_state *state = private_data;
+
+ if (child->pid == state->pid) {
+ state->child = child;
+ return false;
+ }
+ return true;
+}
+
+void winbind_child_died(pid_t pid)
+{
+ struct winbind_child_died_state state = { .pid = pid };
+
+ forall_children(winbind_child_died_fn, &state);
+
+ if (state.child == NULL) {
+ DEBUG(5, ("Already reaped child %u died\n", (unsigned int)pid));
+ return;
+ }
+
+ state.child->pid = 0;
+}
+
+/* Ensure any negative cache entries with the netbios or realm names are removed. */
+
+void winbindd_flush_negative_conn_cache(struct winbindd_domain *domain)
+{
+ flush_negative_conn_cache_for_domain(domain->name);
+ if (domain->alt_name != NULL) {
+ flush_negative_conn_cache_for_domain(domain->alt_name);
+ }
+}
+
+/*
+ * Parent winbindd process sets its own debug level first and then
+ * sends a message to all the winbindd children to adjust their debug
+ * level to that of parents.
+ */
+
+struct winbind_msg_relay_state {
+ struct messaging_context *msg_ctx;
+ uint32_t msg_type;
+ DATA_BLOB *data;
+};
+
+static bool winbind_msg_relay_fn(struct winbindd_child *child,
+ void *private_data)
+{
+ struct winbind_msg_relay_state *state = private_data;
+
+ DBG_DEBUG("sending message to pid %u.\n",
+ (unsigned int)child->pid);
+
+ messaging_send(state->msg_ctx, pid_to_procid(child->pid),
+ state->msg_type, state->data);
+ return true;
+}
+
+void winbind_msg_debug(struct messaging_context *msg_ctx,
+ void *private_data,
+ uint32_t msg_type,
+ struct server_id server_id,
+ DATA_BLOB *data)
+{
+ struct winbind_msg_relay_state state = {
+ .msg_ctx = msg_ctx, .msg_type = msg_type, .data = data
+ };
+
+ DEBUG(10,("winbind_msg_debug: got debug message.\n"));
+
+ debug_message(msg_ctx, private_data, MSG_DEBUG, server_id, data);
+
+ forall_children(winbind_msg_relay_fn, &state);
+}
+
+void winbind_disconnect_dc_parent(struct messaging_context *msg_ctx,
+ void *private_data,
+ uint32_t msg_type,
+ struct server_id server_id,
+ DATA_BLOB *data)
+{
+ struct winbind_msg_relay_state state = {
+ .msg_ctx = msg_ctx, .msg_type = msg_type, .data = data
+ };
+
+ DBG_DEBUG("Got disconnect_dc message\n");
+
+ forall_children(winbind_msg_relay_fn, &state);
+}
+
+static bool winbindd_child_msg_filter(struct messaging_rec *rec,
+ void *private_data)
+{
+ struct winbindd_child *child = talloc_get_type_abort(private_data,
+ struct winbindd_child);
+
+ if (rec->msg_type == MSG_SMB_CONF_UPDATED) {
+ DBG_DEBUG("Got reload-config message\n");
+ winbindd_reload_services_file(child->logfilename);
+ }
+
+ return false;
+}
+
+/* React on 'smbcontrol winbindd reload-config' in the same way as on SIGHUP*/
+void winbindd_msg_reload_services_parent(struct messaging_context *msg,
+ void *private_data,
+ uint32_t msg_type,
+ struct server_id server_id,
+ DATA_BLOB *data)
+{
+ struct winbind_msg_relay_state state = {
+ .msg_ctx = msg,
+ .msg_type = msg_type,
+ .data = data,
+ };
+ bool ok;
+
+ DBG_DEBUG("Got reload-config message\n");
+
+ /* Flush various caches */
+ winbindd_flush_caches();
+
+ winbindd_reload_services_file((const char *)private_data);
+
+ /* Set tevent_thread_call_depth_set_callback according to debug level */
+ if (lp_winbind_debug_traceid() && debuglevel_get() > 1) {
+ tevent_thread_call_depth_set_callback(winbind_call_flow, NULL);
+ } else {
+ tevent_thread_call_depth_set_callback(NULL, NULL);
+ }
+
+ ok = add_trusted_domains_dc();
+ if (!ok) {
+ DBG_ERR("add_trusted_domains_dc() failed\n");
+ }
+
+ forall_children(winbind_msg_relay_fn, &state);
+}
+
+/* Set our domains as offline and forward the offline message to our children. */
+
+struct winbind_msg_on_offline_state {
+ struct messaging_context *msg_ctx;
+ uint32_t msg_type;
+};
+
+static bool winbind_msg_on_offline_fn(struct winbindd_child *child,
+ void *private_data)
+{
+ struct winbind_msg_on_offline_state *state = private_data;
+
+ if (child->domain->internal) {
+ return true;
+ }
+
+ /*
+ * Each winbindd child should only process requests for one
+ * domain - make sure we only set it online / offline for that
+ * domain.
+ */
+ DBG_DEBUG("sending message to pid %u for domain %s.\n",
+ (unsigned int)child->pid, child->domain->name);
+
+ messaging_send_buf(state->msg_ctx,
+ pid_to_procid(child->pid),
+ state->msg_type,
+ (const uint8_t *)child->domain->name,
+ strlen(child->domain->name)+1);
+
+ return true;
+}
+
+void winbind_msg_offline(struct messaging_context *msg_ctx,
+ void *private_data,
+ uint32_t msg_type,
+ struct server_id server_id,
+ DATA_BLOB *data)
+{
+ struct winbind_msg_on_offline_state state = {
+ .msg_ctx = msg_ctx,
+ .msg_type = MSG_WINBIND_OFFLINE,
+ };
+ struct winbindd_domain *domain;
+
+ DEBUG(10,("winbind_msg_offline: got offline message.\n"));
+
+ if (!lp_winbind_offline_logon()) {
+ DEBUG(10,("winbind_msg_offline: rejecting offline message.\n"));
+ return;
+ }
+
+ /* Set our global state as offline. */
+ if (!set_global_winbindd_state_offline()) {
+ DEBUG(10,("winbind_msg_offline: offline request failed.\n"));
+ return;
+ }
+
+ /* Set all our domains as offline. */
+ for (domain = domain_list(); domain; domain = domain->next) {
+ if (domain->internal) {
+ continue;
+ }
+ DEBUG(5,("winbind_msg_offline: marking %s offline.\n", domain->name));
+ domain->online = false;
+ }
+
+ forall_domain_children(winbind_msg_on_offline_fn, &state);
+}
+
+/* Set our domains as online and forward the online message to our children. */
+
+void winbind_msg_online(struct messaging_context *msg_ctx,
+ void *private_data,
+ uint32_t msg_type,
+ struct server_id server_id,
+ DATA_BLOB *data)
+{
+ struct winbind_msg_on_offline_state state = {
+ .msg_ctx = msg_ctx,
+ .msg_type = MSG_WINBIND_ONLINE,
+ };
+
+ DEBUG(10,("winbind_msg_online: got online message.\n"));
+
+ if (!lp_winbind_offline_logon()) {
+ DEBUG(10,("winbind_msg_online: rejecting online message.\n"));
+ return;
+ }
+
+ /* Set our global state as online. */
+ set_global_winbindd_state_online();
+
+ smb_nscd_flush_user_cache();
+ smb_nscd_flush_group_cache();
+
+ /* Tell all our child domains to go online online. */
+ forall_domain_children(winbind_msg_on_offline_fn, &state);
+}
+
+static const char *collect_onlinestatus(TALLOC_CTX *mem_ctx)
+{
+ struct winbindd_domain *domain;
+ char *buf = NULL;
+
+ if ((buf = talloc_asprintf(mem_ctx, "global:%s ",
+ get_global_winbindd_state_offline() ?
+ "Offline":"Online")) == NULL) {
+ return NULL;
+ }
+
+ for (domain = domain_list(); domain; domain = domain->next) {
+ if ((buf = talloc_asprintf_append_buffer(buf, "%s:%s ",
+ domain->name,
+ domain->online ?
+ "Online":"Offline")) == NULL) {
+ return NULL;
+ }
+ }
+
+ buf = talloc_asprintf_append_buffer(buf, "\n");
+
+ DEBUG(5,("collect_onlinestatus: %s", buf));
+
+ return buf;
+}
+
+void winbind_msg_onlinestatus(struct messaging_context *msg_ctx,
+ void *private_data,
+ uint32_t msg_type,
+ struct server_id server_id,
+ DATA_BLOB *data)
+{
+ TALLOC_CTX *mem_ctx;
+ const char *message;
+
+ DEBUG(5,("winbind_msg_onlinestatus received.\n"));
+
+ mem_ctx = talloc_init("winbind_msg_onlinestatus");
+ if (mem_ctx == NULL) {
+ return;
+ }
+
+ message = collect_onlinestatus(mem_ctx);
+ if (message == NULL) {
+ talloc_destroy(mem_ctx);
+ return;
+ }
+
+ messaging_send_buf(msg_ctx, server_id, MSG_WINBIND_ONLINESTATUS,
+ (const uint8_t *)message, strlen(message) + 1);
+
+ talloc_destroy(mem_ctx);
+}
+
+void winbind_msg_dump_domain_list(struct messaging_context *msg_ctx,
+ void *private_data,
+ uint32_t msg_type,
+ struct server_id server_id,
+ DATA_BLOB *data)
+{
+ TALLOC_CTX *mem_ctx;
+ const char *message = NULL;
+ const char *domain = NULL;
+ char *s = NULL;
+ NTSTATUS status;
+ struct winbindd_domain *dom = NULL;
+
+ DEBUG(5,("winbind_msg_dump_domain_list received.\n"));
+
+ mem_ctx = talloc_init("winbind_msg_dump_domain_list");
+ if (!mem_ctx) {
+ return;
+ }
+
+ if (data->length > 0) {
+ domain = (const char *)data->data;
+ }
+
+ if (domain) {
+
+ DEBUG(5,("winbind_msg_dump_domain_list for domain: %s\n",
+ domain));
+
+ message = NDR_PRINT_STRUCT_STRING(mem_ctx, winbindd_domain,
+ find_domain_from_name_noinit(domain));
+ if (!message) {
+ talloc_destroy(mem_ctx);
+ return;
+ }
+
+ messaging_send_buf(msg_ctx, server_id,
+ MSG_WINBIND_DUMP_DOMAIN_LIST,
+ (const uint8_t *)message, strlen(message) + 1);
+
+ talloc_destroy(mem_ctx);
+
+ return;
+ }
+
+ DEBUG(5,("winbind_msg_dump_domain_list all domains\n"));
+
+ for (dom = domain_list(); dom; dom=dom->next) {
+ message = NDR_PRINT_STRUCT_STRING(mem_ctx, winbindd_domain, dom);
+ if (!message) {
+ talloc_destroy(mem_ctx);
+ return;
+ }
+
+ s = talloc_asprintf_append(s, "%s\n", message);
+ if (!s) {
+ talloc_destroy(mem_ctx);
+ return;
+ }
+ }
+
+ status = messaging_send_buf(msg_ctx, server_id,
+ MSG_WINBIND_DUMP_DOMAIN_LIST,
+ (uint8_t *)s, strlen(s) + 1);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0,("failed to send message: %s\n",
+ nt_errstr(status)));
+ }
+
+ talloc_destroy(mem_ctx);
+}
+
+static void account_lockout_policy_handler(struct tevent_context *ctx,
+ struct tevent_timer *te,
+ struct timeval now,
+ void *private_data)
+{
+ struct winbindd_child *child =
+ (struct winbindd_child *)private_data;
+ TALLOC_CTX *mem_ctx = NULL;
+ struct samr_DomInfo12 lockout_policy;
+ NTSTATUS result;
+
+ DEBUG(10,("account_lockout_policy_handler called\n"));
+
+ TALLOC_FREE(child->lockout_policy_event);
+
+ if ( !winbindd_can_contact_domain( child->domain ) ) {
+ DEBUG(10,("account_lockout_policy_handler: Removing myself since I "
+ "do not have an incoming trust to domain %s\n",
+ child->domain->name));
+
+ return;
+ }
+
+ mem_ctx = talloc_init("account_lockout_policy_handler ctx");
+ if (!mem_ctx) {
+ result = NT_STATUS_NO_MEMORY;
+ } else {
+ result = wb_cache_lockout_policy(child->domain, mem_ctx,
+ &lockout_policy);
+ }
+ TALLOC_FREE(mem_ctx);
+
+ if (!NT_STATUS_IS_OK(result)) {
+ DEBUG(10,("account_lockout_policy_handler: lockout_policy failed error %s\n",
+ nt_errstr(result)));
+ }
+
+ child->lockout_policy_event = tevent_add_timer(global_event_context(), NULL,
+ timeval_current_ofs(3600, 0),
+ account_lockout_policy_handler,
+ child);
+}
+
+static time_t get_machine_password_timeout(void)
+{
+ /* until we have gpo support use lp setting */
+ return lp_machine_password_timeout();
+}
+
+static bool calculate_next_machine_pwd_change(const char *domain,
+ struct timeval *t)
+{
+ time_t pass_last_set_time;
+ time_t timeout;
+ time_t next_change;
+ struct timeval tv;
+ char *pw;
+
+ pw = secrets_fetch_machine_password(domain,
+ &pass_last_set_time,
+ NULL);
+
+ if (pw == NULL) {
+ DEBUG(0,("cannot fetch own machine password ????\n"));
+ return false;
+ }
+
+ SAFE_FREE(pw);
+
+ timeout = get_machine_password_timeout();
+ if (timeout == 0) {
+ DEBUG(10,("machine password never expires\n"));
+ return false;
+ }
+
+ tv.tv_sec = pass_last_set_time;
+ DEBUG(10, ("password last changed %s\n",
+ timeval_string(talloc_tos(), &tv, false)));
+ tv.tv_sec += timeout;
+ DEBUGADD(10, ("password valid until %s\n",
+ timeval_string(talloc_tos(), &tv, false)));
+
+ if (time(NULL) < (pass_last_set_time + timeout)) {
+ next_change = pass_last_set_time + timeout;
+ DEBUG(10,("machine password still valid until: %s\n",
+ http_timestring(talloc_tos(), next_change)));
+ *t = timeval_set(next_change, 0);
+
+ if (lp_clustering()) {
+ uint8_t randbuf;
+ /*
+ * When having a cluster, we have several
+ * winbinds racing for the password change. In
+ * the machine_password_change_handler()
+ * function we check if someone else was
+ * faster when the event triggers. We add a
+ * 255-second random delay here, so that we
+ * don't run to change the password at the
+ * exact same moment.
+ */
+ generate_random_buffer(&randbuf, sizeof(randbuf));
+ DEBUG(10, ("adding %d seconds randomness\n",
+ (int)randbuf));
+ t->tv_sec += randbuf;
+ }
+ return true;
+ }
+
+ DEBUG(10,("machine password expired, needs immediate change\n"));
+
+ *t = timeval_zero();
+
+ return true;
+}
+
+static void machine_password_change_handler(struct tevent_context *ctx,
+ struct tevent_timer *te,
+ struct timeval now,
+ void *private_data)
+{
+ struct messaging_context *msg_ctx = global_messaging_context();
+ struct winbindd_child *child =
+ (struct winbindd_child *)private_data;
+ struct rpc_pipe_client *netlogon_pipe = NULL;
+ struct netlogon_creds_cli_context *netlogon_creds_ctx = NULL;
+ NTSTATUS result;
+ struct timeval next_change;
+
+ DEBUG(10,("machine_password_change_handler called\n"));
+
+ TALLOC_FREE(child->machine_password_change_event);
+
+ if (!calculate_next_machine_pwd_change(child->domain->name,
+ &next_change)) {
+ DEBUG(10, ("calculate_next_machine_pwd_change failed\n"));
+ return;
+ }
+
+ DEBUG(10, ("calculate_next_machine_pwd_change returned %s\n",
+ timeval_string(talloc_tos(), &next_change, false)));
+
+ if (!timeval_expired(&next_change)) {
+ DEBUG(10, ("Someone else has already changed the pw\n"));
+ goto done;
+ }
+
+ if (!winbindd_can_contact_domain(child->domain)) {
+ DEBUG(10,("machine_password_change_handler: Removing myself since I "
+ "do not have an incoming trust to domain %s\n",
+ child->domain->name));
+ return;
+ }
+
+ result = cm_connect_netlogon_secure(child->domain,
+ &netlogon_pipe,
+ &netlogon_creds_ctx);
+ if (!NT_STATUS_IS_OK(result)) {
+ DEBUG(10,("machine_password_change_handler: "
+ "failed to connect netlogon pipe: %s\n",
+ nt_errstr(result)));
+ return;
+ }
+
+ result = trust_pw_change(netlogon_creds_ctx,
+ msg_ctx,
+ netlogon_pipe->binding_handle,
+ child->domain->name,
+ child->domain->dcname,
+ false); /* force */
+
+ DEBUG(10, ("machine_password_change_handler: "
+ "trust_pw_change returned %s\n",
+ nt_errstr(result)));
+
+ if (NT_STATUS_EQUAL(result, NT_STATUS_ACCESS_DENIED) ) {
+ DEBUG(3,("machine_password_change_handler: password set returned "
+ "ACCESS_DENIED. Maybe the trust account "
+ "password was changed and we didn't know it. "
+ "Killing connections to domain %s\n",
+ child->domain->name));
+ invalidate_cm_connection(child->domain);
+ }
+
+ if (!calculate_next_machine_pwd_change(child->domain->name,
+ &next_change)) {
+ DEBUG(10, ("calculate_next_machine_pwd_change failed\n"));
+ return;
+ }
+
+ DEBUG(10, ("calculate_next_machine_pwd_change returned %s\n",
+ timeval_string(talloc_tos(), &next_change, false)));
+
+ if (!NT_STATUS_IS_OK(result)) {
+ struct timeval tmp;
+ /*
+ * In case of failure, give the DC a minute to recover
+ */
+ tmp = timeval_current_ofs(60, 0);
+ next_change = timeval_max(&next_change, &tmp);
+ }
+
+done:
+ child->machine_password_change_event = tevent_add_timer(global_event_context(), NULL,
+ next_change,
+ machine_password_change_handler,
+ child);
+}
+
+/* Deal with a request to go offline. */
+
+static void child_msg_offline(struct messaging_context *msg,
+ void *private_data,
+ uint32_t msg_type,
+ struct server_id server_id,
+ DATA_BLOB *data)
+{
+ struct winbindd_domain *domain;
+ struct winbindd_domain *primary_domain = NULL;
+ const char *domainname = (const char *)data->data;
+
+ if (data->data == NULL || data->length == 0) {
+ return;
+ }
+
+ DEBUG(5,("child_msg_offline received for domain %s.\n", domainname));
+
+ if (!lp_winbind_offline_logon()) {
+ DEBUG(10,("child_msg_offline: rejecting offline message.\n"));
+ return;
+ }
+
+ primary_domain = find_our_domain();
+
+ /* Mark the requested domain offline. */
+
+ for (domain = domain_list(); domain; domain = domain->next) {
+ if (domain->internal) {
+ continue;
+ }
+ if (strequal(domain->name, domainname)) {
+ DEBUG(5,("child_msg_offline: marking %s offline.\n", domain->name));
+ set_domain_offline(domain);
+ /* we are in the trusted domain, set the primary domain
+ * offline too */
+ if (domain != primary_domain) {
+ set_domain_offline(primary_domain);
+ }
+ }
+ }
+}
+
+/* Deal with a request to go online. */
+
+static void child_msg_online(struct messaging_context *msg,
+ void *private_data,
+ uint32_t msg_type,
+ struct server_id server_id,
+ DATA_BLOB *data)
+{
+ struct winbindd_domain *domain;
+ struct winbindd_domain *primary_domain = NULL;
+ const char *domainname = (const char *)data->data;
+
+ if (data->data == NULL || data->length == 0) {
+ return;
+ }
+
+ DEBUG(5,("child_msg_online received for domain %s.\n", domainname));
+
+ if (!lp_winbind_offline_logon()) {
+ DEBUG(10,("child_msg_online: rejecting online message.\n"));
+ return;
+ }
+
+ primary_domain = find_our_domain();
+
+ /* Set our global state as online. */
+ set_global_winbindd_state_online();
+
+ /* Try and mark everything online - delete any negative cache entries
+ to force a reconnect now. */
+
+ for (domain = domain_list(); domain; domain = domain->next) {
+ if (domain->internal) {
+ continue;
+ }
+ if (strequal(domain->name, domainname)) {
+ DEBUG(5,("child_msg_online: requesting %s to go online.\n", domain->name));
+ winbindd_flush_negative_conn_cache(domain);
+ set_domain_online_request(domain);
+
+ /* we can be in trusted domain, which will contact primary domain
+ * we have to bring primary domain online in trusted domain process
+ * see, winbindd_dual_pam_auth() --> winbindd_dual_pam_auth_samlogon()
+ * --> contact_domain = find_our_domain()
+ * */
+ if (domain != primary_domain) {
+ winbindd_flush_negative_conn_cache(primary_domain);
+ set_domain_online_request(primary_domain);
+ }
+ }
+ }
+}
+
+struct winbindd_reinit_after_fork_state {
+ const struct winbindd_child *myself;
+};
+
+static bool winbindd_reinit_after_fork_fn(struct winbindd_child *child,
+ void *private_data)
+{
+ struct winbindd_reinit_after_fork_state *state = private_data;
+
+ if (child == state->myself) {
+ return true;
+ }
+
+ /* Destroy all possible events in child list. */
+ TALLOC_FREE(child->lockout_policy_event);
+ TALLOC_FREE(child->machine_password_change_event);
+
+ /*
+ * Children should never be able to send each other messages,
+ * all messages must go through the parent.
+ */
+ child->pid = (pid_t)0;
+
+ /*
+ * Close service sockets to all other children
+ */
+ if (child->sock != -1) {
+ close(child->sock);
+ child->sock = -1;
+ }
+
+ return true;
+}
+
+NTSTATUS winbindd_reinit_after_fork(const struct winbindd_child *myself,
+ const char *logfilename)
+{
+ struct winbindd_reinit_after_fork_state state = { .myself = myself };
+ struct winbindd_domain *domain;
+ NTSTATUS status;
+
+ status = reinit_after_fork(
+ global_messaging_context(),
+ global_event_context(),
+ true);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0,("reinit_after_fork() failed\n"));
+ return status;
+ }
+ initialize_password_db(true, global_event_context());
+
+ close_conns_after_fork();
+
+ if (logfilename != NULL) {
+ lp_set_logfile(logfilename);
+ reopen_logs();
+ }
+
+ if (!winbindd_setup_sig_term_handler(false)) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ if (!winbindd_setup_sig_hup_handler(logfilename)) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ /* Stop zombies in children */
+ CatchChild();
+
+ /* Don't handle the same messages as our parent. */
+ messaging_deregister(global_messaging_context(),
+ MSG_SMB_CONF_UPDATED, NULL);
+ messaging_deregister(global_messaging_context(),
+ MSG_SHUTDOWN, NULL);
+ messaging_deregister(global_messaging_context(),
+ MSG_WINBIND_OFFLINE, NULL);
+ messaging_deregister(global_messaging_context(),
+ MSG_WINBIND_ONLINE, NULL);
+ messaging_deregister(global_messaging_context(),
+ MSG_WINBIND_ONLINESTATUS, NULL);
+ messaging_deregister(global_messaging_context(),
+ MSG_WINBIND_DUMP_DOMAIN_LIST, NULL);
+ messaging_deregister(global_messaging_context(),
+ MSG_DEBUG, NULL);
+
+ messaging_deregister(global_messaging_context(),
+ MSG_WINBIND_DOMAIN_OFFLINE, NULL);
+ messaging_deregister(global_messaging_context(),
+ MSG_WINBIND_DOMAIN_ONLINE, NULL);
+
+ /* We have destroyed all events in the winbindd_event_context
+ * in reinit_after_fork(), so clean out all possible pending
+ * event pointers. */
+
+ /* Deal with check_online_events. */
+
+ for (domain = domain_list(); domain; domain = domain->next) {
+ TALLOC_FREE(domain->check_online_event);
+ }
+
+ /* Ensure we're not handling a credential cache event inherited
+ * from our parent. */
+
+ ccache_remove_all_after_fork();
+
+ forall_children(winbindd_reinit_after_fork_fn, &state);
+
+ return NT_STATUS_OK;
+}
+
+/*
+ * In a child there will be only one domain, reference that here.
+ */
+static struct winbindd_domain *child_domain;
+
+struct winbindd_domain *wb_child_domain(void)
+{
+ return child_domain;
+}
+
+struct child_handler_state {
+ struct winbindd_child *child;
+ struct winbindd_cli_state cli;
+};
+
+static void child_handler(struct tevent_context *ev, struct tevent_fd *fde,
+ uint16_t flags, void *private_data)
+{
+ struct child_handler_state *state =
+ (struct child_handler_state *)private_data;
+ NTSTATUS status;
+ uint64_t parent_traceid;
+
+ /* fetch a request from the main daemon */
+ status = child_read_request(state->cli.sock, state->cli.request);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ /* we lost contact with our parent */
+ _exit(0);
+ }
+
+ /* read traceid from request */
+ parent_traceid = state->cli.request->traceid;
+ debug_traceid_set(parent_traceid);
+
+ DEBUG(4,("child daemon request %d\n",
+ (int)state->cli.request->cmd));
+
+ ZERO_STRUCTP(state->cli.response);
+ state->cli.request->null_term = '\0';
+ state->cli.mem_ctx = talloc_tos();
+ child_process_request(state->child, &state->cli);
+
+ DEBUG(4, ("Finished processing child request %d\n",
+ (int)state->cli.request->cmd));
+
+ SAFE_FREE(state->cli.request->extra_data.data);
+
+ status = child_write_response(state->cli.sock, state->cli.response);
+ if (!NT_STATUS_IS_OK(status)) {
+ exit(1);
+ }
+}
+
+static bool fork_domain_child(struct winbindd_child *child)
+{
+ int fdpair[2];
+ struct child_handler_state state;
+ struct winbindd_request request;
+ struct winbindd_response response;
+ struct winbindd_domain *primary_domain = NULL;
+ NTSTATUS status;
+ ssize_t nwritten;
+ struct tevent_fd *fde;
+ struct tevent_req *req = NULL;
+
+ if (child->domain) {
+ DEBUG(10, ("fork_domain_child called for domain '%s'\n",
+ child->domain->name));
+ } else {
+ DEBUG(10, ("fork_domain_child called without domain.\n"));
+ }
+
+ if (socketpair(AF_UNIX, SOCK_STREAM, 0, fdpair) != 0) {
+ DEBUG(0, ("Could not open child pipe: %s\n",
+ strerror(errno)));
+ return False;
+ }
+
+ ZERO_STRUCT(state);
+ state.child = child;
+ state.cli.pid = getpid();
+ state.cli.request = &request;
+ state.cli.response = &response;
+
+ child->pid = fork();
+
+ if (child->pid == -1) {
+ DEBUG(0, ("Could not fork: %s\n", strerror(errno)));
+ close(fdpair[0]);
+ close(fdpair[1]);
+ return False;
+ }
+
+ if (child->pid != 0) {
+ /* Parent */
+ ssize_t nread;
+ int rc;
+
+ close(fdpair[0]);
+
+ nread = sys_read(fdpair[1], &status, sizeof(status));
+ if (nread != sizeof(status)) {
+ DEBUG(1, ("fork_domain_child: Could not read child status: "
+ "nread=%d, error=%s\n", (int)nread,
+ strerror(errno)));
+ close(fdpair[1]);
+ return false;
+ }
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(1, ("fork_domain_child: Child status is %s\n",
+ nt_errstr(status)));
+ close(fdpair[1]);
+ return false;
+ }
+
+ child->monitor_fde = tevent_add_fd(global_event_context(),
+ global_event_context(),
+ fdpair[1],
+ TEVENT_FD_READ,
+ child_socket_readable,
+ child);
+ if (child->monitor_fde == NULL) {
+ DBG_WARNING("tevent_add_fd failed\n");
+ close(fdpair[1]);
+ return false;
+ }
+
+ rc = set_blocking(fdpair[1], false);
+ if (rc < 0) {
+ close(fdpair[1]);
+ return false;
+ }
+
+ child->sock = fdpair[1];
+
+ return true;
+ }
+
+ /* Child */
+ child_domain = child->domain;
+
+ DEBUG(10, ("Child process %d\n", (int)getpid()));
+
+ state.cli.sock = fdpair[0];
+ close(fdpair[1]);
+
+ /* Reset traceid and deactivate call_depth tracking */
+ if (lp_winbind_debug_traceid()) {
+ debug_traceid_set(1);
+ tevent_thread_call_depth_set_callback(NULL, NULL);
+ }
+
+ status = winbindd_reinit_after_fork(child, child->logfilename);
+
+ /* setup callbacks again, one of them is removed in reinit_after_fork */
+ if (lp_winbind_debug_traceid()) {
+ winbind_debug_traceid_setup(global_event_context());
+ }
+
+ nwritten = sys_write(state.cli.sock, &status, sizeof(status));
+ if (nwritten != sizeof(status)) {
+ DEBUG(1, ("fork_domain_child: Could not write status: "
+ "nwritten=%d, error=%s\n", (int)nwritten,
+ strerror(errno)));
+ _exit(0);
+ }
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(1, ("winbindd_reinit_after_fork failed: %s\n",
+ nt_errstr(status)));
+ _exit(0);
+ }
+
+ if (child_domain != NULL) {
+ process_set_title("wb[%s]", "domain child [%s]", child_domain->name);
+ } else if (is_idmap_child(child)) {
+ process_set_title("wb-idmap", "idmap child");
+ }
+
+ /* Handle online/offline messages. */
+ messaging_register(global_messaging_context(), NULL,
+ MSG_WINBIND_OFFLINE, child_msg_offline);
+ messaging_register(global_messaging_context(), NULL,
+ MSG_WINBIND_ONLINE, child_msg_online);
+ messaging_register(global_messaging_context(), NULL,
+ MSG_DEBUG, debug_message);
+ messaging_register(global_messaging_context(), NULL,
+ MSG_WINBIND_IP_DROPPED,
+ winbind_msg_ip_dropped);
+ messaging_register(global_messaging_context(), NULL,
+ MSG_WINBIND_DISCONNECT_DC,
+ winbind_msg_disconnect_dc);
+
+ req = messaging_filtered_read_send(global_event_context(),
+ global_event_context(),
+ global_messaging_context(),
+ winbindd_child_msg_filter,
+ child);
+ if (req == NULL) {
+ DBG_ERR("messaging_filtered_read_send failed\n");
+ _exit(1);
+ }
+
+ primary_domain = find_our_domain();
+
+ if (primary_domain == NULL) {
+ smb_panic("no primary domain found");
+ }
+
+ /* It doesn't matter if we allow cache login,
+ * try to bring domain online after fork. */
+ if ( child->domain ) {
+ child->domain->startup = True;
+ child->domain->startup_time = time_mono(NULL);
+ /* we can be in primary domain or in trusted domain
+ * If we are in trusted domain, set the primary domain
+ * in start-up mode */
+ if (!(child->domain->internal)) {
+ set_domain_online_request(child->domain);
+ if (!(child->domain->primary)) {
+ primary_domain->startup = True;
+ primary_domain->startup_time = time_mono(NULL);
+ set_domain_online_request(primary_domain);
+ }
+ }
+ }
+
+ /* We might be in the idmap child...*/
+ if (child->domain && !(child->domain->internal) &&
+ lp_winbind_offline_logon()) {
+
+ set_domain_online_request(child->domain);
+
+ if (primary_domain && (primary_domain != child->domain)) {
+ /* We need to talk to the primary
+ * domain as well as the trusted
+ * domain inside a trusted domain
+ * child.
+ * See the code in :
+ * set_dc_type_and_flags_trustinfo()
+ * for details.
+ */
+ set_domain_online_request(primary_domain);
+ }
+
+ child->lockout_policy_event = tevent_add_timer(
+ global_event_context(), NULL, timeval_zero(),
+ account_lockout_policy_handler,
+ child);
+ }
+
+ if (child->domain && child->domain->primary &&
+ !USE_KERBEROS_KEYTAB &&
+ lp_server_role() == ROLE_DOMAIN_MEMBER) {
+
+ struct timeval next_change;
+
+ if (calculate_next_machine_pwd_change(child->domain->name,
+ &next_change)) {
+ child->machine_password_change_event = tevent_add_timer(
+ global_event_context(), NULL, next_change,
+ machine_password_change_handler,
+ child);
+ }
+ }
+
+ fde = tevent_add_fd(global_event_context(), NULL, state.cli.sock,
+ TEVENT_FD_READ, child_handler, &state);
+ if (fde == NULL) {
+ DEBUG(1, ("tevent_add_fd failed\n"));
+ _exit(1);
+ }
+
+ while (1) {
+
+ int ret;
+ TALLOC_CTX *frame = talloc_stackframe();
+
+ ret = tevent_loop_once(global_event_context());
+ if (ret != 0) {
+ DEBUG(1, ("tevent_loop_once failed: %s\n",
+ strerror(errno)));
+ _exit(1);
+ }
+
+ if (child->domain && child->domain->startup &&
+ (time_mono(NULL) > child->domain->startup_time + 30)) {
+ /* No longer in "startup" mode. */
+ DEBUG(10,("fork_domain_child: domain %s no longer in 'startup' mode.\n",
+ child->domain->name ));
+ child->domain->startup = False;
+ }
+
+ TALLOC_FREE(frame);
+ }
+}
+
+void winbind_msg_ip_dropped_parent(struct messaging_context *msg_ctx,
+ void *private_data,
+ uint32_t msg_type,
+ struct server_id server_id,
+ DATA_BLOB *data)
+{
+ struct winbind_msg_relay_state state = {
+ .msg_ctx = msg_ctx,
+ .msg_type = msg_type,
+ .data = data,
+ };
+
+ winbind_msg_ip_dropped(msg_ctx, private_data, msg_type,
+ server_id, data);
+
+ forall_children(winbind_msg_relay_fn, &state);
+}
+
+void winbindd_terminate(bool is_parent)
+{
+ if (is_parent) {
+ /* When parent goes away we should
+ * remove the socket file. Not so
+ * when children terminate.
+ */
+ char *path = NULL;
+
+ if (asprintf(&path, "%s/%s",
+ lp_winbindd_socket_directory(), WINBINDD_SOCKET_NAME) > 0) {
+ unlink(path);
+ SAFE_FREE(path);
+ }
+ }
+
+ idmap_close();
+
+ netlogon_creds_cli_close_global_db();
+
+#if 0
+ if (interactive) {
+ TALLOC_CTX *mem_ctx = talloc_init("end_description");
+ char *description = talloc_describe_all(mem_ctx);
+
+ DEBUG(3, ("tallocs left:\n%s\n", description));
+ talloc_destroy(mem_ctx);
+ }
+#endif
+
+ if (is_parent) {
+ pidfile_unlink(lp_pid_directory(), "winbindd");
+ }
+
+ exit(0);
+}
+
+static void winbindd_sig_term_handler(struct tevent_context *ev,
+ struct tevent_signal *se,
+ int signum,
+ int count,
+ void *siginfo,
+ void *private_data)
+{
+ bool *p = talloc_get_type_abort(private_data, bool);
+ bool is_parent = *p;
+
+ TALLOC_FREE(p);
+
+ DEBUG(0,("Got sig[%d] terminate (is_parent=%d)\n",
+ signum, is_parent));
+ winbindd_terminate(is_parent);
+}
+
+bool winbindd_setup_sig_term_handler(bool parent)
+{
+ struct tevent_signal *se;
+ bool *is_parent;
+
+ is_parent = talloc(global_event_context(), bool);
+ if (!is_parent) {
+ return false;
+ }
+
+ *is_parent = parent;
+
+ se = tevent_add_signal(global_event_context(),
+ is_parent,
+ SIGTERM, 0,
+ winbindd_sig_term_handler,
+ is_parent);
+ if (!se) {
+ DEBUG(0,("failed to setup SIGTERM handler\n"));
+ talloc_free(is_parent);
+ return false;
+ }
+
+ se = tevent_add_signal(global_event_context(),
+ is_parent,
+ SIGINT, 0,
+ winbindd_sig_term_handler,
+ is_parent);
+ if (!se) {
+ DEBUG(0,("failed to setup SIGINT handler\n"));
+ talloc_free(is_parent);
+ return false;
+ }
+
+ se = tevent_add_signal(global_event_context(),
+ is_parent,
+ SIGQUIT, 0,
+ winbindd_sig_term_handler,
+ is_parent);
+ if (!se) {
+ DEBUG(0,("failed to setup SIGINT handler\n"));
+ talloc_free(is_parent);
+ return false;
+ }
+
+ return true;
+}
+
+static void flush_caches_noinit(void)
+{
+ /*
+ * We need to invalidate cached user list entries on a SIGHUP
+ * otherwise cached access denied errors due to restrict anonymous
+ * hang around until the sequence number changes.
+ * NB
+ * Skip uninitialized domains when flush cache.
+ * If domain is not initialized, it means it is never
+ * used or never become online. look, wcache_invalidate_cache()
+ * -> get_cache() -> init_dc_connection(). It causes a lot of traffic
+ * for unused domains and large traffic for primary domain's DC if there
+ * are many domains..
+ */
+
+ if (!wcache_invalidate_cache_noinit()) {
+ DEBUG(0, ("invalidating the cache failed; revalidate the cache\n"));
+ if (!winbindd_cache_validate_and_initialize()) {
+ exit(1);
+ }
+ }
+}
+
+static void winbindd_sig_hup_handler(struct tevent_context *ev,
+ struct tevent_signal *se,
+ int signum,
+ int count,
+ void *siginfo,
+ void *private_data)
+{
+ const char *file = (const char *)private_data;
+
+ DEBUG(1,("Reloading services after SIGHUP\n"));
+ flush_caches_noinit();
+ winbindd_reload_services_file(file);
+}
+
+bool winbindd_setup_sig_hup_handler(const char *lfile)
+{
+ struct tevent_signal *se;
+ char *file = NULL;
+
+ if (lfile) {
+ file = talloc_strdup(global_event_context(),
+ lfile);
+ if (!file) {
+ return false;
+ }
+ }
+
+ se = tevent_add_signal(global_event_context(),
+ global_event_context(),
+ SIGHUP, 0,
+ winbindd_sig_hup_handler,
+ file);
+ if (!se) {
+ return false;
+ }
+
+ return true;
+}
diff --git a/source3/winbindd/winbindd_dual_ndr.c b/source3/winbindd/winbindd_dual_ndr.c
new file mode 100644
index 0000000..7835bd3
--- /dev/null
+++ b/source3/winbindd/winbindd_dual_ndr.c
@@ -0,0 +1,615 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Provide parent->child communication based on NDR marshalling
+
+ Copyright (C) Volker Lendecke 2009
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+/*
+ * This file implements an RPC between winbind parent and child processes,
+ * leveraging the autogenerated marshalling routines for MSRPC. This is not
+ * MSRPC, as it does not go through the whole DCERPC fragmentation, we just
+ * leverage much the same infrastructure we already have for it.
+ */
+
+#include "includes.h"
+#include "winbindd/winbindd.h"
+#include "winbindd/winbindd_proto.h"
+#include "ntdomain.h"
+#include "librpc/rpc/dcesrv_core.h"
+#include "librpc/gen_ndr/ndr_winbind.h"
+#include "rpc_server/rpc_config.h"
+#include "rpc_server/rpc_server.h"
+#include "rpc_dce.h"
+#include "lib/tsocket/tsocket.h"
+
+struct wbint_bh_state {
+ struct winbindd_domain *domain;
+ struct winbindd_child *child;
+};
+
+static bool wbint_bh_is_connected(struct dcerpc_binding_handle *h)
+{
+ struct wbint_bh_state *hs = dcerpc_binding_handle_data(h,
+ struct wbint_bh_state);
+
+ if ((hs->domain == NULL) && (hs->child == NULL)) {
+ return false;
+ }
+
+ return true;
+}
+
+static uint32_t wbint_bh_set_timeout(struct dcerpc_binding_handle *h,
+ uint32_t timeout)
+{
+ /* TODO: implement timeouts */
+ return UINT32_MAX;
+}
+
+struct wbint_bh_raw_call_state {
+ struct winbindd_domain *domain;
+ uint32_t opnum;
+ DATA_BLOB in_data;
+ struct winbindd_request request;
+ struct winbindd_response *response;
+ DATA_BLOB out_data;
+};
+
+static void wbint_bh_raw_call_child_done(struct tevent_req *subreq);
+static void wbint_bh_raw_call_domain_done(struct tevent_req *subreq);
+
+static struct tevent_req *wbint_bh_raw_call_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct dcerpc_binding_handle *h,
+ const struct GUID *object,
+ uint32_t opnum,
+ uint32_t in_flags,
+ const uint8_t *in_data,
+ size_t in_length)
+{
+ struct wbint_bh_state *hs =
+ dcerpc_binding_handle_data(h,
+ struct wbint_bh_state);
+ struct tevent_req *req;
+ struct wbint_bh_raw_call_state *state;
+ bool ok;
+ struct tevent_req *subreq;
+
+ req = tevent_req_create(mem_ctx, &state,
+ struct wbint_bh_raw_call_state);
+ if (req == NULL) {
+ return NULL;
+ }
+ state->domain = hs->domain;
+ state->opnum = opnum;
+ state->in_data.data = discard_const_p(uint8_t, in_data);
+ state->in_data.length = in_length;
+
+ ok = wbint_bh_is_connected(h);
+ if (!ok) {
+ tevent_req_nterror(req, NT_STATUS_CONNECTION_DISCONNECTED);
+ return tevent_req_post(req, ev);
+ }
+
+ if ((state->domain != NULL)
+ && wcache_fetch_ndr(state, state->domain, state->opnum,
+ &state->in_data, &state->out_data)) {
+ DBG_DEBUG("Got opnum %"PRIu32" for domain %s from cache\n",
+ state->opnum, state->domain->name);
+ tevent_req_done(req);
+ return tevent_req_post(req, ev);
+ }
+
+ state->request.cmd = WINBINDD_DUAL_NDRCMD;
+ state->request.data.ndrcmd = state->opnum;
+ state->request.extra_data.data = (char *)state->in_data.data;
+ state->request.extra_len = state->in_data.length;
+
+ if (hs->child != NULL) {
+ subreq = wb_child_request_send(state, ev, hs->child,
+ &state->request);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(
+ subreq, wbint_bh_raw_call_child_done, req);
+ return req;
+ }
+
+ subreq = wb_domain_request_send(state, ev, hs->domain,
+ &state->request);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, wbint_bh_raw_call_domain_done, req);
+
+ return req;
+}
+
+static void wbint_bh_raw_call_child_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req =
+ tevent_req_callback_data(subreq,
+ struct tevent_req);
+ struct wbint_bh_raw_call_state *state =
+ tevent_req_data(req,
+ struct wbint_bh_raw_call_state);
+ int ret, err;
+
+ ret = wb_child_request_recv(subreq, state, &state->response, &err);
+ TALLOC_FREE(subreq);
+ if (ret == -1) {
+ NTSTATUS status = map_nt_error_from_unix(err);
+ tevent_req_nterror(req, status);
+ return;
+ }
+
+ state->out_data = data_blob_talloc(state,
+ state->response->extra_data.data,
+ state->response->length - sizeof(struct winbindd_response));
+ if (state->response->extra_data.data && !state->out_data.data) {
+ tevent_req_oom(req);
+ return;
+ }
+
+ if (state->domain != NULL) {
+ wcache_store_ndr(state->domain, state->opnum,
+ &state->in_data, &state->out_data);
+ }
+
+ tevent_req_done(req);
+}
+
+static void wbint_bh_raw_call_domain_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req =
+ tevent_req_callback_data(subreq,
+ struct tevent_req);
+ struct wbint_bh_raw_call_state *state =
+ tevent_req_data(req,
+ struct wbint_bh_raw_call_state);
+ int ret, err;
+
+ ret = wb_domain_request_recv(subreq, state, &state->response, &err);
+ TALLOC_FREE(subreq);
+ if (ret == -1) {
+ NTSTATUS status = map_nt_error_from_unix(err);
+ tevent_req_nterror(req, status);
+ return;
+ }
+
+ state->out_data = data_blob_talloc(state,
+ state->response->extra_data.data,
+ state->response->length - sizeof(struct winbindd_response));
+ if (state->response->extra_data.data && !state->out_data.data) {
+ tevent_req_oom(req);
+ return;
+ }
+
+ if (state->domain != NULL) {
+ wcache_store_ndr(state->domain, state->opnum,
+ &state->in_data, &state->out_data);
+ }
+
+ tevent_req_done(req);
+}
+
+static NTSTATUS wbint_bh_raw_call_recv(struct tevent_req *req,
+ TALLOC_CTX *mem_ctx,
+ uint8_t **out_data,
+ size_t *out_length,
+ uint32_t *out_flags)
+{
+ struct wbint_bh_raw_call_state *state =
+ tevent_req_data(req,
+ struct wbint_bh_raw_call_state);
+ NTSTATUS status;
+
+ if (tevent_req_is_nterror(req, &status)) {
+ tevent_req_received(req);
+ return status;
+ }
+
+ *out_data = talloc_move(mem_ctx, &state->out_data.data);
+ *out_length = state->out_data.length;
+ *out_flags = 0;
+ tevent_req_received(req);
+ return NT_STATUS_OK;
+}
+
+struct wbint_bh_disconnect_state {
+ uint8_t _dummy;
+};
+
+static struct tevent_req *wbint_bh_disconnect_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct dcerpc_binding_handle *h)
+{
+ struct wbint_bh_state *hs = dcerpc_binding_handle_data(h,
+ struct wbint_bh_state);
+ struct tevent_req *req;
+ struct wbint_bh_disconnect_state *state;
+ bool ok;
+
+ req = tevent_req_create(mem_ctx, &state,
+ struct wbint_bh_disconnect_state);
+ if (req == NULL) {
+ return NULL;
+ }
+
+ ok = wbint_bh_is_connected(h);
+ if (!ok) {
+ tevent_req_nterror(req, NT_STATUS_CONNECTION_DISCONNECTED);
+ return tevent_req_post(req, ev);
+ }
+
+ /*
+ * TODO: do a real async disconnect ...
+ */
+ hs->domain = NULL;
+ hs->child = NULL;
+
+ tevent_req_done(req);
+ return tevent_req_post(req, ev);
+}
+
+static NTSTATUS wbint_bh_disconnect_recv(struct tevent_req *req)
+{
+ NTSTATUS status;
+
+ if (tevent_req_is_nterror(req, &status)) {
+ tevent_req_received(req);
+ return status;
+ }
+
+ tevent_req_received(req);
+ return NT_STATUS_OK;
+}
+
+static bool wbint_bh_ref_alloc(struct dcerpc_binding_handle *h)
+{
+ return true;
+}
+
+static void wbint_bh_do_ndr_print(struct dcerpc_binding_handle *h,
+ ndr_flags_type ndr_flags,
+ const void *_struct_ptr,
+ const struct ndr_interface_call *call)
+{
+ void *struct_ptr = discard_const(_struct_ptr);
+
+ if (DEBUGLEVEL < 10) {
+ return;
+ }
+
+ if (ndr_flags & NDR_IN) {
+ ndr_print_function_debug(call->ndr_print,
+ call->name,
+ ndr_flags,
+ struct_ptr);
+ }
+ if (ndr_flags & NDR_OUT) {
+ ndr_print_function_debug(call->ndr_print,
+ call->name,
+ ndr_flags,
+ struct_ptr);
+ }
+}
+
+static const struct dcerpc_binding_handle_ops wbint_bh_ops = {
+ .name = "wbint",
+ .is_connected = wbint_bh_is_connected,
+ .set_timeout = wbint_bh_set_timeout,
+ .raw_call_send = wbint_bh_raw_call_send,
+ .raw_call_recv = wbint_bh_raw_call_recv,
+ .disconnect_send = wbint_bh_disconnect_send,
+ .disconnect_recv = wbint_bh_disconnect_recv,
+
+ .ref_alloc = wbint_bh_ref_alloc,
+ .do_ndr_print = wbint_bh_do_ndr_print,
+};
+
+static NTSTATUS make_internal_ncacn_conn(TALLOC_CTX *mem_ctx,
+ const struct ndr_interface_table *table,
+ struct dcerpc_ncacn_conn **_out)
+{
+ struct dcerpc_ncacn_conn *ncacn_conn = NULL;
+
+ ncacn_conn = talloc_zero(mem_ctx, struct dcerpc_ncacn_conn);
+ if (ncacn_conn == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ ncacn_conn->p.mem_ctx = mem_ctx;
+
+ *_out = ncacn_conn;
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS find_ncalrpc_default_endpoint(struct dcesrv_context *dce_ctx,
+ struct dcesrv_endpoint **ep)
+{
+ TALLOC_CTX *tmp_ctx = NULL;
+ struct dcerpc_binding *binding = NULL;
+ NTSTATUS status;
+
+ tmp_ctx = talloc_new(dce_ctx);
+ if (tmp_ctx == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ /*
+ * Some services use a rpcint binding handle in their initialization,
+ * before the server is fully initialized. Search the NCALRPC endpoint
+ * with and without endpoint
+ */
+ status = dcerpc_parse_binding(tmp_ctx, "ncalrpc:", &binding);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto out;
+ }
+
+ status = dcesrv_find_endpoint(dce_ctx, binding, ep);
+ if (NT_STATUS_IS_OK(status)) {
+ goto out;
+ }
+
+ status = dcerpc_parse_binding(tmp_ctx, "ncalrpc:[WINBIND]", &binding);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto out;
+ }
+
+ status = dcesrv_find_endpoint(dce_ctx, binding, ep);
+ if (NT_STATUS_IS_OK(status)) {
+ goto out;
+ }
+
+ status = dcerpc_parse_binding(tmp_ctx, "ncalrpc:[DEFAULT]", &binding);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto out;
+ }
+
+ status = dcesrv_find_endpoint(dce_ctx, binding, ep);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto out;
+ }
+
+out:
+ talloc_free(tmp_ctx);
+ return status;
+}
+
+static NTSTATUS make_internal_dcesrv_connection(TALLOC_CTX *mem_ctx,
+ const struct ndr_interface_table *ndr_table,
+ struct dcerpc_ncacn_conn *ncacn_conn,
+ struct dcesrv_connection **_out)
+{
+ struct dcesrv_connection *conn = NULL;
+ struct dcesrv_connection_context *context = NULL;
+ struct dcesrv_endpoint *endpoint = NULL;
+ NTSTATUS status;
+
+ conn = talloc_zero(mem_ctx, struct dcesrv_connection);
+ if (conn == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ conn->dce_ctx = global_dcesrv_context();
+ conn->preferred_transfer = &ndr_transfer_syntax_ndr;
+ conn->transport.private_data = ncacn_conn;
+
+ status = find_ncalrpc_default_endpoint(conn->dce_ctx, &endpoint);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto fail;
+ }
+ conn->endpoint = endpoint;
+
+ conn->default_auth_state = talloc_zero(conn, struct dcesrv_auth);
+ if (conn->default_auth_state == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ goto fail;
+ }
+ conn->default_auth_state->auth_finished = true;
+
+ context = talloc_zero(conn, struct dcesrv_connection_context);
+ if (context == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ goto fail;
+ }
+ context->conn = conn;
+ context->context_id = 0;
+ context->transfer_syntax = *(conn->preferred_transfer);
+ context->iface = find_interface_by_syntax_id(
+ conn->endpoint, &ndr_table->syntax_id);
+ if (context->iface == NULL) {
+ status = NT_STATUS_RPC_INTERFACE_NOT_FOUND;
+ goto fail;
+ }
+
+ DLIST_ADD(conn->contexts, context);
+
+ *_out = conn;
+
+ return NT_STATUS_OK;
+fail:
+ talloc_free(conn);
+ return status;
+}
+
+static NTSTATUS set_remote_addresses(struct dcesrv_connection *conn,
+ int sock)
+{
+ struct sockaddr_storage st = { 0 };
+ struct sockaddr *sar = (struct sockaddr *)&st;
+ struct tsocket_address *remote = NULL;
+ struct tsocket_address *local = NULL;
+ socklen_t sa_len = sizeof(st);
+ NTSTATUS status;
+ int ret;
+
+ ZERO_STRUCT(st);
+ ret = getpeername(sock, sar, &sa_len);
+ if (ret != 0) {
+ status = map_nt_error_from_unix(ret);
+ DBG_ERR("getpeername failed: %s\n", nt_errstr(status));
+ return status;
+ }
+
+ ret = tsocket_address_bsd_from_sockaddr(conn, sar, sa_len, &remote);
+ if (ret != 0) {
+ status = map_nt_error_from_unix(ret);
+ DBG_ERR("tsocket_address_bsd_from_sockaddr failed: %s\n",
+ nt_errstr(status));
+ return status;
+ }
+
+ ZERO_STRUCT(st);
+ ret = getsockname(sock, sar, &sa_len);
+ if (ret != 0) {
+ status = map_nt_error_from_unix(ret);
+ DBG_ERR("getsockname failed: %s\n", nt_errstr(status));
+ return status;
+ }
+
+ ret = tsocket_address_bsd_from_sockaddr(conn, sar, sa_len, &local);
+ if (ret != 0) {
+ status = map_nt_error_from_unix(ret);
+ DBG_ERR("tsocket_address_bsd_from_sockaddr failed: %s\n",
+ nt_errstr(status));
+ return status;
+ }
+
+ conn->local_address = talloc_move(conn, &local);
+ conn->remote_address = talloc_move(conn, &remote);
+
+ return NT_STATUS_OK;
+}
+
+/* initialise a wbint binding handle */
+struct dcerpc_binding_handle *wbint_binding_handle(TALLOC_CTX *mem_ctx,
+ struct winbindd_domain *domain,
+ struct winbindd_child *child)
+{
+ struct dcerpc_binding_handle *h;
+ struct wbint_bh_state *hs;
+
+ h = dcerpc_binding_handle_create(mem_ctx,
+ &wbint_bh_ops,
+ NULL,
+ &ndr_table_winbind,
+ &hs,
+ struct wbint_bh_state,
+ __location__);
+ if (h == NULL) {
+ return NULL;
+ }
+ hs->domain = domain;
+ hs->child = child;
+
+ return h;
+}
+
+enum winbindd_result winbindd_dual_ndrcmd(struct winbindd_domain *domain,
+ struct winbindd_cli_state *state)
+{
+ struct dcerpc_ncacn_conn *ncacn_conn = NULL;
+ struct dcesrv_connection *dcesrv_conn = NULL;
+ struct dcesrv_call_state *dcesrv_call = NULL;
+ struct data_blob_list_item *rep = NULL;
+ struct dcesrv_context_callbacks *cb = NULL;
+ uint32_t opnum = state->request->data.ndrcmd;
+ TALLOC_CTX *mem_ctx;
+ NTSTATUS status;
+
+ DBG_DEBUG("Running command %s (domain '%s')\n",
+ ndr_table_winbind.calls[opnum].name,
+ domain ? domain->name : "(null)");
+
+ mem_ctx = talloc_stackframe();
+ if (mem_ctx == NULL) {
+ DBG_ERR("No memory\n");
+ return WINBINDD_ERROR;
+ }
+
+ status = make_internal_ncacn_conn(mem_ctx,
+ &ndr_table_winbind,
+ &ncacn_conn);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto out;
+ }
+
+ status = make_internal_dcesrv_connection(ncacn_conn,
+ &ndr_table_winbind,
+ ncacn_conn,
+ &dcesrv_conn);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto out;
+ }
+
+ status = set_remote_addresses(dcesrv_conn, state->sock);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto out;
+ }
+
+ dcesrv_call = talloc_zero(dcesrv_conn, struct dcesrv_call_state);
+ if (dcesrv_call == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ goto out;
+ }
+
+ dcesrv_call->conn = dcesrv_conn;
+ dcesrv_call->context = dcesrv_conn->contexts;
+ dcesrv_call->auth_state = dcesrv_conn->default_auth_state;
+
+ ZERO_STRUCT(dcesrv_call->pkt);
+ dcesrv_call->pkt.u.bind.assoc_group_id = 0;
+
+ cb = dcesrv_call->conn->dce_ctx->callbacks;
+ status = cb->assoc_group.find(
+ dcesrv_call, cb->assoc_group.private_data);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto out;
+ }
+
+ ZERO_STRUCT(dcesrv_call->pkt);
+ dcesrv_call->pkt.u.request.opnum = opnum;
+ dcesrv_call->pkt.u.request.context_id = 0;
+ dcesrv_call->pkt.u.request.stub_and_verifier =
+ data_blob_const(state->request->extra_data.data,
+ state->request->extra_len);
+
+ status = dcesrv_call_dispatch_local(dcesrv_call);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto out;
+ }
+
+ rep = dcesrv_call->replies;
+ DLIST_REMOVE(dcesrv_call->replies, rep);
+
+ state->response->extra_data.data = talloc_steal(state->mem_ctx,
+ rep->blob.data);
+ state->response->length += rep->blob.length;
+
+ talloc_free(rep);
+
+out:
+ talloc_free(mem_ctx);
+ if (NT_STATUS_IS_OK(status)) {
+ return WINBINDD_OK;
+ }
+ return WINBINDD_ERROR;
+}
diff --git a/source3/winbindd/winbindd_dual_srv.c b/source3/winbindd/winbindd_dual_srv.c
new file mode 100644
index 0000000..bbdaf6e
--- /dev/null
+++ b/source3/winbindd/winbindd_dual_srv.c
@@ -0,0 +1,2127 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ In-Child server implementation of the routines defined in wbint.idl
+
+ Copyright (C) Volker Lendecke 2009
+ Copyright (C) Guenther Deschner 2009
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "winbindd/winbindd.h"
+#include "winbindd/winbindd_proto.h"
+#include "rpc_client/cli_pipe.h"
+#include "ntdomain.h"
+#include "librpc/rpc/dcesrv_core.h"
+#include "librpc/gen_ndr/ndr_winbind.h"
+#include "librpc/gen_ndr/ndr_winbind_scompat.h"
+#include "../librpc/gen_ndr/ndr_netlogon_c.h"
+#include "../librpc/gen_ndr/ndr_lsa_c.h"
+#include "idmap.h"
+#include "../libcli/security/security.h"
+#include "../libcli/auth/netlogon_creds_cli.h"
+#include "passdb.h"
+#include "../source4/dsdb/samdb/samdb.h"
+#include "rpc_client/cli_netlogon.h"
+#include "rpc_client/util_netlogon.h"
+#include "libsmb/dsgetdcname.h"
+#include "lib/global_contexts.h"
+
+NTSTATUS _wbint_Ping(struct pipes_struct *p, struct wbint_Ping *r)
+{
+ *r->out.out_data = r->in.in_data;
+ return NT_STATUS_OK;
+}
+
+NTSTATUS _wbint_InitConnection(struct pipes_struct *p,
+ struct wbint_InitConnection *r)
+{
+ struct winbindd_domain *domain = wb_child_domain();
+
+ if (r->in.dcname != NULL && strlen(r->in.dcname) > 0) {
+ TALLOC_FREE(domain->dcname);
+ domain->dcname = talloc_strdup(domain, r->in.dcname);
+ if (domain->dcname == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ }
+
+ init_dc_connection(domain, false);
+
+ if (!domain->initialized) {
+ /*
+ * If we return error here we can't do any cached
+ * authentication, but we may be in disconnected mode and can't
+ * initialize correctly. Do what the previous code did and just
+ * return without initialization, once we go online we'll
+ * re-initialize.
+ */
+ DBG_INFO("%s returning without initialization online = %d\n",
+ domain->name, (int)domain->online);
+ }
+
+ *r->out.name = talloc_strdup(p->mem_ctx, domain->name);
+ if (*r->out.name == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ if (domain->alt_name != NULL) {
+ *r->out.alt_name = talloc_strdup(p->mem_ctx, domain->alt_name);
+ if (*r->out.alt_name == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ }
+
+ r->out.sid = dom_sid_dup(p->mem_ctx, &domain->sid);
+ if (r->out.sid == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ *r->out.flags = 0;
+ if (domain->native_mode) {
+ *r->out.flags |= WB_DOMINFO_DOMAIN_NATIVE;
+ }
+ if (domain->active_directory) {
+ *r->out.flags |= WB_DOMINFO_DOMAIN_AD;
+ }
+ if (domain->primary) {
+ *r->out.flags |= WB_DOMINFO_DOMAIN_PRIMARY;
+ }
+
+ return NT_STATUS_OK;
+}
+
+bool reset_cm_connection_on_error(struct winbindd_domain *domain,
+ struct dcerpc_binding_handle *b,
+ NTSTATUS status)
+{
+ if (NT_STATUS_EQUAL(status, NT_STATUS_ACCESS_DENIED) ||
+ NT_STATUS_EQUAL(status, NT_STATUS_RPC_SEC_PKG_ERROR) ||
+ NT_STATUS_EQUAL(status, NT_STATUS_NETWORK_ACCESS_DENIED)) {
+ invalidate_cm_connection(domain);
+ domain->conn.netlogon_force_reauth = true;
+ return true;
+ }
+
+ if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
+ NT_STATUS_EQUAL(status, NT_STATUS_IO_DEVICE_ERROR))
+ {
+ invalidate_cm_connection(domain);
+ /* We invalidated the connection. */
+ return true;
+ }
+
+ if (b != NULL && !dcerpc_binding_handle_is_connected(b)) {
+ invalidate_cm_connection(domain);
+ return true;
+ }
+
+ return false;
+}
+
+NTSTATUS _wbint_LookupSid(struct pipes_struct *p, struct wbint_LookupSid *r)
+{
+ struct winbindd_domain *domain = wb_child_domain();
+ char *dom_name;
+ char *name;
+ enum lsa_SidType type;
+ NTSTATUS status;
+
+ if (domain == NULL) {
+ return NT_STATUS_REQUEST_NOT_ACCEPTED;
+ }
+
+ status = wb_cache_sid_to_name(domain, p->mem_ctx, r->in.sid,
+ &dom_name, &name, &type);
+ reset_cm_connection_on_error(domain, NULL, status);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ *r->out.domain = dom_name;
+ *r->out.name = name;
+ *r->out.type = type;
+ return NT_STATUS_OK;
+}
+
+NTSTATUS _wbint_LookupSids(struct pipes_struct *p, struct wbint_LookupSids *r)
+{
+ struct winbindd_domain *domain = wb_child_domain();
+ struct lsa_RefDomainList *domains = r->out.domains;
+ NTSTATUS status;
+ bool retry = false;
+
+ if (domain == NULL) {
+ return NT_STATUS_REQUEST_NOT_ACCEPTED;
+ }
+
+ /*
+ * This breaks the winbindd_domain->methods abstraction: This
+ * is only called for remote domains, and both winbindd_msrpc
+ * and winbindd_ad call into lsa_lookupsids anyway. Caching is
+ * done at the wbint RPC layer.
+ */
+again:
+ status = rpc_lookup_sids(p->mem_ctx, domain, r->in.sids,
+ &domains, &r->out.names);
+
+ if (domains != NULL) {
+ r->out.domains = domains;
+ }
+
+ if (!retry && reset_cm_connection_on_error(domain, NULL, status)) {
+ retry = true;
+ goto again;
+ }
+
+ return status;
+}
+
+NTSTATUS _wbint_LookupName(struct pipes_struct *p, struct wbint_LookupName *r)
+{
+ struct winbindd_domain *domain = wb_child_domain();
+ NTSTATUS status;
+
+ if (domain == NULL) {
+ return NT_STATUS_REQUEST_NOT_ACCEPTED;
+ }
+
+ status = wb_cache_name_to_sid(domain, p->mem_ctx, r->in.domain,
+ r->in.name, r->in.flags,
+ r->out.sid, r->out.type);
+ reset_cm_connection_on_error(domain, NULL, status);
+ return status;
+}
+
+NTSTATUS _wbint_Sids2UnixIDs(struct pipes_struct *p,
+ struct wbint_Sids2UnixIDs *r)
+{
+ uint32_t i;
+
+ struct lsa_DomainInfo *d;
+ struct wbint_TransID *ids;
+ uint32_t num_ids;
+
+ struct id_map **id_map_ptrs = NULL;
+ struct idmap_domain *dom;
+ NTSTATUS status = NT_STATUS_NO_MEMORY;
+
+ if (r->in.domains->count != 1) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ d = &r->in.domains->domains[0];
+ ids = r->in.ids->ids;
+ num_ids = r->in.ids->num_ids;
+
+ dom = idmap_find_domain_with_sid(d->name.string, d->sid);
+ if (dom == NULL) {
+ struct dom_sid_buf buf;
+ DEBUG(10, ("idmap domain %s:%s not found\n",
+ d->name.string,
+ dom_sid_str_buf(d->sid, &buf)));
+
+ for (i=0; i<num_ids; i++) {
+
+ ids[i].xid = (struct unixid) {
+ .id = UINT32_MAX,
+ .type = ID_TYPE_NOT_SPECIFIED
+ };
+ }
+
+ return NT_STATUS_OK;
+ }
+
+ id_map_ptrs = id_map_ptrs_init(talloc_tos(), num_ids);
+ if (id_map_ptrs == NULL) {
+ goto nomem;
+ }
+
+ /*
+ * Convert the input data into a list of id_map structs
+ * suitable for handing in to the idmap sids_to_unixids
+ * method.
+ */
+
+ for (i=0; i<num_ids; i++) {
+ struct id_map *m = id_map_ptrs[i];
+
+ sid_compose(m->sid, d->sid, ids[i].rid);
+ m->status = ID_UNKNOWN;
+ m->xid = (struct unixid) { .type = ids[i].type_hint };
+ }
+
+ status = dom->methods->sids_to_unixids(dom, id_map_ptrs);
+
+ if (NT_STATUS_EQUAL(status, STATUS_SOME_UNMAPPED)) {
+ /*
+ * This is okay. We need to transfer the mapped ones
+ * up to our caller. The individual mappings carry the
+ * information whether they are mapped or not.
+ */
+ status = NT_STATUS_OK;
+ }
+
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(10, ("sids_to_unixids returned %s\n",
+ nt_errstr(status)));
+ goto done;
+ }
+
+ /*
+ * Extract the results for handing them back to the caller.
+ */
+
+ for (i=0; i<num_ids; i++) {
+ struct id_map *m = id_map_ptrs[i];
+
+ if (m->status == ID_REQUIRE_TYPE) {
+ ids[i].xid.id = UINT32_MAX;
+ ids[i].xid.type = ID_TYPE_WB_REQUIRE_TYPE;
+ continue;
+ }
+
+ if (!idmap_unix_id_is_in_range(m->xid.id, dom)) {
+ DBG_DEBUG("id %"PRIu32" is out of range "
+ "%"PRIu32"-%"PRIu32" for domain %s\n",
+ m->xid.id, dom->low_id, dom->high_id,
+ dom->name);
+ m->status = ID_UNMAPPED;
+ }
+
+ if (m->status == ID_MAPPED) {
+ ids[i].xid = m->xid;
+ } else {
+ ids[i].xid.id = UINT32_MAX;
+ ids[i].xid.type = ID_TYPE_NOT_SPECIFIED;
+ }
+ }
+
+ goto done;
+nomem:
+ status = NT_STATUS_NO_MEMORY;
+done:
+ TALLOC_FREE(id_map_ptrs);
+ return status;
+}
+
+NTSTATUS _wbint_UnixIDs2Sids(struct pipes_struct *p,
+ struct wbint_UnixIDs2Sids *r)
+{
+ struct id_map **maps;
+ NTSTATUS status;
+ uint32_t i;
+
+ maps = id_map_ptrs_init(talloc_tos(), r->in.num_ids);
+ if (maps == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ for (i=0; i<r->in.num_ids; i++) {
+ maps[i]->status = ID_UNKNOWN;
+ maps[i]->xid = r->in.xids[i];
+ }
+
+ status = idmap_backend_unixids_to_sids(maps, r->in.domain_name,
+ r->in.domain_sid);
+ if (!NT_STATUS_IS_OK(status)) {
+ TALLOC_FREE(maps);
+ return status;
+ }
+
+ for (i=0; i<r->in.num_ids; i++) {
+ if (maps[i]->status == ID_MAPPED) {
+ r->out.xids[i] = maps[i]->xid;
+ sid_copy(&r->out.sids[i], maps[i]->sid);
+ } else {
+ r->out.sids[i] = (struct dom_sid) { 0 };
+ }
+ }
+
+ TALLOC_FREE(maps);
+
+ return NT_STATUS_OK;
+}
+
+NTSTATUS _wbint_AllocateUid(struct pipes_struct *p, struct wbint_AllocateUid *r)
+{
+ struct unixid xid;
+ NTSTATUS status;
+
+ status = idmap_allocate_uid(&xid);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ *r->out.uid = xid.id;
+ return NT_STATUS_OK;
+}
+
+NTSTATUS _wbint_AllocateGid(struct pipes_struct *p, struct wbint_AllocateGid *r)
+{
+ struct unixid xid;
+ NTSTATUS status;
+
+ status = idmap_allocate_gid(&xid);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ *r->out.gid = xid.id;
+ return NT_STATUS_OK;
+}
+
+NTSTATUS _wbint_GetNssInfo(struct pipes_struct *p, struct wbint_GetNssInfo *r)
+{
+ struct idmap_domain *domain;
+ NTSTATUS status;
+
+ domain = idmap_find_domain(r->in.info->domain_name);
+ if ((domain == NULL) || (domain->query_user == NULL)) {
+ return NT_STATUS_REQUEST_NOT_ACCEPTED;
+ }
+
+ status = domain->query_user(domain, r->in.info);
+ return status;
+}
+
+NTSTATUS _wbint_LookupUserAliases(struct pipes_struct *p,
+ struct wbint_LookupUserAliases *r)
+{
+ struct winbindd_domain *domain = wb_child_domain();
+ NTSTATUS status;
+
+ if (domain == NULL) {
+ return NT_STATUS_REQUEST_NOT_ACCEPTED;
+ }
+
+ status = wb_cache_lookup_useraliases(domain, p->mem_ctx,
+ r->in.sids->num_sids,
+ r->in.sids->sids,
+ &r->out.rids->num_rids,
+ &r->out.rids->rids);
+ reset_cm_connection_on_error(domain, NULL, status);
+ return status;
+}
+
+NTSTATUS _wbint_LookupUserGroups(struct pipes_struct *p,
+ struct wbint_LookupUserGroups *r)
+{
+ struct winbindd_domain *domain = wb_child_domain();
+ NTSTATUS status;
+
+ if (domain == NULL) {
+ return NT_STATUS_REQUEST_NOT_ACCEPTED;
+ }
+
+ status = wb_cache_lookup_usergroups(domain, p->mem_ctx, r->in.sid,
+ &r->out.sids->num_sids,
+ &r->out.sids->sids);
+ reset_cm_connection_on_error(domain, NULL, status);
+ return status;
+}
+
+NTSTATUS _wbint_QuerySequenceNumber(struct pipes_struct *p,
+ struct wbint_QuerySequenceNumber *r)
+{
+ struct winbindd_domain *domain = wb_child_domain();
+ NTSTATUS status;
+
+ if (domain == NULL) {
+ return NT_STATUS_REQUEST_NOT_ACCEPTED;
+ }
+
+ status = wb_cache_sequence_number(domain, r->out.sequence);
+ reset_cm_connection_on_error(domain, NULL, status);
+ return status;
+}
+
+NTSTATUS _wbint_LookupGroupMembers(struct pipes_struct *p,
+ struct wbint_LookupGroupMembers *r)
+{
+ struct winbindd_domain *domain = wb_child_domain();
+ uint32_t i, num_names;
+ struct dom_sid *sid_mem;
+ char **names;
+ uint32_t *name_types;
+ NTSTATUS status;
+
+ if (domain == NULL) {
+ return NT_STATUS_REQUEST_NOT_ACCEPTED;
+ }
+
+ status = wb_cache_lookup_groupmem(domain, p->mem_ctx, r->in.sid,
+ r->in.type, &num_names, &sid_mem,
+ &names, &name_types);
+ reset_cm_connection_on_error(domain, NULL, status);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ r->out.members->num_principals = num_names;
+ r->out.members->principals = talloc_array(
+ r->out.members, struct wbint_Principal, num_names);
+ if (r->out.members->principals == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ for (i=0; i<num_names; i++) {
+ struct wbint_Principal *m = &r->out.members->principals[i];
+ sid_copy(&m->sid, &sid_mem[i]);
+ m->name = talloc_move(r->out.members->principals, &names[i]);
+ m->type = (enum lsa_SidType)name_types[i];
+ }
+
+ return NT_STATUS_OK;
+}
+
+NTSTATUS _wbint_LookupAliasMembers(struct pipes_struct *p,
+ struct wbint_LookupAliasMembers *r)
+{
+ struct winbindd_domain *domain = wb_child_domain();
+ NTSTATUS status;
+
+ if (domain == NULL) {
+ return NT_STATUS_REQUEST_NOT_ACCEPTED;
+ }
+ status = wb_cache_lookup_aliasmem(domain,
+ p->mem_ctx,
+ r->in.sid,
+ r->in.type,
+ &r->out.sids->num_sids,
+ &r->out.sids->sids);
+ reset_cm_connection_on_error(domain, NULL, status);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ return NT_STATUS_OK;
+}
+
+NTSTATUS _wbint_QueryGroupList(struct pipes_struct *p,
+ struct wbint_QueryGroupList *r)
+{
+ TALLOC_CTX *frame = NULL;
+ struct winbindd_domain *domain = wb_child_domain();
+ uint32_t i;
+ uint32_t num_local_groups = 0;
+ struct wb_acct_info *local_groups = NULL;
+ uint32_t num_dom_groups = 0;
+ struct wb_acct_info *dom_groups = NULL;
+ uint32_t ti = 0;
+ uint64_t num_total = 0;
+ struct wbint_Principal *result;
+ NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
+ bool include_local_groups = false;
+
+ if (domain == NULL) {
+ return NT_STATUS_REQUEST_NOT_ACCEPTED;
+ }
+
+ frame = talloc_stackframe();
+
+ switch (lp_server_role()) {
+ case ROLE_ACTIVE_DIRECTORY_DC:
+ if (domain->internal) {
+ /*
+ * we want to include local groups
+ * for BUILTIN and WORKGROUP
+ */
+ include_local_groups = true;
+ }
+ break;
+ case ROLE_DOMAIN_MEMBER:
+ /*
+ * This is needed for GETGRENT to show also e.g. BUILTIN/users.
+ * Otherwise the test_membership_user (smbtorture
+ * local.nss.membership) would fail (getgrouplist() would
+ * reports BUILTIN/users).
+ */
+ if (domain->internal) {
+ /*
+ * we want to include local groups
+ * for BUILTIN and LOCALSAM
+ */
+ include_local_groups = true;
+ }
+ break;
+ default:
+ /*
+ * We might include local groups in more
+ * setups later, but that requires more work
+ * elsewhere.
+ */
+ break;
+ }
+
+ if (include_local_groups) {
+ status = wb_cache_enum_local_groups(domain, frame,
+ &num_local_groups,
+ &local_groups);
+ reset_cm_connection_on_error(domain, NULL, status);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto out;
+ }
+ }
+
+ status = wb_cache_enum_dom_groups(domain, frame,
+ &num_dom_groups,
+ &dom_groups);
+ reset_cm_connection_on_error(domain, NULL, status);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto out;
+ }
+
+ num_total = num_local_groups + num_dom_groups;
+ if (num_total > UINT32_MAX) {
+ status = NT_STATUS_INTERNAL_ERROR;
+ goto out;
+ }
+
+ result = talloc_array(frame, struct wbint_Principal, num_total);
+ if (result == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ goto out;
+ }
+
+ for (i = 0; i < num_local_groups; i++) {
+ struct wb_acct_info *lg = &local_groups[i];
+ struct wbint_Principal *rg = &result[ti++];
+
+ sid_compose(&rg->sid, &domain->sid, lg->rid);
+ rg->type = SID_NAME_ALIAS;
+ rg->name = talloc_strdup(result, lg->acct_name);
+ if (rg->name == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ goto out;
+ }
+ }
+ num_local_groups = 0;
+
+ for (i = 0; i < num_dom_groups; i++) {
+ struct wb_acct_info *dg = &dom_groups[i];
+ struct wbint_Principal *rg = &result[ti++];
+
+ sid_compose(&rg->sid, &domain->sid, dg->rid);
+ rg->type = SID_NAME_DOM_GRP;
+ rg->name = talloc_strdup(result, dg->acct_name);
+ if (rg->name == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ goto out;
+ }
+ }
+ num_dom_groups = 0;
+
+ r->out.groups->num_principals = ti;
+ r->out.groups->principals = talloc_move(r->out.groups, &result);
+
+ status = NT_STATUS_OK;
+out:
+ TALLOC_FREE(frame);
+ return status;
+}
+
+NTSTATUS _wbint_QueryUserRidList(struct pipes_struct *p,
+ struct wbint_QueryUserRidList *r)
+{
+ struct winbindd_domain *domain = wb_child_domain();
+ NTSTATUS status;
+
+ if (domain == NULL) {
+ return NT_STATUS_REQUEST_NOT_ACCEPTED;
+ }
+
+ /*
+ * Right now this is overkill. We should add a backend call
+ * just querying the rids.
+ */
+
+ status = wb_cache_query_user_list(domain, p->mem_ctx,
+ &r->out.rids->rids);
+ reset_cm_connection_on_error(domain, NULL, status);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ r->out.rids->num_rids = talloc_array_length(r->out.rids->rids);
+
+ return NT_STATUS_OK;
+}
+
+NTSTATUS _wbint_DsGetDcName(struct pipes_struct *p, struct wbint_DsGetDcName *r)
+{
+ struct winbindd_domain *domain = wb_child_domain();
+ struct rpc_pipe_client *netlogon_pipe;
+ struct netr_DsRGetDCNameInfo *dc_info;
+ NTSTATUS status;
+ WERROR werr;
+ unsigned int orig_timeout;
+ struct dcerpc_binding_handle *b;
+ bool retry = false;
+ bool try_dsrgetdcname = false;
+
+ if (domain == NULL) {
+ return dsgetdcname(p->mem_ctx, global_messaging_context(),
+ r->in.domain_name, r->in.domain_guid,
+ r->in.site_name ? r->in.site_name : "",
+ r->in.flags,
+ r->out.dc_info);
+ }
+
+ if (domain->active_directory) {
+ try_dsrgetdcname = true;
+ }
+
+reconnect:
+ status = cm_connect_netlogon(domain, &netlogon_pipe);
+
+ reset_cm_connection_on_error(domain, NULL, status);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(10, ("Can't contact the NETLOGON pipe\n"));
+ return status;
+ }
+
+ b = netlogon_pipe->binding_handle;
+
+ /* This call can take a long time - allow the server to time out.
+ 35 seconds should do it. */
+
+ orig_timeout = rpccli_set_timeout(netlogon_pipe, 35000);
+
+ if (try_dsrgetdcname) {
+ status = dcerpc_netr_DsRGetDCName(b,
+ p->mem_ctx, domain->dcname,
+ r->in.domain_name, NULL, r->in.domain_guid,
+ r->in.flags, r->out.dc_info, &werr);
+ if (NT_STATUS_IS_OK(status) && W_ERROR_IS_OK(werr)) {
+ goto done;
+ }
+ if (!retry &&
+ reset_cm_connection_on_error(domain, NULL, status))
+ {
+ retry = true;
+ goto reconnect;
+ }
+ try_dsrgetdcname = false;
+ retry = false;
+ }
+
+ /*
+ * Fallback to less capable methods
+ */
+
+ dc_info = talloc_zero(r->out.dc_info, struct netr_DsRGetDCNameInfo);
+ if (dc_info == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ goto done;
+ }
+
+ if (r->in.flags & DS_PDC_REQUIRED) {
+ status = dcerpc_netr_GetDcName(b,
+ p->mem_ctx, domain->dcname,
+ r->in.domain_name, &dc_info->dc_unc, &werr);
+ } else {
+ status = dcerpc_netr_GetAnyDCName(b,
+ p->mem_ctx, domain->dcname,
+ r->in.domain_name, &dc_info->dc_unc, &werr);
+ }
+
+ if (!retry && reset_cm_connection_on_error(domain, b, status)) {
+ retry = true;
+ goto reconnect;
+ }
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(10, ("dcerpc_netr_Get[Any]DCName failed: %s\n",
+ nt_errstr(status)));
+ goto done;
+ }
+ if (!W_ERROR_IS_OK(werr)) {
+ DEBUG(10, ("dcerpc_netr_Get[Any]DCName failed: %s\n",
+ win_errstr(werr)));
+ status = werror_to_ntstatus(werr);
+ goto done;
+ }
+
+ *r->out.dc_info = dc_info;
+ status = NT_STATUS_OK;
+
+done:
+ /* And restore our original timeout. */
+ rpccli_set_timeout(netlogon_pipe, orig_timeout);
+
+ return status;
+}
+
+NTSTATUS _wbint_LookupRids(struct pipes_struct *p, struct wbint_LookupRids *r)
+{
+ struct winbindd_domain *domain = wb_child_domain();
+ char *domain_name;
+ char **names;
+ enum lsa_SidType *types;
+ struct wbint_Principal *result;
+ NTSTATUS status;
+ uint32_t i;
+
+ if (domain == NULL) {
+ return NT_STATUS_REQUEST_NOT_ACCEPTED;
+ }
+
+ status = wb_cache_rids_to_names(domain, talloc_tos(), r->in.domain_sid,
+ r->in.rids->rids, r->in.rids->num_rids,
+ &domain_name, &names, &types);
+ reset_cm_connection_on_error(domain, NULL, status);
+ if (!NT_STATUS_IS_OK(status) &&
+ !NT_STATUS_EQUAL(status, STATUS_SOME_UNMAPPED)) {
+ return status;
+ }
+
+ *r->out.domain_name = talloc_move(r->out.domain_name, &domain_name);
+
+ result = talloc_array(p->mem_ctx, struct wbint_Principal,
+ r->in.rids->num_rids);
+ if (result == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ for (i=0; i<r->in.rids->num_rids; i++) {
+ sid_compose(&result[i].sid, r->in.domain_sid,
+ r->in.rids->rids[i]);
+ result[i].type = types[i];
+ result[i].name = talloc_move(result, &names[i]);
+ }
+ TALLOC_FREE(types);
+ TALLOC_FREE(names);
+
+ r->out.names->num_principals = r->in.rids->num_rids;
+ r->out.names->principals = result;
+ return NT_STATUS_OK;
+}
+
+NTSTATUS _wbint_CheckMachineAccount(struct pipes_struct *p,
+ struct wbint_CheckMachineAccount *r)
+{
+ struct winbindd_domain *domain;
+ int num_retries = 0;
+ NTSTATUS status;
+
+ domain = wb_child_domain();
+ if (domain == NULL) {
+ return NT_STATUS_REQUEST_NOT_ACCEPTED;
+ }
+
+again:
+ invalidate_cm_connection(domain);
+ domain->conn.netlogon_force_reauth = true;
+
+ {
+ struct rpc_pipe_client *netlogon_pipe = NULL;
+ struct netlogon_creds_cli_context *netlogon_creds_ctx = NULL;
+ status = cm_connect_netlogon_secure(domain,
+ &netlogon_pipe,
+ &netlogon_creds_ctx);
+ }
+
+ /* There is a race condition between fetching the trust account
+ password and the periodic machine password change. So it's
+ possible that the trust account password has been changed on us.
+ We are returned NT_STATUS_ACCESS_DENIED if this happens. */
+
+#define MAX_RETRIES 3
+
+ if ((num_retries < MAX_RETRIES)
+ && NT_STATUS_EQUAL(status, NT_STATUS_ACCESS_DENIED)) {
+ num_retries++;
+ goto again;
+ }
+
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(3, ("could not open handle to NETLOGON pipe\n"));
+ goto done;
+ }
+
+ /* Pass back result code - zero for success, other values for
+ specific failures. */
+
+ DEBUG(3,("domain %s secret is %s\n", domain->name,
+ NT_STATUS_IS_OK(status) ? "good" : "bad"));
+
+ done:
+ DEBUG(NT_STATUS_IS_OK(status) ? 5 : 2,
+ ("Checking the trust account password for domain %s returned %s\n",
+ domain->name, nt_errstr(status)));
+
+ return status;
+}
+
+NTSTATUS _wbint_ChangeMachineAccount(struct pipes_struct *p,
+ struct wbint_ChangeMachineAccount *r)
+{
+ struct messaging_context *msg_ctx = global_messaging_context();
+ struct winbindd_domain *domain;
+ NTSTATUS status;
+ struct rpc_pipe_client *netlogon_pipe = NULL;
+ struct netlogon_creds_cli_context *netlogon_creds_ctx = NULL;
+
+ domain = wb_child_domain();
+ if (domain == NULL) {
+ return NT_STATUS_REQUEST_NOT_ACCEPTED;
+ }
+
+ if (r->in.dcname != NULL && r->in.dcname[0] != '\0') {
+ invalidate_cm_connection(domain);
+ TALLOC_FREE(domain->dcname);
+
+ domain->dcname = talloc_strdup(domain, r->in.dcname);
+ if (domain->dcname == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ goto done;
+ }
+ domain->force_dc = true;
+
+ DBG_NOTICE("attempt connection to change trust account "
+ "password for %s at %s\n",
+ domain->name, domain->dcname);
+ }
+
+ status = cm_connect_netlogon_secure(domain,
+ &netlogon_pipe,
+ &netlogon_creds_ctx);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(3, ("could not open handle to NETLOGON pipe\n"));
+ goto done;
+ }
+
+ status = trust_pw_change(netlogon_creds_ctx,
+ msg_ctx,
+ netlogon_pipe->binding_handle,
+ domain->name,
+ domain->dcname,
+ true); /* force */
+
+ /* Pass back result code - zero for success, other values for
+ specific failures. */
+
+ DEBUG(3,("domain %s secret %s\n", domain->name,
+ NT_STATUS_IS_OK(status) ? "changed" : "unchanged"));
+
+ done:
+ DEBUG(NT_STATUS_IS_OK(status) ? 5 :
+ domain->force_dc ? 0 : 2,
+ ("Changing the trust account password for domain %s at %s "
+ "(forced: %s) returned %s\n",
+ domain->name, domain->dcname, domain->force_dc ? "yes" : "no",
+ nt_errstr(status)));
+ domain->force_dc = false;
+
+ return status;
+}
+
+NTSTATUS _wbint_PingDc(struct pipes_struct *p, struct wbint_PingDc *r)
+{
+ NTSTATUS status;
+ struct winbindd_domain *domain;
+ struct rpc_pipe_client *netlogon_pipe;
+ union netr_CONTROL_QUERY_INFORMATION info;
+ WERROR werr;
+ fstring logon_server;
+ struct dcerpc_binding_handle *b;
+ bool retry = false;
+
+ domain = wb_child_domain();
+ if (domain == NULL) {
+ return NT_STATUS_REQUEST_NOT_ACCEPTED;
+ }
+
+reconnect:
+ status = cm_connect_netlogon(domain, &netlogon_pipe);
+ reset_cm_connection_on_error(domain, NULL, status);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(3, ("could not open handle to NETLOGON pipe: %s\n",
+ nt_errstr(status)));
+ return status;
+ }
+
+ b = netlogon_pipe->binding_handle;
+
+ fstr_sprintf(logon_server, "\\\\%s", domain->dcname);
+ *r->out.dcname = talloc_strdup(p->mem_ctx, domain->dcname);
+ if (*r->out.dcname == NULL) {
+ DEBUG(2, ("Could not allocate memory\n"));
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ /*
+ * This provokes a WERR_NOT_SUPPORTED error message. This is
+ * documented in the wspp docs. I could not get a successful
+ * call to work, but the main point here is testing that the
+ * netlogon pipe works.
+ */
+ status = dcerpc_netr_LogonControl(b, p->mem_ctx,
+ logon_server, NETLOGON_CONTROL_QUERY,
+ 2, &info, &werr);
+
+ if (!retry && reset_cm_connection_on_error(domain, b, status)) {
+ retry = true;
+ goto reconnect;
+ }
+
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(2, ("dcerpc_netr_LogonControl failed: %s\n",
+ nt_errstr(status)));
+ return status;
+ }
+
+ if (!W_ERROR_EQUAL(werr, WERR_NOT_SUPPORTED)) {
+ DEBUG(2, ("dcerpc_netr_LogonControl returned %s, expected "
+ "WERR_NOT_SUPPORTED\n",
+ win_errstr(werr)));
+ return werror_to_ntstatus(werr);
+ }
+
+ DEBUG(5, ("winbindd_dual_ping_dc succeeded\n"));
+ return NT_STATUS_OK;
+}
+
+NTSTATUS _winbind_DsrUpdateReadOnlyServerDnsRecords(struct pipes_struct *p,
+ struct winbind_DsrUpdateReadOnlyServerDnsRecords *r)
+{
+ struct winbindd_domain *domain;
+ NTSTATUS status;
+ struct rpc_pipe_client *netlogon_pipe = NULL;
+ struct netlogon_creds_cli_context *netlogon_creds_ctx = NULL;
+ struct dcerpc_binding_handle *b = NULL;
+ bool retry = false;
+
+ domain = wb_child_domain();
+ if (domain == NULL) {
+ return NT_STATUS_REQUEST_NOT_ACCEPTED;
+ }
+
+reconnect:
+ status = cm_connect_netlogon_secure(domain,
+ &netlogon_pipe,
+ &netlogon_creds_ctx);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(3, ("could not open handle to NETLOGON pipe\n"));
+ goto done;
+ }
+
+ b = netlogon_pipe->binding_handle;
+
+ status = netlogon_creds_cli_DsrUpdateReadOnlyServerDnsRecords(netlogon_creds_ctx,
+ netlogon_pipe->binding_handle,
+ r->in.site_name,
+ r->in.dns_ttl,
+ r->in.dns_names);
+
+ if (!retry && reset_cm_connection_on_error(domain, b, status)) {
+ retry = true;
+ goto reconnect;
+ }
+
+ /* Pass back result code - zero for success, other values for
+ specific failures. */
+
+ DEBUG(3,("DNS records for domain %s %s\n", domain->name,
+ NT_STATUS_IS_OK(status) ? "changed" : "unchanged"));
+
+ done:
+ DEBUG(NT_STATUS_IS_OK(status) ? 5 : 2,
+ ("Update of DNS records via RW DC %s returned %s\n",
+ domain->name, nt_errstr(status)));
+
+ return status;
+}
+
+NTSTATUS _winbind_SamLogon(struct pipes_struct *p,
+ struct winbind_SamLogon *r)
+{
+ struct dcesrv_call_state *dce_call = p->dce_call;
+ struct dcesrv_connection *dcesrv_conn = dce_call->conn;
+ const struct tsocket_address *local_address =
+ dcesrv_connection_get_local_address(dcesrv_conn);
+ const struct tsocket_address *remote_address =
+ dcesrv_connection_get_remote_address(dcesrv_conn);
+ struct winbindd_domain *domain;
+ NTSTATUS status;
+ struct netr_IdentityInfo *identity_info = NULL;
+ DATA_BLOB lm_response, nt_response;
+ DATA_BLOB challenge = data_blob_null;
+ uint32_t flags = 0;
+ uint16_t validation_level;
+ union netr_Validation *validation = NULL;
+ bool interactive = false;
+
+ /*
+ * Make sure we start with authoritative=true,
+ * it will only set to false if we don't know the
+ * domain.
+ */
+ r->out.authoritative = true;
+
+ domain = wb_child_domain();
+ if (domain == NULL) {
+ return NT_STATUS_REQUEST_NOT_ACCEPTED;
+ }
+
+ switch (r->in.validation_level) {
+ case 3:
+ case 6:
+ break;
+ default:
+ return NT_STATUS_REQUEST_NOT_ACCEPTED;
+ }
+
+ switch (r->in.logon_level) {
+ case NetlogonInteractiveInformation:
+ case NetlogonServiceInformation:
+ case NetlogonInteractiveTransitiveInformation:
+ case NetlogonServiceTransitiveInformation:
+ if (r->in.logon.password == NULL) {
+ return NT_STATUS_REQUEST_NOT_ACCEPTED;
+ }
+
+ interactive = true;
+ identity_info = &r->in.logon.password->identity_info;
+
+ challenge = data_blob_null;
+ lm_response = data_blob_talloc(p->mem_ctx,
+ r->in.logon.password->lmpassword.hash,
+ sizeof(r->in.logon.password->lmpassword.hash));
+ nt_response = data_blob_talloc(p->mem_ctx,
+ r->in.logon.password->ntpassword.hash,
+ sizeof(r->in.logon.password->ntpassword.hash));
+ break;
+
+ case NetlogonNetworkInformation:
+ case NetlogonNetworkTransitiveInformation:
+ if (r->in.logon.network == NULL) {
+ return NT_STATUS_REQUEST_NOT_ACCEPTED;
+ }
+
+ interactive = false;
+ identity_info = &r->in.logon.network->identity_info;
+
+ challenge = data_blob_talloc(p->mem_ctx,
+ r->in.logon.network->challenge,
+ 8);
+ lm_response = data_blob_talloc(p->mem_ctx,
+ r->in.logon.network->lm.data,
+ r->in.logon.network->lm.length);
+ nt_response = data_blob_talloc(p->mem_ctx,
+ r->in.logon.network->nt.data,
+ r->in.logon.network->nt.length);
+ break;
+
+ case NetlogonGenericInformation:
+ if (r->in.logon.generic == NULL) {
+ return NT_STATUS_REQUEST_NOT_ACCEPTED;
+ }
+
+ identity_info = &r->in.logon.generic->identity_info;
+ /*
+ * Not implemented here...
+ */
+ return NT_STATUS_REQUEST_NOT_ACCEPTED;
+
+ default:
+ return NT_STATUS_REQUEST_NOT_ACCEPTED;
+ }
+
+ status = winbind_dual_SamLogon(domain, p->mem_ctx,
+ interactive,
+ identity_info->parameter_control,
+ identity_info->account_name.string,
+ identity_info->domain_name.string,
+ identity_info->workstation.string,
+ identity_info->logon_id,
+ "SamLogon",
+ 0,
+ challenge,
+ lm_response, nt_response,
+ remote_address,
+ local_address,
+ &r->out.authoritative,
+ true, /* skip_sam */
+ &flags,
+ &validation_level,
+ &validation);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ switch (r->in.validation_level) {
+ case 3:
+ status = map_validation_to_info3(p->mem_ctx,
+ validation_level,
+ validation,
+ &r->out.validation.sam3);
+ TALLOC_FREE(validation);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ return NT_STATUS_OK;
+ case 6:
+ status = map_validation_to_info6(p->mem_ctx,
+ validation_level,
+ validation,
+ &r->out.validation.sam6);
+ TALLOC_FREE(validation);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ return NT_STATUS_OK;
+ }
+
+ smb_panic(__location__);
+ return NT_STATUS_INTERNAL_ERROR;
+}
+
+static WERROR _winbind_LogonControl_REDISCOVER(struct pipes_struct *p,
+ struct winbindd_domain *domain,
+ struct winbind_LogonControl *r)
+{
+ NTSTATUS status;
+ struct rpc_pipe_client *netlogon_pipe = NULL;
+ struct netlogon_creds_cli_context *netlogon_creds_ctx = NULL;
+ struct netr_NETLOGON_INFO_2 *info2 = NULL;
+ WERROR check_result = WERR_INTERNAL_ERROR;
+
+ info2 = talloc_zero(p->mem_ctx, struct netr_NETLOGON_INFO_2);
+ if (info2 == NULL) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+
+ if (domain->internal) {
+ check_result = WERR_OK;
+ goto check_return;
+ }
+
+ /*
+ * For now we just force a reconnect
+ *
+ * TODO: take care of the optional '\dcname'
+ */
+ invalidate_cm_connection(domain);
+ domain->conn.netlogon_force_reauth = true;
+ status = cm_connect_netlogon_secure(domain,
+ &netlogon_pipe,
+ &netlogon_creds_ctx);
+ reset_cm_connection_on_error(domain, NULL, status);
+ if (NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
+ status = NT_STATUS_NO_LOGON_SERVERS;
+ }
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(2, ("%s: domain[%s/%s] cm_connect_netlogon() returned %s\n",
+ __func__, domain->name, domain->alt_name,
+ nt_errstr(status)));
+ /*
+ * Here we return a top level error!
+ * This is different than TC_QUERY or TC_VERIFY.
+ */
+ return ntstatus_to_werror(status);
+ }
+ check_result = WERR_OK;
+
+check_return:
+ info2->pdc_connection_status = WERR_OK;
+ if (domain->dcname != NULL) {
+ info2->flags |= NETLOGON_HAS_IP;
+ info2->flags |= NETLOGON_HAS_TIMESERV;
+ info2->trusted_dc_name = talloc_asprintf(info2, "\\\\%s",
+ domain->dcname);
+ if (info2->trusted_dc_name == NULL) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+ } else {
+ info2->trusted_dc_name = talloc_strdup(info2, "");
+ if (info2->trusted_dc_name == NULL) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+ }
+ info2->tc_connection_status = check_result;
+
+ if (!W_ERROR_IS_OK(info2->pdc_connection_status) ||
+ !W_ERROR_IS_OK(info2->tc_connection_status))
+ {
+ DEBUG(2, ("%s: domain[%s/%s] dcname[%s] "
+ "pdc_connection[%s] tc_connection[%s]\n",
+ __func__, domain->name, domain->alt_name,
+ domain->dcname,
+ win_errstr(info2->pdc_connection_status),
+ win_errstr(info2->tc_connection_status)));
+ }
+
+ r->out.query->info2 = info2;
+
+ DEBUG(5, ("%s: succeeded.\n", __func__));
+ return WERR_OK;
+}
+
+static WERROR _winbind_LogonControl_TC_QUERY(struct pipes_struct *p,
+ struct winbindd_domain *domain,
+ struct winbind_LogonControl *r)
+{
+ NTSTATUS status;
+ struct rpc_pipe_client *netlogon_pipe = NULL;
+ struct netlogon_creds_cli_context *netlogon_creds_ctx = NULL;
+ struct netr_NETLOGON_INFO_2 *info2 = NULL;
+ WERROR check_result = WERR_INTERNAL_ERROR;
+
+ info2 = talloc_zero(p->mem_ctx, struct netr_NETLOGON_INFO_2);
+ if (info2 == NULL) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+
+ if (domain->internal) {
+ check_result = WERR_OK;
+ goto check_return;
+ }
+
+ status = cm_connect_netlogon_secure(domain,
+ &netlogon_pipe,
+ &netlogon_creds_ctx);
+ reset_cm_connection_on_error(domain, NULL, status);
+ if (NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
+ status = NT_STATUS_NO_LOGON_SERVERS;
+ }
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(3, ("could not open handle to NETLOGON pipe: %s\n",
+ nt_errstr(status)));
+ check_result = ntstatus_to_werror(status);
+ goto check_return;
+ }
+ check_result = WERR_OK;
+
+check_return:
+ info2->pdc_connection_status = WERR_OK;
+ if (domain->dcname != NULL) {
+ info2->flags |= NETLOGON_HAS_IP;
+ info2->flags |= NETLOGON_HAS_TIMESERV;
+ info2->trusted_dc_name = talloc_asprintf(info2, "\\\\%s",
+ domain->dcname);
+ if (info2->trusted_dc_name == NULL) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+ } else {
+ info2->trusted_dc_name = talloc_strdup(info2, "");
+ if (info2->trusted_dc_name == NULL) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+ }
+ info2->tc_connection_status = check_result;
+
+ if (!W_ERROR_IS_OK(info2->pdc_connection_status) ||
+ !W_ERROR_IS_OK(info2->tc_connection_status))
+ {
+ DEBUG(2, ("%s: domain[%s/%s] dcname[%s] "
+ "pdc_connection[%s] tc_connection[%s]\n",
+ __func__, domain->name, domain->alt_name,
+ domain->dcname,
+ win_errstr(info2->pdc_connection_status),
+ win_errstr(info2->tc_connection_status)));
+ }
+
+ r->out.query->info2 = info2;
+
+ DEBUG(5, ("%s: succeeded.\n", __func__));
+ return WERR_OK;
+}
+
+static WERROR _winbind_LogonControl_TC_VERIFY(struct pipes_struct *p,
+ struct winbindd_domain *domain,
+ struct winbind_LogonControl *r)
+{
+ TALLOC_CTX *frame = talloc_stackframe();
+ NTSTATUS status;
+ NTSTATUS result;
+ struct lsa_String trusted_domain_name = {};
+ struct lsa_StringLarge trusted_domain_name_l = {};
+ struct rpc_pipe_client *local_lsa_pipe = NULL;
+ struct policy_handle local_lsa_policy = {};
+ struct dcerpc_binding_handle *local_lsa = NULL;
+ struct rpc_pipe_client *netlogon_pipe = NULL;
+ struct netlogon_creds_cli_context *netlogon_creds_ctx = NULL;
+ struct cli_credentials *creds = NULL;
+ struct samr_Password *cur_nt_hash = NULL;
+ uint32_t trust_attributes = 0;
+ struct samr_Password new_owf_password = {};
+ bool cmp_new = false;
+ struct samr_Password old_owf_password = {};
+ bool cmp_old = false;
+ const struct lsa_TrustDomainInfoInfoEx *local_tdo = NULL;
+ bool fetch_fti = false;
+ struct lsa_ForestTrustInformation *new_fti = NULL;
+ struct netr_TrustInfo *trust_info = NULL;
+ struct netr_NETLOGON_INFO_2 *info2 = NULL;
+ struct dcerpc_binding_handle *b = NULL;
+ WERROR check_result = WERR_INTERNAL_ERROR;
+ WERROR verify_result = WERR_INTERNAL_ERROR;
+ bool retry = false;
+
+ trusted_domain_name.string = domain->name;
+ trusted_domain_name_l.string = domain->name;
+
+ info2 = talloc_zero(p->mem_ctx, struct netr_NETLOGON_INFO_2);
+ if (info2 == NULL) {
+ TALLOC_FREE(frame);
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+
+ if (domain->internal) {
+ check_result = WERR_OK;
+ goto check_return;
+ }
+
+ status = pdb_get_trust_credentials(domain->name,
+ domain->alt_name,
+ frame,
+ &creds);
+ if (NT_STATUS_IS_OK(status)) {
+ cur_nt_hash = cli_credentials_get_nt_hash(creds, frame);
+ TALLOC_FREE(creds);
+ }
+
+ if (!domain->primary) {
+ union lsa_TrustedDomainInfo *tdi = NULL;
+
+ status = open_internal_lsa_conn(frame, &local_lsa_pipe,
+ &local_lsa_policy);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0,("%s:%s: open_internal_lsa_conn() failed - %s\n",
+ __location__, __func__, nt_errstr(status)));
+ TALLOC_FREE(frame);
+ return WERR_INTERNAL_ERROR;
+ }
+ local_lsa = local_lsa_pipe->binding_handle;
+
+ status = dcerpc_lsa_QueryTrustedDomainInfoByName(local_lsa, frame,
+ &local_lsa_policy,
+ &trusted_domain_name,
+ LSA_TRUSTED_DOMAIN_INFO_INFO_EX,
+ &tdi, &result);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0,("%s:%s: local_lsa.QueryTrustedDomainInfoByName(%s) failed - %s\n",
+ __location__, __func__, domain->name, nt_errstr(status)));
+ TALLOC_FREE(frame);
+ return WERR_INTERNAL_ERROR;
+ }
+ if (NT_STATUS_EQUAL(result, NT_STATUS_OBJECT_NAME_NOT_FOUND)) {
+ DEBUG(1,("%s:%s: domain[%s] not found via LSA, might be removed already.\n",
+ __location__, __func__, domain->name));
+ TALLOC_FREE(frame);
+ return WERR_NO_SUCH_DOMAIN;
+ }
+ if (!NT_STATUS_IS_OK(result)) {
+ DEBUG(0,("%s:%s: local_lsa.QueryTrustedDomainInfoByName(%s) returned %s\n",
+ __location__, __func__, domain->name, nt_errstr(result)));
+ TALLOC_FREE(frame);
+ return WERR_INTERNAL_ERROR;
+ }
+ if (tdi == NULL) {
+ DEBUG(0,("%s:%s: local_lsa.QueryTrustedDomainInfoByName() "
+ "returned no trusted domain information\n",
+ __location__, __func__));
+ TALLOC_FREE(frame);
+ return WERR_INTERNAL_ERROR;
+ }
+
+ local_tdo = &tdi->info_ex;
+ trust_attributes = local_tdo->trust_attributes;
+ }
+
+ if (trust_attributes & LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE) {
+ struct lsa_ForestTrustInformation *old_fti = NULL;
+
+ status = dcerpc_lsa_lsaRQueryForestTrustInformation(local_lsa, frame,
+ &local_lsa_policy,
+ &trusted_domain_name,
+ LSA_FOREST_TRUST_DOMAIN_INFO,
+ &old_fti, &result);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0,("%s:%s: local_lsa.lsaRQueryForestTrustInformation(%s) failed %s\n",
+ __location__, __func__, domain->name, nt_errstr(status)));
+ TALLOC_FREE(frame);
+ return WERR_INTERNAL_ERROR;
+ }
+ if (NT_STATUS_EQUAL(result, NT_STATUS_NOT_FOUND)) {
+ DEBUG(2,("%s: no forest trust information available for domain[%s] yet.\n",
+ __func__, domain->name));
+ old_fti = NULL;
+ fetch_fti = true;
+ result = NT_STATUS_OK;
+ }
+ if (!NT_STATUS_IS_OK(result)) {
+ DEBUG(0,("%s:%s: local_lsa.lsaRQueryForestTrustInformation(%s) returned %s\n",
+ __location__, __func__, domain->name, nt_errstr(result)));
+ TALLOC_FREE(frame);
+ return WERR_INTERNAL_ERROR;
+ }
+
+ TALLOC_FREE(old_fti);
+ }
+
+reconnect:
+ status = cm_connect_netlogon_secure(domain,
+ &netlogon_pipe,
+ &netlogon_creds_ctx);
+ reset_cm_connection_on_error(domain, NULL, status);
+ if (NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
+ status = NT_STATUS_NO_LOGON_SERVERS;
+ }
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(3, ("could not open handle to NETLOGON pipe: %s\n",
+ nt_errstr(status)));
+ check_result = ntstatus_to_werror(status);
+ goto check_return;
+ }
+ check_result = WERR_OK;
+ b = netlogon_pipe->binding_handle;
+
+ if (cur_nt_hash == NULL) {
+ verify_result = WERR_NO_TRUST_LSA_SECRET;
+ goto verify_return;
+ }
+
+ if (fetch_fti) {
+ status = netlogon_creds_cli_GetForestTrustInformation(netlogon_creds_ctx,
+ b, frame,
+ &new_fti);
+ if (NT_STATUS_EQUAL(status, NT_STATUS_RPC_PROCNUM_OUT_OF_RANGE)) {
+ status = NT_STATUS_NOT_SUPPORTED;
+ }
+ if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_SUPPORTED)) {
+ new_fti = NULL;
+ status = NT_STATUS_OK;
+ }
+ if (!NT_STATUS_IS_OK(status)) {
+ if (!retry &&
+ reset_cm_connection_on_error(domain, b, status))
+ {
+ retry = true;
+ goto reconnect;
+ }
+ DEBUG(2, ("netlogon_creds_cli_GetForestTrustInformation(%s)"
+ "failed: %s\n",
+ domain->name, nt_errstr(status)));
+ check_result = ntstatus_to_werror(status);
+ goto check_return;
+ }
+ }
+
+ if (new_fti != NULL) {
+ struct lsa_ForestTrustInformation old_fti = {};
+ struct lsa_ForestTrustInformation *merged_fti = NULL;
+ struct lsa_ForestTrustCollisionInfo *collision_info = NULL;
+
+ status = dsdb_trust_merge_forest_info(frame, local_tdo,
+ &old_fti, new_fti,
+ &merged_fti);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0,("%s:%s: dsdb_trust_merge_forest_info(%s) failed %s\n",
+ __location__, __func__,
+ domain->name, nt_errstr(status)));
+ TALLOC_FREE(frame);
+ return ntstatus_to_werror(status);
+ }
+
+ status = dcerpc_lsa_lsaRSetForestTrustInformation(local_lsa, frame,
+ &local_lsa_policy,
+ &trusted_domain_name_l,
+ LSA_FOREST_TRUST_DOMAIN_INFO,
+ merged_fti,
+ 0, /* check_only=0 => store it! */
+ &collision_info,
+ &result);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0,("%s:%s: local_lsa.lsaRSetForestTrustInformation(%s) failed %s\n",
+ __location__, __func__, domain->name, nt_errstr(status)));
+ TALLOC_FREE(frame);
+ return WERR_INTERNAL_ERROR;
+ }
+ if (!NT_STATUS_IS_OK(result)) {
+ DEBUG(0,("%s:%s: local_lsa.lsaRSetForestTrustInformation(%s) returned %s\n",
+ __location__, __func__, domain->name, nt_errstr(result)));
+ TALLOC_FREE(frame);
+ return ntstatus_to_werror(result);
+ }
+ }
+
+ status = netlogon_creds_cli_ServerGetTrustInfo(netlogon_creds_ctx,
+ b, frame,
+ &new_owf_password,
+ &old_owf_password,
+ &trust_info);
+ if (NT_STATUS_EQUAL(status, NT_STATUS_RPC_PROCNUM_OUT_OF_RANGE)) {
+ status = NT_STATUS_NOT_SUPPORTED;
+ }
+ if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_SUPPORTED)) {
+ DEBUG(5, ("netlogon_creds_cli_ServerGetTrustInfo failed: %s\n",
+ nt_errstr(status)));
+ verify_result = WERR_OK;
+ goto verify_return;
+ }
+ if (!NT_STATUS_IS_OK(status)) {
+ if (!retry && reset_cm_connection_on_error(domain, b, status)) {
+ retry = true;
+ goto reconnect;
+ }
+ DEBUG(2, ("netlogon_creds_cli_ServerGetTrustInfo failed: %s\n",
+ nt_errstr(status)));
+
+ if (!dcerpc_binding_handle_is_connected(b)) {
+ check_result = ntstatus_to_werror(status);
+ goto check_return;
+ } else {
+ verify_result = ntstatus_to_werror(status);
+ goto verify_return;
+ }
+ }
+
+ if (trust_info != NULL && trust_info->count >= 1) {
+ uint32_t diff = trust_info->data[0] ^ trust_attributes;
+
+ if (diff & LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE) {
+ verify_result = WERR_DOMAIN_TRUST_INCONSISTENT;
+ goto verify_return;
+ }
+ }
+
+ cmp_new = mem_equal_const_time(new_owf_password.hash,
+ cur_nt_hash->hash,
+ sizeof(cur_nt_hash->hash));
+ cmp_old = mem_equal_const_time(old_owf_password.hash,
+ cur_nt_hash->hash,
+ sizeof(cur_nt_hash->hash));
+ if (!cmp_new && !cmp_old) {
+ DEBUG(1,("%s:Error: credentials for domain[%s/%s] doesn't match "
+ "any password known to dcname[%s]\n",
+ __func__, domain->name, domain->alt_name,
+ domain->dcname));
+ verify_result = WERR_WRONG_PASSWORD;
+ goto verify_return;
+ }
+
+ if (!cmp_new) {
+ DEBUG(2,("%s:Warning: credentials for domain[%s/%s] only match "
+ "against the old password known to dcname[%s]\n",
+ __func__, domain->name, domain->alt_name,
+ domain->dcname));
+ }
+
+ verify_result = WERR_OK;
+ goto verify_return;
+
+check_return:
+ verify_result = check_result;
+verify_return:
+ info2->flags |= NETLOGON_VERIFY_STATUS_RETURNED;
+ info2->pdc_connection_status = verify_result;
+ if (domain->dcname != NULL) {
+ info2->flags |= NETLOGON_HAS_IP;
+ info2->flags |= NETLOGON_HAS_TIMESERV;
+ info2->trusted_dc_name = talloc_asprintf(info2, "\\\\%s",
+ domain->dcname);
+ if (info2->trusted_dc_name == NULL) {
+ TALLOC_FREE(frame);
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+ } else {
+ info2->trusted_dc_name = talloc_strdup(info2, "");
+ if (info2->trusted_dc_name == NULL) {
+ TALLOC_FREE(frame);
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+ }
+ info2->tc_connection_status = check_result;
+
+ if (!W_ERROR_IS_OK(info2->pdc_connection_status) ||
+ !W_ERROR_IS_OK(info2->tc_connection_status))
+ {
+ DEBUG(2, ("%s: domain[%s/%s] dcname[%s] "
+ "pdc_connection[%s] tc_connection[%s]\n",
+ __func__, domain->name, domain->alt_name,
+ domain->dcname,
+ win_errstr(info2->pdc_connection_status),
+ win_errstr(info2->tc_connection_status)));
+ }
+
+ r->out.query->info2 = info2;
+
+ DEBUG(5, ("%s: succeeded.\n", __func__));
+ TALLOC_FREE(frame);
+ return WERR_OK;
+}
+
+static WERROR _winbind_LogonControl_CHANGE_PASSWORD(struct pipes_struct *p,
+ struct winbindd_domain *domain,
+ struct winbind_LogonControl *r)
+{
+ struct messaging_context *msg_ctx = global_messaging_context();
+ NTSTATUS status;
+ struct rpc_pipe_client *netlogon_pipe = NULL;
+ struct netlogon_creds_cli_context *netlogon_creds_ctx = NULL;
+ struct cli_credentials *creds = NULL;
+ struct samr_Password *cur_nt_hash = NULL;
+ struct netr_NETLOGON_INFO_1 *info1 = NULL;
+ struct dcerpc_binding_handle *b;
+ WERROR change_result = WERR_OK;
+ bool retry = false;
+
+ info1 = talloc_zero(p->mem_ctx, struct netr_NETLOGON_INFO_1);
+ if (info1 == NULL) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+
+ if (domain->internal) {
+ return WERR_NOT_SUPPORTED;
+ }
+
+ status = pdb_get_trust_credentials(domain->name,
+ domain->alt_name,
+ p->mem_ctx,
+ &creds);
+ if (NT_STATUS_IS_OK(status)) {
+ cur_nt_hash = cli_credentials_get_nt_hash(creds, p->mem_ctx);
+ TALLOC_FREE(creds);
+ }
+
+reconnect:
+ status = cm_connect_netlogon_secure(domain,
+ &netlogon_pipe,
+ &netlogon_creds_ctx);
+ reset_cm_connection_on_error(domain, NULL, status);
+ if (NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
+ status = NT_STATUS_NO_LOGON_SERVERS;
+ }
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(2, ("%s: domain[%s/%s] cm_connect_netlogon() returned %s\n",
+ __func__, domain->name, domain->alt_name,
+ nt_errstr(status)));
+ /*
+ * Here we return a top level error!
+ * This is different than TC_QUERY or TC_VERIFY.
+ */
+ return ntstatus_to_werror(status);
+ }
+ b = netlogon_pipe->binding_handle;
+
+ if (cur_nt_hash == NULL) {
+ change_result = WERR_NO_TRUST_LSA_SECRET;
+ goto change_return;
+ }
+ TALLOC_FREE(cur_nt_hash);
+
+ status = trust_pw_change(netlogon_creds_ctx,
+ msg_ctx, b, domain->name,
+ domain->dcname,
+ true); /* force */
+ if (!NT_STATUS_IS_OK(status)) {
+ if (!retry && reset_cm_connection_on_error(domain, b, status)) {
+ retry = true;
+ goto reconnect;
+ }
+
+ DEBUG(1, ("trust_pw_change(%s): %s\n",
+ domain->name, nt_errstr(status)));
+
+ change_result = ntstatus_to_werror(status);
+ goto change_return;
+ }
+
+ change_result = WERR_OK;
+
+change_return:
+ info1->pdc_connection_status = change_result;
+
+ if (!W_ERROR_IS_OK(info1->pdc_connection_status)) {
+ DEBUG(2, ("%s: domain[%s/%s] dcname[%s] "
+ "pdc_connection[%s]\n",
+ __func__, domain->name, domain->alt_name,
+ domain->dcname,
+ win_errstr(info1->pdc_connection_status)));
+ }
+
+ r->out.query->info1 = info1;
+
+ DEBUG(5, ("%s: succeeded.\n", __func__));
+ return WERR_OK;
+}
+
+WERROR _winbind_LogonControl(struct pipes_struct *p,
+ struct winbind_LogonControl *r)
+{
+ struct winbindd_domain *domain;
+
+ domain = wb_child_domain();
+ if (domain == NULL) {
+ return WERR_NO_SUCH_DOMAIN;
+ }
+
+ switch (r->in.function_code) {
+ case NETLOGON_CONTROL_REDISCOVER:
+ if (r->in.level != 2) {
+ return WERR_INVALID_PARAMETER;
+ }
+ return _winbind_LogonControl_REDISCOVER(p, domain, r);
+ case NETLOGON_CONTROL_TC_QUERY:
+ if (r->in.level != 2) {
+ return WERR_INVALID_PARAMETER;
+ }
+ return _winbind_LogonControl_TC_QUERY(p, domain, r);
+ case NETLOGON_CONTROL_TC_VERIFY:
+ if (r->in.level != 2) {
+ return WERR_INVALID_PARAMETER;
+ }
+ return _winbind_LogonControl_TC_VERIFY(p, domain, r);
+ case NETLOGON_CONTROL_CHANGE_PASSWORD:
+ if (r->in.level != 1) {
+ return WERR_INVALID_PARAMETER;
+ }
+ return _winbind_LogonControl_CHANGE_PASSWORD(p, domain, r);
+ default:
+ break;
+ }
+
+ DEBUG(4, ("%s: function_code[0x%x] not supported\n",
+ __func__, r->in.function_code));
+ return WERR_NOT_SUPPORTED;
+}
+
+WERROR _winbind_GetForestTrustInformation(struct pipes_struct *p,
+ struct winbind_GetForestTrustInformation *r)
+{
+ TALLOC_CTX *frame = talloc_stackframe();
+ NTSTATUS status, result;
+ struct winbindd_domain *domain;
+ struct rpc_pipe_client *netlogon_pipe = NULL;
+ struct netlogon_creds_cli_context *netlogon_creds_ctx = NULL;
+ struct dcerpc_binding_handle *b;
+ bool retry = false;
+ struct lsa_String trusted_domain_name = {};
+ struct lsa_StringLarge trusted_domain_name_l = {};
+ union lsa_TrustedDomainInfo *tdi = NULL;
+ const struct lsa_TrustDomainInfoInfoEx *tdo = NULL;
+ struct lsa_ForestTrustInformation _old_fti = {};
+ struct lsa_ForestTrustInformation *old_fti = NULL;
+ struct lsa_ForestTrustInformation *new_fti = NULL;
+ struct lsa_ForestTrustInformation *merged_fti = NULL;
+ struct lsa_ForestTrustCollisionInfo *collision_info = NULL;
+ bool update_fti = false;
+ struct rpc_pipe_client *local_lsa_pipe;
+ struct policy_handle local_lsa_policy;
+ struct dcerpc_binding_handle *local_lsa = NULL;
+
+ domain = wb_child_domain();
+ if (domain == NULL) {
+ TALLOC_FREE(frame);
+ return WERR_NO_SUCH_DOMAIN;
+ }
+
+ /*
+ * checking for domain->internal and domain->primary
+ * makes sure we only do some work when running as DC.
+ */
+
+ if (domain->internal) {
+ TALLOC_FREE(frame);
+ return WERR_NO_SUCH_DOMAIN;
+ }
+
+ if (domain->primary) {
+ TALLOC_FREE(frame);
+ return WERR_NO_SUCH_DOMAIN;
+ }
+
+ trusted_domain_name.string = domain->name;
+ trusted_domain_name_l.string = domain->name;
+
+ status = open_internal_lsa_conn(frame, &local_lsa_pipe,
+ &local_lsa_policy);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0,("%s:%s: open_internal_lsa_conn() failed - %s\n",
+ __location__, __func__, nt_errstr(status)));
+ TALLOC_FREE(frame);
+ return WERR_INTERNAL_ERROR;
+ }
+ local_lsa = local_lsa_pipe->binding_handle;
+
+ status = dcerpc_lsa_QueryTrustedDomainInfoByName(local_lsa, frame,
+ &local_lsa_policy,
+ &trusted_domain_name,
+ LSA_TRUSTED_DOMAIN_INFO_INFO_EX,
+ &tdi, &result);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0,("%s:%s: local_lsa.QueryTrustedDomainInfoByName(%s) failed - %s\n",
+ __location__, __func__, domain->name, nt_errstr(status)));
+ TALLOC_FREE(frame);
+ return WERR_INTERNAL_ERROR;
+ }
+ if (NT_STATUS_EQUAL(result, NT_STATUS_OBJECT_NAME_NOT_FOUND)) {
+ DEBUG(1,("%s:%s: domain[%s] not found via LSA, might be removed already.\n",
+ __location__, __func__, domain->name));
+ TALLOC_FREE(frame);
+ return WERR_NO_SUCH_DOMAIN;
+ }
+ if (!NT_STATUS_IS_OK(result)) {
+ DEBUG(0,("%s:%s: local_lsa.QueryTrustedDomainInfoByName(%s) returned %s\n",
+ __location__, __func__, domain->name, nt_errstr(result)));
+ TALLOC_FREE(frame);
+ return WERR_INTERNAL_ERROR;
+ }
+ if (tdi == NULL) {
+ DEBUG(0,("%s:%s: local_lsa.QueryTrustedDomainInfoByName() "
+ "returned no trusted domain information\n",
+ __location__, __func__));
+ TALLOC_FREE(frame);
+ return WERR_INTERNAL_ERROR;
+ }
+
+ tdo = &tdi->info_ex;
+
+ if (!(tdo->trust_attributes & LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE)) {
+ DEBUG(2,("%s: tdo[%s/%s] is no forest trust attributes[0x%08X]\n",
+ __func__, tdo->netbios_name.string,
+ tdo->domain_name.string,
+ (unsigned)tdo->trust_attributes));
+ TALLOC_FREE(frame);
+ return WERR_NO_SUCH_DOMAIN;
+ }
+
+ if (r->in.flags & ~DS_GFTI_UPDATE_TDO) {
+ TALLOC_FREE(frame);
+ return WERR_INVALID_FLAGS;
+ }
+
+reconnect:
+ status = cm_connect_netlogon_secure(domain,
+ &netlogon_pipe,
+ &netlogon_creds_ctx);
+ reset_cm_connection_on_error(domain, NULL, status);
+ if (NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
+ status = NT_STATUS_NO_LOGON_SERVERS;
+ }
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(3, ("could not open handle to NETLOGON pipe: %s\n",
+ nt_errstr(status)));
+ TALLOC_FREE(frame);
+ return ntstatus_to_werror(status);
+ }
+ b = netlogon_pipe->binding_handle;
+
+ status = netlogon_creds_cli_GetForestTrustInformation(netlogon_creds_ctx,
+ b, p->mem_ctx,
+ &new_fti);
+ if (!NT_STATUS_IS_OK(status)) {
+ if (!retry && reset_cm_connection_on_error(domain, b, status)) {
+ retry = true;
+ goto reconnect;
+ }
+ DEBUG(2, ("netlogon_creds_cli_GetForestTrustInformation(%s) failed: %s\n",
+ domain->name, nt_errstr(status)));
+ TALLOC_FREE(frame);
+ return ntstatus_to_werror(status);
+ }
+
+ *r->out.forest_trust_info = new_fti;
+
+ if (r->in.flags & DS_GFTI_UPDATE_TDO) {
+ update_fti = true;
+ }
+
+ status = dcerpc_lsa_lsaRQueryForestTrustInformation(local_lsa, frame,
+ &local_lsa_policy,
+ &trusted_domain_name,
+ LSA_FOREST_TRUST_DOMAIN_INFO,
+ &old_fti, &result);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0,("%s:%s: local_lsa.lsaRQueryForestTrustInformation(%s) failed %s\n",
+ __location__, __func__, domain->name, nt_errstr(status)));
+ TALLOC_FREE(frame);
+ return WERR_INTERNAL_ERROR;
+ }
+ if (NT_STATUS_EQUAL(result, NT_STATUS_NOT_FOUND)) {
+ DEBUG(2,("%s: no forest trust information available for domain[%s] yet.\n",
+ __func__, domain->name));
+ update_fti = true;
+ old_fti = &_old_fti;
+ result = NT_STATUS_OK;
+ }
+ if (!NT_STATUS_IS_OK(result)) {
+ DEBUG(0,("%s:%s: local_lsa.lsaRQueryForestTrustInformation(%s) returned %s\n",
+ __location__, __func__, domain->name, nt_errstr(result)));
+ TALLOC_FREE(frame);
+ return WERR_INTERNAL_ERROR;
+ }
+
+ if (old_fti == NULL) {
+ DEBUG(0,("%s:%s: local_lsa.lsaRQueryForestTrustInformation() "
+ "returned success without returning forest trust information\n",
+ __location__, __func__));
+ TALLOC_FREE(frame);
+ return WERR_INTERNAL_ERROR;
+ }
+
+ if (!update_fti) {
+ goto done;
+ }
+
+ status = dsdb_trust_merge_forest_info(frame, tdo, old_fti, new_fti,
+ &merged_fti);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0,("%s:%s: dsdb_trust_merge_forest_info(%s) failed %s\n",
+ __location__, __func__, domain->name, nt_errstr(status)));
+ TALLOC_FREE(frame);
+ return ntstatus_to_werror(status);
+ }
+
+ status = dcerpc_lsa_lsaRSetForestTrustInformation(local_lsa, frame,
+ &local_lsa_policy,
+ &trusted_domain_name_l,
+ LSA_FOREST_TRUST_DOMAIN_INFO,
+ merged_fti,
+ 0, /* check_only=0 => store it! */
+ &collision_info,
+ &result);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0,("%s:%s: local_lsa.lsaRSetForestTrustInformation(%s) failed %s\n",
+ __location__, __func__, domain->name, nt_errstr(status)));
+ TALLOC_FREE(frame);
+ return WERR_INTERNAL_ERROR;
+ }
+ if (!NT_STATUS_IS_OK(result)) {
+ DEBUG(0,("%s:%s: local_lsa.lsaRSetForestTrustInformation(%s) returned %s\n",
+ __location__, __func__, domain->name, nt_errstr(result)));
+ TALLOC_FREE(frame);
+ return ntstatus_to_werror(result);
+ }
+
+done:
+ DEBUG(5, ("_winbind_GetForestTrustInformation succeeded\n"));
+ TALLOC_FREE(frame);
+ return WERR_OK;
+}
+
+NTSTATUS _winbind_SendToSam(struct pipes_struct *p, struct winbind_SendToSam *r)
+{
+ struct winbindd_domain *domain;
+ NTSTATUS status;
+ struct rpc_pipe_client *netlogon_pipe;
+ struct netlogon_creds_cli_context *netlogon_creds_ctx = NULL;
+ struct dcerpc_binding_handle *b = NULL;
+ bool retry = false;
+
+ DEBUG(5, ("_winbind_SendToSam received\n"));
+ domain = wb_child_domain();
+ if (domain == NULL) {
+ return NT_STATUS_REQUEST_NOT_ACCEPTED;
+ }
+
+reconnect:
+ status = cm_connect_netlogon_secure(domain,
+ &netlogon_pipe,
+ &netlogon_creds_ctx);
+ reset_cm_connection_on_error(domain, NULL, status);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(3, ("could not open handle to NETLOGON pipe\n"));
+ return status;
+ }
+
+ b = netlogon_pipe->binding_handle;
+
+ status = netlogon_creds_cli_SendToSam(netlogon_creds_ctx,
+ b,
+ &r->in.message);
+ if (!retry && reset_cm_connection_on_error(domain, b, status)) {
+ retry = true;
+ goto reconnect;
+ }
+
+ return status;
+}
+
+NTSTATUS _wbint_ListTrustedDomains(struct pipes_struct *p,
+ struct wbint_ListTrustedDomains *r)
+{
+ struct winbindd_domain *domain = wb_child_domain();
+ uint32_t i;
+ NTSTATUS result;
+ struct netr_DomainTrustList trusts;
+ uint32_t count = 0;
+ struct netr_DomainTrust *array = NULL;
+ pid_t client_pid;
+
+ if (domain == NULL) {
+ return NT_STATUS_REQUEST_NOT_ACCEPTED;
+ }
+
+ /* Cut client_pid to 32bit */
+ client_pid = r->in.client_pid;
+ if ((uint64_t)client_pid != r->in.client_pid) {
+ DBG_DEBUG("pid out of range\n");
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ DBG_NOTICE("[%s %"PRIu32"]: list trusted domains\n",
+ r->in.client_name, client_pid);
+
+ result = wb_cache_trusted_domains(domain, p->mem_ctx, &trusts);
+ if (!NT_STATUS_IS_OK(result)) {
+ DBG_NOTICE("wb_cache_trusted_domains returned %s\n",
+ nt_errstr(result));
+ return result;
+ }
+
+ for (i=0; i<trusts.count; i++) {
+ struct netr_DomainTrust *st = &trusts.array[i];
+ struct netr_DomainTrust *dt = NULL;
+
+ if (st->sid == NULL) {
+ continue;
+ }
+ if (dom_sid_equal(st->sid, &global_sid_NULL)) {
+ continue;
+ }
+
+ array = talloc_realloc(r->out.domains, array,
+ struct netr_DomainTrust,
+ count + 1);
+ if (array == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ dt = &array[count];
+
+ *dt = (struct netr_DomainTrust) {
+ .trust_flags = st->trust_flags,
+ .trust_type = st->trust_type,
+ .trust_attributes = st->trust_attributes,
+ .netbios_name = talloc_move(array, &st->netbios_name),
+ .dns_name = talloc_move(array, &st->dns_name),
+ };
+
+ dt->sid = dom_sid_dup(array, st->sid);
+ if (dt->sid == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ count++;
+ }
+
+ r->out.domains->array = array;
+ r->out.domains->count = count;
+ return NT_STATUS_OK;
+}
+
+#include "librpc/gen_ndr/ndr_winbind_scompat.c"
diff --git a/source3/winbindd/winbindd_endgrent.c b/source3/winbindd/winbindd_endgrent.c
new file mode 100644
index 0000000..6fdeb8f
--- /dev/null
+++ b/source3/winbindd/winbindd_endgrent.c
@@ -0,0 +1,54 @@
+/*
+ Unix SMB/CIFS implementation.
+ async implementation of WINBINDD_ENDGRENT
+ Copyright (C) Volker Lendecke 2009
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "winbindd.h"
+
+struct winbindd_endgrent_state {
+ uint8_t dummy;
+};
+
+struct tevent_req *winbindd_endgrent_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct winbindd_cli_state *cli,
+ struct winbindd_request *request)
+{
+ struct tevent_req *req;
+ struct winbindd_endgrent_state *state;
+
+ req = tevent_req_create(mem_ctx, &state,
+ struct winbindd_endgrent_state);
+ if (req == NULL) {
+ return NULL;
+ }
+ D_NOTICE("[%s (%u)] Winbind external command ENDGRENT start.\n",
+ cli->client_name,
+ (unsigned int)cli->pid);
+
+ TALLOC_FREE(cli->grent_state);
+ tevent_req_done(req);
+ return tevent_req_post(req, ev);
+}
+
+NTSTATUS winbindd_endgrent_recv(struct tevent_req *req,
+ struct winbindd_response *presp)
+{
+ D_NOTICE("Winbind external command ENDGRENT end.\n");
+ return NT_STATUS_OK;
+}
diff --git a/source3/winbindd/winbindd_endpwent.c b/source3/winbindd/winbindd_endpwent.c
new file mode 100644
index 0000000..a7c14cb
--- /dev/null
+++ b/source3/winbindd/winbindd_endpwent.c
@@ -0,0 +1,55 @@
+/*
+ Unix SMB/CIFS implementation.
+ async implementation of WINBINDD_ENDPWENT
+ Copyright (C) Volker Lendecke 2009
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "winbindd.h"
+
+struct winbindd_endpwent_state {
+ uint8_t dummy;
+};
+
+struct tevent_req *winbindd_endpwent_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct winbindd_cli_state *cli,
+ struct winbindd_request *request)
+{
+ struct tevent_req *req;
+ struct winbindd_endpwent_state *state;
+
+ req = tevent_req_create(mem_ctx, &state,
+ struct winbindd_endpwent_state);
+ if (req == NULL) {
+ return NULL;
+ }
+
+ D_NOTICE("[%s (%u)] Winbind external command ENDPWENT start.\n",
+ cli->client_name,
+ (unsigned int)cli->pid);
+
+ TALLOC_FREE(cli->pwent_state);
+ tevent_req_done(req);
+ return tevent_req_post(req, ev);
+}
+
+NTSTATUS winbindd_endpwent_recv(struct tevent_req *req,
+ struct winbindd_response *presp)
+{
+ D_NOTICE("Winbind external command ENDPWENT end.\n");
+ return NT_STATUS_OK;
+}
diff --git a/source3/winbindd/winbindd_getdcname.c b/source3/winbindd/winbindd_getdcname.c
new file mode 100644
index 0000000..690e840
--- /dev/null
+++ b/source3/winbindd/winbindd_getdcname.c
@@ -0,0 +1,95 @@
+/*
+ Unix SMB/CIFS implementation.
+ async implementation of WINBINDD_GETDCNAME
+ Copyright (C) Volker Lendecke 2009
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "winbindd.h"
+#include "librpc/gen_ndr/ndr_winbind_c.h"
+#include "lib/util/string_wrappers.h"
+
+struct winbindd_getdcname_state {
+ struct netr_DsRGetDCNameInfo *dcinfo;
+};
+
+static void winbindd_getdcname_done(struct tevent_req *subreq);
+
+struct tevent_req *winbindd_getdcname_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct winbindd_cli_state *cli,
+ struct winbindd_request *request)
+{
+ struct tevent_req *req, *subreq;
+ struct winbindd_getdcname_state *state;
+
+ req = tevent_req_create(mem_ctx, &state,
+ struct winbindd_getdcname_state);
+ if (req == NULL) {
+ return NULL;
+ }
+
+ request->domain_name[sizeof(request->domain_name)-1] = '\0';
+
+ D_NOTICE("[%s (%u)] Winbind external command GETDCNAME start.\n"
+ "Search DCNAME for domain %s.\n",
+ cli->client_name,
+ (unsigned int)cli->pid,
+ request->domain_name);
+
+ subreq = wb_dsgetdcname_send(state, ev, request->domain_name, NULL,
+ NULL, 0);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, winbindd_getdcname_done, req);
+ return req;
+}
+
+static void winbindd_getdcname_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct winbindd_getdcname_state *state = tevent_req_data(
+ req, struct winbindd_getdcname_state);
+ NTSTATUS status;
+
+ status = wb_dsgetdcname_recv(subreq, state, &state->dcinfo);
+ TALLOC_FREE(subreq);
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+ tevent_req_done(req);
+}
+
+NTSTATUS winbindd_getdcname_recv(struct tevent_req *req,
+ struct winbindd_response *response)
+{
+ struct winbindd_getdcname_state *state = tevent_req_data(
+ req, struct winbindd_getdcname_state);
+ NTSTATUS status;
+
+ if (tevent_req_is_nterror(req, &status)) {
+ D_WARNING("getdcname failed: %s\n", nt_errstr(status));
+ return status;
+ }
+ fstrcpy(response->data.dc_name, strip_hostname(state->dcinfo->dc_unc));
+
+ D_NOTICE("Winbind external command GETDCNAME end.\n"
+ "Got DCNAME '%s'.\n",
+ response->data.dc_name);
+ return NT_STATUS_OK;
+}
diff --git a/source3/winbindd/winbindd_getgrent.c b/source3/winbindd/winbindd_getgrent.c
new file mode 100644
index 0000000..e8e2b66
--- /dev/null
+++ b/source3/winbindd/winbindd_getgrent.c
@@ -0,0 +1,213 @@
+/*
+ Unix SMB/CIFS implementation.
+ async implementation of WINBINDD_GETGRENT
+ Copyright (C) Volker Lendecke 2009
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "winbindd.h"
+
+struct winbindd_getgrent_state {
+ struct tevent_context *ev;
+ struct winbindd_cli_state *cli;
+ uint32_t max_groups;
+ uint32_t num_groups;
+ struct winbindd_gr *groups;
+ struct db_context **members;
+};
+
+static void winbindd_getgrent_done(struct tevent_req *subreq);
+
+struct tevent_req *winbindd_getgrent_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct winbindd_cli_state *cli,
+ struct winbindd_request *request)
+{
+ struct tevent_req *req, *subreq;
+ struct winbindd_getgrent_state *state;
+
+ req = tevent_req_create(mem_ctx, &state,
+ struct winbindd_getgrent_state);
+ if (req == NULL) {
+ return NULL;
+ }
+ state->ev = ev;
+ state->num_groups = 0;
+ state->cli = cli;
+
+ D_NOTICE("[%s (%u)] Winbind external command GETGRENT start.\n"
+ "The caller (%s) provided room for %"PRIu32" entries.\n",
+ cli->client_name,
+ (unsigned int)cli->pid,
+ cli->client_name,
+ request->data.num_entries);
+
+ if (cli->grent_state == NULL) {
+ D_NOTICE("The grent state from winbindd client state is NULL.\n");
+ tevent_req_nterror(req, NT_STATUS_NO_MORE_ENTRIES);
+ return tevent_req_post(req, ev);
+ }
+
+ state->max_groups = MIN(500, request->data.num_entries);
+ if (state->max_groups == 0) {
+ tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
+ return tevent_req_post(req, ev);
+ }
+
+ state->groups = talloc_zero_array(state, struct winbindd_gr,
+ state->max_groups);
+ if (tevent_req_nomem(state->groups, req)) {
+ return tevent_req_post(req, ev);
+ }
+
+ state->members = talloc_array(state, struct db_context *,
+ state->max_groups);
+ if (tevent_req_nomem(state->members, req)) {
+ TALLOC_FREE(state->groups);
+ return tevent_req_post(req, ev);
+ }
+
+ subreq = wb_next_grent_send(state, ev, lp_winbind_expand_groups(),
+ cli->grent_state,
+ &state->groups[state->num_groups]);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, winbindd_getgrent_done, req);
+ return req;
+}
+
+static void winbindd_getgrent_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct winbindd_getgrent_state *state = tevent_req_data(
+ req, struct winbindd_getgrent_state);
+ NTSTATUS status;
+
+ status = wb_next_grent_recv(subreq, state,
+ &state->members[state->num_groups]);
+ TALLOC_FREE(subreq);
+ if (NT_STATUS_EQUAL(status, NT_STATUS_NO_MORE_ENTRIES)) {
+ D_WARNING("winbindd_getgrent_done: done with %"PRIu32" groups\n",
+ state->num_groups);
+ TALLOC_FREE(state->cli->grent_state);
+ tevent_req_done(req);
+ return;
+ }
+ if (!NT_STATUS_IS_OK(status)) {
+ tevent_req_nterror(req, status);
+ return;
+ }
+ state->num_groups += 1;
+ if (state->num_groups >= state->max_groups) {
+ D_DEBUG("winbindd_getgrent_done: Got enough groups: %"PRIu32"\n",
+ state->num_groups);
+ tevent_req_done(req);
+ return;
+ }
+ if (state->cli->grent_state == NULL) {
+ D_DEBUG("winbindd_getgrent_done: endgrent called in between\n");
+ tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
+ return;
+ }
+ subreq = wb_next_grent_send(state, state->ev,
+ lp_winbind_expand_groups(),
+ state->cli->grent_state,
+ &state->groups[state->num_groups]);
+ if (tevent_req_nomem(subreq, req)) {
+ return;
+ }
+ tevent_req_set_callback(subreq, winbindd_getgrent_done, req);
+}
+
+NTSTATUS winbindd_getgrent_recv(struct tevent_req *req,
+ struct winbindd_response *response)
+{
+ struct winbindd_getgrent_state *state = tevent_req_data(
+ req, struct winbindd_getgrent_state);
+ NTSTATUS status;
+ char **memberstrings;
+ char *result;
+ size_t base_memberofs, total_memberlen;
+ uint32_t i;
+
+ if (tevent_req_is_nterror(req, &status)) {
+ TALLOC_FREE(state->cli->grent_state);
+ D_WARNING("getgrent failed: %s\n", nt_errstr(status));
+ return status;
+ }
+
+ if (state->num_groups == 0) {
+ return NT_STATUS_NO_MORE_ENTRIES;
+ }
+
+ memberstrings = talloc_array(talloc_tos(), char *, state->num_groups);
+ if (memberstrings == NULL) {
+ TALLOC_FREE(state->cli->grent_state);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ total_memberlen = 0;
+
+ for (i=0; i<state->num_groups; i++) {
+ int num_members;
+
+ status = winbindd_print_groupmembers(
+ state->members[i], memberstrings, &num_members,
+ &memberstrings[i]);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ TALLOC_FREE(memberstrings);
+ TALLOC_FREE(state->cli->grent_state);
+ return status;
+ }
+ TALLOC_FREE(state->members[i]);
+
+ state->groups[i].num_gr_mem = num_members;
+ state->groups[i].gr_mem_ofs = total_memberlen;
+
+ total_memberlen += talloc_get_size(memberstrings[i]);
+ }
+
+ base_memberofs = state->num_groups * sizeof(struct winbindd_gr);
+
+ result = talloc_realloc(state, state->groups, char,
+ base_memberofs + total_memberlen);
+ if (result == NULL) {
+ TALLOC_FREE(state->cli->grent_state);
+ return NT_STATUS_NO_MEMORY;
+ }
+ state->groups = (struct winbindd_gr *)result;
+
+ for (i=0; i<state->num_groups; i++) {
+ memcpy(result + base_memberofs + state->groups[i].gr_mem_ofs,
+ memberstrings[i], talloc_get_size(memberstrings[i]));
+ TALLOC_FREE(memberstrings[i]);
+ }
+
+ TALLOC_FREE(memberstrings);
+
+ response->data.num_entries = state->num_groups;
+ response->length += talloc_get_size(result);
+ response->extra_data.data = talloc_move(response, &result);
+
+ D_NOTICE("Winbind external command GETGRENT end.\n"
+ "Received %"PRIu32" entries.\n",
+ response->data.num_entries);
+
+ return NT_STATUS_OK;
+}
diff --git a/source3/winbindd/winbindd_getgrgid.c b/source3/winbindd/winbindd_getgrgid.c
new file mode 100644
index 0000000..4edd81b
--- /dev/null
+++ b/source3/winbindd/winbindd_getgrgid.c
@@ -0,0 +1,156 @@
+/*
+ Unix SMB/CIFS implementation.
+ async implementation of WINBINDD_GETGRGID
+ Copyright (C) Volker Lendecke 2009
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "winbindd.h"
+#include "libcli/security/dom_sid.h"
+
+struct winbindd_getgrgid_state {
+ struct tevent_context *ev;
+ struct unixid xid;
+ struct dom_sid *sid;
+ const char *domname;
+ const char *name;
+ gid_t gid;
+ struct db_context *members;
+};
+
+static void winbindd_getgrgid_gid2sid_done(struct tevent_req *subreq);
+static void winbindd_getgrgid_done(struct tevent_req *subreq);
+
+struct tevent_req *winbindd_getgrgid_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct winbindd_cli_state *cli,
+ struct winbindd_request *request)
+{
+ struct tevent_req *req, *subreq;
+ struct winbindd_getgrgid_state *state;
+
+ req = tevent_req_create(mem_ctx, &state,
+ struct winbindd_getgrgid_state);
+ if (req == NULL) {
+ return NULL;
+ }
+ state->ev = ev;
+
+ D_NOTICE("[%s (%u)] Winbind external command GETGRGID start.\n"
+ "gid=%u\n",
+ cli->client_name,
+ (unsigned int)cli->pid,
+ (int)request->data.gid);
+
+ state->xid = (struct unixid) {
+ .id = request->data.uid, .type = ID_TYPE_GID };
+
+ subreq = wb_xids2sids_send(state, ev, &state->xid, 1);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, winbindd_getgrgid_gid2sid_done,
+ req);
+ return req;
+}
+
+static void winbindd_getgrgid_gid2sid_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct winbindd_getgrgid_state *state = tevent_req_data(
+ req, struct winbindd_getgrgid_state);
+ NTSTATUS status;
+
+ status = wb_xids2sids_recv(subreq, state, &state->sid);
+ TALLOC_FREE(subreq);
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+ if (is_null_sid(state->sid)) {
+ tevent_req_nterror(req, NT_STATUS_NO_SUCH_GROUP);
+ return;
+ }
+
+ subreq = wb_getgrsid_send(state, state->ev, state->sid,
+ lp_winbind_expand_groups());
+ if (tevent_req_nomem(subreq, req)) {
+ return;
+ }
+ tevent_req_set_callback(subreq, winbindd_getgrgid_done, req);
+}
+
+static void winbindd_getgrgid_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct winbindd_getgrgid_state *state = tevent_req_data(
+ req, struct winbindd_getgrgid_state);
+ NTSTATUS status;
+
+ status = wb_getgrsid_recv(subreq, state, &state->domname, &state->name,
+ &state->gid, &state->members);
+ TALLOC_FREE(subreq);
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+ tevent_req_done(req);
+}
+
+NTSTATUS winbindd_getgrgid_recv(struct tevent_req *req,
+ struct winbindd_response *response)
+{
+ struct winbindd_getgrgid_state *state = tevent_req_data(
+ req, struct winbindd_getgrgid_state);
+ NTSTATUS status;
+ int num_members;
+ char *buf;
+
+ if (tevent_req_is_nterror(req, &status)) {
+ struct dom_sid_buf sidbuf;
+ D_WARNING("Could not convert sid %s: %s\n",
+ dom_sid_str_buf(state->sid, &sidbuf),
+ nt_errstr(status));
+ return status;
+ }
+
+ if (!fill_grent(talloc_tos(), &response->data.gr, state->domname,
+ state->name, state->gid)) {
+ D_WARNING("fill_grent failed\n");
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ status = winbindd_print_groupmembers(state->members, response,
+ &num_members, &buf);
+ if (!NT_STATUS_IS_OK(status)) {
+ D_WARNING("Failed with %s.\n", nt_errstr(status));
+ return status;
+ }
+
+ response->data.gr.num_gr_mem = (uint32_t)num_members;
+
+ /* Group membership lives at start of extra data */
+
+ response->data.gr.gr_mem_ofs = 0;
+ response->extra_data.data = buf;
+ response->length += talloc_get_size(response->extra_data.data);
+
+ D_NOTICE("Winbind external command GETGRGID end.\n"
+ "Returning %"PRIu32" group member(s).\n",
+ response->data.gr.num_gr_mem);
+
+ return NT_STATUS_OK;
+}
diff --git a/source3/winbindd/winbindd_getgrnam.c b/source3/winbindd/winbindd_getgrnam.c
new file mode 100644
index 0000000..6b277c2
--- /dev/null
+++ b/source3/winbindd/winbindd_getgrnam.c
@@ -0,0 +1,213 @@
+/*
+ Unix SMB/CIFS implementation.
+ async implementation of WINBINDD_GETGRNAM
+ Copyright (C) Volker Lendecke 2009
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "winbindd.h"
+#include "libcli/security/dom_sid.h"
+#include "lib/util/string_wrappers.h"
+
+struct winbindd_getgrnam_state {
+ struct tevent_context *ev;
+ char *name_namespace;
+ char *name_domain;
+ char *name_group;
+ struct dom_sid sid;
+ const char *domname;
+ const char *name;
+ gid_t gid;
+ struct db_context *members;
+};
+
+static void winbindd_getgrnam_lookupname_done(struct tevent_req *subreq);
+static void winbindd_getgrnam_done(struct tevent_req *subreq);
+
+struct tevent_req *winbindd_getgrnam_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct winbindd_cli_state *cli,
+ struct winbindd_request *request)
+{
+ struct tevent_req *req, *subreq;
+ struct winbindd_getgrnam_state *state;
+ char *tmp;
+ NTSTATUS nt_status;
+ bool ok;
+
+ req = tevent_req_create(mem_ctx, &state,
+ struct winbindd_getgrnam_state);
+ if (req == NULL) {
+ return NULL;
+ }
+ state->ev = ev;
+
+ /* Ensure null termination */
+ request->data.groupname[sizeof(request->data.groupname)-1]='\0';
+
+ D_NOTICE("[%s (%u)] Winbind external command GETGRNAM start.\n"
+ "Searching group name '%s'.\n",
+ cli->client_name,
+ (unsigned int)cli->pid,
+ request->data.groupname);
+
+ nt_status = normalize_name_unmap(state, request->data.groupname, &tmp);
+ /* If we didn't map anything in the above call, just reset the
+ tmp pointer to the original string */
+ if (!NT_STATUS_IS_OK(nt_status) &&
+ !NT_STATUS_EQUAL(nt_status, NT_STATUS_FILE_RENAMED))
+ {
+ tmp = request->data.groupname;
+ }
+
+ /* Parse domain and groupname */
+
+ ok = parse_domain_user(state, tmp,
+ &state->name_namespace,
+ &state->name_domain,
+ &state->name_group);
+ if (!ok) {
+ DBG_INFO("Could not parse domain user: %s\n", tmp);
+ tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
+ return tevent_req_post(req, ev);
+ }
+
+ /* if no domain or our local domain and no local tdb group, default to
+ * our local domain for aliases */
+
+ if ( !*(state->name_domain) || strequal(state->name_domain,
+ get_global_sam_name()) ) {
+ TALLOC_FREE(state->name_domain);
+ state->name_domain = talloc_strdup(state,
+ get_global_sam_name());
+ if (tevent_req_nomem(state->name_domain, req)) {
+ return tevent_req_post(req, ev);
+ }
+ }
+
+ subreq = wb_lookupname_send(state, ev,
+ state->name_namespace,
+ state->name_domain,
+ state->name_group,
+ 0);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, winbindd_getgrnam_lookupname_done,
+ req);
+ return req;
+}
+
+static void winbindd_getgrnam_lookupname_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct winbindd_getgrnam_state *state = tevent_req_data(
+ req, struct winbindd_getgrnam_state);
+ enum lsa_SidType type;
+ NTSTATUS status;
+
+ status = wb_lookupname_recv(subreq, &state->sid, &type);
+ TALLOC_FREE(subreq);
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+
+ switch (type) {
+ case SID_NAME_DOM_GRP:
+ case SID_NAME_ALIAS:
+ case SID_NAME_WKN_GRP:
+ /*
+ * Also give user types a chance:
+ * These might be user sids mapped to the ID_TYPE_BOTH,
+ * and in that case we should construct a group struct.
+ */
+ case SID_NAME_USER:
+ case SID_NAME_COMPUTER:
+ break;
+ default:
+ tevent_req_nterror(req, NT_STATUS_NO_SUCH_GROUP);
+ return;
+ }
+
+ subreq = wb_getgrsid_send(state, state->ev, &state->sid,
+ lp_winbind_expand_groups());
+ if (tevent_req_nomem(subreq, req)) {
+ return;
+ }
+ tevent_req_set_callback(subreq, winbindd_getgrnam_done, req);
+}
+
+static void winbindd_getgrnam_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct winbindd_getgrnam_state *state = tevent_req_data(
+ req, struct winbindd_getgrnam_state);
+ NTSTATUS status;
+
+ status = wb_getgrsid_recv(subreq, state, &state->domname, &state->name,
+ &state->gid, &state->members);
+ TALLOC_FREE(subreq);
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+ tevent_req_done(req);
+}
+
+NTSTATUS winbindd_getgrnam_recv(struct tevent_req *req,
+ struct winbindd_response *response)
+{
+ struct winbindd_getgrnam_state *state = tevent_req_data(
+ req, struct winbindd_getgrnam_state);
+ NTSTATUS status;
+ int num_members;
+ char *buf;
+
+ if (tevent_req_is_nterror(req, &status)) {
+ struct dom_sid_buf sidbuf;
+ D_WARNING("Could not convert sid %s: %s\n",
+ dom_sid_str_buf(&state->sid, &sidbuf),
+ nt_errstr(status));
+ return status;
+ }
+
+ if (!fill_grent(talloc_tos(), &response->data.gr, state->domname,
+ state->name, state->gid)) {
+ D_WARNING("fill_grent failed\n");
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ status = winbindd_print_groupmembers(state->members, response,
+ &num_members, &buf);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ response->data.gr.num_gr_mem = (uint32_t)num_members;
+
+ /* Group membership lives at start of extra data */
+
+ response->data.gr.gr_mem_ofs = 0;
+ response->extra_data.data = buf;
+ response->length += talloc_get_size(response->extra_data.data);
+
+ D_NOTICE("Winbind external command GETGRNAM end.\n"
+ "Returning %"PRIu32" member(s).\n",
+ response->data.gr.num_gr_mem);
+
+ return NT_STATUS_OK;
+}
diff --git a/source3/winbindd/winbindd_getgroups.c b/source3/winbindd/winbindd_getgroups.c
new file mode 100644
index 0000000..c1c108e
--- /dev/null
+++ b/source3/winbindd/winbindd_getgroups.c
@@ -0,0 +1,283 @@
+/*
+ Unix SMB/CIFS implementation.
+ async implementation of WINBINDD_GETGROUPS
+ Copyright (C) Volker Lendecke 2009
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "winbindd.h"
+#include "passdb/lookup_sid.h" /* only for LOOKUP_NAME_NO_NSS flag */
+#include "libcli/security/dom_sid.h"
+
+struct winbindd_getgroups_state {
+ struct tevent_context *ev;
+ char *namespace;
+ char *domname;
+ char *username;
+ struct dom_sid sid;
+ enum lsa_SidType type;
+ uint32_t num_sids;
+ struct dom_sid *sids;
+ uint32_t num_gids;
+ gid_t *gids;
+};
+
+static void winbindd_getgroups_lookupname_done(struct tevent_req *subreq);
+static void winbindd_getgroups_gettoken_done(struct tevent_req *subreq);
+static void winbindd_getgroups_sid2gid_done(struct tevent_req *subreq);
+
+struct tevent_req *winbindd_getgroups_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct winbindd_cli_state *cli,
+ struct winbindd_request *request)
+{
+ struct tevent_req *req, *subreq;
+ struct winbindd_getgroups_state *state;
+ char *domuser, *mapped_user;
+ NTSTATUS status;
+ bool ok;
+
+ req = tevent_req_create(mem_ctx, &state,
+ struct winbindd_getgroups_state);
+ if (req == NULL) {
+ return NULL;
+ }
+ state->ev = ev;
+
+ /* Ensure null termination */
+ request->data.username[sizeof(request->data.username)-1]='\0';
+
+ D_NOTICE("[%s (%u)] Winbind external command GETGROUPS start.\n"
+ "Searching groups for username '%s'.\n",
+ cli->client_name,
+ (unsigned int)cli->pid,
+ request->data.username);
+
+ domuser = request->data.username;
+
+ status = normalize_name_unmap(state, domuser, &mapped_user);
+
+ if (NT_STATUS_IS_OK(status)
+ || NT_STATUS_EQUAL(status, NT_STATUS_FILE_RENAMED)) {
+ /* normalize_name_unmapped did something */
+ domuser = mapped_user;
+ }
+
+ ok = parse_domain_user(state, domuser,
+ &state->namespace,
+ &state->domname,
+ &state->username);
+ if (!ok) {
+ D_WARNING("Could not parse domain user: %s\n", domuser);
+ tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
+ return tevent_req_post(req, ev);
+ }
+
+ subreq = wb_lookupname_send(state, ev,
+ state->namespace,
+ state->domname,
+ state->username,
+ LOOKUP_NAME_NO_NSS);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, winbindd_getgroups_lookupname_done,
+ req);
+ return req;
+}
+
+static void winbindd_getgroups_lookupname_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct winbindd_getgroups_state *state = tevent_req_data(
+ req, struct winbindd_getgroups_state);
+ NTSTATUS status;
+
+ status = wb_lookupname_recv(subreq, &state->sid, &state->type);
+ TALLOC_FREE(subreq);
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+
+ subreq = wb_gettoken_send(state, state->ev, &state->sid, true);
+ if (tevent_req_nomem(subreq, req)) {
+ return;
+ }
+ tevent_req_set_callback(subreq, winbindd_getgroups_gettoken_done, req);
+}
+
+static void winbindd_getgroups_gettoken_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct winbindd_getgroups_state *state = tevent_req_data(
+ req, struct winbindd_getgroups_state);
+ NTSTATUS status;
+
+ status = wb_gettoken_recv(subreq, state, &state->num_sids,
+ &state->sids);
+ TALLOC_FREE(subreq);
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+
+ /*
+ * Convert the group SIDs to gids. state->sids[0] contains the user
+ * sid. If the idmap backend uses ID_TYPE_BOTH, we might need the
+ * the id of the user sid in the list of group sids, so map the
+ * complete token.
+ */
+
+ subreq = wb_sids2xids_send(state, state->ev,
+ state->sids, state->num_sids);
+ if (tevent_req_nomem(subreq, req)) {
+ return;
+ }
+ tevent_req_set_callback(subreq, winbindd_getgroups_sid2gid_done, req);
+}
+
+static void winbindd_getgroups_sid2gid_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct winbindd_getgroups_state *state = tevent_req_data(
+ req, struct winbindd_getgroups_state);
+ NTSTATUS status;
+ struct unixid *xids;
+ uint32_t i;
+
+ xids = talloc_array(state, struct unixid, state->num_sids);
+ if (tevent_req_nomem(xids, req)) {
+ return;
+ }
+ for (i=0; i < state->num_sids; i++) {
+ xids[i].type = ID_TYPE_NOT_SPECIFIED;
+ xids[i].id = UINT32_MAX;
+ }
+
+ status = wb_sids2xids_recv(subreq, xids, state->num_sids);
+ TALLOC_FREE(subreq);
+ if (NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED) ||
+ NT_STATUS_EQUAL(status, STATUS_SOME_UNMAPPED))
+ {
+ status = NT_STATUS_OK;
+ }
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+
+ state->gids = talloc_array(state, gid_t, state->num_sids);
+ if (tevent_req_nomem(state->gids, req)) {
+ return;
+ }
+ state->num_gids = 0;
+
+ for (i=0; i < state->num_sids; i++) {
+ bool include_gid = false;
+ const char *debug_missing = NULL;
+
+ switch (xids[i].type) {
+ case ID_TYPE_NOT_SPECIFIED:
+ debug_missing = "not specified";
+ break;
+ case ID_TYPE_UID:
+ if (i != 0) {
+ debug_missing = "uid";
+ }
+ break;
+ case ID_TYPE_GID:
+ case ID_TYPE_BOTH:
+ include_gid = true;
+ break;
+ case ID_TYPE_WB_REQUIRE_TYPE:
+ /*
+ * these are internal between winbindd
+ * parent and child.
+ */
+ smb_panic(__location__);
+ break;
+ }
+
+ if (!include_gid) {
+ struct dom_sid_buf sidbuf;
+
+ if (debug_missing == NULL) {
+ continue;
+ }
+
+ D_WARNING("WARNING: skipping unix id (%"PRIu32") for sid %s "
+ "from group list because the idmap type "
+ "is %s. "
+ "This might be a security problem when ACLs "
+ "contain DENY ACEs!\n",
+ (unsigned)xids[i].id,
+ dom_sid_str_buf(&state->sids[i], &sidbuf),
+ debug_missing);
+ continue;
+ }
+
+ state->gids[state->num_gids] = (gid_t)xids[i].id;
+ state->num_gids += 1;
+ }
+
+ /*
+ * This should not fail, as it does not do any reallocation,
+ * just updating the talloc size.
+ */
+ state->gids = talloc_realloc(state, state->gids, gid_t, state->num_gids);
+ if (tevent_req_nomem(state->gids, req)) {
+ return;
+ }
+
+ tevent_req_done(req);
+}
+
+NTSTATUS winbindd_getgroups_recv(struct tevent_req *req,
+ struct winbindd_response *response)
+{
+ struct winbindd_getgroups_state *state = tevent_req_data(
+ req, struct winbindd_getgroups_state);
+ NTSTATUS status;
+ uint32_t i;
+
+ if (tevent_req_is_nterror(req, &status)) {
+ struct dom_sid_buf buf;
+ D_WARNING("Could not convert sid %s: %s\n",
+ dom_sid_str_buf(&state->sid, &buf),
+ nt_errstr(status));
+ return status;
+ }
+
+ response->data.num_entries = state->num_gids;
+
+ D_NOTICE("Winbind external command GETGROUPS end.\n"
+ "Received %"PRIu32" entries.\n",
+ response->data.num_entries);
+ if (CHECK_DEBUGLVL(DBGLVL_NOTICE)) {
+ for (i = 0; i < state->num_gids; i++) {
+ D_NOTICE("%"PRIu32": GID %u\n", i, state->gids[i]);
+ }
+ }
+
+ if (state->num_gids > 0) {
+ response->extra_data.data = talloc_move(response,
+ &state->gids);
+ response->length += state->num_gids * sizeof(gid_t);
+ }
+
+ return NT_STATUS_OK;
+}
diff --git a/source3/winbindd/winbindd_getpwent.c b/source3/winbindd/winbindd_getpwent.c
new file mode 100644
index 0000000..5a78883
--- /dev/null
+++ b/source3/winbindd/winbindd_getpwent.c
@@ -0,0 +1,162 @@
+/*
+ Unix SMB/CIFS implementation.
+ async implementation of WINBINDD_GETPWENT
+ Copyright (C) Volker Lendecke 2009
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "winbindd.h"
+
+struct winbindd_getpwent_state {
+ struct tevent_context *ev;
+ struct winbindd_cli_state *cli;
+ uint32_t max_users;
+ uint32_t num_users;
+ struct winbindd_pw *users;
+};
+
+static void winbindd_getpwent_done(struct tevent_req *subreq);
+
+struct tevent_req *winbindd_getpwent_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct winbindd_cli_state *cli,
+ struct winbindd_request *request)
+{
+ struct tevent_req *req, *subreq;
+ struct winbindd_getpwent_state *state;
+
+ req = tevent_req_create(mem_ctx, &state,
+ struct winbindd_getpwent_state);
+ if (req == NULL) {
+ return NULL;
+ }
+ state->ev = ev;
+ state->num_users = 0;
+ state->cli = cli;
+
+ D_NOTICE("[%s (%u)] Winbind external command GETPWENT start.\n"
+ "The caller (%s) provided room for %d entries.\n",
+ cli->client_name,
+ (unsigned int)cli->pid,
+ cli->client_name,
+ request->data.num_entries);
+
+ if (cli->pwent_state == NULL) {
+ tevent_req_nterror(req, NT_STATUS_NO_MORE_ENTRIES);
+ return tevent_req_post(req, ev);
+ }
+
+ state->max_users = MIN(500, request->data.num_entries);
+ if (state->max_users == 0) {
+ tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
+ return tevent_req_post(req, ev);
+ }
+
+ state->users = talloc_zero_array(state, struct winbindd_pw,
+ state->max_users);
+ if (tevent_req_nomem(state->users, req)) {
+ return tevent_req_post(req, ev);
+ }
+
+ subreq = wb_next_pwent_send(state, ev, cli->pwent_state,
+ &state->users[state->num_users]);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, winbindd_getpwent_done, req);
+ return req;
+}
+
+static void winbindd_getpwent_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct winbindd_getpwent_state *state = tevent_req_data(
+ req, struct winbindd_getpwent_state);
+ NTSTATUS status;
+
+ status = wb_next_pwent_recv(subreq);
+ TALLOC_FREE(subreq);
+ if (NT_STATUS_EQUAL(status, NT_STATUS_NO_MORE_ENTRIES)) {
+ D_DEBUG("winbindd_getpwent_done: done with %"PRIu32" users\n",
+ state->num_users);
+ TALLOC_FREE(state->cli->pwent_state);
+ tevent_req_done(req);
+ return;
+ }
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+ state->num_users += 1;
+ if (state->num_users >= state->max_users) {
+ D_DEBUG("winbindd_getpwent_done: Got enough users: %"PRIu32"\n",
+ state->num_users);
+ tevent_req_done(req);
+ return;
+ }
+ if (state->cli->pwent_state == NULL) {
+ D_DEBUG("winbindd_getpwent_done: endpwent called in between\n");
+ tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
+ return;
+ }
+ subreq = wb_next_pwent_send(state, state->ev, state->cli->pwent_state,
+ &state->users[state->num_users]);
+ if (tevent_req_nomem(subreq, req)) {
+ return;
+ }
+ tevent_req_set_callback(subreq, winbindd_getpwent_done, req);
+}
+
+NTSTATUS winbindd_getpwent_recv(struct tevent_req *req,
+ struct winbindd_response *response)
+{
+ struct winbindd_getpwent_state *state = tevent_req_data(
+ req, struct winbindd_getpwent_state);
+ NTSTATUS status;
+ uint32_t i;
+
+ if (tevent_req_is_nterror(req, &status)) {
+ TALLOC_FREE(state->cli->pwent_state);
+ D_WARNING("getpwent failed: %s\n", nt_errstr(status));
+ return status;
+ }
+
+ D_NOTICE("Winbind external command GETPWENT end.\n"
+ "Received %"PRIu32" entries.\n"
+ "(name:passwd:uid:gid:gecos:dir:shell)\n",
+ state->num_users);
+
+ if (state->num_users == 0) {
+ return NT_STATUS_NO_MORE_ENTRIES;
+ }
+
+ for (i = 0; i < state->num_users; i++) {
+ D_NOTICE("%"PRIu32": %s:%s:%u:%u:%s:%s:%s\n",
+ i,
+ state->users[i].pw_name,
+ state->users[i].pw_passwd,
+ (unsigned int)state->users[i].pw_uid,
+ (unsigned int)state->users[i].pw_gid,
+ state->users[i].pw_gecos,
+ state->users[i].pw_dir,
+ state->users[i].pw_shell
+ );
+ }
+ response->data.num_entries = state->num_users;
+ response->extra_data.data = talloc_move(response, &state->users);
+ response->length += state->num_users * sizeof(struct winbindd_pw);
+ return NT_STATUS_OK;
+}
diff --git a/source3/winbindd/winbindd_getpwnam.c b/source3/winbindd/winbindd_getpwnam.c
new file mode 100644
index 0000000..2bf15c0
--- /dev/null
+++ b/source3/winbindd/winbindd_getpwnam.c
@@ -0,0 +1,163 @@
+/*
+ Unix SMB/CIFS implementation.
+ async implementation of WINBINDD_GETPWNAM
+ Copyright (C) Volker Lendecke 2009
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "winbindd.h"
+#include "passdb/lookup_sid.h" /* only for LOOKUP_NAME_NO_NSS flag */
+#include "libcli/security/dom_sid.h"
+
+struct winbindd_getpwnam_state {
+ struct tevent_context *ev;
+ char *namespace;
+ char *domname;
+ char *username;
+ struct dom_sid sid;
+ enum lsa_SidType type;
+ struct winbindd_pw pw;
+};
+
+static void winbindd_getpwnam_lookupname_done(struct tevent_req *subreq);
+static void winbindd_getpwnam_done(struct tevent_req *subreq);
+
+struct tevent_req *winbindd_getpwnam_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct winbindd_cli_state *cli,
+ struct winbindd_request *request)
+{
+ struct tevent_req *req, *subreq;
+ struct winbindd_getpwnam_state *state;
+ char *domuser, *mapped_user;
+ NTSTATUS status;
+ bool ok;
+
+ req = tevent_req_create(mem_ctx, &state,
+ struct winbindd_getpwnam_state);
+ if (req == NULL) {
+ return NULL;
+ }
+ state->ev = ev;
+
+ /* Ensure null termination */
+ request->data.username[sizeof(request->data.username)-1]='\0';
+
+ D_NOTICE("[%s (%u)] Winbind external command GETPWNAM start.\n"
+ "Query username '%s'.\n",
+ cli->client_name,
+ (unsigned int)cli->pid,
+ request->data.username);
+
+ domuser = request->data.username;
+
+ status = normalize_name_unmap(state, domuser, &mapped_user);
+
+ if (NT_STATUS_IS_OK(status)
+ || NT_STATUS_EQUAL(status, NT_STATUS_FILE_RENAMED)) {
+ /* normalize_name_unmapped did something */
+ domuser = mapped_user;
+ }
+
+ ok = parse_domain_user(state,
+ domuser,
+ &state->namespace,
+ &state->domname,
+ &state->username);
+ if (!ok) {
+ D_WARNING("Could not parse domain user: %s\n", domuser);
+ tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
+ return tevent_req_post(req, ev);
+ }
+
+ subreq = wb_lookupname_send(state, ev,
+ state->namespace,
+ state->domname,
+ state->username,
+ LOOKUP_NAME_NO_NSS);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, winbindd_getpwnam_lookupname_done,
+ req);
+ return req;
+}
+
+static void winbindd_getpwnam_lookupname_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct winbindd_getpwnam_state *state = tevent_req_data(
+ req, struct winbindd_getpwnam_state);
+ NTSTATUS status;
+
+ status = wb_lookupname_recv(subreq, &state->sid, &state->type);
+ TALLOC_FREE(subreq);
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+
+ subreq = wb_getpwsid_send(state, state->ev, &state->sid, &state->pw);
+ if (tevent_req_nomem(subreq, req)) {
+ return;
+ }
+ tevent_req_set_callback(subreq, winbindd_getpwnam_done, req);
+}
+
+static void winbindd_getpwnam_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ NTSTATUS status;
+
+ status = wb_getpwsid_recv(subreq);
+ TALLOC_FREE(subreq);
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+ tevent_req_done(req);
+}
+
+NTSTATUS winbindd_getpwnam_recv(struct tevent_req *req,
+ struct winbindd_response *response)
+{
+ struct winbindd_getpwnam_state *state = tevent_req_data(
+ req, struct winbindd_getpwnam_state);
+ NTSTATUS status;
+
+ if (tevent_req_is_nterror(req, &status)) {
+ struct dom_sid_buf buf;
+ D_WARNING("Could not convert sid %s: %s\n",
+ dom_sid_str_buf(&state->sid, &buf),
+ nt_errstr(status));
+ return status;
+ }
+ response->data.pw = state->pw;
+
+ D_NOTICE("Winbind external command GETPWNAM end.\n"
+ "(name:passwd:uid:gid:gecos:dir:shell)\n"
+ "%s:%s:%u:%u:%s:%s:%s\n",
+ state->pw.pw_name,
+ state->pw.pw_passwd,
+ (unsigned int)state->pw.pw_uid,
+ (unsigned int)state->pw.pw_gid,
+ state->pw.pw_gecos,
+ state->pw.pw_dir,
+ state->pw.pw_shell
+ );
+
+ return NT_STATUS_OK;
+}
diff --git a/source3/winbindd/winbindd_getpwsid.c b/source3/winbindd/winbindd_getpwsid.c
new file mode 100644
index 0000000..8e0a3e3
--- /dev/null
+++ b/source3/winbindd/winbindd_getpwsid.c
@@ -0,0 +1,109 @@
+/*
+ Unix SMB/CIFS implementation.
+ async implementation of WINBINDD_GETPWSID
+ Copyright (C) Volker Lendecke 2009
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "winbindd.h"
+#include "../libcli/security/security.h"
+
+struct winbindd_getpwsid_state {
+ struct dom_sid sid;
+ struct winbindd_pw pw;
+};
+
+static void winbindd_getpwsid_done(struct tevent_req *subreq);
+
+struct tevent_req *winbindd_getpwsid_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct winbindd_cli_state *cli,
+ struct winbindd_request *request)
+{
+ struct tevent_req *req, *subreq;
+ struct winbindd_getpwsid_state *state;
+
+ req = tevent_req_create(mem_ctx, &state,
+ struct winbindd_getpwsid_state);
+ if (req == NULL) {
+ return NULL;
+ }
+
+ /* Ensure null termination */
+ request->data.sid[sizeof(request->data.sid)-1]='\0';
+
+ D_NOTICE("[%s (%u)] Winbind external command GETPWSID start.\n"
+ "sid=%s\n",
+ cli->client_name,
+ (unsigned int)cli->pid,
+ request->data.sid);
+
+ if (!string_to_sid(&state->sid, request->data.sid)) {
+ D_WARNING("Could not get convert sid %s from string\n",
+ request->data.sid);
+ tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
+ return tevent_req_post(req, ev);
+ }
+
+ subreq = wb_getpwsid_send(state, ev, &state->sid, &state->pw);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, winbindd_getpwsid_done, req);
+ return req;
+}
+
+static void winbindd_getpwsid_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ NTSTATUS status;
+
+ status = wb_getpwsid_recv(subreq);
+ TALLOC_FREE(subreq);
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+ tevent_req_done(req);
+}
+
+NTSTATUS winbindd_getpwsid_recv(struct tevent_req *req,
+ struct winbindd_response *response)
+{
+ struct winbindd_getpwsid_state *state = tevent_req_data(
+ req, struct winbindd_getpwsid_state);
+ NTSTATUS status;
+
+ if (tevent_req_is_nterror(req, &status)) {
+ D_WARNING("Failed with %s.\n", nt_errstr(status));
+ return status;
+ }
+ response->data.pw = state->pw;
+
+ D_NOTICE("Winbind external command GETPWSID end.\n"
+ "(name:passwd:uid:gid:gecos:dir:shell)\n"
+ "%s:%s:%u:%u:%s:%s:%s\n",
+ state->pw.pw_name,
+ state->pw.pw_passwd,
+ (unsigned int)state->pw.pw_uid,
+ (unsigned int)state->pw.pw_gid,
+ state->pw.pw_gecos,
+ state->pw.pw_dir,
+ state->pw.pw_shell
+ );
+
+ return NT_STATUS_OK;
+}
diff --git a/source3/winbindd/winbindd_getpwuid.c b/source3/winbindd/winbindd_getpwuid.c
new file mode 100644
index 0000000..509e013
--- /dev/null
+++ b/source3/winbindd/winbindd_getpwuid.c
@@ -0,0 +1,137 @@
+/*
+ Unix SMB/CIFS implementation.
+ async implementation of WINBINDD_GETPWUID
+ Copyright (C) Volker Lendecke 2009
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "winbindd.h"
+#include "libcli/security/dom_sid.h"
+
+struct winbindd_getpwuid_state {
+ struct tevent_context *ev;
+ struct unixid xid;
+ struct dom_sid *sid;
+ struct winbindd_pw pw;
+};
+
+static void winbindd_getpwuid_uid2sid_done(struct tevent_req *subreq);
+static void winbindd_getpwuid_done(struct tevent_req *subreq);
+
+struct tevent_req *winbindd_getpwuid_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct winbindd_cli_state *cli,
+ struct winbindd_request *request)
+{
+ struct tevent_req *req, *subreq;
+ struct winbindd_getpwuid_state *state;
+
+ req = tevent_req_create(mem_ctx, &state,
+ struct winbindd_getpwuid_state);
+ if (req == NULL) {
+ return NULL;
+ }
+ state->ev = ev;
+
+ D_NOTICE("[%s (%u)] Winbind external command GETPWUID start.\n"
+ "Search UID %u.\n",
+ cli->client_name,
+ (unsigned int)cli->pid,
+ (int)request->data.uid);
+
+ state->xid = (struct unixid) {
+ .id = request->data.uid, .type = ID_TYPE_UID };
+
+ subreq = wb_xids2sids_send(state, ev, &state->xid, 1);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, winbindd_getpwuid_uid2sid_done,
+ req);
+ return req;
+}
+
+static void winbindd_getpwuid_uid2sid_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct winbindd_getpwuid_state *state = tevent_req_data(
+ req, struct winbindd_getpwuid_state);
+ NTSTATUS status;
+
+ status = wb_xids2sids_recv(subreq, state, &state->sid);
+ TALLOC_FREE(subreq);
+ if (tevent_req_nterror(req, status)) {
+ D_WARNING("Failed with %s.\n", nt_errstr(status));
+ return;
+ }
+ if (is_null_sid(state->sid)) {
+ tevent_req_nterror(req, NT_STATUS_NO_SUCH_USER);
+ D_WARNING("Failed with NT_STATUS_NO_SUCH_USER.\n");
+ return;
+ }
+
+ subreq = wb_getpwsid_send(state, state->ev, state->sid, &state->pw);
+ if (tevent_req_nomem(subreq, req)) {
+ return;
+ }
+ tevent_req_set_callback(subreq, winbindd_getpwuid_done, req);
+}
+
+static void winbindd_getpwuid_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ NTSTATUS status;
+
+ status = wb_getpwsid_recv(subreq);
+ TALLOC_FREE(subreq);
+ if (tevent_req_nterror(req, status)) {
+ D_WARNING("Failed with %s.\n", nt_errstr(status));
+ return;
+ }
+ tevent_req_done(req);
+}
+
+NTSTATUS winbindd_getpwuid_recv(struct tevent_req *req,
+ struct winbindd_response *response)
+{
+ struct winbindd_getpwuid_state *state = tevent_req_data(
+ req, struct winbindd_getpwuid_state);
+ NTSTATUS status;
+
+ if (tevent_req_is_nterror(req, &status)) {
+ struct dom_sid_buf buf;
+ D_WARNING("Could not convert sid %s: %s\n",
+ dom_sid_str_buf(state->sid, &buf),
+ nt_errstr(status));
+ return status;
+ }
+ response->data.pw = state->pw;
+ D_NOTICE("Winbind external command GETPWUID end.\n"
+ "(name:passwd:uid:gid:gecos:dir:shell)\n"
+ "%s:%s:%u:%u:%s:%s:%s\n",
+ state->pw.pw_name,
+ state->pw.pw_passwd,
+ (unsigned int)state->pw.pw_uid,
+ (unsigned int)state->pw.pw_gid,
+ state->pw.pw_gecos,
+ state->pw.pw_dir,
+ state->pw.pw_shell
+ );
+
+ return NT_STATUS_OK;
+}
diff --git a/source3/winbindd/winbindd_getsidaliases.c b/source3/winbindd/winbindd_getsidaliases.c
new file mode 100644
index 0000000..bf8fcdd
--- /dev/null
+++ b/source3/winbindd/winbindd_getsidaliases.c
@@ -0,0 +1,160 @@
+/*
+ Unix SMB/CIFS implementation.
+ async implementation of WINBINDD_GETSIDALIASES
+ Copyright (C) Volker Lendecke 2009
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "winbindd.h"
+#include "../libcli/security/security.h"
+
+struct winbindd_getsidaliases_state {
+ struct dom_sid sid;
+ uint32_t num_aliases;
+ uint32_t *aliases;
+};
+
+static void winbindd_getsidaliases_done(struct tevent_req *subreq);
+
+struct tevent_req *winbindd_getsidaliases_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct winbindd_cli_state *cli,
+ struct winbindd_request *request)
+{
+ struct tevent_req *req, *subreq;
+ struct winbindd_getsidaliases_state *state;
+ struct winbindd_domain *domain;
+ uint32_t num_sids, i;
+ struct dom_sid *sids;
+
+ req = tevent_req_create(mem_ctx, &state,
+ struct winbindd_getsidaliases_state);
+ if (req == NULL) {
+ return NULL;
+ }
+
+ /* Ensure null termination */
+ request->data.sid[sizeof(request->data.sid)-1]='\0';
+
+ if (!string_to_sid(&state->sid, request->data.sid)) {
+ D_WARNING("Could not get convert sid %s from string\n",
+ request->data.sid);
+ tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
+ return tevent_req_post(req, ev);
+ }
+
+ domain = find_domain_from_sid_noinit(&state->sid);
+ if (domain == NULL) {
+ D_WARNING("could not find domain entry for sid %s\n",
+ request->data.sid);
+ tevent_req_nterror(req, NT_STATUS_NO_SUCH_DOMAIN);
+ return tevent_req_post(req, ev);
+ }
+
+ num_sids = 0;
+ sids = NULL;
+
+ if (request->extra_data.data != NULL) {
+ if (request->extra_data.data[request->extra_len-1] != '\0') {
+ D_WARNING("Got non-NULL terminated sidlist\n");
+ tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
+ return tevent_req_post(req, ev);
+ }
+ if (!parse_sidlist(state, request->extra_data.data,
+ &sids, &num_sids)) {
+ D_WARNING("Could not parse SID list: %s\n",
+ request->extra_data.data);
+ tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
+ return tevent_req_post(req, ev);
+ }
+ }
+
+ D_NOTICE("[%s (%u)] Winbind external command GETSIDALIASES start.\n"
+ "sid=%s\n",
+ cli->client_name,
+ (unsigned int)cli->pid,
+ request->data.sid);
+ if (CHECK_DEBUGLVL(DBGLVL_DEBUG)) {
+ for (i = 0; i < num_sids; i++) {
+ struct dom_sid_buf sidstr;
+ D_NOTICE("%"PRIu32": %s\n",
+ i, dom_sid_str_buf(&sids[i], &sidstr));
+ }
+ }
+
+ subreq = wb_lookupuseraliases_send(state, ev, domain, num_sids, sids);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, winbindd_getsidaliases_done, req);
+ return req;
+}
+
+static void winbindd_getsidaliases_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct winbindd_getsidaliases_state *state = tevent_req_data(
+ req, struct winbindd_getsidaliases_state);
+ NTSTATUS status;
+
+ status = wb_lookupuseraliases_recv(subreq, state, &state->num_aliases,
+ &state->aliases);
+ TALLOC_FREE(subreq);
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+ tevent_req_done(req);
+}
+
+NTSTATUS winbindd_getsidaliases_recv(struct tevent_req *req,
+ struct winbindd_response *response)
+{
+ struct winbindd_getsidaliases_state *state = tevent_req_data(
+ req, struct winbindd_getsidaliases_state);
+ NTSTATUS status;
+ uint32_t i;
+ char *sidlist;
+
+ if (tevent_req_is_nterror(req, &status)) {
+ D_WARNING("Failed with %s.\n", nt_errstr(status));
+ return status;
+ }
+
+ sidlist = talloc_strdup(response, "");
+
+ D_NOTICE("Winbind external command GETSIDALIASES end.\n"
+ "Received %"PRIu32" alias(es).\n",
+ state->num_aliases);
+ for (i=0; i<state->num_aliases; i++) {
+ struct dom_sid sid;
+ struct dom_sid_buf tmp;
+ sid_compose(&sid, &state->sid, state->aliases[i]);
+
+ talloc_asprintf_addbuf(
+ &sidlist, "%s\n", dom_sid_str_buf(&sid, &tmp));
+ D_NOTICE("%"PRIu32": %s\n", i, dom_sid_str_buf(&sid, &tmp));
+ }
+
+ if (sidlist == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ response->extra_data.data = sidlist;
+ response->length += talloc_get_size(sidlist);
+ response->data.num_entries = state->num_aliases;
+ return NT_STATUS_OK;
+}
diff --git a/source3/winbindd/winbindd_getuserdomgroups.c b/source3/winbindd/winbindd_getuserdomgroups.c
new file mode 100644
index 0000000..75eb437
--- /dev/null
+++ b/source3/winbindd/winbindd_getuserdomgroups.c
@@ -0,0 +1,123 @@
+/*
+ Unix SMB/CIFS implementation.
+ async implementation of WINBINDD_GETUSERDOMGROUPS
+ Copyright (C) Volker Lendecke 2009
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "winbindd.h"
+#include "../libcli/security/security.h"
+
+struct winbindd_getuserdomgroups_state {
+ struct dom_sid sid;
+ uint32_t num_sids;
+ struct dom_sid *sids;
+};
+
+static void winbindd_getuserdomgroups_done(struct tevent_req *subreq);
+
+struct tevent_req *winbindd_getuserdomgroups_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct winbindd_cli_state *cli,
+ struct winbindd_request *request)
+{
+ struct tevent_req *req, *subreq;
+ struct winbindd_getuserdomgroups_state *state;
+
+ req = tevent_req_create(mem_ctx, &state,
+ struct winbindd_getuserdomgroups_state);
+ if (req == NULL) {
+ return NULL;
+ }
+
+ /* Ensure null termination */
+ request->data.sid[sizeof(request->data.sid)-1]='\0';
+
+ D_NOTICE("[%s (%u)] Winbind external command GETUSERDOMGROUPS start.\n"
+ "sid=%s\n",
+ cli->client_name,
+ (unsigned int)cli->pid,
+ request->data.sid);
+
+ if (!string_to_sid(&state->sid, request->data.sid)) {
+ D_WARNING("Could not get convert sid %s from string\n",
+ request->data.sid);
+ tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
+ return tevent_req_post(req, ev);
+ }
+
+ subreq = wb_gettoken_send(state, ev, &state->sid, false);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, winbindd_getuserdomgroups_done, req);
+ return req;
+}
+
+static void winbindd_getuserdomgroups_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct winbindd_getuserdomgroups_state *state = tevent_req_data(
+ req, struct winbindd_getuserdomgroups_state);
+ NTSTATUS status;
+
+ status = wb_gettoken_recv(subreq, state, &state->num_sids,
+ &state->sids);
+ TALLOC_FREE(subreq);
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+ tevent_req_done(req);
+}
+
+NTSTATUS winbindd_getuserdomgroups_recv(struct tevent_req *req,
+ struct winbindd_response *response)
+{
+ struct winbindd_getuserdomgroups_state *state = tevent_req_data(
+ req, struct winbindd_getuserdomgroups_state);
+ NTSTATUS status;
+ uint32_t i;
+ char *sidlist;
+
+ if (tevent_req_is_nterror(req, &status)) {
+ D_WARNING("Failed with %s.\n", nt_errstr(status));
+ return status;
+ }
+
+ sidlist = talloc_strdup(response, "");
+ if (sidlist == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ D_NOTICE("Winbind external command GETUSERDOMGROUPS end.\n"
+ "Received %"PRIu32" entries.\n",
+ state->num_sids);
+ for (i=0; i<state->num_sids; i++) {
+ struct dom_sid_buf tmp;
+ sidlist = talloc_asprintf_append_buffer(
+ sidlist, "%s\n",
+ dom_sid_str_buf(&state->sids[i], &tmp));
+ if (sidlist == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ D_NOTICE("%"PRIu32": %s\n",
+ i, dom_sid_str_buf(&state->sids[i], &tmp));
+ }
+ response->extra_data.data = sidlist;
+ response->length += talloc_get_size(sidlist);
+ response->data.num_entries = state->num_sids;
+ return NT_STATUS_OK;
+}
diff --git a/source3/winbindd/winbindd_getusersids.c b/source3/winbindd/winbindd_getusersids.c
new file mode 100644
index 0000000..a285c1f
--- /dev/null
+++ b/source3/winbindd/winbindd_getusersids.c
@@ -0,0 +1,128 @@
+/*
+ Unix SMB/CIFS implementation.
+ async implementation of WINBINDD_GETUSERSIDS
+ Copyright (C) Volker Lendecke 2009
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "winbindd.h"
+#include "../libcli/security/security.h"
+
+struct winbindd_getusersids_state {
+ struct dom_sid sid;
+ uint32_t num_sids;
+ struct dom_sid *sids;
+};
+
+static void winbindd_getusersids_done(struct tevent_req *subreq);
+
+struct tevent_req *winbindd_getusersids_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct winbindd_cli_state *cli,
+ struct winbindd_request *request)
+{
+ struct tevent_req *req, *subreq;
+ struct winbindd_getusersids_state *state;
+
+ req = tevent_req_create(mem_ctx, &state,
+ struct winbindd_getusersids_state);
+ if (req == NULL) {
+ return NULL;
+ }
+
+ /* Ensure null termination */
+ request->data.sid[sizeof(request->data.sid)-1]='\0';
+
+ D_NOTICE("[%s (%u)] Winbind external command GETUSERSIDS start.\n"
+ "sid=%s\n",
+ cli->client_name,
+ (unsigned int)cli->pid,
+ request->data.sid);
+
+ if (!string_to_sid(&state->sid, request->data.sid)) {
+ D_WARNING("Returning NT_STATUS_INVALID_PARAMETER.\n"
+ "Could not get convert sid %s from string\n",
+ request->data.sid);
+ tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
+ return tevent_req_post(req, ev);
+ }
+
+ subreq = wb_gettoken_send(state, ev, &state->sid, true);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, winbindd_getusersids_done, req);
+ return req;
+}
+
+static void winbindd_getusersids_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct winbindd_getusersids_state *state = tevent_req_data(
+ req, struct winbindd_getusersids_state);
+ NTSTATUS status;
+
+ status = wb_gettoken_recv(subreq, state, &state->num_sids,
+ &state->sids);
+ TALLOC_FREE(subreq);
+ if (tevent_req_nterror(req, status)) {
+ D_WARNING("wb_gettoken_recv failed with %s.\n",
+ nt_errstr(status));
+ return;
+ }
+ tevent_req_done(req);
+}
+
+NTSTATUS winbindd_getusersids_recv(struct tevent_req *req,
+ struct winbindd_response *response)
+{
+ struct winbindd_getusersids_state *state = tevent_req_data(
+ req, struct winbindd_getusersids_state);
+ struct dom_sid_buf sidbuf;
+ NTSTATUS status;
+ uint32_t i;
+ char *result;
+
+ if (tevent_req_is_nterror(req, &status)) {
+ D_WARNING("Could not convert sid %s: %s\n",
+ dom_sid_str_buf(&state->sid, &sidbuf),
+ nt_errstr(status));
+ return status;
+ }
+
+ result = talloc_strdup(response, "");
+ if (result == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ D_NOTICE("Winbind external command GETUSERSIDS end.\n"
+ "Got %"PRIu32" SID(s).\n", state->num_sids);
+ for (i=0; i<state->num_sids; i++) {
+ D_NOTICE("%"PRIu32": %s\n",
+ i,
+ dom_sid_str_buf(&state->sids[i], &sidbuf));
+ talloc_asprintf_addbuf(
+ &result,
+ "%s\n",
+ dom_sid_str_buf(&state->sids[i], &sidbuf));
+ }
+
+ response->data.num_entries = state->num_sids;
+ response->extra_data.data = result;
+ response->length += talloc_get_size(result);
+ return NT_STATUS_OK;
+}
diff --git a/source3/winbindd/winbindd_gpupdate.c b/source3/winbindd/winbindd_gpupdate.c
new file mode 100644
index 0000000..1ab20fb
--- /dev/null
+++ b/source3/winbindd/winbindd_gpupdate.c
@@ -0,0 +1,184 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * Group Policy Update event for winbindd
+ * Copyright (C) David Mulder 2017
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+#include "includes.h"
+#include "param/param.h"
+#include "param/loadparm.h"
+#include "winbindd.h"
+#include "lib/global_contexts.h"
+
+/*
+ * gpupdate_interval()
+ * return Random integer between 5400 and 7200, the group policy update
+ * interval in seconds
+ *
+ * Group Policy should be updated every 90 minutes in the background,
+ * with a random offset between 0 and 30 minutes. This ensures multiple
+ * clients will not update at the same time.
+ */
+#define GPUPDATE_INTERVAL (90*60)
+#define GPUPDATE_RAND_OFFSET (30*60)
+static uint32_t gpupdate_interval(void)
+{
+ int rand_int_offset = generate_random() % GPUPDATE_RAND_OFFSET;
+ return GPUPDATE_INTERVAL+rand_int_offset;
+}
+
+struct gpupdate_state {
+ TALLOC_CTX *ctx;
+ struct loadparm_context *lp_ctx;
+};
+
+static void gpupdate_cmd_done(struct tevent_req *subreq);
+
+static void gpupdate_callback(struct tevent_context *ev,
+ struct tevent_timer *tim,
+ struct timeval current_time,
+ void *private_data)
+{
+ struct tevent_timer *time_event;
+ struct timeval schedule;
+ struct tevent_req *req = NULL;
+ struct gpupdate_state *data =
+ talloc_get_type_abort(private_data, struct gpupdate_state);
+ const char *const *gpupdate_cmd =
+ lpcfg_gpo_update_command(data->lp_ctx);
+ const char *smbconf = lpcfg_configfile(data->lp_ctx);
+ if (smbconf == NULL) {
+ smbconf = lp_default_path();
+ }
+
+ /* Execute gpupdate */
+ req = samba_runcmd_send(data->ctx, ev, timeval_zero(), 2, 0,
+ gpupdate_cmd,
+ "-s",
+ smbconf,
+ "--target=Computer",
+ "--machine-pass",
+ NULL);
+ if (req == NULL) {
+ DEBUG(0, ("Failed to execute the gpupdate command\n"));
+ return;
+ }
+
+ tevent_req_set_callback(req, gpupdate_cmd_done, NULL);
+
+ /* Schedule the next event */
+ schedule = tevent_timeval_current_ofs(gpupdate_interval(), 0);
+ time_event = tevent_add_timer(ev, data->ctx, schedule,
+ gpupdate_callback, data);
+ if (time_event == NULL) {
+ DEBUG(0, ("Failed scheduling the next gpupdate event\n"));
+ }
+}
+
+void gpupdate_init(void)
+{
+ struct tevent_timer *time_event;
+ struct timeval schedule;
+ TALLOC_CTX * ctx = talloc_new(global_event_context());
+ struct gpupdate_state *data = talloc(ctx, struct gpupdate_state);
+ struct loadparm_context *lp_ctx =
+ loadparm_init_s3(NULL, loadparm_s3_helpers());
+
+ /*
+ * Check if gpupdate is enabled for winbind, if not
+ * return without scheduling any events.
+ */
+ if (!lpcfg_apply_group_policies(lp_ctx)) {
+ return;
+ }
+
+ /*
+ * Execute the first event immediately, future events
+ * will execute on the gpupdate interval, which is every
+ * 90 to 120 minutes (at random).
+ */
+ schedule = tevent_timeval_current_ofs(0, 0);
+ data->ctx = ctx;
+ data->lp_ctx = lp_ctx;
+ if (data->lp_ctx == NULL) {
+ smb_panic("Could not load smb.conf\n");
+ }
+ time_event = tevent_add_timer(global_event_context(), data->ctx,
+ schedule, gpupdate_callback, data);
+ if (time_event == NULL) {
+ DEBUG(0, ("Failed scheduling the gpupdate event\n"));
+ }
+}
+
+void gpupdate_user_init(const char *user)
+{
+ struct tevent_req *req = NULL;
+ TALLOC_CTX *ctx = talloc_new(global_event_context());
+ struct loadparm_context *lp_ctx =
+ loadparm_init_s3(NULL, loadparm_s3_helpers());
+ const char *const *gpupdate_cmd = lpcfg_gpo_update_command(lp_ctx);
+ const char *smbconf = lpcfg_configfile(lp_ctx);
+ if (smbconf == NULL) {
+ smbconf = lp_default_path();
+ }
+
+ if (ctx == NULL) {
+ DBG_ERR("talloc_new failed\n");
+ return;
+ }
+
+ /*
+ * Check if gpupdate is enabled for winbind, if not
+ * return without applying user policy.
+ */
+ if (!lpcfg_apply_group_policies(lp_ctx)) {
+ return;
+ }
+
+ /*
+ * Execute gpupdate for the user immediately.
+ * TODO: This should be scheduled to reapply every 90 to 120 minutes.
+ * Logoff will need to handle cancelling these events though, and
+ * multiple timers cannot be run for the same user, even if there are
+ * multiple active sessions.
+ */
+ req = samba_runcmd_send(ctx, global_event_context(),
+ timeval_zero(), 2, 0,
+ gpupdate_cmd,
+ "-s",
+ smbconf,
+ "--target=User",
+ "-U",
+ user,
+ NULL);
+ if (req == NULL) {
+ DBG_ERR("Failed to execute the gpupdate command\n");
+ return;
+ }
+
+ tevent_req_set_callback(req, gpupdate_cmd_done, NULL);
+}
+
+static void gpupdate_cmd_done(struct tevent_req *subreq)
+{
+ int sys_errno;
+ int ret;
+
+ ret = samba_runcmd_recv(subreq, &sys_errno);
+ TALLOC_FREE(subreq);
+ if (ret != 0) {
+ DBG_ERR("gpupdate failed with exit status %d\n", sys_errno);
+ }
+}
diff --git a/source3/winbindd/winbindd_group.c b/source3/winbindd/winbindd_group.c
new file mode 100644
index 0000000..b233c8e
--- /dev/null
+++ b/source3/winbindd/winbindd_group.c
@@ -0,0 +1,156 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Winbind daemon for ntdom nss module
+
+ Copyright (C) Tim Potter 2000
+ Copyright (C) Jeremy Allison 2001.
+ Copyright (C) Gerald (Jerry) Carter 2003.
+ Copyright (C) Volker Lendecke 2005
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "winbindd.h"
+#include "lib/dbwrap/dbwrap.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_WINBIND
+
+/* Fill a grent structure from various other information */
+
+bool fill_grent(TALLOC_CTX *mem_ctx, struct winbindd_gr *gr,
+ const char *dom_name, const char *gr_name, gid_t unix_gid)
+{
+ const char *full_group_name;
+ char *mapped_name = NULL;
+ NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
+
+ nt_status = normalize_name_map(mem_ctx, dom_name, gr_name,
+ &mapped_name);
+
+ D_DEBUG("Filling domain '%s' and group '%s'.\n", dom_name, gr_name);
+ /* Basic whitespace replacement */
+ if (NT_STATUS_IS_OK(nt_status)) {
+ full_group_name = fill_domain_username_talloc(mem_ctx, dom_name,
+ mapped_name, true);
+ }
+ /* Mapped to an alias */
+ else if (NT_STATUS_EQUAL(nt_status, NT_STATUS_FILE_RENAMED)) {
+ full_group_name = mapped_name;
+ }
+ /* no change */
+ else {
+ full_group_name = fill_domain_username_talloc(mem_ctx, dom_name,
+ gr_name, True );
+ }
+
+ if (full_group_name == NULL) {
+ D_DEBUG("Returning false, since there is no full group name.\n");
+ return false;
+ }
+
+ gr->gr_gid = unix_gid;
+
+ /* Group name and password */
+
+ strlcpy(gr->gr_name, full_group_name, sizeof(gr->gr_name));
+ strlcpy(gr->gr_passwd, "x", sizeof(gr->gr_passwd));
+
+ D_DEBUG("Returning true. Full group name is '%s'.\n", gr_name);
+ return True;
+}
+
+struct getgr_countmem {
+ int num;
+ size_t len;
+};
+
+static int getgr_calc_memberlen(struct db_record *rec, void *private_data)
+{
+ struct getgr_countmem *buf = private_data;
+ TDB_DATA data = dbwrap_record_get_value(rec);
+ size_t len;
+
+ buf->num += 1;
+
+ len = buf->len + data.dsize;
+ if (len < buf->len) {
+ return 0;
+ }
+ buf->len = len;
+ return 0;
+}
+
+struct getgr_stringmem {
+ size_t ofs;
+ char *buf;
+};
+
+static int getgr_unparse_members(struct db_record *rec, void *private_data)
+{
+ struct getgr_stringmem *buf = private_data;
+ TDB_DATA data = dbwrap_record_get_value(rec);
+ int len;
+
+ len = data.dsize-1;
+
+ memcpy(buf->buf + buf->ofs, data.dptr, len);
+ buf->ofs += len;
+ buf->buf[buf->ofs] = ',';
+ buf->ofs += 1;
+ return 0;
+}
+
+NTSTATUS winbindd_print_groupmembers(struct db_context *members,
+ TALLOC_CTX *mem_ctx,
+ int *num_members, char **result)
+{
+ struct getgr_countmem c;
+ struct getgr_stringmem m;
+ int count;
+ NTSTATUS status;
+
+ c.num = 0;
+ c.len = 0;
+
+ status = dbwrap_traverse(members, getgr_calc_memberlen, &c, &count);
+ if (!NT_STATUS_IS_OK(status)) {
+ DBG_NOTICE("dbwrap_traverse failed: %s\n", nt_errstr(status));
+ return status;
+ }
+
+ m.ofs = 0;
+ m.buf = talloc_array(mem_ctx, char, c.len);
+ if (m.buf == NULL) {
+ D_WARNING("talloc failed\n");
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ status = dbwrap_traverse(members, getgr_unparse_members, &m, &count);
+ if (!NT_STATUS_IS_OK(status)) {
+ TALLOC_FREE(m.buf);
+ DBG_NOTICE("dbwrap_traverse failed: %s\n", nt_errstr(status));
+ return status;
+ }
+ if (c.len > 0) {
+ m.buf[c.len - 1] = '\0';
+ }
+
+ *num_members = c.num;
+ *result = m.buf;
+ D_DEBUG("Returning %d member(s).\n", *num_members);
+ return NT_STATUS_OK;
+}
diff --git a/source3/winbindd/winbindd_idmap.c b/source3/winbindd/winbindd_idmap.c
new file mode 100644
index 0000000..3622112
--- /dev/null
+++ b/source3/winbindd/winbindd_idmap.c
@@ -0,0 +1,436 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Async helpers for blocking functions
+
+ Copyright (C) Volker Lendecke 2005
+ Copyright (C) Gerald Carter 2006
+ Copyright (C) Simo Sorce 2007
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "winbindd.h"
+#include "../libcli/security/security.h"
+#include "passdb/lookup_sid.h"
+#include "lib/global_contexts.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_WINBIND
+
+static struct winbindd_child *static_idmap_child = NULL;
+
+/*
+ * Map idmap ranges to domain names, taken from smb.conf. This is
+ * stored in the parent winbind and used to assemble xids2sids/sids2xids calls
+ * into per-idmap-domain chunks.
+ */
+static struct wb_parent_idmap_config static_parent_idmap_config;
+
+struct winbindd_child *idmap_child(void)
+{
+ return static_idmap_child;
+}
+
+bool is_idmap_child(const struct winbindd_child *child)
+{
+ if (child == static_idmap_child) {
+ return true;
+ }
+
+ return false;
+}
+
+pid_t idmap_child_pid(void)
+{
+ return static_idmap_child->pid;
+}
+
+struct dcerpc_binding_handle *idmap_child_handle(void)
+{
+ /*
+ * The caller needs to use wb_parent_idmap_setup_send/recv
+ * before talking to the idmap child!
+ */
+ SMB_ASSERT(static_parent_idmap_config.num_doms > 0);
+ return static_idmap_child->binding_handle;
+}
+
+static void init_idmap_child_done(struct tevent_req *subreq);
+
+NTSTATUS init_idmap_child(TALLOC_CTX *mem_ctx)
+{
+ struct tevent_req *subreq = NULL;
+
+ if (static_idmap_child != NULL) {
+ DBG_ERR("idmap child already allocated\n");
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+
+ static_idmap_child = talloc_zero(mem_ctx, struct winbindd_child);
+ if (static_idmap_child == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ subreq = wb_parent_idmap_setup_send(static_idmap_child,
+ global_event_context());
+ if (subreq == NULL) {
+ /*
+ * This is only an optimization, so we're free to
+ * to ignore errors
+ */
+ DBG_ERR("wb_parent_idmap_setup_send() failed\n");
+ return NT_STATUS_NO_MEMORY;
+ }
+ tevent_req_set_callback(subreq, init_idmap_child_done, NULL);
+ DBG_DEBUG("wb_parent_idmap_setup_send() started\n");
+ return NT_STATUS_OK;
+}
+
+static void init_idmap_child_done(struct tevent_req *subreq)
+{
+ const struct wb_parent_idmap_config *cfg = NULL;
+ NTSTATUS status;
+
+ status = wb_parent_idmap_setup_recv(subreq, &cfg);
+ TALLOC_FREE(subreq);
+ if (!NT_STATUS_IS_OK(status)) {
+ /*
+ * This is only an optimization, so we're free to
+ * to ignore errors
+ */
+ DBG_ERR("wb_parent_idmap_setup_recv() failed %s\n",
+ nt_errstr(status));
+ return;
+ }
+
+ DBG_DEBUG("wb_parent_idmap_setup_recv() finished\n");
+}
+
+struct wb_parent_idmap_setup_state {
+ struct tevent_context *ev;
+ struct wb_parent_idmap_config *cfg;
+ size_t dom_idx;
+};
+
+static void wb_parent_idmap_setup_cleanup(struct tevent_req *req,
+ enum tevent_req_state req_state)
+{
+ struct wb_parent_idmap_setup_state *state =
+ tevent_req_data(req,
+ struct wb_parent_idmap_setup_state);
+
+ if (req_state == TEVENT_REQ_DONE) {
+ state->cfg = NULL;
+ return;
+ }
+
+ if (state->cfg == NULL) {
+ return;
+ }
+
+ state->cfg->num_doms = 0;
+ state->cfg->initialized = false;
+ TALLOC_FREE(state->cfg->doms);
+ state->cfg = NULL;
+}
+
+static void wb_parent_idmap_setup_queue_wait_done(struct tevent_req *subreq);
+static bool wb_parent_idmap_setup_scan_config(const char *domname,
+ void *private_data);
+static void wb_parent_idmap_setup_lookupname_next(struct tevent_req *req);
+static void wb_parent_idmap_setup_lookupname_done(struct tevent_req *subreq);
+
+struct tevent_req *wb_parent_idmap_setup_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev)
+{
+ struct tevent_req *req = NULL;
+ struct wb_parent_idmap_setup_state *state = NULL;
+ struct tevent_req *subreq = NULL;
+
+ req = tevent_req_create(mem_ctx, &state,
+ struct wb_parent_idmap_setup_state);
+ if (req == NULL) {
+ return NULL;
+ }
+ *state = (struct wb_parent_idmap_setup_state) {
+ .ev = ev,
+ .cfg = &static_parent_idmap_config,
+ .dom_idx = 0,
+ };
+
+ if (state->cfg->initialized) {
+ tevent_req_done(req);
+ return tevent_req_post(req, ev);
+ }
+
+ if (state->cfg->queue == NULL) {
+ state->cfg->queue = tevent_queue_create(NULL,
+ "wb_parent_idmap_config_queue");
+ if (tevent_req_nomem(state->cfg->queue, req)) {
+ return tevent_req_post(req, ev);
+ }
+ }
+
+ subreq = tevent_queue_wait_send(state, state->ev, state->cfg->queue);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq,
+ wb_parent_idmap_setup_queue_wait_done,
+ req);
+
+ return req;
+}
+
+static void wb_parent_idmap_setup_queue_wait_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req =
+ tevent_req_callback_data(subreq,
+ struct tevent_req);
+ struct wb_parent_idmap_setup_state *state =
+ tevent_req_data(req,
+ struct wb_parent_idmap_setup_state);
+ bool ok;
+
+ /*
+ * Note we don't call TALLOC_FREE(subreq) here in order to block the
+ * queue until tevent_req_received() in wb_parent_idmap_setup_recv()
+ * will destroy it implicitly.
+ */
+ ok = tevent_queue_wait_recv(subreq);
+ if (!ok) {
+ DBG_ERR("tevent_queue_wait_recv() failed\n");
+ tevent_req_nterror(req, NT_STATUS_INTERNAL_ERROR);
+ return;
+ }
+
+ if (state->cfg->num_doms != 0) {
+ /*
+ * If we're not the first one we're done.
+ */
+ tevent_req_done(req);
+ return;
+ }
+
+ /*
+ * From this point we start changing state->cfg,
+ * which is &static_parent_idmap_config,
+ * so we better setup a cleanup function
+ * to undo the changes on failure.
+ */
+ tevent_req_set_cleanup_fn(req, wb_parent_idmap_setup_cleanup);
+
+ /*
+ * Put the passdb idmap domain first. We always need to try
+ * there first.
+ */
+ state->cfg->doms = talloc_zero_array(NULL,
+ struct wb_parent_idmap_config_dom,
+ 1);
+ if (tevent_req_nomem(state->cfg->doms, req)) {
+ return;
+ }
+ state->cfg->doms[0].low_id = 0;
+ state->cfg->doms[0].high_id = UINT_MAX;
+ state->cfg->doms[0].name = talloc_strdup(state->cfg->doms,
+ get_global_sam_name());
+ if (tevent_req_nomem(state->cfg->doms[0].name, req)) {
+ return;
+ }
+ state->cfg->num_doms += 1;
+
+ lp_scan_idmap_domains(wb_parent_idmap_setup_scan_config, req);
+ if (!tevent_req_is_in_progress(req)) {
+ return;
+ }
+
+ wb_parent_idmap_setup_lookupname_next(req);
+}
+
+static bool wb_parent_idmap_setup_scan_config(const char *domname,
+ void *private_data)
+{
+ struct tevent_req *req =
+ talloc_get_type_abort(private_data,
+ struct tevent_req);
+ struct wb_parent_idmap_setup_state *state =
+ tevent_req_data(req,
+ struct wb_parent_idmap_setup_state);
+ struct wb_parent_idmap_config_dom *map = NULL;
+ size_t i;
+ const char *range;
+ unsigned low_id, high_id;
+ int ret;
+
+ range = idmap_config_const_string(domname, "range", NULL);
+ if (range == NULL) {
+ DBG_DEBUG("No range for domain %s found\n", domname);
+ return false;
+ }
+
+ ret = sscanf(range, "%u - %u", &low_id, &high_id);
+ if (ret != 2) {
+ DBG_DEBUG("Invalid range spec \"%s\" for domain %s\n",
+ range, domname);
+ return false;
+ }
+
+ if (low_id > high_id) {
+ DBG_DEBUG("Invalid range %u - %u for domain %s\n",
+ low_id, high_id, domname);
+ return false;
+ }
+
+ for (i=0; i<state->cfg->num_doms; i++) {
+ if (strequal(domname, state->cfg->doms[i].name)) {
+ map = &state->cfg->doms[i];
+ break;
+ }
+ }
+
+ if (map == NULL) {
+ struct wb_parent_idmap_config_dom *tmp;
+ char *name;
+
+ name = talloc_strdup(state, domname);
+ if (name == NULL) {
+ DBG_ERR("talloc failed\n");
+ return false;
+ }
+
+ tmp = talloc_realloc(
+ NULL, state->cfg->doms, struct wb_parent_idmap_config_dom,
+ state->cfg->num_doms+1);
+ if (tmp == NULL) {
+ DBG_ERR("talloc failed\n");
+ return false;
+ }
+ state->cfg->doms = tmp;
+
+ map = &state->cfg->doms[state->cfg->num_doms];
+ state->cfg->num_doms += 1;
+ ZERO_STRUCTP(map);
+ map->name = talloc_move(state->cfg->doms, &name);
+ }
+
+ map->low_id = low_id;
+ map->high_id = high_id;
+
+ return false;
+}
+
+static void wb_parent_idmap_setup_lookupname_next(struct tevent_req *req)
+{
+ struct wb_parent_idmap_setup_state *state =
+ tevent_req_data(req,
+ struct wb_parent_idmap_setup_state);
+ struct wb_parent_idmap_config_dom *dom =
+ &state->cfg->doms[state->dom_idx];
+ struct tevent_req *subreq = NULL;
+
+ next_domain:
+ if (state->dom_idx == state->cfg->num_doms) {
+ /*
+ * We're done, so start the idmap child
+ */
+ setup_child(NULL, static_idmap_child, "log.winbindd", "idmap");
+ static_parent_idmap_config.initialized = true;
+ tevent_req_done(req);
+ return;
+ }
+
+ if (strequal(dom->name, "*")) {
+ state->dom_idx++;
+ goto next_domain;
+ }
+
+ subreq = wb_lookupname_send(state,
+ state->ev,
+ dom->name,
+ dom->name,
+ "",
+ LOOKUP_NAME_NO_NSS);
+ if (tevent_req_nomem(subreq, req)) {
+ return;
+ }
+ tevent_req_set_callback(subreq,
+ wb_parent_idmap_setup_lookupname_done,
+ req);
+}
+
+static void wb_parent_idmap_setup_lookupname_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req =
+ tevent_req_callback_data(subreq,
+ struct tevent_req);
+ struct wb_parent_idmap_setup_state *state =
+ tevent_req_data(req,
+ struct wb_parent_idmap_setup_state);
+ struct wb_parent_idmap_config_dom *dom =
+ &state->cfg->doms[state->dom_idx];
+ enum lsa_SidType type;
+ NTSTATUS status;
+
+ status = wb_lookupname_recv(subreq, &dom->sid, &type);
+ TALLOC_FREE(subreq);
+ if (!NT_STATUS_IS_OK(status)) {
+ DBG_ERR("Lookup domain name '%s' failed '%s'\n",
+ dom->name,
+ nt_errstr(status));
+
+ state->dom_idx++;
+ wb_parent_idmap_setup_lookupname_next(req);
+ return;
+ }
+
+ if (type != SID_NAME_DOMAIN) {
+ struct dom_sid_buf buf;
+
+ DBG_ERR("SID %s for idmap domain name '%s' "
+ "not a domain SID\n",
+ dom_sid_str_buf(&dom->sid, &buf),
+ dom->name);
+
+ ZERO_STRUCT(dom->sid);
+ }
+
+ state->dom_idx++;
+ wb_parent_idmap_setup_lookupname_next(req);
+
+ return;
+}
+
+NTSTATUS wb_parent_idmap_setup_recv(struct tevent_req *req,
+ const struct wb_parent_idmap_config **_cfg)
+{
+ const struct wb_parent_idmap_config *cfg = &static_parent_idmap_config;
+ NTSTATUS status;
+
+ *_cfg = NULL;
+
+ if (tevent_req_is_nterror(req, &status)) {
+ tevent_req_received(req);
+ return status;
+ }
+
+ /*
+ * Note state->cfg is already set to NULL by
+ * wb_parent_idmap_setup_cleanup()
+ */
+ *_cfg = cfg;
+ tevent_req_received(req);
+ return NT_STATUS_OK;
+}
diff --git a/source3/winbindd/winbindd_irpc.c b/source3/winbindd/winbindd_irpc.c
new file mode 100644
index 0000000..f66d797
--- /dev/null
+++ b/source3/winbindd/winbindd_irpc.c
@@ -0,0 +1,891 @@
+/*
+ Unix SMB/CIFS implementation.
+ async implementation of commands submitted over IRPC
+ Copyright (C) Volker Lendecke 2009
+ Copyright (C) Guenther Deschner 2009
+ Copyright (C) Andrew Bartlett 2014
+ Copyright (C) Andrew Tridgell 2009
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "winbindd.h"
+#include "librpc/gen_ndr/ndr_winbind_c.h"
+#include "source4/lib/messaging/irpc.h"
+#include "librpc/gen_ndr/ndr_winbind.h"
+#include "librpc/gen_ndr/ndr_lsa.h"
+#include "librpc/gen_ndr/ndr_lsa_c.h"
+#include "libcli/security/dom_sid.h"
+#include "passdb/lookup_sid.h" /* only for LOOKUP_NAME_NO_NSS flag */
+#include "librpc/gen_ndr/ndr_irpc.h"
+#include "librpc/gen_ndr/ndr_netlogon.h"
+#include "lib/global_contexts.h"
+#include "lib/param/param.h"
+#include "messages.h"
+
+struct imessaging_context *winbind_imessaging_context(void)
+{
+ static struct imessaging_context *msg = NULL;
+ struct messaging_context *msg_ctx;
+ struct server_id myself;
+ struct loadparm_context *lp_ctx;
+
+ if (msg != NULL) {
+ return msg;
+ }
+
+ msg_ctx = global_messaging_context();
+ if (msg_ctx == NULL) {
+ smb_panic("global_messaging_context failed\n");
+ }
+ myself = messaging_server_id(msg_ctx);
+
+ lp_ctx = loadparm_init_s3(NULL, loadparm_s3_helpers());
+ if (lp_ctx == NULL) {
+ smb_panic("Could not load smb.conf to init winbindd's imessaging context.\n");
+ }
+
+ /*
+ * Note we MUST use the NULL context here, not the autofree context,
+ * to avoid side effects in forked children exiting.
+ */
+ msg = imessaging_init(NULL, lp_ctx, myself, global_event_context());
+ talloc_unlink(NULL, lp_ctx);
+
+ if (msg == NULL) {
+ smb_panic("Could not init winbindd's messaging context.\n");
+ }
+ return msg;
+}
+
+struct wb_irpc_forward_state {
+ struct irpc_message *msg;
+ const char *opname;
+ struct dcesrv_call_state *dce_call;
+};
+
+/*
+ called when the forwarded rpc request is finished
+ */
+static void wb_irpc_forward_callback(struct tevent_req *subreq)
+{
+ struct wb_irpc_forward_state *st =
+ tevent_req_callback_data(subreq,
+ struct wb_irpc_forward_state);
+ const char *opname = st->opname;
+ NTSTATUS status;
+
+ status = dcerpc_binding_handle_call_recv(subreq);
+ TALLOC_FREE(subreq);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0,("RPC callback failed for %s - %s\n",
+ opname, nt_errstr(status)));
+ irpc_send_reply(st->msg, status);
+ return;
+ }
+
+ irpc_send_reply(st->msg, status);
+}
+
+
+
+/**
+ * Forward a RPC call using IRPC to another task
+ */
+
+static NTSTATUS wb_irpc_forward_rpc_call(struct irpc_message *msg, TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ void *r, uint32_t callid,
+ const char *opname,
+ struct winbindd_domain *domain,
+ uint32_t timeout)
+{
+ struct wb_irpc_forward_state *st;
+ struct dcerpc_binding_handle *binding_handle;
+ struct tevent_req *subreq;
+
+ st = talloc(mem_ctx, struct wb_irpc_forward_state);
+ if (st == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ st->msg = msg;
+ st->opname = opname;
+
+ binding_handle = dom_child_handle(domain);
+ if (binding_handle == NULL) {
+ DEBUG(0,("%s: Failed to forward request to winbind handler for %s\n",
+ opname, domain->name));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ /* reset timeout for the handle */
+ dcerpc_binding_handle_set_timeout(binding_handle, timeout);
+
+ /* forward the call */
+ subreq = dcerpc_binding_handle_call_send(st, ev,
+ binding_handle,
+ NULL, &ndr_table_winbind,
+ callid,
+ msg, r);
+ if (subreq == NULL) {
+ DEBUG(0,("%s: Failed to forward request to winbind handler for %s\n",
+ opname, domain->name));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ /* mark the request as replied async */
+ msg->defer_reply = true;
+
+ /* setup the callback */
+ tevent_req_set_callback(subreq, wb_irpc_forward_callback, st);
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS wb_irpc_DsrUpdateReadOnlyServerDnsRecords(struct irpc_message *msg,
+ struct winbind_DsrUpdateReadOnlyServerDnsRecords *req)
+{
+ struct winbindd_domain *domain = find_our_domain();
+ if (domain == NULL) {
+ return NT_STATUS_NO_SUCH_DOMAIN;
+ }
+
+ DEBUG(5, ("wb_irpc_DsrUpdateReadOnlyServerDnsRecords called\n"));
+
+ return wb_irpc_forward_rpc_call(msg, msg,
+ global_event_context(),
+ req, NDR_WINBIND_DSRUPDATEREADONLYSERVERDNSRECORDS,
+ "winbind_DsrUpdateReadOnlyServerDnsRecords",
+ domain, IRPC_CALL_TIMEOUT);
+}
+
+static NTSTATUS wb_irpc_SamLogon(struct irpc_message *msg,
+ struct winbind_SamLogon *req)
+{
+ struct winbindd_domain *domain;
+ struct netr_IdentityInfo *identity_info;
+ const char *target_domain_name = NULL;
+ const char *account_name = NULL;
+
+ /*
+ * Make sure we start with authoritative=true,
+ * it will only set to false if we don't know the
+ * domain.
+ */
+ req->out.authoritative = true;
+
+ switch (req->in.logon_level) {
+ case NetlogonInteractiveInformation:
+ case NetlogonServiceInformation:
+ case NetlogonInteractiveTransitiveInformation:
+ case NetlogonServiceTransitiveInformation:
+ if (req->in.logon.password == NULL) {
+ return NT_STATUS_REQUEST_NOT_ACCEPTED;
+ }
+ identity_info = &req->in.logon.password->identity_info;
+ break;
+
+ case NetlogonNetworkInformation:
+ case NetlogonNetworkTransitiveInformation:
+ if (req->in.logon.network == NULL) {
+ return NT_STATUS_REQUEST_NOT_ACCEPTED;
+ }
+
+ identity_info = &req->in.logon.network->identity_info;
+ break;
+
+ case NetlogonGenericInformation:
+ if (req->in.logon.generic == NULL) {
+ return NT_STATUS_REQUEST_NOT_ACCEPTED;
+ }
+
+ identity_info = &req->in.logon.generic->identity_info;
+ break;
+
+ default:
+ return NT_STATUS_REQUEST_NOT_ACCEPTED;
+ }
+
+ target_domain_name = identity_info->domain_name.string;
+ if (target_domain_name == NULL) {
+ target_domain_name = "";
+ }
+
+ account_name = identity_info->account_name.string;
+ if (account_name == NULL) {
+ account_name = "";
+ }
+
+ if (IS_DC && target_domain_name[0] == '\0') {
+ const char *p = NULL;
+
+ p = strchr_m(account_name, '@');
+ if (p != NULL) {
+ target_domain_name = p + 1;
+ }
+ }
+
+ if (IS_DC && target_domain_name[0] == '\0') {
+ DBG_ERR("target_domain[%s] account[%s]\n",
+ target_domain_name, account_name);
+ return NT_STATUS_REQUEST_NOT_ACCEPTED;
+ }
+
+ domain = find_auth_domain(0, target_domain_name);
+ if (domain == NULL) {
+ DBG_INFO("target_domain[%s] for account[%s] not known\n",
+ target_domain_name, account_name);
+ req->out.result = NT_STATUS_NO_SUCH_USER;
+ req->out.authoritative = 0;
+ return NT_STATUS_OK;
+ }
+
+ DEBUG(5, ("wb_irpc_SamLogon called\n"));
+
+ return wb_irpc_forward_rpc_call(msg, msg,
+ global_event_context(),
+ req, NDR_WINBIND_SAMLOGON,
+ "winbind_SamLogon",
+ domain, IRPC_CALL_TIMEOUT);
+}
+
+static NTSTATUS wb_irpc_LogonControl(struct irpc_message *msg,
+ struct winbind_LogonControl *req)
+{
+ TALLOC_CTX *frame = talloc_stackframe();
+ char *domain_name = NULL;
+ struct winbindd_domain *domain = NULL;
+
+ DEBUG(5, ("wb_irpc_LogonControl called\n"));
+
+ switch (req->in.function_code) {
+ case NETLOGON_CONTROL_REDISCOVER:
+ case NETLOGON_CONTROL_TC_QUERY:
+ case NETLOGON_CONTROL_CHANGE_PASSWORD:
+ case NETLOGON_CONTROL_TC_VERIFY:
+ if (req->in.data->domain == NULL) {
+ TALLOC_FREE(frame);
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ domain_name = talloc_strdup(frame, req->in.data->domain);
+ if (domain_name == NULL) {
+ req->out.result = WERR_NOT_ENOUGH_MEMORY;
+ TALLOC_FREE(frame);
+ return NT_STATUS_OK;
+ }
+
+ break;
+ default:
+ TALLOC_FREE(frame);
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+
+ if (req->in.function_code == NETLOGON_CONTROL_REDISCOVER) {
+ char *p = NULL;
+
+ /*
+ * NETLOGON_CONTROL_REDISCOVER
+ * gets an optional \dcname appended to the domain name
+ */
+ p = strchr_m(domain_name, '\\');
+ if (p != NULL) {
+ *p = '\0';
+ }
+ }
+
+ domain = find_domain_from_name_noinit(domain_name);
+ if (domain == NULL) {
+ req->out.result = WERR_NO_SUCH_DOMAIN;
+ TALLOC_FREE(frame);
+ return NT_STATUS_OK;
+ }
+
+ TALLOC_FREE(frame);
+ return wb_irpc_forward_rpc_call(msg, msg,
+ global_event_context(),
+ req, NDR_WINBIND_LOGONCONTROL,
+ "winbind_LogonControl",
+ domain, 45 /* timeout */);
+}
+
+static NTSTATUS wb_irpc_GetForestTrustInformation(struct irpc_message *msg,
+ struct winbind_GetForestTrustInformation *req)
+{
+ struct winbindd_domain *domain = NULL;
+
+ if (req->in.trusted_domain_name == NULL) {
+ req->out.result = WERR_NO_SUCH_DOMAIN;
+ return NT_STATUS_OK;
+ }
+
+ domain = find_trust_from_name_noinit(req->in.trusted_domain_name);
+ if (domain == NULL) {
+ req->out.result = WERR_NO_SUCH_DOMAIN;
+ return NT_STATUS_OK;
+ }
+
+ /*
+ * checking for domain->internal and domain->primary
+ * makes sure we only do some work when running as DC.
+ */
+
+ if (domain->internal) {
+ req->out.result = WERR_NO_SUCH_DOMAIN;
+ return NT_STATUS_OK;
+ }
+
+ if (domain->primary) {
+ req->out.result = WERR_NO_SUCH_DOMAIN;
+ return NT_STATUS_OK;
+ }
+
+ DEBUG(5, ("wb_irpc_GetForestTrustInformation called\n"));
+
+ return wb_irpc_forward_rpc_call(msg, msg,
+ global_event_context(),
+ req, NDR_WINBIND_GETFORESTTRUSTINFORMATION,
+ "winbind_GetForestTrustInformation",
+ domain, 45 /* timeout */);
+}
+
+static NTSTATUS wb_irpc_SendToSam(struct irpc_message *msg,
+ struct winbind_SendToSam *req)
+{
+ /* TODO make sure that it is RWDC */
+ struct winbindd_domain *domain = find_our_domain();
+ if (domain == NULL) {
+ return NT_STATUS_NO_SUCH_DOMAIN;
+ }
+
+ DEBUG(5, ("wb_irpc_SendToSam called\n"));
+
+ return wb_irpc_forward_rpc_call(msg, msg,
+ global_event_context(),
+ req, NDR_WINBIND_SENDTOSAM,
+ "winbind_SendToSam",
+ domain, IRPC_CALL_TIMEOUT);
+}
+
+struct wb_irpc_lsa_LookupSids3_state {
+ struct irpc_message *msg;
+ struct lsa_LookupSids3 *req;
+};
+
+static void wb_irpc_lsa_LookupSids3_done(struct tevent_req *subreq);
+
+static NTSTATUS wb_irpc_lsa_LookupSids3_call(struct irpc_message *msg,
+ struct lsa_LookupSids3 *req)
+{
+ struct wb_irpc_lsa_LookupSids3_state *state = NULL;
+ struct tevent_req *subreq = NULL;
+ struct dom_sid *sids = NULL;
+ uint32_t i;
+
+ state = talloc_zero(msg, struct wb_irpc_lsa_LookupSids3_state);
+ if (state == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ state->msg = msg;
+ state->req = req;
+
+ state->req->out.domains = talloc_zero(state->msg,
+ struct lsa_RefDomainList *);
+ if (state->req->out.domains == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ state->req->out.names = talloc_zero(state->msg,
+ struct lsa_TransNameArray2);
+ if (state->req->out.names == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ state->req->out.count = talloc_zero(state->msg, uint32_t);
+ if (state->req->out.count == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ state->req->out.names->names = talloc_zero_array(state->msg,
+ struct lsa_TranslatedName2,
+ req->in.sids->num_sids);
+ if (state->req->out.names->names == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ sids = talloc_zero_array(state, struct dom_sid,
+ req->in.sids->num_sids);
+ if (sids == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ for (i = 0; i < req->in.sids->num_sids; i++) {
+ if (req->in.sids->sids[i].sid == NULL) {
+ return NT_STATUS_REQUEST_NOT_ACCEPTED;
+ }
+
+ sids[i] = *req->in.sids->sids[i].sid;
+ }
+
+ subreq = wb_lookupsids_send(msg,
+ global_event_context(),
+ sids, req->in.sids->num_sids);
+ if (subreq == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ tevent_req_set_callback(subreq, wb_irpc_lsa_LookupSids3_done, state);
+ msg->defer_reply = true;
+
+ return NT_STATUS_OK;
+}
+
+static void wb_irpc_lsa_LookupSids3_done(struct tevent_req *subreq)
+{
+ struct wb_irpc_lsa_LookupSids3_state *state =
+ tevent_req_callback_data(subreq,
+ struct wb_irpc_lsa_LookupSids3_state);
+ struct lsa_RefDomainList *domains = NULL;
+ struct lsa_TransNameArray *names = NULL;
+ NTSTATUS status;
+ uint32_t i;
+
+ status = wb_lookupsids_recv(subreq, state->msg,
+ &domains, &names);
+ TALLOC_FREE(subreq);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0,("RPC callback failed for %s - %s\n",
+ __func__, nt_errstr(status)));
+ irpc_send_reply(state->msg, status);
+ return;
+ }
+
+ if (names->count > state->req->in.sids->num_sids) {
+ status = NT_STATUS_INTERNAL_ERROR;
+ DEBUG(0,("RPC callback failed for %s - %s\n",
+ __func__, nt_errstr(status)));
+ irpc_send_reply(state->msg, status);
+ return;
+ }
+
+ *state->req->out.domains = domains;
+ for (i = 0; i < names->count; i++) {
+ struct lsa_TranslatedName2 *n2 =
+ &state->req->out.names->names[i];
+
+ n2->sid_type = names->names[i].sid_type;
+ n2->name = names->names[i].name;
+ n2->sid_index = names->names[i].sid_index;
+ n2->unknown = 0;
+
+ if (n2->sid_type != SID_NAME_UNKNOWN) {
+ (*state->req->out.count)++;
+ }
+ }
+ state->req->out.names->count = names->count;
+
+ if (*state->req->out.count == 0) {
+ state->req->out.result = NT_STATUS_NONE_MAPPED;
+ } else if (*state->req->out.count != names->count) {
+ state->req->out.result = NT_STATUS_SOME_NOT_MAPPED;
+ } else {
+ state->req->out.result = NT_STATUS_OK;
+ }
+
+ irpc_send_reply(state->msg, NT_STATUS_OK);
+ return;
+}
+
+struct wb_irpc_lsa_LookupNames4_name {
+ void *state;
+ uint32_t idx;
+ const char *namespace;
+ const char *domain;
+ char *name;
+ struct dom_sid sid;
+ enum lsa_SidType type;
+ struct dom_sid *authority_sid;
+};
+
+struct wb_irpc_lsa_LookupNames4_state {
+ struct irpc_message *msg;
+ struct lsa_LookupNames4 *req;
+ struct wb_irpc_lsa_LookupNames4_name *names;
+ uint32_t num_pending;
+ uint32_t num_domain_sids;
+ struct dom_sid *domain_sids;
+};
+
+static void wb_irpc_lsa_LookupNames4_done(struct tevent_req *subreq);
+
+static NTSTATUS wb_irpc_lsa_LookupNames4_call(struct irpc_message *msg,
+ struct lsa_LookupNames4 *req)
+{
+ struct wb_irpc_lsa_LookupNames4_state *state = NULL;
+ struct tevent_req *subreq = NULL;
+ uint32_t i;
+
+
+ state = talloc_zero(msg, struct wb_irpc_lsa_LookupNames4_state);
+ if (state == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ state->msg = msg;
+ state->req = req;
+
+ state->req->out.domains = talloc_zero(state->msg,
+ struct lsa_RefDomainList *);
+ if (state->req->out.domains == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ state->req->out.sids = talloc_zero(state->msg,
+ struct lsa_TransSidArray3);
+ if (state->req->out.sids == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ state->req->out.count = talloc_zero(state->msg, uint32_t);
+ if (state->req->out.count == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ state->req->out.sids->sids = talloc_zero_array(state->msg,
+ struct lsa_TranslatedSid3,
+ req->in.num_names);
+ if (state->req->out.sids->sids == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ state->names = talloc_zero_array(state,
+ struct wb_irpc_lsa_LookupNames4_name,
+ req->in.num_names);
+ if (state->names == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ for (i = 0; i < req->in.num_names; i++) {
+ struct wb_irpc_lsa_LookupNames4_name *nstate =
+ &state->names[i];
+ char *p = NULL;
+
+ if (req->in.names[i].string == NULL) {
+ DBG_ERR("%s: name[%s] NT_STATUS_REQUEST_NOT_ACCEPTED.\n",
+ __location__, req->in.names[i].string);
+ return NT_STATUS_REQUEST_NOT_ACCEPTED;
+ }
+ nstate->state = state;
+ nstate->idx = i;
+ nstate->name = talloc_strdup(state->names,
+ req->in.names[i].string);
+ if (nstate->name == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ nstate->type = SID_NAME_UNKNOWN;
+
+ /* cope with the name being a fully qualified name */
+ p = strchr(nstate->name, '\\');
+ if (p != NULL) {
+ *p = 0;
+ nstate->domain = nstate->name;
+ nstate->namespace = nstate->domain;
+ nstate->name = p+1;
+ } else if ((p = strchr(nstate->name, '@')) != NULL) {
+ /* upn */
+ nstate->domain = "";
+ nstate->namespace = p + 1;
+ } else {
+ /*
+ * TODO: select the domain based on
+ * req->in.level and req->in.client_revision
+ *
+ * For now we don't allow this.
+ */
+ DBG_ERR("%s: name[%s] NT_STATUS_REQUEST_NOT_ACCEPTED.\n",
+ __location__, nstate->name);
+ return NT_STATUS_REQUEST_NOT_ACCEPTED;
+ }
+
+ subreq = wb_lookupname_send(msg,
+ global_event_context(),
+ nstate->namespace,
+ nstate->domain,
+ nstate->name,
+ LOOKUP_NAME_NO_NSS);
+ if (subreq == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ tevent_req_set_callback(subreq,
+ wb_irpc_lsa_LookupNames4_done,
+ nstate);
+ state->num_pending++;
+ }
+
+ msg->defer_reply = true;
+
+ return NT_STATUS_OK;
+}
+
+static void wb_irpc_lsa_LookupNames4_domains_done(struct tevent_req *subreq);
+
+static void wb_irpc_lsa_LookupNames4_done(struct tevent_req *subreq)
+{
+ struct wb_irpc_lsa_LookupNames4_name *nstate =
+ (struct wb_irpc_lsa_LookupNames4_name *)
+ tevent_req_callback_data_void(subreq);
+ struct wb_irpc_lsa_LookupNames4_state *state =
+ talloc_get_type_abort(nstate->state,
+ struct wb_irpc_lsa_LookupNames4_state);
+ struct dom_sid_buf buf;
+ NTSTATUS status;
+
+ SMB_ASSERT(state->num_pending > 0);
+ state->num_pending--;
+ status = wb_lookupname_recv(subreq, &nstate->sid, &nstate->type);
+ TALLOC_FREE(subreq);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0,("RPC callback failed for %s - %s\n",
+ __func__, nt_errstr(status)));
+ irpc_send_reply(state->msg, status);
+ return;
+ }
+
+ status = dom_sid_split_rid(state, &nstate->sid,
+ &nstate->authority_sid, NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ DBG_ERR("dom_sid_split_rid(%s) failed - %s\n",
+ dom_sid_str_buf(&nstate->sid, &buf),
+ nt_errstr(status));
+ irpc_send_reply(state->msg, status);
+ return;
+ }
+
+ status = add_sid_to_array_unique(state,
+ nstate->authority_sid,
+ &state->domain_sids,
+ &state->num_domain_sids);
+ if (!NT_STATUS_IS_OK(status)) {
+ DBG_ERR("add_sid_to_array_unique(%s) failed - %s\n",
+ dom_sid_str_buf(nstate->authority_sid, &buf),
+ nt_errstr(status));
+ irpc_send_reply(state->msg, status);
+ return;
+ }
+
+ if (state->num_pending > 0) {
+ /*
+ * wait for more...
+ */
+ return;
+ }
+
+ /*
+ * Now resolve all domains back to a name
+ * to get a good lsa_RefDomainList
+ */
+ subreq = wb_lookupsids_send(state,
+ global_event_context(),
+ state->domain_sids,
+ state->num_domain_sids);
+ if (subreq == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ DBG_ERR("wb_lookupsids_send - %s\n",
+ nt_errstr(status));
+ irpc_send_reply(state->msg, status);
+ return;
+ }
+ tevent_req_set_callback(subreq,
+ wb_irpc_lsa_LookupNames4_domains_done,
+ state);
+
+ return;
+}
+
+static void wb_irpc_lsa_LookupNames4_domains_done(struct tevent_req *subreq)
+{
+ struct wb_irpc_lsa_LookupNames4_state *state =
+ tevent_req_callback_data(subreq,
+ struct wb_irpc_lsa_LookupNames4_state);
+ struct lsa_RefDomainList *domains = NULL;
+ struct lsa_TransNameArray *names = NULL;
+ NTSTATUS status;
+ uint32_t i;
+
+ status = wb_lookupsids_recv(subreq, state->msg,
+ &domains, &names);
+ TALLOC_FREE(subreq);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0,("RPC callback failed for %s - %s\n",
+ __func__, nt_errstr(status)));
+ irpc_send_reply(state->msg, status);
+ return;
+ }
+
+ *state->req->out.domains = domains;
+ for (i = 0; i < state->req->in.num_names; i++) {
+ struct wb_irpc_lsa_LookupNames4_name *nstate =
+ &state->names[i];
+ struct lsa_TranslatedSid3 *s3 =
+ &state->req->out.sids->sids[i];
+ uint32_t di;
+
+ s3->sid_type = nstate->type;
+ if (s3->sid_type != SID_NAME_UNKNOWN) {
+ s3->sid = &nstate->sid;
+ } else {
+ s3->sid = NULL;
+ }
+ s3->sid_index = UINT32_MAX;
+ for (di = 0; di < domains->count; di++) {
+ bool match;
+
+ if (domains->domains[di].sid == NULL) {
+ continue;
+ }
+
+ match = dom_sid_equal(nstate->authority_sid,
+ domains->domains[di].sid);
+ if (match) {
+ s3->sid_index = di;
+ break;
+ }
+ }
+ if (s3->sid_type != SID_NAME_UNKNOWN) {
+ (*state->req->out.count)++;
+ }
+ }
+ state->req->out.sids->count = state->req->in.num_names;
+
+ if (*state->req->out.count == 0) {
+ state->req->out.result = NT_STATUS_NONE_MAPPED;
+ } else if (*state->req->out.count != state->req->in.num_names) {
+ state->req->out.result = NT_STATUS_SOME_NOT_MAPPED;
+ } else {
+ state->req->out.result = NT_STATUS_OK;
+ }
+
+ irpc_send_reply(state->msg, NT_STATUS_OK);
+ return;
+}
+
+struct wb_irpc_GetDCName_state {
+ struct irpc_message *msg;
+ struct wbint_DsGetDcName *req;
+};
+
+static void wb_irpc_GetDCName_done(struct tevent_req *subreq);
+
+static NTSTATUS wb_irpc_GetDCName(struct irpc_message *msg,
+ struct wbint_DsGetDcName *req)
+{
+
+ struct tevent_req *subreq = NULL;
+ struct wb_irpc_GetDCName_state *state = NULL;
+
+ state = talloc_zero(msg, struct wb_irpc_GetDCName_state);
+ if (state == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ state->msg = msg;
+ state->req = req;
+
+ subreq = wb_dsgetdcname_send(msg,
+ global_event_context(),
+ req->in.domain_name,
+ req->in.domain_guid,
+ req->in.site_name,
+ req->in.flags);
+ if (subreq == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ tevent_req_set_callback(subreq,
+ wb_irpc_GetDCName_done,
+ state);
+
+ msg->defer_reply = true;
+
+ return NT_STATUS_OK;
+}
+
+static void wb_irpc_GetDCName_done(struct tevent_req *subreq)
+{
+ struct wb_irpc_GetDCName_state *state = tevent_req_callback_data(
+ subreq, struct wb_irpc_GetDCName_state);
+ NTSTATUS status;
+
+ status = wb_dsgetdcname_recv(subreq, state->msg,
+ state->req->out.dc_info);
+ TALLOC_FREE(subreq);
+ if (!NT_STATUS_IS_OK(status)) {
+ DBG_INFO("RPC callback failed for %s - %s\n", "DSGETDCNAME",
+ nt_errstr(status));
+ }
+
+ state->req->out.result = status;
+
+ irpc_send_reply(state->msg, NT_STATUS_OK);
+}
+
+NTSTATUS wb_irpc_register(void)
+{
+ NTSTATUS status;
+
+ status = IRPC_REGISTER(winbind_imessaging_context(), winbind, WINBIND_DSRUPDATEREADONLYSERVERDNSRECORDS,
+ wb_irpc_DsrUpdateReadOnlyServerDnsRecords, NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ status = IRPC_REGISTER(winbind_imessaging_context(), winbind, WINBIND_SAMLOGON,
+ wb_irpc_SamLogon, NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ status = IRPC_REGISTER(winbind_imessaging_context(), winbind,
+ WINBIND_LOGONCONTROL,
+ wb_irpc_LogonControl, NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ status = IRPC_REGISTER(winbind_imessaging_context(), winbind,
+ WINBIND_GETFORESTTRUSTINFORMATION,
+ wb_irpc_GetForestTrustInformation, NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ status = IRPC_REGISTER(winbind_imessaging_context(), winbind, WINBIND_SENDTOSAM,
+ wb_irpc_SendToSam, NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ status = IRPC_REGISTER(winbind_imessaging_context(),
+ lsarpc, LSA_LOOKUPSIDS3,
+ wb_irpc_lsa_LookupSids3_call, NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ status = IRPC_REGISTER(winbind_imessaging_context(),
+ lsarpc, LSA_LOOKUPNAMES4,
+ wb_irpc_lsa_LookupNames4_call, NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ status = IRPC_REGISTER(winbind_imessaging_context(),
+ winbind, WBINT_DSGETDCNAME,
+ wb_irpc_GetDCName, NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ return NT_STATUS_OK;
+}
diff --git a/source3/winbindd/winbindd_list_groups.c b/source3/winbindd/winbindd_list_groups.c
new file mode 100644
index 0000000..272c638
--- /dev/null
+++ b/source3/winbindd/winbindd_list_groups.c
@@ -0,0 +1,233 @@
+/*
+ Unix SMB/CIFS implementation.
+ async implementation of WINBINDD_LIST_GROUPS
+ Copyright (C) Volker Lendecke 2009
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "util/debug.h"
+#include "winbindd.h"
+#include "librpc/gen_ndr/ndr_winbind_c.h"
+
+struct winbindd_list_groups_domstate {
+ struct tevent_req *subreq;
+ struct winbindd_domain *domain;
+ struct wbint_Principals groups;
+};
+
+struct winbindd_list_groups_state {
+ uint32_t num_received;
+ /* All domains */
+ uint32_t num_domains;
+ struct winbindd_list_groups_domstate *domains;
+};
+
+static void winbindd_list_groups_done(struct tevent_req *subreq);
+
+struct tevent_req *winbindd_list_groups_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct winbindd_cli_state *cli,
+ struct winbindd_request *request)
+{
+ struct tevent_req *req;
+ struct winbindd_list_groups_state *state;
+ struct winbindd_domain *domain;
+ uint32_t i;
+
+ req = tevent_req_create(mem_ctx, &state,
+ struct winbindd_list_groups_state);
+ if (req == NULL) {
+ return NULL;
+ }
+
+ D_NOTICE("[%s (%u)] Winbind external command LIST_GROUPS start.\n"
+ "WBFLAG_FROM_NSS is %s, winbind enum groups is %d.\n",
+ cli->client_name,
+ (unsigned int)cli->pid,
+ request->wb_flags & WBFLAG_FROM_NSS ? "Set" : "Unset",
+ lp_winbind_enum_groups());
+
+ if (request->wb_flags & WBFLAG_FROM_NSS && !lp_winbind_enum_groups()) {
+ tevent_req_done(req);
+ return tevent_req_post(req, ev);
+ }
+
+ /* Ensure null termination */
+ request->domain_name[sizeof(request->domain_name)-1]='\0';
+
+ if (request->domain_name[0] != '\0') {
+ state->num_domains = 1;
+ D_DEBUG("List groups for domain %s.\n", request->domain_name);
+ } else {
+ state->num_domains = 0;
+ for (domain = domain_list(); domain; domain = domain->next) {
+ state->num_domains += 1;
+ }
+ D_DEBUG("List groups for %"PRIu32" domain(s).\n", state->num_domains);
+ }
+
+ state->domains = talloc_array(state,
+ struct winbindd_list_groups_domstate,
+ state->num_domains);
+ if (tevent_req_nomem(state->domains, req)) {
+ return tevent_req_post(req, ev);
+ }
+
+ if (request->domain_name[0] != '\0') {
+ ZERO_STRUCT(state->domains[0].groups);
+
+ state->domains[0].domain = find_domain_from_name_noinit(
+ request->domain_name);
+ if (state->domains[0].domain == NULL) {
+ tevent_req_nterror(req, NT_STATUS_NO_SUCH_DOMAIN);
+ return tevent_req_post(req, ev);
+ }
+ } else {
+ i = 0;
+ for (domain = domain_list(); domain; domain = domain->next) {
+ ZERO_STRUCT(state->domains[i].groups);
+
+ state->domains[i].domain = domain;
+ i++;
+ }
+ }
+
+ for (i=0; i<state->num_domains; i++) {
+ struct winbindd_list_groups_domstate *d = &state->domains[i];
+
+ d->subreq = dcerpc_wbint_QueryGroupList_send(
+ state->domains, ev, dom_child_handle(d->domain),
+ &d->groups);
+ if (tevent_req_nomem(d->subreq, req)) {
+ TALLOC_FREE(state->domains);
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(d->subreq, winbindd_list_groups_done,
+ req);
+ }
+ state->num_received = 0;
+ return req;
+}
+
+static void winbindd_list_groups_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct winbindd_list_groups_state *state = tevent_req_data(
+ req, struct winbindd_list_groups_state);
+ NTSTATUS status, result;
+ uint32_t i;
+
+ status = dcerpc_wbint_QueryGroupList_recv(subreq, state->domains,
+ &result);
+
+ for (i=0; i<state->num_domains; i++) {
+ if (subreq == state->domains[i].subreq) {
+ break;
+ }
+ }
+ if (i < state->num_domains) {
+ struct winbindd_list_groups_domstate *d = &state->domains[i];
+
+ D_DEBUG("Domain %s returned %"PRIu32" groups\n", d->domain->name,
+ d->groups.num_principals);
+
+ d->subreq = NULL;
+
+ if (!NT_STATUS_IS_OK(status) || !NT_STATUS_IS_OK(result)) {
+ D_WARNING("list_groups for domain %s failed\n",
+ d->domain->name);
+ d->groups.num_principals = 0;
+ }
+ }
+
+ TALLOC_FREE(subreq);
+
+ state->num_received += 1;
+
+ if (state->num_received >= state->num_domains) {
+ tevent_req_done(req);
+ }
+}
+
+NTSTATUS winbindd_list_groups_recv(struct tevent_req *req,
+ struct winbindd_response *response)
+{
+ struct winbindd_list_groups_state *state = tevent_req_data(
+ req, struct winbindd_list_groups_state);
+ NTSTATUS status;
+ char *result;
+ uint32_t i, j, num_entries = 0;
+ size_t len;
+
+ D_NOTICE("Winbind external command LIST_GROUPS end.\n");
+ if (tevent_req_is_nterror(req, &status)) {
+ D_WARNING("Failed with %s.\n", nt_errstr(status));
+ return status;
+ }
+
+ len = 0;
+ response->data.num_entries = 0;
+ for (i=0; i<state->num_domains; i++) {
+ struct winbindd_list_groups_domstate *d = &state->domains[i];
+
+ for (j=0; j<d->groups.num_principals; j++) {
+ const char *name;
+ name = fill_domain_username_talloc(response, d->domain->name,
+ d->groups.principals[j].name,
+ True);
+ if (name == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ len += strlen(name)+1;
+ }
+ response->data.num_entries += d->groups.num_principals;
+ }
+
+ result = talloc_array(response, char, len+1);
+ if (result == 0) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ len = 0;
+ for (i=0; i<state->num_domains; i++) {
+ struct winbindd_list_groups_domstate *d = &state->domains[i];
+
+ for (j=0; j<d->groups.num_principals; j++) {
+ const char *name;
+ size_t this_len;
+ name = fill_domain_username_talloc(response, d->domain->name,
+ d->groups.principals[j].name,
+ True);
+ if (name == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ this_len = strlen(name);
+ memcpy(result+len, name, this_len);
+ len += this_len;
+ result[len] = ',';
+ len += 1;
+ num_entries++;
+ }
+ }
+ result[len-1] = '\0';
+
+ response->data.num_entries = num_entries;
+ response->extra_data.data = result;
+ response->length += len;
+
+ return NT_STATUS_OK;
+}
diff --git a/source3/winbindd/winbindd_list_users.c b/source3/winbindd/winbindd_list_users.c
new file mode 100644
index 0000000..8630672
--- /dev/null
+++ b/source3/winbindd/winbindd_list_users.c
@@ -0,0 +1,216 @@
+/*
+ Unix SMB/CIFS implementation.
+ async implementation of WINBINDD_LIST_USERS
+ Copyright (C) Volker Lendecke 2009
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "winbindd.h"
+#include "librpc/gen_ndr/ndr_winbind_c.h"
+#include "lib/util/strv.h"
+
+struct winbindd_list_users_domstate {
+ struct tevent_req *subreq;
+ struct winbindd_domain *domain;
+ char *users;
+};
+
+struct winbindd_list_users_state {
+ size_t num_received;
+ /* All domains */
+ size_t num_domains;
+ struct winbindd_list_users_domstate *domains;
+};
+
+static void winbindd_list_users_done(struct tevent_req *subreq);
+
+struct tevent_req *winbindd_list_users_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct winbindd_cli_state *cli,
+ struct winbindd_request *request)
+{
+ struct tevent_req *req;
+ struct winbindd_list_users_state *state;
+ struct winbindd_domain *domain;
+ size_t i;
+
+ req = tevent_req_create(mem_ctx, &state,
+ struct winbindd_list_users_state);
+ if (req == NULL) {
+ return NULL;
+ }
+ D_NOTICE("[%s (%u)] Winbind external command LIST_USERS start.\n"
+ "WBFLAG_FROM_NSS is %s, winbind enum users is %d.\n",
+ cli->client_name,
+ (unsigned int)cli->pid,
+ request->wb_flags & WBFLAG_FROM_NSS ? "Set" : "Unset",
+ lp_winbind_enum_users());
+
+ if (request->wb_flags & WBFLAG_FROM_NSS && !lp_winbind_enum_users()) {
+ tevent_req_done(req);
+ return tevent_req_post(req, ev);
+ }
+
+ /* Ensure null termination */
+ request->domain_name[sizeof(request->domain_name)-1]='\0';
+
+ D_NOTICE("Listing users for domain %s\n", request->domain_name);
+ if (request->domain_name[0] != '\0') {
+ state->num_domains = 1;
+ } else {
+ state->num_domains = 0;
+ for (domain = domain_list(); domain; domain = domain->next) {
+ state->num_domains += 1;
+ }
+ }
+
+ state->domains = talloc_array(state,
+ struct winbindd_list_users_domstate,
+ state->num_domains);
+ if (tevent_req_nomem(state->domains, req)) {
+ return tevent_req_post(req, ev);
+ }
+
+ if (request->domain_name[0] != '\0') {
+ state->domains[0].domain = find_domain_from_name_noinit(
+ request->domain_name);
+ if (state->domains[0].domain == NULL) {
+ tevent_req_nterror(req, NT_STATUS_NO_SUCH_DOMAIN);
+ return tevent_req_post(req, ev);
+ }
+ } else {
+ i = 0;
+ for (domain = domain_list(); domain; domain = domain->next) {
+ state->domains[i++].domain = domain;
+ }
+ }
+
+ for (i=0; i<state->num_domains; i++) {
+ struct winbindd_list_users_domstate *d = &state->domains[i];
+ /*
+ * Use "state" as a talloc memory context since it has type
+ * "struct tevent_req". This is needed to make tevent call depth
+ * tracking working as expected.
+ * After calling wb_query_user_list_send(), re-parent back to
+ * "state->domains" to make TALLOC_FREE(state->domains) working.
+ */
+ d->subreq = wb_query_user_list_send(state, ev, d->domain);
+ d->subreq = talloc_reparent(state, state->domains, d->subreq);
+ if (tevent_req_nomem(d->subreq, req)) {
+ TALLOC_FREE(state->domains);
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(d->subreq, winbindd_list_users_done,
+ req);
+ }
+ state->num_received = 0;
+ return req;
+}
+
+static void winbindd_list_users_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct winbindd_list_users_state *state = tevent_req_data(
+ req, struct winbindd_list_users_state);
+ struct winbindd_list_users_domstate *d;
+ NTSTATUS status;
+ size_t i;
+
+ for (i=0; i<state->num_domains; i++) {
+ if (subreq == state->domains[i].subreq) {
+ break;
+ }
+ }
+ if (i == state->num_domains) {
+ tevent_req_nterror(req, NT_STATUS_INTERNAL_ERROR);
+ return;
+ }
+
+ d = &state->domains[i];
+
+ status = wb_query_user_list_recv(subreq, state->domains,
+ &d->users);
+ TALLOC_FREE(subreq);
+ if (!NT_STATUS_IS_OK(status)) {
+ /*
+ * Just skip this domain
+ */
+ d->users = NULL;
+ }
+
+ state->num_received += 1;
+
+ if (state->num_received >= state->num_domains) {
+ tevent_req_done(req);
+ }
+}
+
+NTSTATUS winbindd_list_users_recv(struct tevent_req *req,
+ struct winbindd_response *response)
+{
+ struct winbindd_list_users_state *state = tevent_req_data(
+ req, struct winbindd_list_users_state);
+ NTSTATUS status;
+ char *result;
+ size_t i, len;
+
+ D_NOTICE("Winbind external command LIST_USERS end.\n");
+ if (tevent_req_is_nterror(req, &status)) {
+ D_WARNING("Failed with %s.\n", nt_errstr(status));
+ return status;
+ }
+
+ result = NULL;
+
+ for (i=0; i<state->num_domains; i++) {
+ struct winbindd_list_users_domstate *d = &state->domains[i];
+ int ret;
+
+ if (d->users == NULL) {
+ continue;
+ }
+
+ ret = strv_append(state, &result, d->users);
+ if (ret != 0) {
+ return map_nt_error_from_unix(ret);
+ }
+ }
+
+ len = talloc_get_size(result);
+
+ response->extra_data.data = talloc_steal(response, result);
+ response->length += len;
+ response->data.num_entries = 0;
+
+ if (result != NULL && len >= 1) {
+ len -= 1;
+ response->data.num_entries = 1;
+
+ for (i=0; i<len; i++) {
+ if (result[i] == '\0') {
+ result[i] = ',';
+ response->data.num_entries += 1;
+ }
+ }
+ }
+
+ D_NOTICE("Got %"PRIu32" user(s):\n%s\n",
+ response->data.num_entries,
+ result);
+
+ return NT_STATUS_OK;
+}
diff --git a/source3/winbindd/winbindd_locator.c b/source3/winbindd/winbindd_locator.c
new file mode 100644
index 0000000..c915bf2
--- /dev/null
+++ b/source3/winbindd/winbindd_locator.c
@@ -0,0 +1,56 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Winbind daemon - miscellaneous other functions
+
+ Copyright (C) Tim Potter 2000
+ Copyright (C) Andrew Bartlett 2002
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "winbindd.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_WINBIND
+
+
+static struct winbindd_child *static_locator_child = NULL;
+
+struct winbindd_child *locator_child(void)
+{
+ return static_locator_child;
+}
+
+struct dcerpc_binding_handle *locator_child_handle(void)
+{
+ return static_locator_child->binding_handle;
+}
+
+NTSTATUS init_locator_child(TALLOC_CTX *mem_ctx)
+{
+ if (static_locator_child != NULL) {
+ DBG_ERR("locator child already allocated\n");
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+
+ static_locator_child = talloc_zero(mem_ctx, struct winbindd_child);
+ if (static_locator_child == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ setup_child(NULL, static_locator_child, "log.winbindd", "locator");
+ return NT_STATUS_OK;
+}
diff --git a/source3/winbindd/winbindd_lookupname.c b/source3/winbindd/winbindd_lookupname.c
new file mode 100644
index 0000000..f7af104
--- /dev/null
+++ b/source3/winbindd/winbindd_lookupname.c
@@ -0,0 +1,130 @@
+/*
+ Unix SMB/CIFS implementation.
+ async implementation of WINBINDD_LOOKUPNAME
+ Copyright (C) Volker Lendecke 2009
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "winbindd.h"
+#include "libcli/security/dom_sid.h"
+
+struct winbindd_lookupname_state {
+ struct tevent_context *ev;
+ struct dom_sid sid;
+ enum lsa_SidType type;
+};
+
+static void winbindd_lookupname_done(struct tevent_req *subreq);
+
+struct tevent_req *winbindd_lookupname_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct winbindd_cli_state *cli,
+ struct winbindd_request *request)
+{
+ struct tevent_req *req, *subreq;
+ struct winbindd_lookupname_state *state;
+ char *p = NULL;
+ const char *domname = NULL;
+ const char *name = NULL;
+ const char *namespace = NULL;
+
+ req = tevent_req_create(mem_ctx, &state,
+ struct winbindd_lookupname_state);
+ if (req == NULL) {
+ return NULL;
+ }
+
+ D_NOTICE("[%s (%u)] Winbind external command LOOKUPNAME start.\n",
+ cli->client_name,
+ (unsigned int)cli->pid);
+
+ state->ev = ev;
+
+ /* Ensure null termination */
+ request->data.name.dom_name[
+ sizeof(request->data.name.dom_name)-1]='\0';
+ request->data.name.name[sizeof(request->data.name.name)-1]='\0';
+
+ if (strlen(request->data.name.dom_name) == 0) {
+ /* cope with the name being a fully qualified name */
+ p = strstr(request->data.name.name, lp_winbind_separator());
+ if (p != NULL) {
+ *p = '\0';
+ domname = request->data.name.name;
+ namespace = domname;
+ name = p + 1;
+ } else {
+ p = strchr(request->data.name.name, '@');
+ if (p != NULL) {
+ /* upn */
+ namespace = p + 1;
+ } else {
+ namespace = "";
+ }
+ domname = "";
+ name = request->data.name.name;
+ }
+ } else {
+ domname = request->data.name.dom_name;
+ namespace = domname;
+ name = request->data.name.name;
+ }
+
+ D_NOTICE("lookupname %s%s%s\n", domname, lp_winbind_separator(), name);
+
+ subreq = wb_lookupname_send(state, ev, namespace, domname, name, 0);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, winbindd_lookupname_done, req);
+ return req;
+}
+
+static void winbindd_lookupname_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct winbindd_lookupname_state *state = tevent_req_data(
+ req, struct winbindd_lookupname_state);
+ NTSTATUS status;
+
+ status = wb_lookupname_recv(subreq, &state->sid, &state->type);
+ TALLOC_FREE(subreq);
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+ tevent_req_done(req);
+}
+
+NTSTATUS winbindd_lookupname_recv(struct tevent_req *req,
+ struct winbindd_response *response)
+{
+ struct winbindd_lookupname_state *state = tevent_req_data(
+ req, struct winbindd_lookupname_state);
+ NTSTATUS status;
+
+ D_NOTICE("Winbind external command LOOKUPNAME end.\n");
+ if (tevent_req_is_nterror(req, &status)) {
+ struct dom_sid_buf buf;
+ D_WARNING("Could not convert SID %s, error is %s\n",
+ dom_sid_str_buf(&state->sid, &buf),
+ nt_errstr(status));
+ return status;
+ }
+ sid_to_fstring(response->data.sid.sid, &state->sid);
+ response->data.sid.type = state->type;
+ return NT_STATUS_OK;
+}
diff --git a/source3/winbindd/winbindd_lookuprids.c b/source3/winbindd/winbindd_lookuprids.c
new file mode 100644
index 0000000..fc8fa46
--- /dev/null
+++ b/source3/winbindd/winbindd_lookuprids.c
@@ -0,0 +1,200 @@
+/*
+ Unix SMB/CIFS implementation.
+ async implementation of WINBINDD_LOOKUPRIDS
+ Copyright (C) Volker Lendecke 2009
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "winbindd.h"
+#include "librpc/gen_ndr/ndr_winbind_c.h"
+#include "../libcli/security/security.h"
+#include "lib/util/smb_strtox.h"
+#include "lib/util/string_wrappers.h"
+
+struct winbindd_lookuprids_state {
+ struct tevent_context *ev;
+ struct dom_sid domain_sid;
+ const char *domain_name;
+ struct wbint_RidArray rids;
+ struct wbint_Principals names;
+};
+
+static bool parse_ridlist(TALLOC_CTX *mem_ctx, char *ridstr,
+ uint32_t **prids, uint32_t *pnum_rids);
+
+static void winbindd_lookuprids_done(struct tevent_req *subreq);
+
+struct tevent_req *winbindd_lookuprids_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct winbindd_cli_state *cli,
+ struct winbindd_request *request)
+{
+ struct tevent_req *req, *subreq;
+ struct winbindd_lookuprids_state *state;
+ struct winbindd_domain *domain;
+
+ req = tevent_req_create(mem_ctx, &state,
+ struct winbindd_lookuprids_state);
+ if (req == NULL) {
+ return NULL;
+ }
+ state->ev = ev;
+
+ /* Ensure null termination */
+ request->data.sid[sizeof(request->data.sid)-1]='\0';
+
+ DEBUG(3, ("lookuprids (%s)\n", request->data.sid));
+
+ if (!string_to_sid(&state->domain_sid, request->data.sid)) {
+ DEBUG(5, ("%s not a SID\n", request->data.sid));
+ tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
+ return tevent_req_post(req, ev);
+ }
+
+ domain = find_lookup_domain_from_sid(&state->domain_sid);
+ if (domain == NULL) {
+ struct dom_sid_buf buf;
+ DEBUG(5, ("Domain for sid %s not found\n",
+ dom_sid_str_buf(&state->domain_sid, &buf)));
+ tevent_req_nterror(req, NT_STATUS_NO_SUCH_DOMAIN);
+ return tevent_req_post(req, ev);
+ }
+
+ if (request->extra_data.data[request->extra_len-1] != '\0') {
+ DEBUG(5, ("extra_data not 0-terminated\n"));
+ tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
+ return tevent_req_post(req, ev);
+ }
+
+ if (!parse_ridlist(state, request->extra_data.data,
+ &state->rids.rids, &state->rids.num_rids)) {
+ DEBUG(5, ("parse_ridlist failed\n"));
+ tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
+ return tevent_req_post(req, ev);
+ }
+
+ subreq = dcerpc_wbint_LookupRids_send(
+ state, ev, dom_child_handle(domain), &state->domain_sid,
+ &state->rids, &state->domain_name, &state->names);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, winbindd_lookuprids_done, req);
+ return req;
+}
+
+static void winbindd_lookuprids_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct winbindd_lookuprids_state *state = tevent_req_data(
+ req, struct winbindd_lookuprids_state);
+ NTSTATUS status, result;
+
+ status = dcerpc_wbint_LookupRids_recv(subreq, state, &result);
+ TALLOC_FREE(subreq);
+ if (any_nt_status_not_ok(status, result, &status)) {
+ tevent_req_nterror(req, status);
+ return;
+ }
+ tevent_req_done(req);
+}
+
+NTSTATUS winbindd_lookuprids_recv(struct tevent_req *req,
+ struct winbindd_response *response)
+{
+ struct winbindd_lookuprids_state *state = tevent_req_data(
+ req, struct winbindd_lookuprids_state);
+ NTSTATUS status;
+ char *result;
+ uint32_t i;
+
+ if (tevent_req_is_nterror(req, &status)) {
+ DEBUG(5, ("Lookuprids failed: %s\n",nt_errstr(status)));
+ return status;
+ }
+
+ result = talloc_strdup(response, "");
+ if (result == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ for (i=0; i<state->names.num_principals; i++) {
+ struct wbint_Principal *p = &state->names.principals[i];
+
+ result = talloc_asprintf_append_buffer(
+ result, "%d %s\n", (int)p->type, p->name);
+ if (result == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ }
+
+ fstrcpy(response->data.domain_name, state->domain_name);
+ response->extra_data.data = result;
+ response->length += talloc_get_size(result);
+ return NT_STATUS_OK;
+}
+
+static bool parse_ridlist(TALLOC_CTX *mem_ctx, char *ridstr,
+ uint32_t **prids, uint32_t *pnum_rids)
+{
+ uint32_t i, num_rids;
+ uint32_t *rids;
+ char *p;
+
+ if (ridstr == NULL) {
+ return false;
+ }
+
+ p = ridstr;
+ num_rids = 0;
+
+ /* count rids */
+
+ while ((p = strchr(p, '\n')) != NULL) {
+ p += 1;
+ num_rids += 1;
+ }
+
+ if (num_rids == 0) {
+ *pnum_rids = 0;
+ *prids = NULL;
+ return true;
+ }
+
+ rids = talloc_array(mem_ctx, uint32_t, num_rids);
+ if (rids == NULL) {
+ return false;
+ }
+
+ p = ridstr;
+
+ for (i=0; i<num_rids; i++) {
+ char *q;
+ int error = 0;
+
+ rids[i] = smb_strtoul(p, &q, 10, &error, SMB_STR_STANDARD);
+ if (error != 0 || *q != '\n') {
+ DEBUG(0, ("Got invalid ridstr: %s\n", p));
+ return false;
+ }
+ p = q+1;
+ }
+
+ *pnum_rids = num_rids;
+ *prids = rids;
+ return true;
+}
diff --git a/source3/winbindd/winbindd_lookupsid.c b/source3/winbindd/winbindd_lookupsid.c
new file mode 100644
index 0000000..e20966b
--- /dev/null
+++ b/source3/winbindd/winbindd_lookupsid.c
@@ -0,0 +1,104 @@
+/*
+ Unix SMB/CIFS implementation.
+ async implementation of WINBINDD_LOOKUPSID
+ Copyright (C) Volker Lendecke 2009
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "winbindd.h"
+#include "../libcli/security/security.h"
+#include "lib/util/string_wrappers.h"
+
+struct winbindd_lookupsid_state {
+ struct dom_sid sid;
+ enum lsa_SidType type;
+ const char *domname;
+ const char *name;
+};
+
+static void winbindd_lookupsid_done(struct tevent_req *subreq);
+
+struct tevent_req *winbindd_lookupsid_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct winbindd_cli_state *cli,
+ struct winbindd_request *request)
+{
+ struct tevent_req *req, *subreq;
+ struct winbindd_lookupsid_state *state;
+
+ req = tevent_req_create(mem_ctx, &state,
+ struct winbindd_lookupsid_state);
+ if (req == NULL) {
+ return NULL;
+ }
+
+ /* Ensure null termination */
+ request->data.sid[sizeof(request->data.sid)-1]='\0';
+
+ DEBUG(3, ("lookupsid %s\n", request->data.sid));
+
+ if (!string_to_sid(&state->sid, request->data.sid)) {
+ DEBUG(5, ("%s not a SID\n", request->data.sid));
+ tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
+ return tevent_req_post(req, ev);
+ }
+
+ subreq = wb_lookupsid_send(state, ev, &state->sid);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, winbindd_lookupsid_done, req);
+ return req;
+}
+
+static void winbindd_lookupsid_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct winbindd_lookupsid_state *state = tevent_req_data(
+ req, struct winbindd_lookupsid_state);
+ NTSTATUS status;
+
+ status = wb_lookupsid_recv(subreq, state, &state->type,
+ &state->domname, &state->name);
+ TALLOC_FREE(subreq);
+ if (!NT_STATUS_IS_OK(status)) {
+ tevent_req_nterror(req, status);
+ return;
+ }
+ tevent_req_done(req);
+}
+
+NTSTATUS winbindd_lookupsid_recv(struct tevent_req *req,
+ struct winbindd_response *response)
+{
+ struct winbindd_lookupsid_state *state = tevent_req_data(
+ req, struct winbindd_lookupsid_state);
+ NTSTATUS status;
+
+ if (tevent_req_is_nterror(req, &status)) {
+ struct dom_sid_buf buf;
+ DEBUG(5, ("Could not lookup sid %s: %s\n",
+ dom_sid_str_buf(&state->sid, &buf),
+ nt_errstr(status)));
+ return status;
+ }
+
+ fstrcpy(response->data.name.dom_name, state->domname);
+ fstrcpy(response->data.name.name, state->name);
+ response->data.name.type = state->type;
+ return NT_STATUS_OK;
+}
diff --git a/source3/winbindd/winbindd_lookupsids.c b/source3/winbindd/winbindd_lookupsids.c
new file mode 100644
index 0000000..a289fd8
--- /dev/null
+++ b/source3/winbindd/winbindd_lookupsids.c
@@ -0,0 +1,144 @@
+/*
+ Unix SMB/CIFS implementation.
+ async implementation of WINBINDD_LOOKUPSIDS
+ Copyright (C) Volker Lendecke 2011
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "winbindd.h"
+#include "../libcli/security/security.h"
+
+struct winbindd_lookupsids_state {
+ struct dom_sid *sids;
+ uint32_t num_sids;
+ struct lsa_RefDomainList *domains;
+ struct lsa_TransNameArray *names;
+};
+
+static void winbindd_lookupsids_done(struct tevent_req *subreq);
+
+struct tevent_req *winbindd_lookupsids_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct winbindd_cli_state *cli,
+ struct winbindd_request *request)
+{
+ struct tevent_req *req, *subreq;
+ struct winbindd_lookupsids_state *state;
+
+ req = tevent_req_create(mem_ctx, &state,
+ struct winbindd_lookupsids_state);
+ if (req == NULL) {
+ return NULL;
+ }
+
+ DEBUG(3, ("lookupsids\n"));
+
+ if (request->extra_len == 0) {
+ tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
+ return tevent_req_post(req, ev);
+ }
+ if (request->extra_data.data[request->extra_len-1] != '\0') {
+ DEBUG(10, ("Got invalid sids list\n"));
+ tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
+ return tevent_req_post(req, ev);
+ }
+ if (!parse_sidlist(state, request->extra_data.data,
+ &state->sids, &state->num_sids)) {
+ DEBUG(10, ("parse_sidlist failed\n"));
+ tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
+ return tevent_req_post(req, ev);
+ }
+ subreq = wb_lookupsids_send(state, ev, state->sids, state->num_sids);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, winbindd_lookupsids_done, req);
+ return req;
+}
+
+static void winbindd_lookupsids_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct winbindd_lookupsids_state *state = tevent_req_data(
+ req, struct winbindd_lookupsids_state);
+ NTSTATUS status;
+
+ status = wb_lookupsids_recv(subreq, state, &state->domains,
+ &state->names);
+ TALLOC_FREE(subreq);
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+ tevent_req_done(req);
+}
+
+NTSTATUS winbindd_lookupsids_recv(struct tevent_req *req,
+ struct winbindd_response *response)
+{
+ struct winbindd_lookupsids_state *state = tevent_req_data(
+ req, struct winbindd_lookupsids_state);
+ NTSTATUS status;
+ char *result;
+ uint32_t i;
+
+ if (tevent_req_is_nterror(req, &status)) {
+ DEBUG(5, ("wb_lookupsids failed: %s\n", nt_errstr(status)));
+ return status;
+ }
+
+ result = talloc_asprintf(response, "%d\n", (int)state->domains->count);
+ if (result == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ for (i=0; i<state->domains->count; i++) {
+ struct dom_sid_buf sid_str;
+
+ result = talloc_asprintf_append_buffer(
+ result, "%s %s\n",
+ dom_sid_str_buf(state->domains->domains[i].sid,
+ &sid_str),
+ state->domains->domains[i].name.string);
+ if (result == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ }
+
+ result = talloc_asprintf_append_buffer(
+ result, "%d\n", (int)state->names->count);
+ if (result == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ for (i=0; i<state->names->count; i++) {
+ struct lsa_TranslatedName *name;
+
+ name = &state->names->names[i];
+
+ result = talloc_asprintf_append_buffer(
+ result, "%d %d %s\n",
+ (int)name->sid_index, (int)name->sid_type,
+ name->name.string);
+ if (result == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ }
+
+ response->extra_data.data = result;
+ response->length += talloc_get_size(result);
+ return NT_STATUS_OK;
+}
diff --git a/source3/winbindd/winbindd_misc.c b/source3/winbindd/winbindd_misc.c
new file mode 100644
index 0000000..3dbbc2f
--- /dev/null
+++ b/source3/winbindd/winbindd_misc.c
@@ -0,0 +1,513 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Winbind daemon - miscellaneous other functions
+
+ Copyright (C) Tim Potter 2000
+ Copyright (C) Andrew Bartlett 2002
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "winbindd.h"
+#include "libcli/security/dom_sid.h"
+#include "lib/util/string_wrappers.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_WINBIND
+
+static char *get_trust_type_string(TALLOC_CTX *mem_ctx,
+ struct winbindd_tdc_domain *tdc,
+ struct winbindd_domain *domain)
+{
+ enum netr_SchannelType secure_channel_type = SEC_CHAN_NULL;
+ char *s = NULL;
+
+ if (domain != NULL) {
+ secure_channel_type = domain->secure_channel_type;
+ }
+
+ switch (secure_channel_type) {
+ case SEC_CHAN_NULL: {
+ if (domain == NULL) {
+ DBG_ERR("Missing domain [%s]\n",
+ tdc->domain_name);
+ return NULL;
+ }
+ if (domain->routing_domain == NULL) {
+ DBG_ERR("Missing routing for domain [%s]\n",
+ tdc->domain_name);
+ return NULL;
+ }
+ s = talloc_asprintf(mem_ctx, "Routed (via %s)",
+ domain->routing_domain->name);
+ if (s == NULL) {
+ return NULL;
+ }
+ break;
+ }
+
+ case SEC_CHAN_LOCAL:
+ s = talloc_strdup(mem_ctx, "Local");
+ if (s == NULL) {
+ return NULL;
+ }
+ break;
+
+ case SEC_CHAN_WKSTA:
+ s = talloc_strdup(mem_ctx, "Workstation");
+ if (s == NULL) {
+ return NULL;
+ }
+ break;
+
+ case SEC_CHAN_BDC: {
+ int role = lp_server_role();
+
+ if (role == ROLE_DOMAIN_PDC || role == ROLE_IPA_DC) {
+ s = talloc_strdup(mem_ctx, "PDC");
+ if (s == NULL) {
+ return NULL;
+ }
+ break;
+ }
+
+ if (role == ROLE_DOMAIN_BDC) {
+ s = talloc_strdup(mem_ctx, "BDC");
+ if (s == NULL) {
+ return NULL;
+ }
+ break;
+ }
+
+ s = talloc_strdup(mem_ctx, "RWDC");
+ if (s == NULL) {
+ return NULL;
+ }
+ break;
+ }
+
+ case SEC_CHAN_RODC:
+ s = talloc_strdup(mem_ctx, "RODC");
+ if (s == NULL) {
+ return NULL;
+ }
+ break;
+
+ case SEC_CHAN_DNS_DOMAIN:
+ if (tdc->trust_attribs & LSA_TRUST_ATTRIBUTE_QUARANTINED_DOMAIN) {
+ s = talloc_strdup(mem_ctx, "External");
+ if (s == NULL) {
+ return NULL;
+ }
+ break;
+ }
+ if (tdc->trust_attribs & LSA_TRUST_ATTRIBUTE_WITHIN_FOREST) {
+ s = talloc_strdup(mem_ctx, "In Forest");
+ if (s == NULL) {
+ return NULL;
+ }
+ break;
+ }
+ if (tdc->trust_attribs & LSA_TRUST_ATTRIBUTE_TREAT_AS_EXTERNAL) {
+ s = talloc_strdup(mem_ctx, "External");
+ if (s == NULL) {
+ return NULL;
+ }
+ break;
+ }
+ if (tdc->trust_attribs & LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE) {
+ s = talloc_strdup(mem_ctx, "Forest");
+ if (s == NULL) {
+ return NULL;
+ }
+ break;
+ }
+ s = talloc_strdup(mem_ctx, "External");
+ if (s == NULL) {
+ return NULL;
+ }
+ break;
+
+ case SEC_CHAN_DOMAIN:
+ s = talloc_strdup(mem_ctx, "External");
+ if (s == NULL) {
+ return NULL;
+ }
+ break;
+
+ default:
+ DBG_ERR("Unhandled secure_channel_type %d for domain[%s]\n",
+ secure_channel_type, tdc->domain_name);
+ return NULL;
+ }
+
+ return s;
+}
+
+static bool trust_is_inbound(struct winbindd_tdc_domain *domain)
+{
+ if (domain->trust_flags & NETR_TRUST_FLAG_INBOUND) {
+ return true;
+ }
+ return false;
+}
+
+static bool trust_is_outbound(struct winbindd_tdc_domain *domain)
+{
+ if (domain->trust_flags & NETR_TRUST_FLAG_OUTBOUND) {
+ return true;
+ }
+ return false;
+}
+
+static bool trust_is_transitive(struct winbindd_tdc_domain *domain)
+{
+ bool transitive = false;
+
+ /*
+ * Beware: order matters
+ */
+
+ if (domain->trust_attribs & LSA_TRUST_ATTRIBUTE_WITHIN_FOREST) {
+ transitive = true;
+ }
+
+ if (domain->trust_attribs & LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE) {
+ transitive = true;
+ }
+
+ if (domain->trust_attribs & LSA_TRUST_ATTRIBUTE_NON_TRANSITIVE) {
+ transitive = false;
+ }
+
+ if (domain->trust_attribs & LSA_TRUST_ATTRIBUTE_QUARANTINED_DOMAIN) {
+ transitive = false;
+ }
+
+ if (domain->trust_flags & NETR_TRUST_FLAG_PRIMARY) {
+ transitive = true;
+ }
+
+ return transitive;
+}
+
+bool winbindd_list_trusted_domains(struct winbindd_cli_state *state)
+{
+ struct winbindd_tdc_domain *dom_list = NULL;
+ size_t num_domains = 0;
+ int extra_data_len = 0;
+ char *extra_data = NULL;
+ size_t i = 0;
+ bool ret = false;
+
+ DBG_NOTICE("[%s (%u)]: list trusted domains\n",
+ state->client_name,
+ (unsigned int)state->pid);
+
+ if( !wcache_tdc_fetch_list( &dom_list, &num_domains )) {
+ goto done;
+ }
+
+ extra_data = talloc_strdup(state->mem_ctx, "");
+ if (extra_data == NULL) {
+ goto done;
+ }
+
+ for ( i = 0; i < num_domains; i++ ) {
+ struct winbindd_domain *domain;
+ bool is_online = true;
+ struct winbindd_tdc_domain *d = NULL;
+ char *trust_type = NULL;
+ struct dom_sid_buf buf;
+
+ d = &dom_list[i];
+ domain = find_domain_from_name_noinit(d->domain_name);
+ if (domain) {
+ is_online = domain->online;
+ }
+
+ trust_type = get_trust_type_string(talloc_tos(), d, domain);
+ if (trust_type == NULL) {
+ continue;
+ }
+
+ extra_data = talloc_asprintf_append_buffer(
+ extra_data,
+ "%s\\%s\\%s\\%s\\%s\\%s\\%s\\%s\n",
+ d->domain_name,
+ d->dns_name ? d->dns_name : "",
+ dom_sid_str_buf(&d->sid, &buf),
+ trust_type,
+ trust_is_transitive(d) ? "Yes" : "No",
+ trust_is_inbound(d) ? "Yes" : "No",
+ trust_is_outbound(d) ? "Yes" : "No",
+ is_online ? "Online" : "Offline" );
+
+ TALLOC_FREE(trust_type);
+ }
+
+ state->response->data.num_entries = num_domains;
+
+ extra_data_len = strlen(extra_data);
+ if (extra_data_len > 0) {
+
+ /* Strip the last \n */
+ extra_data[extra_data_len-1] = '\0';
+
+ state->response->extra_data.data = extra_data;
+ state->response->length += extra_data_len;
+ }
+
+ ret = true;
+done:
+ TALLOC_FREE( dom_list );
+ return ret;
+}
+
+bool winbindd_dc_info(struct winbindd_cli_state *cli)
+{
+ struct winbindd_domain *domain;
+ char *dc_name, *dc_ip;
+
+ cli->request->domain_name[sizeof(cli->request->domain_name)-1] = '\0';
+
+ DBG_NOTICE("[%s (%u)]: domain_info [%s]\n",
+ cli->client_name,
+ (unsigned int)cli->pid,
+ cli->request->domain_name);
+
+ if (cli->request->domain_name[0] != '\0') {
+ domain = find_trust_from_name_noinit(
+ cli->request->domain_name);
+ if (domain == NULL) {
+ DEBUG(10, ("Could not find domain %s\n",
+ cli->request->domain_name));
+ return false;
+ }
+ } else {
+ domain = find_our_domain();
+ }
+
+ if (!fetch_current_dc_from_gencache(
+ talloc_tos(), domain->name, &dc_name, &dc_ip)) {
+ DEBUG(10, ("fetch_current_dc_from_gencache(%s) failed\n",
+ domain->name));
+ return false;
+ }
+
+ cli->response->data.num_entries = 1;
+ cli->response->extra_data.data = talloc_asprintf(
+ cli->mem_ctx, "%s\n%s\n", dc_name, dc_ip);
+
+ TALLOC_FREE(dc_name);
+ TALLOC_FREE(dc_ip);
+
+ if (cli->response->extra_data.data == NULL) {
+ return false;
+ }
+
+ /* must add one to length to copy the 0 for string termination */
+ cli->response->length +=
+ strlen((char *)cli->response->extra_data.data) + 1;
+
+ return true;
+}
+
+bool winbindd_ping(struct winbindd_cli_state *state)
+{
+ DBG_NOTICE("[%s (%u)]: ping\n",
+ state->client_name,
+ (unsigned int)state->pid);
+ return true;
+}
+
+/* List various tidbits of information */
+
+bool winbindd_info(struct winbindd_cli_state *state)
+{
+
+ DBG_NOTICE("[%s (%u)]: request misc info\n",
+ state->client_name,
+ (unsigned int)state->pid);
+
+ state->response->data.info.winbind_separator = *lp_winbind_separator();
+ fstrcpy(state->response->data.info.samba_version, samba_version_string());
+ return true;
+}
+
+/* Tell the client the current interface version */
+
+bool winbindd_interface_version(struct winbindd_cli_state *state)
+{
+ DBG_NOTICE("[%s (%u)]: request interface version (version = %d)\n",
+ state->client_name,
+ (unsigned int)state->pid,
+ WINBIND_INTERFACE_VERSION);
+
+ state->response->data.interface_version = WINBIND_INTERFACE_VERSION;
+ return true;
+}
+
+/* What domain are we a member of? */
+
+bool winbindd_domain_name(struct winbindd_cli_state *state)
+{
+ DBG_NOTICE("[%s (%u)]: request domain name\n",
+ state->client_name,
+ (unsigned int)state->pid);
+
+ fstrcpy(state->response->data.domain_name, lp_workgroup());
+ return true;
+}
+
+/* What's my name again? */
+
+bool winbindd_netbios_name(struct winbindd_cli_state *state)
+{
+ DBG_NOTICE("[%s (%u)]: request netbios name\n",
+ state->client_name,
+ (unsigned int)state->pid);
+
+ fstrcpy(state->response->data.netbios_name, lp_netbios_name());
+ return true;
+}
+
+/* Where can I find the privileged pipe? */
+
+char *get_winbind_priv_pipe_dir(void)
+{
+ return state_path(talloc_tos(), WINBINDD_PRIV_SOCKET_SUBDIR);
+}
+
+bool winbindd_priv_pipe_dir(struct winbindd_cli_state *state)
+{
+ char *priv_dir;
+
+ DBG_NOTICE("[%s (%u)]: request location of privileged pipe\n",
+ state->client_name,
+ (unsigned int)state->pid);
+
+ priv_dir = get_winbind_priv_pipe_dir();
+ state->response->extra_data.data = talloc_move(state->mem_ctx,
+ &priv_dir);
+
+ /* must add one to length to copy the 0 for string termination */
+ state->response->length +=
+ strlen((char *)state->response->extra_data.data) + 1;
+
+ DBG_NOTICE("[%s (%u)]: response location of privileged pipe: %s\n",
+ state->client_name,
+ (unsigned int)state->pid,
+ priv_dir);
+
+ return true;
+}
+
+static void winbindd_setup_max_fds(void)
+{
+ int num_fds = MAX_OPEN_FUDGEFACTOR;
+ int actual_fds;
+
+ num_fds += lp_winbind_max_clients();
+ /* Add some more to account for 2 sockets open
+ when the client transitions from unprivileged
+ to privileged socket
+ */
+ num_fds += lp_winbind_max_clients() / 10;
+
+ /* Add one socket per child process
+ (yeah there are child processes other than the
+ domain children but only domain children can vary
+ with configuration
+ */
+ num_fds += lp_winbind_max_domain_connections() *
+ (lp_allow_trusted_domains() ? WINBIND_MAX_DOMAINS_HINT : 1);
+
+ actual_fds = set_maxfiles(num_fds);
+
+ if (actual_fds < num_fds) {
+ DEBUG(1, ("winbindd_setup_max_fds: Information only: "
+ "requested %d open files, %d are available.\n",
+ num_fds, actual_fds));
+ }
+}
+
+bool winbindd_reload_services_file(const char *lfile)
+{
+ const struct loadparm_substitution *lp_sub =
+ loadparm_s3_global_substitution();
+ bool ret;
+
+ if (lp_loaded()) {
+ char *fname = lp_next_configfile(talloc_tos(), lp_sub);
+
+ if (file_exist(fname) && !strcsequal(fname,get_dyn_CONFIGFILE())) {
+ set_dyn_CONFIGFILE(fname);
+ }
+ TALLOC_FREE(fname);
+ }
+
+ reopen_logs();
+ ret = lp_load_global(get_dyn_CONFIGFILE());
+
+ /* if this is a child, restore the logfile to the special
+ name - <domain>, idmap, etc. */
+ if (lfile && *lfile) {
+ lp_set_logfile(lfile);
+ }
+
+ reopen_logs();
+ load_interfaces();
+ winbindd_setup_max_fds();
+
+ return(ret);
+}
+
+static size_t *debug_call_depth = NULL;
+
+void winbind_debug_call_depth_setup(size_t *depth)
+{
+ debug_call_depth = depth;
+}
+
+void winbind_call_flow(void *private_data,
+ enum tevent_thread_call_depth_cmd cmd,
+ struct tevent_req *req,
+ size_t depth,
+ const char *fname)
+{
+ switch (cmd) {
+ case TEVENT_CALL_FLOW_REQ_CREATE:
+ *debug_call_depth = depth;
+ DEBUG(20, ("flow: -> %s\n", fname));
+ break;
+ case TEVENT_CALL_FLOW_REQ_NOTIFY_CB:
+ *debug_call_depth = depth;
+ DEBUG(20, ("flow: <- %s\n", fname));
+ break;
+ case TEVENT_CALL_FLOW_REQ_QUEUE_TRIGGER:
+ *debug_call_depth = depth;
+ break;
+ case TEVENT_CALL_FLOW_REQ_RESET:
+ *debug_call_depth = depth;
+ break;
+ case TEVENT_CALL_FLOW_REQ_CANCEL:
+ case TEVENT_CALL_FLOW_REQ_CLEANUP:
+ case TEVENT_CALL_FLOW_REQ_QUEUE_ENTER:
+ case TEVENT_CALL_FLOW_REQ_QUEUE_LEAVE:
+ break;
+ }
+}
diff --git a/source3/winbindd/winbindd_msrpc.c b/source3/winbindd/winbindd_msrpc.c
new file mode 100644
index 0000000..a7bd9be
--- /dev/null
+++ b/source3/winbindd/winbindd_msrpc.c
@@ -0,0 +1,1124 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Winbind rpc backend functions
+
+ Copyright (C) Tim Potter 2000-2001,2003
+ Copyright (C) Andrew Tridgell 2001
+ Copyright (C) Volker Lendecke 2005
+ Copyright (C) Guenther Deschner 2008 (pidl conversion)
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "winbindd.h"
+#include "winbindd_rpc.h"
+
+#include "../librpc/gen_ndr/ndr_samr_c.h"
+#include "rpc_client/cli_pipe.h"
+#include "rpc_client/cli_samr.h"
+#include "rpc_client/cli_lsarpc.h"
+#include "../libcli/security/security.h"
+#include "libsmb/samlogon_cache.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_WINBIND
+
+static NTSTATUS winbindd_lookup_names(TALLOC_CTX *mem_ctx,
+ struct winbindd_domain *domain,
+ uint32_t num_names,
+ const char **names,
+ const char ***domains,
+ struct dom_sid **sids,
+ enum lsa_SidType **types);
+
+/* Query display info for a domain. This returns enough information plus a
+ bit extra to give an overview of domain users for the User Manager
+ application. */
+static NTSTATUS msrpc_query_user_list(struct winbindd_domain *domain,
+ TALLOC_CTX *mem_ctx,
+ uint32_t **prids)
+{
+ struct rpc_pipe_client *samr_pipe = NULL;
+ struct policy_handle dom_pol;
+ uint32_t *rids = NULL;
+ TALLOC_CTX *tmp_ctx;
+ NTSTATUS status;
+
+ DEBUG(3, ("msrpc_query_user_list\n"));
+
+ tmp_ctx = talloc_stackframe();
+ if (tmp_ctx == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ if ( !winbindd_can_contact_domain( domain ) ) {
+ DEBUG(10,("query_user_list: No incoming trust for domain %s\n",
+ domain->name));
+ status = NT_STATUS_OK;
+ goto done;
+ }
+
+ status = cm_connect_sam(domain, tmp_ctx, false, &samr_pipe, &dom_pol);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto done;
+ }
+
+ status = rpc_query_user_list(tmp_ctx,
+ samr_pipe,
+ &dom_pol,
+ &domain->sid,
+ &rids);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto done;
+ }
+
+ if (prids) {
+ *prids = talloc_move(mem_ctx, &rids);
+ }
+
+done:
+ TALLOC_FREE(rids);
+ TALLOC_FREE(tmp_ctx);
+ return status;
+}
+
+/* list all domain groups */
+static NTSTATUS msrpc_enum_dom_groups(struct winbindd_domain *domain,
+ TALLOC_CTX *mem_ctx,
+ uint32_t *pnum_info,
+ struct wb_acct_info **pinfo)
+{
+ struct rpc_pipe_client *samr_pipe;
+ struct policy_handle dom_pol;
+ struct wb_acct_info *info = NULL;
+ uint32_t num_info = 0;
+ TALLOC_CTX *tmp_ctx;
+ NTSTATUS status;
+
+ DEBUG(3,("msrpc_enum_dom_groups\n"));
+
+ if (pnum_info) {
+ *pnum_info = 0;
+ }
+
+ tmp_ctx = talloc_stackframe();
+ if (tmp_ctx == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ if ( !winbindd_can_contact_domain( domain ) ) {
+ DEBUG(10,("enum_domain_groups: No incoming trust for domain %s\n",
+ domain->name));
+ status = NT_STATUS_OK;
+ goto done;
+ }
+
+ status = cm_connect_sam(domain, tmp_ctx, false, &samr_pipe, &dom_pol);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto done;
+ }
+
+ status = rpc_enum_dom_groups(tmp_ctx,
+ samr_pipe,
+ &dom_pol,
+ &num_info,
+ &info);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto done;
+ }
+
+ if (pnum_info) {
+ *pnum_info = num_info;
+ }
+
+ if (pinfo) {
+ *pinfo = talloc_move(mem_ctx, &info);
+ }
+
+done:
+ TALLOC_FREE(tmp_ctx);
+ return status;
+}
+
+/* List all domain groups */
+
+static NTSTATUS msrpc_enum_local_groups(struct winbindd_domain *domain,
+ TALLOC_CTX *mem_ctx,
+ uint32_t *pnum_info,
+ struct wb_acct_info **pinfo)
+{
+ struct rpc_pipe_client *samr_pipe;
+ struct policy_handle dom_pol;
+ struct wb_acct_info *info = NULL;
+ uint32_t num_info = 0;
+ TALLOC_CTX *tmp_ctx;
+ NTSTATUS status;
+
+ DEBUG(3,("msrpc_enum_local_groups\n"));
+
+ if (pnum_info) {
+ *pnum_info = 0;
+ }
+
+ tmp_ctx = talloc_stackframe();
+ if (tmp_ctx == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ if ( !winbindd_can_contact_domain( domain ) ) {
+ DEBUG(10,("enum_local_groups: No incoming trust for domain %s\n",
+ domain->name));
+ status = NT_STATUS_OK;
+ goto done;
+ }
+
+ status = cm_connect_sam(domain, tmp_ctx, false, &samr_pipe, &dom_pol);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto done;
+ }
+
+ status = rpc_enum_local_groups(mem_ctx,
+ samr_pipe,
+ &dom_pol,
+ &num_info,
+ &info);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto done;
+ }
+
+ if (pnum_info) {
+ *pnum_info = num_info;
+ }
+
+ if (pinfo) {
+ *pinfo = talloc_move(mem_ctx, &info);
+ }
+
+done:
+ TALLOC_FREE(tmp_ctx);
+ return status;
+}
+
+/* convert a single name to a sid in a domain */
+static NTSTATUS msrpc_name_to_sid(struct winbindd_domain *domain,
+ TALLOC_CTX *mem_ctx,
+ const char *domain_name,
+ const char *name,
+ uint32_t flags,
+ const char **pdom_name,
+ struct dom_sid *sid,
+ enum lsa_SidType *type)
+{
+ NTSTATUS result;
+ struct dom_sid *sids = NULL;
+ enum lsa_SidType *types = NULL;
+ char *full_name = NULL;
+ const char *names[1];
+ const char **domains;
+ NTSTATUS name_map_status = NT_STATUS_UNSUCCESSFUL;
+ char *mapped_name = NULL;
+
+ if (name == NULL || *name=='\0') {
+ full_name = talloc_asprintf(mem_ctx, "%s", domain_name);
+ } else if (domain_name == NULL || *domain_name == '\0') {
+ full_name = talloc_asprintf(mem_ctx, "%s", name);
+ } else {
+ full_name = talloc_asprintf(mem_ctx, "%s\\%s", domain_name, name);
+ }
+ if (!full_name) {
+ DEBUG(0, ("talloc_asprintf failed!\n"));
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ DEBUG(3, ("msrpc_name_to_sid: name=%s\n", full_name));
+
+ name_map_status = normalize_name_unmap(mem_ctx, full_name,
+ &mapped_name);
+
+ /* Reset the full_name pointer if we mapped anything */
+
+ if (NT_STATUS_IS_OK(name_map_status) ||
+ NT_STATUS_EQUAL(name_map_status, NT_STATUS_FILE_RENAMED))
+ {
+ full_name = mapped_name;
+ }
+
+ DEBUG(3,("name_to_sid [rpc] %s for domain %s\n",
+ full_name?full_name:"", domain_name ));
+
+ names[0] = full_name;
+
+ result = winbindd_lookup_names(mem_ctx, domain, 1,
+ names, &domains,
+ &sids, &types);
+ if (!NT_STATUS_IS_OK(result))
+ return result;
+
+ /* Return rid and type if lookup successful */
+
+ if (pdom_name != NULL) {
+ const char *dom_name;
+
+ dom_name = talloc_strdup(mem_ctx, domains[0]);
+ if (dom_name == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ *pdom_name = dom_name;
+ }
+
+ sid_copy(sid, &sids[0]);
+ *type = types[0];
+
+ return NT_STATUS_OK;
+}
+
+/*
+ convert a domain SID to a user or group name
+*/
+static NTSTATUS msrpc_sid_to_name(struct winbindd_domain *domain,
+ TALLOC_CTX *mem_ctx,
+ const struct dom_sid *sid,
+ char **domain_name,
+ char **name,
+ enum lsa_SidType *type)
+{
+ char **domains;
+ char **names;
+ enum lsa_SidType *types = NULL;
+ NTSTATUS result;
+ NTSTATUS name_map_status = NT_STATUS_UNSUCCESSFUL;
+ char *mapped_name = NULL;
+ struct dom_sid_buf buf;
+
+ DEBUG(3, ("msrpc_sid_to_name: %s for domain %s\n",
+ dom_sid_str_buf(sid, &buf),
+ domain->name));
+
+ result = winbindd_lookup_sids(mem_ctx,
+ domain,
+ 1,
+ sid,
+ &domains,
+ &names,
+ &types);
+ if (!NT_STATUS_IS_OK(result)) {
+ DEBUG(2,("msrpc_sid_to_name: failed to lookup sids: %s\n",
+ nt_errstr(result)));
+ return result;
+ }
+
+
+ *type = (enum lsa_SidType)types[0];
+ *domain_name = domains[0];
+ *name = names[0];
+
+ DEBUG(5,("Mapped sid to [%s]\\[%s]\n", domains[0], *name));
+
+ name_map_status = normalize_name_map(mem_ctx, domain->name, *name,
+ &mapped_name);
+ if (NT_STATUS_IS_OK(name_map_status) ||
+ NT_STATUS_EQUAL(name_map_status, NT_STATUS_FILE_RENAMED))
+ {
+ *name = mapped_name;
+ DEBUG(5,("returning mapped name -- %s\n", *name));
+ }
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS msrpc_rids_to_names(struct winbindd_domain *domain,
+ TALLOC_CTX *mem_ctx,
+ const struct dom_sid *sid,
+ uint32_t *rids,
+ size_t num_rids,
+ char **domain_name,
+ char ***names,
+ enum lsa_SidType **types)
+{
+ char **domains;
+ NTSTATUS result;
+ struct dom_sid *sids;
+ size_t i;
+ char **ret_names;
+
+ DEBUG(3, ("msrpc_rids_to_names: domain %s\n", domain->name ));
+
+ if (num_rids) {
+ sids = talloc_array(mem_ctx, struct dom_sid, num_rids);
+ if (sids == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ } else {
+ sids = NULL;
+ }
+
+ for (i=0; i<num_rids; i++) {
+ if (!sid_compose(&sids[i], sid, rids[i])) {
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+ }
+
+ result = winbindd_lookup_sids(mem_ctx,
+ domain,
+ num_rids,
+ sids,
+ &domains,
+ names,
+ types);
+
+ if (!NT_STATUS_IS_OK(result) &&
+ !NT_STATUS_EQUAL(result, STATUS_SOME_UNMAPPED)) {
+ return result;
+ }
+
+ ret_names = *names;
+ for (i=0; i<num_rids; i++) {
+ NTSTATUS name_map_status = NT_STATUS_UNSUCCESSFUL;
+ char *mapped_name = NULL;
+
+ if ((*types)[i] != SID_NAME_UNKNOWN) {
+ name_map_status = normalize_name_map(mem_ctx,
+ domain->name,
+ ret_names[i],
+ &mapped_name);
+ if (NT_STATUS_IS_OK(name_map_status) ||
+ NT_STATUS_EQUAL(name_map_status, NT_STATUS_FILE_RENAMED))
+ {
+ ret_names[i] = mapped_name;
+ }
+
+ *domain_name = domains[i];
+ }
+ }
+
+ return result;
+}
+
+/* Lookup groups a user is a member of. I wish Unix had a call like this! */
+static NTSTATUS msrpc_lookup_usergroups(struct winbindd_domain *domain,
+ TALLOC_CTX *mem_ctx,
+ const struct dom_sid *user_sid,
+ uint32_t *pnum_groups,
+ struct dom_sid **puser_grpsids)
+{
+ struct rpc_pipe_client *samr_pipe;
+ struct policy_handle dom_pol;
+ struct dom_sid *user_grpsids = NULL;
+ struct dom_sid_buf buf;
+ uint32_t num_groups = 0;
+ TALLOC_CTX *tmp_ctx;
+ NTSTATUS status;
+
+ DEBUG(3,("msrpc_lookup_usergroups sid=%s\n",
+ dom_sid_str_buf(user_sid, &buf)));
+
+ *pnum_groups = 0;
+
+ tmp_ctx = talloc_stackframe();
+ if (tmp_ctx == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ /* Check if we have a cached user_info_3 */
+ status = lookup_usergroups_cached(tmp_ctx,
+ user_sid,
+ &num_groups,
+ &user_grpsids);
+ if (NT_STATUS_IS_OK(status)) {
+ goto cached;
+ }
+
+ if ( !winbindd_can_contact_domain( domain ) ) {
+ DEBUG(10,("lookup_usergroups: No incoming trust for domain %s\n",
+ domain->name));
+
+ /* Tell the cache manager not to remember this one */
+ status = NT_STATUS_SYNCHRONIZATION_REQUIRED;
+ goto done;
+ }
+
+ /* no cache; hit the wire */
+ status = cm_connect_sam(domain, tmp_ctx, false, &samr_pipe, &dom_pol);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto done;
+ }
+
+ status = rpc_lookup_usergroups(tmp_ctx,
+ samr_pipe,
+ &dom_pol,
+ &domain->sid,
+ user_sid,
+ &num_groups,
+ &user_grpsids);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto done;
+ }
+
+cached:
+ *pnum_groups = num_groups;
+
+ if (puser_grpsids) {
+ *puser_grpsids = talloc_move(mem_ctx, &user_grpsids);
+ }
+
+done:
+ TALLOC_FREE(tmp_ctx);
+ return status;
+ return NT_STATUS_OK;
+}
+
+#define MAX_SAM_ENTRIES_W2K 0x400 /* 1024 */
+
+static NTSTATUS msrpc_lookup_useraliases(struct winbindd_domain *domain,
+ TALLOC_CTX *mem_ctx,
+ uint32_t num_sids, const struct dom_sid *sids,
+ uint32_t *pnum_aliases,
+ uint32_t **palias_rids)
+{
+ struct rpc_pipe_client *samr_pipe;
+ struct policy_handle dom_pol;
+ uint32_t num_aliases = 0;
+ uint32_t *alias_rids = NULL;
+ TALLOC_CTX *tmp_ctx;
+ NTSTATUS status;
+
+ DEBUG(3,("msrpc_lookup_useraliases\n"));
+
+ if (pnum_aliases) {
+ *pnum_aliases = 0;
+ }
+
+ tmp_ctx = talloc_stackframe();
+ if (tmp_ctx == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ if (!winbindd_can_contact_domain(domain)) {
+ DEBUG(10,("msrpc_lookup_useraliases: No incoming trust for domain %s\n",
+ domain->name));
+ /* Tell the cache manager not to remember this one */
+ status = NT_STATUS_SYNCHRONIZATION_REQUIRED;
+ goto done;
+ }
+
+ status = cm_connect_sam(domain, tmp_ctx, false, &samr_pipe, &dom_pol);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto done;
+ }
+
+ status = rpc_lookup_useraliases(tmp_ctx,
+ samr_pipe,
+ &dom_pol,
+ num_sids,
+ sids,
+ &num_aliases,
+ &alias_rids);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto done;
+ }
+
+ if (pnum_aliases) {
+ *pnum_aliases = num_aliases;
+ }
+
+ if (palias_rids) {
+ *palias_rids = talloc_move(mem_ctx, &alias_rids);
+ }
+
+done:
+ TALLOC_FREE(tmp_ctx);
+ return status;
+}
+
+/* lookup alias membership */
+static NTSTATUS msrpc_lookup_aliasmem(struct winbindd_domain *domain,
+ TALLOC_CTX *mem_ctx,
+ const struct dom_sid *alias_sid,
+ enum lsa_SidType type,
+ uint32_t *pnum_sids,
+ struct dom_sid **sid_mem)
+{
+ struct rpc_pipe_client *samr_pipe = NULL;
+ struct policy_handle dom_pol;
+ struct dom_sid *alias_members = NULL;
+ struct dom_sid_buf buf;
+ uint32_t num_groups = 0;
+ TALLOC_CTX *tmp_ctx = talloc_stackframe();
+ NTSTATUS status;
+
+ D_INFO("Lookup alias members in domain=%s for sid=%s.\n",
+ domain->name,
+ dom_sid_str_buf(alias_sid, &buf));
+
+ *pnum_sids = 0;
+
+ if (!winbindd_can_contact_domain(domain)) {
+ D_DEBUG("No incoming trust for domain %s\n", domain->name);
+ status = NT_STATUS_OK;
+ goto done;
+ }
+
+ status = cm_connect_sam(domain, tmp_ctx, false, &samr_pipe, &dom_pol);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto done;
+ }
+
+ status = rpc_lookup_aliasmem(tmp_ctx,
+ samr_pipe,
+ &dom_pol,
+ &domain->sid,
+ alias_sid,
+ type,
+ &num_groups,
+ &alias_members);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto done;
+ }
+
+ *pnum_sids = num_groups;
+ if (sid_mem) {
+ *sid_mem = talloc_move(mem_ctx, &alias_members);
+ }
+
+done:
+ talloc_free(tmp_ctx);
+ return status;
+}
+
+/* Lookup group membership given a rid. */
+static NTSTATUS msrpc_lookup_groupmem(struct winbindd_domain *domain,
+ TALLOC_CTX *mem_ctx,
+ const struct dom_sid *group_sid,
+ enum lsa_SidType type,
+ uint32_t *num_names,
+ struct dom_sid **sid_mem,
+ char ***names,
+ uint32_t **name_types)
+{
+ NTSTATUS status, result;
+ uint32_t i, total_names = 0;
+ struct policy_handle dom_pol, group_pol;
+ uint32_t des_access = SEC_FLAG_MAXIMUM_ALLOWED;
+ uint32_t *rid_mem = NULL;
+ uint32_t group_rid;
+ unsigned int j, r;
+ struct rpc_pipe_client *cli;
+ unsigned int orig_timeout;
+ struct samr_RidAttrArray *rids = NULL;
+ struct dcerpc_binding_handle *b;
+ struct dom_sid_buf buf;
+
+ DEBUG(3,("msrpc_lookup_groupmem: %s sid=%s\n", domain->name,
+ dom_sid_str_buf(group_sid, &buf)));
+
+ if ( !winbindd_can_contact_domain( domain ) ) {
+ DEBUG(10,("lookup_groupmem: No incoming trust for domain %s\n",
+ domain->name));
+ return NT_STATUS_OK;
+ }
+
+ if (!sid_peek_check_rid(&domain->sid, group_sid, &group_rid))
+ return NT_STATUS_UNSUCCESSFUL;
+
+ *num_names = 0;
+
+ result = cm_connect_sam(domain, mem_ctx, false, &cli, &dom_pol);
+ if (!NT_STATUS_IS_OK(result))
+ return result;
+
+ b = cli->binding_handle;
+
+ status = dcerpc_samr_OpenGroup(b, mem_ctx,
+ &dom_pol,
+ des_access,
+ group_rid,
+ &group_pol,
+ &result);
+ if (any_nt_status_not_ok(status, result, &status)) {
+ return status;
+ }
+
+ /* Step #1: Get a list of user rids that are the members of the
+ group. */
+
+ /* This call can take a long time - allow the server to time out.
+ 35 seconds should do it. */
+
+ orig_timeout = rpccli_set_timeout(cli, 35000);
+
+ status = dcerpc_samr_QueryGroupMember(b, mem_ctx,
+ &group_pol,
+ &rids,
+ &result);
+
+ /* And restore our original timeout. */
+ rpccli_set_timeout(cli, orig_timeout);
+
+ {
+ NTSTATUS _result;
+ dcerpc_samr_Close(b, mem_ctx, &group_pol, &_result);
+ }
+
+ if (any_nt_status_not_ok(status, result, &status)) {
+ return status;
+ }
+
+ if (!rids || !rids->count) {
+ names = NULL;
+ name_types = NULL;
+ sid_mem = NULL;
+ return NT_STATUS_OK;
+ }
+
+ *num_names = rids->count;
+ rid_mem = rids->rids;
+
+ /* Step #2: Convert list of rids into list of usernames. Do this
+ in bunches of ~1000 to avoid crashing NT4. It looks like there
+ is a buffer overflow or something like that lurking around
+ somewhere. */
+
+#define MAX_LOOKUP_RIDS 900
+
+ *names = talloc_zero_array(mem_ctx, char *, *num_names);
+ *name_types = talloc_zero_array(mem_ctx, uint32_t, *num_names);
+ *sid_mem = talloc_zero_array(mem_ctx, struct dom_sid, *num_names);
+
+ for (j=0;j<(*num_names);j++)
+ sid_compose(&(*sid_mem)[j], &domain->sid, rid_mem[j]);
+
+ if (*num_names>0 && (!*names || !*name_types))
+ return NT_STATUS_NO_MEMORY;
+
+ for (i = 0; i < *num_names; i += MAX_LOOKUP_RIDS) {
+ int num_lookup_rids = MIN(*num_names - i, MAX_LOOKUP_RIDS);
+ struct lsa_Strings tmp_names;
+ struct samr_Ids tmp_types;
+
+ /* Lookup a chunk of rids */
+
+ status = dcerpc_samr_LookupRids(b, mem_ctx,
+ &dom_pol,
+ num_lookup_rids,
+ &rid_mem[i],
+ &tmp_names,
+ &tmp_types,
+ &result);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ /* see if we have a real error (and yes the
+ STATUS_SOME_UNMAPPED is the one returned from 2k) */
+
+ if (!NT_STATUS_IS_OK(result) &&
+ !NT_STATUS_EQUAL(result, STATUS_SOME_UNMAPPED))
+ return result;
+
+ /* Copy result into array. The talloc system will take
+ care of freeing the temporary arrays later on. */
+
+ if (tmp_names.count != num_lookup_rids) {
+ return NT_STATUS_INVALID_NETWORK_RESPONSE;
+ }
+ if (tmp_types.count != num_lookup_rids) {
+ return NT_STATUS_INVALID_NETWORK_RESPONSE;
+ }
+
+ for (r=0; r<tmp_names.count; r++) {
+ if (tmp_types.ids[r] == SID_NAME_UNKNOWN) {
+ continue;
+ }
+ if (total_names >= *num_names) {
+ break;
+ }
+ (*names)[total_names] = fill_domain_username_talloc(
+ mem_ctx, domain->name,
+ tmp_names.names[r].string, true);
+ (*name_types)[total_names] = tmp_types.ids[r];
+ total_names += 1;
+ }
+ }
+
+ *num_names = total_names;
+
+ return NT_STATUS_OK;
+}
+
+/* get a list of trusted domains */
+static NTSTATUS msrpc_trusted_domains(struct winbindd_domain *domain,
+ TALLOC_CTX *mem_ctx,
+ struct netr_DomainTrustList *ptrust_list)
+{
+ struct rpc_pipe_client *lsa_pipe;
+ struct policy_handle lsa_policy;
+ struct netr_DomainTrust *trusts = NULL;
+ uint32_t num_trusts = 0;
+ TALLOC_CTX *tmp_ctx;
+ NTSTATUS status;
+
+ DEBUG(3,("msrpc_trusted_domains\n"));
+
+ if (ptrust_list) {
+ ZERO_STRUCTP(ptrust_list);
+ }
+
+ tmp_ctx = talloc_stackframe();
+ if (tmp_ctx == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ status = cm_connect_lsa(domain, tmp_ctx, &lsa_pipe, &lsa_policy);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto done;
+ }
+
+ status = rpc_trusted_domains(tmp_ctx,
+ lsa_pipe,
+ &lsa_policy,
+ &num_trusts,
+ &trusts);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto done;
+ }
+
+ if (ptrust_list) {
+ ptrust_list->count = num_trusts;
+ ptrust_list->array = talloc_move(mem_ctx, &trusts);
+ }
+
+done:
+ TALLOC_FREE(tmp_ctx);
+ return status;
+}
+
+/* find the lockout policy for a domain */
+static NTSTATUS msrpc_lockout_policy(struct winbindd_domain *domain,
+ TALLOC_CTX *mem_ctx,
+ struct samr_DomInfo12 *lockout_policy)
+{
+ NTSTATUS status, result;
+ struct rpc_pipe_client *cli;
+ struct policy_handle dom_pol;
+ union samr_DomainInfo *info = NULL;
+ struct dcerpc_binding_handle *b;
+
+ DEBUG(3, ("msrpc_lockout_policy: fetch lockout policy for %s\n", domain->name));
+
+ if ( !winbindd_can_contact_domain( domain ) ) {
+ DEBUG(10,("msrpc_lockout_policy: No incoming trust for domain %s\n",
+ domain->name));
+ return NT_STATUS_NOT_SUPPORTED;
+ }
+
+ status = cm_connect_sam(domain, mem_ctx, false, &cli, &dom_pol);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto done;
+ }
+
+ b = cli->binding_handle;
+
+ status = dcerpc_samr_QueryDomainInfo(b, mem_ctx,
+ &dom_pol,
+ DomainLockoutInformation,
+ &info,
+ &result);
+ if (any_nt_status_not_ok(status, result, &status)) {
+ return status;
+ }
+
+ *lockout_policy = info->info12;
+
+ DEBUG(10,("msrpc_lockout_policy: lockout_threshold %d\n",
+ info->info12.lockout_threshold));
+
+ done:
+
+ return status;
+}
+
+/* find the password policy for a domain */
+static NTSTATUS msrpc_password_policy(struct winbindd_domain *domain,
+ TALLOC_CTX *mem_ctx,
+ struct samr_DomInfo1 *password_policy)
+{
+ NTSTATUS status, result;
+ struct rpc_pipe_client *cli;
+ struct policy_handle dom_pol;
+ union samr_DomainInfo *info = NULL;
+ struct dcerpc_binding_handle *b;
+
+ DEBUG(3, ("msrpc_password_policy: fetch password policy for %s\n",
+ domain->name));
+
+ if ( !winbindd_can_contact_domain( domain ) ) {
+ DEBUG(10,("msrpc_password_policy: No incoming trust for domain %s\n",
+ domain->name));
+ return NT_STATUS_NOT_SUPPORTED;
+ }
+
+ status = cm_connect_sam(domain, mem_ctx, false, &cli, &dom_pol);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto done;
+ }
+
+ b = cli->binding_handle;
+
+ status = dcerpc_samr_QueryDomainInfo(b, mem_ctx,
+ &dom_pol,
+ DomainPasswordInformation,
+ &info,
+ &result);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto done;
+ }
+ if (!NT_STATUS_IS_OK(result)) {
+ goto done;
+ }
+
+ *password_policy = info->info1;
+
+ DEBUG(10,("msrpc_password_policy: min_length_password %d\n",
+ info->info1.min_password_length));
+
+ done:
+
+ return status;
+}
+
+static enum lsa_LookupNamesLevel winbindd_lookup_level(
+ struct winbindd_domain *domain)
+{
+ enum lsa_LookupNamesLevel level = LSA_LOOKUP_NAMES_DOMAINS_ONLY;
+
+ if (domain->internal) {
+ level = LSA_LOOKUP_NAMES_ALL;
+ } else if (domain->secure_channel_type == SEC_CHAN_DNS_DOMAIN) {
+ if (domain->domain_flags & NETR_TRUST_FLAG_IN_FOREST) {
+ /*
+ * TODO:
+ *
+ * Depending on what we want to resolve. We need to use:
+ * 1. LsapLookupXForestReferral(5)/LSA_LOOKUP_NAMES_FOREST_TRUSTS_ONLY
+ * if we want to pass the request into the direction of the forest
+ * root domain. The forest root domain uses
+ * LsapLookupXForestResolve(6)/LSA_LOOKUP_NAMES_UPLEVEL_TRUSTS_ONLY2
+ * when passing the request to trusted forests.
+ * 2. LsapLookupGC(4)/LSA_LOOKUP_NAMES_UPLEVEL_TRUSTS_ONLY
+ * if we're not a GC and want to resolve a name within our own forest.
+ *
+ * As we don't support more than one domain in our own forest
+ * and always try to be a GC for now, we just set
+ * LSA_LOOKUP_NAMES_FOREST_TRUSTS_ONLY.
+ */
+ level = LSA_LOOKUP_NAMES_FOREST_TRUSTS_ONLY;
+ } else if (domain->domain_trust_attribs & LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE) {
+ /*
+ * This is LsapLookupXForestResolve(6)/LSA_LOOKUP_NAMES_UPLEVEL_TRUSTS_ONLY2
+ */
+ level = LSA_LOOKUP_NAMES_UPLEVEL_TRUSTS_ONLY2;
+ } else {
+ /*
+ * This is LsapLookupTDL(3)/LSA_LOOKUP_NAMES_PRIMARY_DOMAIN_ONLY
+ */
+ level = LSA_LOOKUP_NAMES_PRIMARY_DOMAIN_ONLY;
+ }
+ } else if (domain->secure_channel_type == SEC_CHAN_DOMAIN) {
+ /*
+ * This is LsapLookupTDL(3)/LSA_LOOKUP_NAMES_PRIMARY_DOMAIN_ONLY
+ */
+ level = LSA_LOOKUP_NAMES_PRIMARY_DOMAIN_ONLY;
+ } else if (domain->rodc) {
+ level = LSA_LOOKUP_NAMES_RODC_REFERRAL_TO_FULL_DC;
+ } else {
+ /*
+ * This is LsapLookupPDC(2)/LSA_LOOKUP_NAMES_DOMAINS_ONLY
+ */
+ level = LSA_LOOKUP_NAMES_DOMAINS_ONLY;
+ }
+
+ return level;
+}
+
+NTSTATUS winbindd_lookup_sids(TALLOC_CTX *mem_ctx,
+ struct winbindd_domain *domain,
+ uint32_t num_sids,
+ const struct dom_sid *sids,
+ char ***domains,
+ char ***names,
+ enum lsa_SidType **types)
+{
+ NTSTATUS status;
+ NTSTATUS result;
+ struct rpc_pipe_client *cli = NULL;
+ struct dcerpc_binding_handle *b = NULL;
+ struct policy_handle lsa_policy;
+ unsigned int orig_timeout;
+ bool use_lookupsids3 = false;
+ bool retried = false;
+ enum lsa_LookupNamesLevel level = LSA_LOOKUP_NAMES_ALL;
+
+ connect:
+ status = cm_connect_lsat(domain, mem_ctx, &cli, &lsa_policy);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ b = cli->binding_handle;
+
+ if (cli->transport->transport == NCACN_IP_TCP) {
+ use_lookupsids3 = true;
+ }
+
+ level = winbindd_lookup_level(domain);
+
+ /*
+ * This call can take a long time
+ * allow the server to time out.
+ * 35 seconds should do it.
+ */
+ orig_timeout = dcerpc_binding_handle_set_timeout(b, 35000);
+
+ status = dcerpc_lsa_lookup_sids_generic(b,
+ mem_ctx,
+ &lsa_policy,
+ num_sids,
+ sids,
+ level,
+ domains,
+ names,
+ types,
+ use_lookupsids3,
+ &result);
+
+ /* And restore our original timeout. */
+ dcerpc_binding_handle_set_timeout(b, orig_timeout);
+
+ if (reset_cm_connection_on_error(domain, b, status)) {
+ /*
+ * This can happen if the schannel key is not
+ * valid anymore, we need to invalidate the
+ * all connections to the dc and reestablish
+ * a netlogon connection first.
+ */
+ domain->can_do_ncacn_ip_tcp = domain->active_directory;
+ if (!retried) {
+ retried = true;
+ goto connect;
+ }
+ status = NT_STATUS_ACCESS_DENIED;
+ }
+
+ if (any_nt_status_not_ok(status, result, &status)) {
+ return status;
+ }
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS winbindd_lookup_names(TALLOC_CTX *mem_ctx,
+ struct winbindd_domain *domain,
+ uint32_t num_names,
+ const char **names,
+ const char ***domains,
+ struct dom_sid **sids,
+ enum lsa_SidType **types)
+{
+ NTSTATUS status;
+ NTSTATUS result;
+ struct rpc_pipe_client *cli = NULL;
+ struct dcerpc_binding_handle *b = NULL;
+ struct policy_handle lsa_policy;
+ unsigned int orig_timeout = 0;
+ bool use_lookupnames4 = false;
+ bool retried = false;
+ enum lsa_LookupNamesLevel level = LSA_LOOKUP_NAMES_ALL;
+
+ connect:
+ status = cm_connect_lsat(domain, mem_ctx, &cli, &lsa_policy);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ b = cli->binding_handle;
+
+ if (cli->transport->transport == NCACN_IP_TCP) {
+ use_lookupnames4 = true;
+ }
+
+ level = winbindd_lookup_level(domain);
+
+ /*
+ * This call can take a long time
+ * allow the server to time out.
+ * 35 seconds should do it.
+ */
+ orig_timeout = dcerpc_binding_handle_set_timeout(b, 35000);
+
+ status = dcerpc_lsa_lookup_names_generic(b,
+ mem_ctx,
+ &lsa_policy,
+ num_names,
+ (const char **) names,
+ domains,
+ level,
+ sids,
+ types,
+ use_lookupnames4,
+ &result);
+
+ /* And restore our original timeout. */
+ dcerpc_binding_handle_set_timeout(b, orig_timeout);
+
+ if (reset_cm_connection_on_error(domain, b, status)) {
+ /*
+ * This can happen if the schannel key is not
+ * valid anymore, we need to invalidate the
+ * all connections to the dc and reestablish
+ * a netlogon connection first.
+ */
+ if (!retried) {
+ retried = true;
+ goto connect;
+ }
+ status = NT_STATUS_ACCESS_DENIED;
+ }
+
+ if (any_nt_status_not_ok(status, result, &status)) {
+ return status;
+ }
+
+ return NT_STATUS_OK;
+}
+
+/* the rpc backend methods are exposed via this structure */
+struct winbindd_methods msrpc_methods = {
+ False,
+ msrpc_query_user_list,
+ msrpc_enum_dom_groups,
+ msrpc_enum_local_groups,
+ msrpc_name_to_sid,
+ msrpc_sid_to_name,
+ msrpc_rids_to_names,
+ msrpc_lookup_usergroups,
+ msrpc_lookup_useraliases,
+ msrpc_lookup_groupmem,
+ msrpc_lookup_aliasmem,
+ msrpc_lockout_policy,
+ msrpc_password_policy,
+ msrpc_trusted_domains,
+};
diff --git a/source3/winbindd/winbindd_ndr.c b/source3/winbindd/winbindd_ndr.c
new file mode 100644
index 0000000..a52a704
--- /dev/null
+++ b/source3/winbindd/winbindd_ndr.c
@@ -0,0 +1,162 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * winbindd debug helper
+ * Copyright (C) Guenther Deschner 2008
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "includes.h"
+#include "winbindd.h"
+#include "../librpc/gen_ndr/ndr_netlogon.h"
+#include "../librpc/gen_ndr/ndr_security.h"
+#include "../librpc/gen_ndr/ndr_lsa.h"
+#include "../librpc/ndr/libndr.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_WINBIND
+
+/****************************************************************
+****************************************************************/
+
+void ndr_print_winbindd_child(struct ndr_print *ndr,
+ const char *name,
+ const struct winbindd_child *r)
+{
+ ndr_print_struct(ndr, name, "winbindd_child");
+ ndr->depth++;
+ ndr_print_uint32(ndr, "pid", (uint32_t)r->pid);
+#if 0
+ ndr_print_winbindd_domain(ndr, "domain", r->domain);
+#else
+ ndr_print_ptr(ndr, "domain", r->domain);
+#endif
+ ndr_print_string(ndr, "logfilename", r->logfilename);
+ /* struct fd_event event; */
+ ndr_print_ptr(ndr, "lockout_policy_event", r->lockout_policy_event);
+ ndr->depth--;
+}
+
+/****************************************************************
+****************************************************************/
+
+void ndr_print_winbindd_cm_conn(struct ndr_print *ndr,
+ const char *name,
+ const struct winbindd_cm_conn *r)
+{
+ ndr_print_struct(ndr, name, "winbindd_cm_conn");
+ ndr->depth++;
+ ndr_print_ptr(ndr, "cli", r->cli);
+ ndr_print_ptr(ndr, "samr_pipe", r->samr_pipe);
+ ndr_print_policy_handle(ndr, "sam_connect_handle", &r->sam_connect_handle);
+ ndr_print_policy_handle(ndr, "sam_domain_handle", &r->sam_domain_handle);
+ ndr_print_ptr(ndr, "lsa_pipe", r->lsa_pipe);
+ ndr_print_policy_handle(ndr, "lsa_policy", &r->lsa_policy);
+ ndr_print_ptr(ndr, "netlogon_pipe", r->netlogon_pipe);
+ ndr->depth--;
+}
+
+/****************************************************************
+****************************************************************/
+
+#ifdef HAVE_ADS
+extern struct winbindd_methods ads_methods;
+extern struct winbindd_methods reconnect_ads_methods;
+#endif
+extern struct winbindd_methods msrpc_methods;
+extern struct winbindd_methods builtin_passdb_methods;
+extern struct winbindd_methods sam_passdb_methods;
+extern struct winbindd_methods reconnect_methods;
+
+void ndr_print_winbindd_methods(struct ndr_print *ndr,
+ const char *name,
+ const struct winbindd_methods *r)
+{
+ ndr_print_struct(ndr, name, "winbindd_methods");
+ ndr->depth++;
+
+ if (r == NULL) {
+ ndr_print_string(ndr, name, "(NULL)");
+ ndr->depth--;
+ return;
+ }
+
+ if (r == &msrpc_methods) {
+ ndr_print_string(ndr, name, "msrpc_methods");
+#ifdef HAVE_ADS
+ } else if (r == &ads_methods) {
+ ndr_print_string(ndr, name, "ads_methods");
+ } else if (r == &reconnect_ads_methods) {
+ ndr_print_string(ndr, name, "reconnect_ads_methods");
+#endif
+ } else if (r == &builtin_passdb_methods) {
+ ndr_print_string(ndr, name, "builtin_passdb_methods");
+ } else if (r == &sam_passdb_methods) {
+ ndr_print_string(ndr, name, "sam_passdb_methods");
+ } else if (r == &reconnect_methods) {
+ ndr_print_string(ndr, name, "reconnect_methods");
+ } else {
+ ndr_print_string(ndr, name, "UNKNOWN");
+ }
+ ndr->depth--;
+}
+
+/****************************************************************
+****************************************************************/
+
+void ndr_print_winbindd_domain(struct ndr_print *ndr,
+ const char *name,
+ const struct winbindd_domain *r)
+{
+ int i;
+ if (!r) {
+ return;
+ }
+
+ ndr_print_struct(ndr, name, "winbindd_domain");
+ ndr->depth++;
+ ndr_print_string(ndr, "name", r->name);
+ ndr_print_string(ndr, "alt_name", r->alt_name);
+ ndr_print_string(ndr, "forest_name", r->forest_name);
+ ndr_print_dom_sid(ndr, "sid", &r->sid);
+ ndr_print_netr_TrustFlags(ndr, "domain_flags", r->domain_flags);
+ ndr_print_lsa_TrustType(ndr, "domain_type", r->domain_type);
+ ndr_print_lsa_TrustAttributes(ndr, "domain_trust_attribs", r->domain_trust_attribs);
+ ndr_print_bool(ndr, "initialized", r->initialized);
+ ndr_print_bool(ndr, "native_mode", r->native_mode);
+ ndr_print_bool(ndr, "active_directory", r->active_directory);
+ ndr_print_bool(ndr, "primary", r->primary);
+ ndr_print_bool(ndr, "internal", r->internal);
+ ndr_print_bool(ndr, "online", r->online);
+ ndr_print_time_t(ndr, "startup_time", r->startup_time);
+ ndr_print_bool(ndr, "startup", r->startup);
+ ndr_print_winbindd_methods(ndr, "backend", r->backend);
+ ndr_print_ptr(ndr,
+ "backend_data.samr_pipes",
+ r->backend_data.samr_pipes);
+ ndr_print_ptr(ndr,
+ "backend_data.ads_conn",
+ r->backend_data.ads_conn);
+ ndr_print_string(ndr, "dcname", r->dcname);
+ ndr_print_sockaddr_storage(ndr, "dcaddr", &r->dcaddr);
+ ndr_print_time_t(ndr, "last_seq_check", r->last_seq_check);
+ ndr_print_uint32(ndr, "sequence_number", r->sequence_number);
+ ndr_print_NTSTATUS(ndr, "last_status", r->last_status);
+ ndr_print_winbindd_cm_conn(ndr, "conn", &r->conn);
+ for (i=0; i<talloc_array_length(r->children); i++) {
+ ndr_print_winbindd_child(ndr, "children", &r->children[i]);
+ }
+ ndr_print_ptr(ndr, "check_online_event", r->check_online_event);
+ ndr->depth--;
+}
diff --git a/source3/winbindd/winbindd_pam.c b/source3/winbindd/winbindd_pam.c
new file mode 100644
index 0000000..6c890c8
--- /dev/null
+++ b/source3/winbindd/winbindd_pam.c
@@ -0,0 +1,3616 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Winbind daemon - pam auth functions
+
+ Copyright (C) Andrew Tridgell 2000
+ Copyright (C) Tim Potter 2001
+ Copyright (C) Andrew Bartlett 2001-2002
+ Copyright (C) Guenther Deschner 2005
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "ntdomain.h"
+#include "winbindd.h"
+#include "libsmb/namequery.h"
+#include "../libcli/auth/libcli_auth.h"
+#include "libcli/auth/pam_errors.h"
+#include "../librpc/gen_ndr/ndr_samr_c.h"
+#include "librpc/rpc/dcesrv_core.h"
+#include "librpc/gen_ndr/ndr_winbind.h"
+#include "rpc_client/cli_pipe.h"
+#include "rpc_client/cli_samr.h"
+#include "../librpc/gen_ndr/ndr_netlogon.h"
+#include "rpc_client/cli_netlogon.h"
+#include "smb_krb5.h"
+#include "../libcli/security/security.h"
+#include "ads.h"
+#include "../librpc/gen_ndr/krb5pac.h"
+#include "passdb/machine_sid.h"
+#include "auth.h"
+#include "../lib/tsocket/tsocket.h"
+#include "auth/kerberos/pac_utils.h"
+#include "auth/gensec/gensec.h"
+#include "librpc/crypto/gse_krb5.h"
+#include "lib/afs/afs_funcs.h"
+#include "libsmb/samlogon_cache.h"
+#include "rpc_client/util_netlogon.h"
+#include "param/param.h"
+#include "messaging/messaging.h"
+#include "lib/util/string_wrappers.h"
+#include "lib/crypto/gnutls_helpers.h"
+
+#include "lib/crypto/gnutls_helpers.h"
+#include <gnutls/crypto.h>
+#include "lib/global_contexts.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_WINBIND
+
+#define LOGON_KRB5_FAIL_CLOCK_SKEW 0x02000000
+
+static NTSTATUS append_info3_as_txt(TALLOC_CTX *mem_ctx,
+ struct winbindd_response *resp,
+ uint16_t validation_level,
+ union netr_Validation *validation)
+{
+ struct netr_SamInfo3 *info3 = NULL;
+ char *ex = NULL;
+ uint32_t i;
+ NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
+ TALLOC_CTX *frame = talloc_stackframe();
+
+ status = map_validation_to_info3(frame,
+ validation_level,
+ validation,
+ &info3);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto out;
+ }
+
+ resp->data.auth.info3.logon_time =
+ nt_time_to_unix(info3->base.logon_time);
+ resp->data.auth.info3.logoff_time =
+ nt_time_to_unix(info3->base.logoff_time);
+ resp->data.auth.info3.kickoff_time =
+ nt_time_to_unix(info3->base.kickoff_time);
+ resp->data.auth.info3.pass_last_set_time =
+ nt_time_to_unix(info3->base.last_password_change);
+ resp->data.auth.info3.pass_can_change_time =
+ nt_time_to_unix(info3->base.allow_password_change);
+ resp->data.auth.info3.pass_must_change_time =
+ nt_time_to_unix(info3->base.force_password_change);
+
+ resp->data.auth.info3.logon_count = info3->base.logon_count;
+ resp->data.auth.info3.bad_pw_count = info3->base.bad_password_count;
+
+ resp->data.auth.info3.user_rid = info3->base.rid;
+ resp->data.auth.info3.group_rid = info3->base.primary_gid;
+ sid_to_fstring(resp->data.auth.info3.dom_sid, info3->base.domain_sid);
+
+ resp->data.auth.info3.num_groups = info3->base.groups.count;
+ resp->data.auth.info3.user_flgs = info3->base.user_flags;
+
+ resp->data.auth.info3.acct_flags = info3->base.acct_flags;
+ resp->data.auth.info3.num_other_sids = info3->sidcount;
+
+ fstrcpy(resp->data.auth.info3.user_name,
+ info3->base.account_name.string);
+ fstrcpy(resp->data.auth.info3.full_name,
+ info3->base.full_name.string);
+ fstrcpy(resp->data.auth.info3.logon_script,
+ info3->base.logon_script.string);
+ fstrcpy(resp->data.auth.info3.profile_path,
+ info3->base.profile_path.string);
+ fstrcpy(resp->data.auth.info3.home_dir,
+ info3->base.home_directory.string);
+ fstrcpy(resp->data.auth.info3.dir_drive,
+ info3->base.home_drive.string);
+
+ fstrcpy(resp->data.auth.info3.logon_srv,
+ info3->base.logon_server.string);
+ fstrcpy(resp->data.auth.info3.logon_dom,
+ info3->base.logon_domain.string);
+
+ resp->data.auth.validation_level = validation_level;
+ if (validation_level == 6) {
+ fstrcpy(resp->data.auth.info6.dns_domainname,
+ validation->sam6->dns_domainname.string);
+ fstrcpy(resp->data.auth.info6.principal_name,
+ validation->sam6->principal_name.string);
+ }
+
+ ex = talloc_strdup(frame, "");
+ if (ex == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ goto out;
+ }
+
+ for (i=0; i < info3->base.groups.count; i++) {
+ ex = talloc_asprintf_append_buffer(ex, "0x%08X:0x%08X\n",
+ info3->base.groups.rids[i].rid,
+ info3->base.groups.rids[i].attributes);
+ if (ex == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ goto out;
+ }
+ }
+
+ for (i=0; i < info3->sidcount; i++) {
+ struct dom_sid_buf sidbuf;
+
+ ex = talloc_asprintf_append_buffer(
+ ex,
+ "%s:0x%08X\n",
+ dom_sid_str_buf(info3->sids[i].sid, &sidbuf),
+ info3->sids[i].attributes);
+ if (ex == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ goto out;
+ }
+ }
+
+ resp->length += talloc_get_size(ex);
+ resp->extra_data.data = talloc_move(mem_ctx, &ex);
+
+ status = NT_STATUS_OK;
+out:
+ TALLOC_FREE(frame);
+ return status;
+}
+
+static NTSTATUS append_info3_as_ndr(TALLOC_CTX *mem_ctx,
+ struct winbindd_response *resp,
+ struct netr_SamInfo3 *info3)
+{
+ DATA_BLOB blob;
+ enum ndr_err_code ndr_err;
+
+ ndr_err = ndr_push_struct_blob(&blob, mem_ctx, info3,
+ (ndr_push_flags_fn_t)ndr_push_netr_SamInfo3);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ DEBUG(0,("append_info3_as_ndr: failed to append\n"));
+ return ndr_map_error2ntstatus(ndr_err);
+ }
+
+ resp->extra_data.data = blob.data;
+ resp->length += blob.length;
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS append_unix_username(uint16_t validation_level,
+ union netr_Validation *validation,
+ const char *name_domain,
+ const char *name_user,
+ TALLOC_CTX *mem_ctx,
+ char **_unix_username)
+{
+ TALLOC_CTX *tmp_ctx = NULL;
+ const char *nt_username = NULL;
+ const char *nt_domain = NULL;
+ char *unix_username = NULL;
+ struct netr_SamBaseInfo *base_info = NULL;
+ NTSTATUS status;
+
+ tmp_ctx = talloc_new(mem_ctx);
+ if (tmp_ctx == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ /* We've been asked to return the unix username, per
+ 'winbind use default domain' settings and the like */
+
+ switch (validation_level) {
+ case 3:
+ base_info = &validation->sam3->base;
+ break;
+ case 6:
+ base_info = &validation->sam6->base;
+ break;
+ default:
+ DBG_ERR("Invalid validation level %d\n", validation_level);
+ status = NT_STATUS_INTERNAL_ERROR;
+ goto out;
+ }
+
+ nt_domain = talloc_strdup(tmp_ctx, base_info->logon_domain.string);
+ if (!nt_domain) {
+ /* If the server didn't give us one, just use the one
+ * we sent them */
+ nt_domain = name_domain;
+ }
+
+ nt_username = talloc_strdup(tmp_ctx, base_info->account_name.string);
+ if (!nt_username) {
+ /* If the server didn't give us one, just use the one
+ * we sent them */
+ nt_username = name_user;
+ }
+
+ unix_username = fill_domain_username_talloc(tmp_ctx,
+ nt_domain,
+ nt_username,
+ true);
+ if (unix_username == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ goto out;
+ }
+
+ DBG_INFO("Setting unix username to [%s]\n", unix_username);
+
+ *_unix_username = talloc_move(mem_ctx, &unix_username);
+
+ status = NT_STATUS_OK;
+out:
+ TALLOC_FREE(tmp_ctx);
+
+ return status;
+}
+
+static NTSTATUS append_afs_token(uint16_t validation_level,
+ union netr_Validation *validation,
+ const char *name_domain,
+ const char *name_user,
+ TALLOC_CTX *mem_ctx,
+ DATA_BLOB *_blob)
+{
+ TALLOC_CTX *tmp_ctx = NULL;
+ char *afsname = NULL;
+ char *cell;
+ char *token;
+ struct netr_SamBaseInfo *base_info = NULL;
+ NTSTATUS status;
+
+ tmp_ctx = talloc_new(mem_ctx);
+ if (tmp_ctx == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ switch (validation_level) {
+ case 3:
+ base_info = &validation->sam3->base;
+ break;
+ case 6:
+ base_info = &validation->sam6->base;
+ break;
+ default:
+ DBG_ERR("Invalid validation level %d\n", validation_level);
+ status = NT_STATUS_INTERNAL_ERROR;
+ goto out;
+ }
+
+ afsname = talloc_strdup(tmp_ctx, lp_afs_username_map());
+ if (afsname == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ goto out;
+ }
+
+ afsname = talloc_string_sub(tmp_ctx,
+ lp_afs_username_map(),
+ "%D", name_domain);
+ afsname = talloc_string_sub(tmp_ctx, afsname,
+ "%u", name_user);
+ afsname = talloc_string_sub(tmp_ctx, afsname,
+ "%U", name_user);
+
+ {
+ struct dom_sid user_sid;
+ struct dom_sid_buf sidstr;
+
+ sid_compose(&user_sid, base_info->domain_sid, base_info->rid);
+ afsname = talloc_string_sub(
+ tmp_ctx,
+ afsname,
+ "%s",
+ dom_sid_str_buf(&user_sid, &sidstr));
+ }
+
+ if (afsname == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ goto out;
+ }
+
+ if (!strlower_m(afsname)) {
+ status = NT_STATUS_INVALID_PARAMETER;
+ goto out;
+ }
+
+ DEBUG(10, ("Generating token for user %s\n", afsname));
+
+ cell = strchr(afsname, '@');
+
+ if (cell == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ goto out;
+ }
+
+ *cell = '\0';
+ cell += 1;
+
+ token = afs_createtoken_str(afsname, cell);
+ if (token == NULL) {
+ status = NT_STATUS_OK;
+ goto out;
+ }
+
+ talloc_steal(mem_ctx, token);
+ *_blob = data_blob_string_const_null(token);
+
+ status = NT_STATUS_OK;
+out:
+ TALLOC_FREE(tmp_ctx);
+
+ return status;
+}
+
+NTSTATUS extra_data_to_sid_array(const char *group_sid,
+ TALLOC_CTX *mem_ctx,
+ struct wbint_SidArray **_sid_array)
+{
+ TALLOC_CTX *tmp_ctx = NULL;
+ struct wbint_SidArray *sid_array = NULL;
+ struct dom_sid *require_membership_of_sid = NULL;
+ uint32_t num_require_membership_of_sid = 0;
+ char *req_sid = NULL;
+ const char *p = NULL;
+ NTSTATUS status;
+
+ if (_sid_array == NULL) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ *_sid_array = NULL;
+
+ tmp_ctx = talloc_new(mem_ctx);
+ if (tmp_ctx == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ sid_array = talloc_zero(tmp_ctx, struct wbint_SidArray);
+ if (sid_array == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ goto fail;
+ }
+
+ if (!group_sid || !group_sid[0]) {
+ /* NO sid supplied, all users may access */
+ status = NT_STATUS_OK;
+ /*
+ * Always return an allocated wbint_SidArray,
+ * even if the array is empty.
+ */
+ goto out;
+ }
+
+ num_require_membership_of_sid = 0;
+ require_membership_of_sid = NULL;
+ p = group_sid;
+
+ while (next_token_talloc(tmp_ctx, &p, &req_sid, ",")) {
+ struct dom_sid sid;
+
+ if (!string_to_sid(&sid, req_sid)) {
+ DBG_ERR("check_info3_in_group: could not parse %s "
+ "as a SID!\n", req_sid);
+ status = NT_STATUS_INVALID_PARAMETER;
+ goto fail;
+ }
+
+ status = add_sid_to_array(tmp_ctx, &sid,
+ &require_membership_of_sid,
+ &num_require_membership_of_sid);
+ if (!NT_STATUS_IS_OK(status)) {
+ DBG_ERR("add_sid_to_array failed\n");
+ goto fail;
+ }
+ }
+
+ sid_array->num_sids = num_require_membership_of_sid;
+ sid_array->sids = talloc_move(sid_array, &require_membership_of_sid);
+
+ status = NT_STATUS_OK;
+out:
+ *_sid_array = talloc_move(mem_ctx, &sid_array);
+
+fail:
+ TALLOC_FREE(tmp_ctx);
+
+ return status;
+}
+
+static NTSTATUS check_info3_in_group(struct netr_SamInfo3 *info3,
+ struct wbint_SidArray *sid_array)
+/**
+ * Check whether a user belongs to a group or list of groups.
+ *
+ * @param mem_ctx talloc memory context.
+ * @param info3 user information, including group membership info.
+ * @param group_sid One or more groups , separated by commas.
+ *
+ * @return NT_STATUS_OK on success,
+ * NT_STATUS_LOGON_FAILURE if the user does not belong,
+ * or other NT_STATUS_IS_ERR(status) for other kinds of failure.
+ */
+{
+ size_t i;
+ struct security_token *token;
+ NTSTATUS status;
+
+ /* Parse the 'required group' SID */
+
+ if (sid_array == NULL || sid_array->num_sids == 0) {
+ /* NO sid supplied, all users may access */
+ return NT_STATUS_OK;
+ }
+
+ /*
+ * This is a limited-use security_token for the purpose of
+ * checking the SID list below, so no claims need to be added
+ * and se_access_check() will never run.
+ */
+ token = security_token_initialise(talloc_tos(),
+ CLAIMS_EVALUATION_INVALID_STATE);
+ if (token == NULL) {
+ DEBUG(0, ("talloc failed\n"));
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ status = sid_array_from_info3(token, info3,
+ &token->sids,
+ &token->num_sids,
+ true);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ if (!NT_STATUS_IS_OK(status = add_aliases(get_global_sam_sid(),
+ token))
+ || !NT_STATUS_IS_OK(status = add_aliases(&global_sid_Builtin,
+ token))) {
+ DEBUG(3, ("could not add aliases: %s\n",
+ nt_errstr(status)));
+ return status;
+ }
+
+ security_token_debug(DBGC_CLASS, 10, token);
+
+ for (i=0; i<sid_array->num_sids; i++) {
+ struct dom_sid_buf buf;
+ DEBUG(10, ("Checking SID %s\n",
+ dom_sid_str_buf(&sid_array->sids[i],
+ &buf)));
+ if (nt_token_check_sid(&sid_array->sids[i],
+ token)) {
+ DEBUG(10, ("Access ok\n"));
+ return NT_STATUS_OK;
+ }
+ }
+
+ /* Do not distinguish this error from a wrong username/pw */
+
+ return NT_STATUS_LOGON_FAILURE;
+}
+
+struct winbindd_domain *find_auth_domain(uint8_t flags,
+ const char *domain_name)
+{
+ struct winbindd_domain *domain;
+
+ if (IS_DC) {
+ domain = find_domain_from_name_noinit(domain_name);
+ if (domain == NULL) {
+ DEBUG(3, ("Authentication for domain [%s] refused "
+ "as it is not a trusted domain\n",
+ domain_name));
+ return NULL;
+ }
+
+ if (domain->secure_channel_type != SEC_CHAN_NULL) {
+ return domain;
+ }
+
+ return domain->routing_domain;
+ }
+
+ if (strequal(domain_name, get_global_sam_name())) {
+ return find_domain_from_name_noinit(domain_name);
+ }
+
+ if (lp_winbind_use_krb5_enterprise_principals()) {
+ /*
+ * If we use enterprise principals
+ * we always go through our primary domain
+ * and follow the WRONG_REALM replies.
+ */
+ flags &= ~WBFLAG_PAM_CONTACT_TRUSTDOM;
+ }
+
+ /* we can auth against trusted domains */
+ if (flags & WBFLAG_PAM_CONTACT_TRUSTDOM) {
+ domain = find_domain_from_name_noinit(domain_name);
+ if (domain == NULL) {
+ DEBUG(3, ("Authentication for domain [%s] skipped "
+ "as it is not a trusted domain\n",
+ domain_name));
+ } else {
+ return domain;
+ }
+ }
+
+ return find_our_domain();
+}
+
+static NTSTATUS get_password_policy(struct winbindd_domain *domain,
+ TALLOC_CTX *mem_ctx,
+ struct samr_DomInfo1 **_policy)
+{
+ NTSTATUS status;
+ struct samr_DomInfo1 *policy = NULL;
+
+ if ( !winbindd_can_contact_domain( domain ) ) {
+ DBG_INFO("No inbound trust to contact domain %s\n",
+ domain->name);
+ return NT_STATUS_NOT_SUPPORTED;
+ }
+
+ policy = talloc_zero(mem_ctx, struct samr_DomInfo1);
+ if (policy == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ status = wb_cache_password_policy(domain, mem_ctx, policy);
+ if (NT_STATUS_IS_ERR(status)) {
+ TALLOC_FREE(policy);
+ return status;
+ }
+
+ *_policy = talloc_move(mem_ctx, &policy);
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS get_max_bad_attempts_from_lockout_policy(struct winbindd_domain *domain,
+ TALLOC_CTX *mem_ctx,
+ uint16_t *lockout_threshold)
+{
+ NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
+ struct samr_DomInfo12 lockout_policy;
+
+ *lockout_threshold = 0;
+
+ status = wb_cache_lockout_policy(domain, mem_ctx, &lockout_policy);
+ if (NT_STATUS_IS_ERR(status)) {
+ return status;
+ }
+
+ *lockout_threshold = lockout_policy.lockout_threshold;
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS get_pwd_properties(struct winbindd_domain *domain,
+ TALLOC_CTX *mem_ctx,
+ uint32_t *password_properties)
+{
+ NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
+ struct samr_DomInfo1 password_policy;
+
+ *password_properties = 0;
+
+ status = wb_cache_password_policy(domain, mem_ctx, &password_policy);
+ if (NT_STATUS_IS_ERR(status)) {
+ return status;
+ }
+
+ *password_properties = password_policy.password_properties;
+
+ return NT_STATUS_OK;
+}
+
+#ifdef HAVE_KRB5
+
+static const char *generate_krb5_ccache(TALLOC_CTX *mem_ctx,
+ const char *type,
+ uid_t uid,
+ const char **user_ccache_file)
+{
+ /* accept FILE and WRFILE as krb5_cc_type from the client and then
+ * build the full ccname string based on the user's uid here -
+ * Guenther*/
+
+ const char *gen_cc = NULL;
+
+ if (uid != -1) {
+ if (strequal(type, "FILE")) {
+ gen_cc = talloc_asprintf(
+ mem_ctx, "FILE:/tmp/krb5cc_%d", uid);
+ }
+ if (strequal(type, "WRFILE")) {
+ gen_cc = talloc_asprintf(
+ mem_ctx, "WRFILE:/tmp/krb5cc_%d", uid);
+ }
+ if (strequal(type, "KEYRING")) {
+ gen_cc = talloc_asprintf(
+ mem_ctx, "KEYRING:persistent:%d", uid);
+ }
+ if (strequal(type, "KCM")) {
+ gen_cc = talloc_asprintf(mem_ctx,
+ "KCM:%d",
+ uid);
+ }
+
+ if (strnequal(type, "FILE:/", 6) ||
+ strnequal(type, "WRFILE:/", 8) ||
+ strnequal(type, "DIR:/", 5)) {
+
+ /* we allow only one "%u" substitution */
+
+ char *p;
+
+ p = strchr(type, '%');
+ if (p != NULL) {
+
+ p++;
+
+ if (p != NULL && *p == 'u' && strchr(p, '%') == NULL) {
+ char uid_str[sizeof("18446744073709551615")];
+
+ snprintf(uid_str, sizeof(uid_str), "%u", uid);
+
+ gen_cc = talloc_string_sub2(mem_ctx,
+ type,
+ "%u",
+ uid_str,
+ /* remove_unsafe_characters */
+ false,
+ /* replace_once */
+ true,
+ /* allow_trailing_dollar */
+ false);
+ }
+ }
+ }
+ }
+
+ *user_ccache_file = gen_cc;
+
+ if (gen_cc == NULL) {
+ gen_cc = talloc_strdup(mem_ctx, "MEMORY:winbindd_pam_ccache");
+ }
+ if (gen_cc == NULL) {
+ DEBUG(0,("out of memory\n"));
+ return NULL;
+ }
+
+ DEBUG(10, ("using ccache: %s%s\n", gen_cc,
+ (*user_ccache_file == NULL) ? " (internal)":""));
+
+ return gen_cc;
+}
+
+#endif
+
+uid_t get_uid_from_request(struct winbindd_request *request)
+{
+ uid_t uid;
+
+ uid = request->data.auth.uid;
+
+ if (uid == (uid_t)-1) {
+ DEBUG(1,("invalid uid: '%u'\n", (unsigned int)uid));
+ return -1;
+ }
+ return uid;
+}
+
+/**********************************************************************
+ Authenticate a user with a clear text password using Kerberos and fill up
+ ccache if required
+ **********************************************************************/
+
+static NTSTATUS winbindd_raw_kerberos_login(TALLOC_CTX *mem_ctx,
+ struct winbindd_domain *domain,
+ const char *user,
+ const char *pass,
+ const char *krb5_cc_type,
+ uid_t uid,
+ struct netr_SamInfo6 **info6,
+ const char **_krb5ccname)
+{
+#ifdef HAVE_KRB5
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+ krb5_error_code krb5_ret;
+ const char *cc = NULL;
+ const char *principal_s = NULL;
+ char *realm = NULL;
+ char *name_namespace = NULL;
+ char *name_domain = NULL;
+ char *name_user = NULL;
+ time_t ticket_lifetime = 0;
+ time_t renewal_until = 0;
+ time_t time_offset = 0;
+ const char *user_ccache_file;
+ struct PAC_LOGON_INFO *logon_info = NULL;
+ struct PAC_UPN_DNS_INFO *upn_dns_info = NULL;
+ struct PAC_DATA *pac_data = NULL;
+ struct PAC_DATA_CTR *pac_data_ctr = NULL;
+ const char *local_service;
+ uint32_t i;
+ struct netr_SamInfo6 *info6_copy = NULL;
+ char *canon_principal = NULL;
+ char *canon_realm = NULL;
+ bool ok;
+
+ *info6 = NULL;
+
+ if (domain->alt_name == NULL) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ if (_krb5ccname != NULL) {
+ *_krb5ccname = NULL;
+ }
+
+ /* 1st step:
+ * prepare a krb5_cc_cache string for the user */
+
+ if (uid == -1) {
+ DEBUG(0,("no valid uid\n"));
+ }
+
+ cc = generate_krb5_ccache(mem_ctx,
+ krb5_cc_type,
+ uid,
+ &user_ccache_file);
+ if (cc == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+
+ /* 2nd step:
+ * get kerberos properties */
+
+ if (domain->backend_data.ads_conn != NULL) {
+ time_offset = domain->backend_data.ads_conn->auth.time_offset;
+ }
+
+
+ /* 3rd step:
+ * do kerberos auth and setup ccache as the user */
+
+ ok = parse_domain_user(mem_ctx,
+ user,
+ &name_namespace,
+ &name_domain,
+ &name_user);
+ if (!ok) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ realm = talloc_strdup(mem_ctx, domain->alt_name);
+ if (realm == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ if (!strupper_m(realm)) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ if (lp_winbind_use_krb5_enterprise_principals() &&
+ name_namespace[0] != '\0')
+ {
+ principal_s = talloc_asprintf(mem_ctx,
+ "%s@%s@%s",
+ name_user,
+ name_namespace,
+ realm);
+ } else {
+ principal_s = talloc_asprintf(mem_ctx,
+ "%s@%s",
+ name_user,
+ realm);
+ }
+ if (principal_s == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ local_service = talloc_asprintf(mem_ctx, "%s$@%s",
+ lp_netbios_name(), lp_realm());
+ if (local_service == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+
+ /* if this is a user ccache, we need to act as the user to let the krb5
+ * library handle the chown, etc. */
+
+ /************************ ENTERING NON-ROOT **********************/
+
+ if (user_ccache_file != NULL) {
+ set_effective_uid(uid);
+ DEBUG(10,("winbindd_raw_kerberos_login: uid is %d\n", uid));
+ }
+
+ result = kerberos_return_pac(mem_ctx,
+ principal_s,
+ pass,
+ time_offset,
+ &ticket_lifetime,
+ &renewal_until,
+ cc,
+ true,
+ true,
+ WINBINDD_PAM_AUTH_KRB5_RENEW_TIME,
+ NULL,
+ local_service,
+ &canon_principal,
+ &canon_realm,
+ &pac_data_ctr);
+ if (user_ccache_file != NULL) {
+ gain_root_privilege();
+ }
+
+ /************************ RETURNED TO ROOT **********************/
+
+ if (!NT_STATUS_IS_OK(result)) {
+ goto failed;
+ }
+
+ if (pac_data_ctr == NULL) {
+ goto failed;
+ }
+
+ pac_data = pac_data_ctr->pac_data;
+ if (pac_data == NULL) {
+ goto failed;
+ }
+
+ for (i=0; i < pac_data->num_buffers; i++) {
+
+ if (pac_data->buffers[i].type == PAC_TYPE_LOGON_INFO) {
+ logon_info = pac_data->buffers[i].info->logon_info.info;
+ continue;
+ }
+
+ if (pac_data->buffers[i].type == PAC_TYPE_UPN_DNS_INFO) {
+ upn_dns_info = &pac_data->buffers[i].info->upn_dns_info;
+ continue;
+ }
+ }
+
+ if (logon_info == NULL) {
+ DEBUG(10,("Missing logon_info in ticket of %s\n",
+ principal_s));
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ DEBUG(10,("winbindd_raw_kerberos_login: winbindd validated ticket of %s\n",
+ principal_s));
+
+ result = create_info6_from_pac(mem_ctx, logon_info,
+ upn_dns_info, &info6_copy);
+ if (!NT_STATUS_IS_OK(result)) {
+ goto failed;
+ }
+
+ /* if we had a user's ccache then return that string for the pam
+ * environment */
+
+ if (user_ccache_file != NULL) {
+
+ if (_krb5ccname != NULL) {
+ *_krb5ccname = talloc_steal(mem_ctx, user_ccache_file);
+ }
+
+ result = add_ccache_to_list(principal_s,
+ cc,
+ user,
+ pass,
+ realm,
+ uid,
+ time(NULL),
+ ticket_lifetime,
+ renewal_until,
+ false,
+ canon_principal,
+ canon_realm);
+
+ if (!NT_STATUS_IS_OK(result)) {
+ DEBUG(10,("winbindd_raw_kerberos_login: failed to add ccache to list: %s\n",
+ nt_errstr(result)));
+ }
+ } else {
+
+ /* need to delete the memory cred cache, it is not used anymore */
+
+ krb5_ret = ads_kdestroy(cc);
+ if (krb5_ret) {
+ DEBUG(3,("winbindd_raw_kerberos_login: "
+ "could not destroy krb5 credential cache: "
+ "%s\n", error_message(krb5_ret)));
+ }
+
+ }
+ *info6 = info6_copy;
+ return NT_STATUS_OK;
+
+failed:
+ /*
+ * Do not delete an existing valid credential cache, if the user
+ * e.g. enters a wrong password
+ */
+ if ((strequal(krb5_cc_type, "FILE") || strequal(krb5_cc_type, "WRFILE"))
+ && user_ccache_file != NULL) {
+ return result;
+ }
+
+ /* we could have created a new credential cache with a valid tgt in it
+ * but we weren't able to get or verify the service ticket for this
+ * local host and therefore didn't get the PAC, we need to remove that
+ * cache entirely now */
+
+ krb5_ret = ads_kdestroy(cc);
+ if (krb5_ret) {
+ DEBUG(3,("winbindd_raw_kerberos_login: "
+ "could not destroy krb5 credential cache: "
+ "%s\n", error_message(krb5_ret)));
+ }
+
+ if (!NT_STATUS_IS_OK(remove_ccache(user))) {
+ DEBUG(3,("winbindd_raw_kerberos_login: "
+ "could not remove ccache for user %s\n",
+ user));
+ }
+
+ return result;
+#else
+ return NT_STATUS_NOT_SUPPORTED;
+#endif /* HAVE_KRB5 */
+}
+
+/****************************************************************
+****************************************************************/
+
+bool check_request_flags(uint32_t flags)
+{
+ uint32_t flags_edata = WBFLAG_PAM_AFS_TOKEN |
+ WBFLAG_PAM_INFO3_TEXT |
+ WBFLAG_PAM_INFO3_NDR;
+
+ if ( ( (flags & flags_edata) == WBFLAG_PAM_AFS_TOKEN) ||
+ ( (flags & flags_edata) == WBFLAG_PAM_INFO3_NDR) ||
+ ( (flags & flags_edata) == WBFLAG_PAM_INFO3_TEXT)||
+ !(flags & flags_edata) ) {
+ return true;
+ }
+
+ DEBUG(1, ("check_request_flags: invalid request flags[0x%08X]\n",
+ flags));
+
+ return false;
+}
+
+/****************************************************************
+****************************************************************/
+
+NTSTATUS append_auth_data(TALLOC_CTX *mem_ctx,
+ struct winbindd_response *resp,
+ uint32_t request_flags,
+ uint16_t validation_level,
+ union netr_Validation *validation,
+ const char *name_domain,
+ const char *name_user)
+{
+ struct netr_SamInfo3 *info3 = NULL;
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+
+ result = map_validation_to_info3(talloc_tos(),
+ validation_level,
+ validation,
+ &info3);
+ if (!NT_STATUS_IS_OK(result)) {
+ goto out;
+ }
+
+ if (request_flags & WBFLAG_PAM_USER_SESSION_KEY) {
+ memcpy(resp->data.auth.user_session_key,
+ info3->base.key.key,
+ sizeof(resp->data.auth.user_session_key)
+ /* 16 */);
+ }
+
+ if (request_flags & WBFLAG_PAM_LMKEY) {
+ memcpy(resp->data.auth.first_8_lm_hash,
+ info3->base.LMSessKey.key,
+ sizeof(resp->data.auth.first_8_lm_hash)
+ /* 8 */);
+ }
+
+ if (request_flags & WBFLAG_PAM_UNIX_NAME) {
+ char *unix_username = NULL;
+ result = append_unix_username(validation_level,
+ validation,
+ name_domain,
+ name_user,
+ mem_ctx,
+ &unix_username);
+ if (!NT_STATUS_IS_OK(result)) {
+ DEBUG(10,("Failed to append Unix Username: %s\n",
+ nt_errstr(result)));
+ goto out;
+ }
+ fstrcpy(resp->data.auth.unix_username, unix_username);
+ TALLOC_FREE(unix_username);
+ }
+
+ /* currently, anything from here on potentially overwrites extra_data. */
+
+ if (request_flags & WBFLAG_PAM_INFO3_NDR) {
+ result = append_info3_as_ndr(mem_ctx, resp, info3);
+ if (!NT_STATUS_IS_OK(result)) {
+ DEBUG(10,("Failed to append INFO3 (NDR): %s\n",
+ nt_errstr(result)));
+ goto out;
+ }
+ }
+
+ if (request_flags & WBFLAG_PAM_INFO3_TEXT) {
+ result = append_info3_as_txt(mem_ctx, resp,
+ validation_level,
+ validation);
+ if (!NT_STATUS_IS_OK(result)) {
+ DEBUG(10,("Failed to append INFO3 (TXT): %s\n",
+ nt_errstr(result)));
+ goto out;
+ }
+ }
+
+ if (request_flags & WBFLAG_PAM_AFS_TOKEN) {
+ DATA_BLOB blob = data_blob_null;
+ result = append_afs_token(validation_level,
+ validation,
+ name_domain,
+ name_user,
+ mem_ctx,
+ &blob);
+ if (!NT_STATUS_IS_OK(result)) {
+ DEBUG(10,("Failed to append AFS token: %s\n",
+ nt_errstr(result)));
+ goto out;
+ }
+ resp->extra_data.data = blob.data;
+ resp->length += blob.length;
+ }
+
+ result = NT_STATUS_OK;
+out:
+ TALLOC_FREE(info3);
+ return result;
+}
+
+static NTSTATUS winbindd_dual_pam_auth_cached(struct winbindd_domain *domain,
+ bool krb5_auth,
+ const char *user,
+ const char *pass,
+ const char *krb5_cc_type,
+ uid_t uid,
+ TALLOC_CTX *mem_ctx,
+ uint16_t *_validation_level,
+ union netr_Validation **_validation,
+ const char **_krb5ccname)
+{
+ TALLOC_CTX *tmp_ctx = NULL;
+ NTSTATUS result = NT_STATUS_LOGON_FAILURE;
+ uint16_t max_allowed_bad_attempts;
+ char *name_namespace = NULL;
+ char *name_domain = NULL;
+ char *name_user = NULL;
+ struct dom_sid sid;
+ enum lsa_SidType type;
+ uchar new_nt_pass[NT_HASH_LEN];
+ const uint8_t *cached_nt_pass;
+ const uint8_t *cached_salt;
+ struct netr_SamInfo3 *my_info3;
+ time_t kickoff_time, must_change_time;
+ bool password_good = false;
+ bool ok;
+#ifdef HAVE_KRB5
+ struct winbindd_tdc_domain *tdc_domain = NULL;
+#endif
+
+ if (_validation == NULL) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+ *_validation = NULL;
+
+ if (_krb5ccname != NULL) {
+ *_krb5ccname = NULL;
+ }
+
+ DEBUG(10,("winbindd_dual_pam_auth_cached\n"));
+
+ tmp_ctx = talloc_new(mem_ctx);
+ if (tmp_ctx == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ /* Parse domain and username */
+
+ ok = parse_domain_user(tmp_ctx,
+ user,
+ &name_namespace,
+ &name_domain,
+ &name_user);
+ if (!ok) {
+ DBG_DEBUG("parse_domain_user failed\n");
+ result = NT_STATUS_NO_SUCH_USER;
+ goto out;
+ }
+
+ if (!lookup_cached_name(name_namespace,
+ name_domain,
+ name_user,
+ &sid,
+ &type)) {
+ DEBUG(10,("winbindd_dual_pam_auth_cached: no such user in the cache\n"));
+ result = NT_STATUS_NO_SUCH_USER;
+ goto out;
+ }
+
+ if (type != SID_NAME_USER) {
+ DEBUG(10,("winbindd_dual_pam_auth_cached: not a user (%s)\n", sid_type_lookup(type)));
+ result = NT_STATUS_LOGON_FAILURE;
+ goto out;
+ }
+
+ result = winbindd_get_creds(domain,
+ tmp_ctx,
+ &sid,
+ &my_info3,
+ &cached_nt_pass,
+ &cached_salt);
+ if (!NT_STATUS_IS_OK(result)) {
+ DEBUG(10,("winbindd_dual_pam_auth_cached: failed to get creds: %s\n", nt_errstr(result)));
+ goto out;
+ }
+
+ E_md4hash(pass, new_nt_pass);
+
+ dump_data_pw("new_nt_pass", new_nt_pass, NT_HASH_LEN);
+ dump_data_pw("cached_nt_pass", cached_nt_pass, NT_HASH_LEN);
+ if (cached_salt) {
+ dump_data_pw("cached_salt", cached_salt, NT_HASH_LEN);
+ }
+
+ if (cached_salt) {
+ /* In this case we didn't store the nt_hash itself,
+ but the MD5 combination of salt + nt_hash. */
+ uchar salted_hash[NT_HASH_LEN];
+ gnutls_hash_hd_t hash_hnd = NULL;
+ int rc;
+
+ rc = gnutls_hash_init(&hash_hnd, GNUTLS_DIG_MD5);
+ if (rc < 0) {
+ result = gnutls_error_to_ntstatus(
+ rc, NT_STATUS_HASH_NOT_SUPPORTED);
+ goto out;
+ }
+
+ rc = gnutls_hash(hash_hnd, cached_salt, 16);
+ if (rc < 0) {
+ gnutls_hash_deinit(hash_hnd, NULL);
+ result = gnutls_error_to_ntstatus(
+ rc, NT_STATUS_HASH_NOT_SUPPORTED);
+ goto out;
+ }
+ rc = gnutls_hash(hash_hnd, new_nt_pass, 16);
+ if (rc < 0) {
+ gnutls_hash_deinit(hash_hnd, NULL);
+ result = gnutls_error_to_ntstatus(
+ rc, NT_STATUS_HASH_NOT_SUPPORTED);
+ goto out;
+ }
+ gnutls_hash_deinit(hash_hnd, salted_hash);
+
+ password_good = mem_equal_const_time(cached_nt_pass, salted_hash,
+ NT_HASH_LEN);
+ } else {
+ /* Old cached cred - direct store of nt_hash (bad bad bad !). */
+ password_good = mem_equal_const_time(cached_nt_pass, new_nt_pass,
+ NT_HASH_LEN);
+ }
+
+ if (password_good) {
+
+ /* User *DOES* know the password, update logon_time and reset
+ * bad_pw_count */
+
+ my_info3->base.user_flags |= NETLOGON_CACHED_ACCOUNT;
+
+ if (my_info3->base.acct_flags & ACB_AUTOLOCK) {
+ result = NT_STATUS_ACCOUNT_LOCKED_OUT;
+ goto out;
+ }
+
+ if (my_info3->base.acct_flags & ACB_DISABLED) {
+ result = NT_STATUS_ACCOUNT_DISABLED;
+ goto out;
+ }
+
+ if (my_info3->base.acct_flags & ACB_WSTRUST) {
+ result = NT_STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT;
+ goto out;
+ }
+
+ if (my_info3->base.acct_flags & ACB_SVRTRUST) {
+ result = NT_STATUS_NOLOGON_SERVER_TRUST_ACCOUNT;
+ goto out;
+ }
+
+ if (my_info3->base.acct_flags & ACB_DOMTRUST) {
+ result = NT_STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT;
+ goto out;
+ }
+
+ if (!(my_info3->base.acct_flags & ACB_NORMAL)) {
+ DEBUG(0,("winbindd_dual_pam_auth_cached: what's wrong with that one?: 0x%08x\n",
+ my_info3->base.acct_flags));
+ result = NT_STATUS_LOGON_FAILURE;
+ goto out;
+ }
+
+ kickoff_time = nt_time_to_unix(my_info3->base.kickoff_time);
+ if (kickoff_time != 0 && time(NULL) > kickoff_time) {
+ result = NT_STATUS_ACCOUNT_EXPIRED;
+ goto out;
+ }
+
+ must_change_time = nt_time_to_unix(my_info3->base.force_password_change);
+ if (must_change_time != 0 && must_change_time < time(NULL)) {
+ /* we allow grace logons when the password has expired */
+ my_info3->base.user_flags |= NETLOGON_GRACE_LOGON;
+ /* return NT_STATUS_PASSWORD_EXPIRED; */
+ goto success;
+ }
+
+#ifdef HAVE_KRB5
+ if ((krb5_auth) &&
+ ((tdc_domain = wcache_tdc_fetch_domain(tmp_ctx, name_domain)) != NULL) &&
+ ((tdc_domain->trust_type & LSA_TRUST_TYPE_UPLEVEL) ||
+ /* used to cope with the case winbindd starting without network. */
+ !strequal(tdc_domain->domain_name, tdc_domain->dns_name))) {
+ const char *cc = NULL;
+ char *realm = NULL;
+ const char *principal_s = NULL;
+ const char *user_ccache_file;
+
+ if (domain->alt_name == NULL) {
+ result = NT_STATUS_INVALID_PARAMETER;
+ goto out;
+ }
+
+ if (uid == -1) {
+ DEBUG(0,("winbindd_dual_pam_auth_cached: invalid uid\n"));
+ result = NT_STATUS_INVALID_PARAMETER;
+ goto out;
+ }
+
+ cc = generate_krb5_ccache(tmp_ctx,
+ krb5_cc_type,
+ uid,
+ &user_ccache_file);
+ if (cc == NULL) {
+ result = NT_STATUS_NO_MEMORY;
+ goto out;
+ }
+
+ realm = talloc_strdup(tmp_ctx, domain->alt_name);
+ if (realm == NULL) {
+ result = NT_STATUS_NO_MEMORY;
+ goto out;
+ }
+
+ if (!strupper_m(realm)) {
+ result = NT_STATUS_INVALID_PARAMETER;
+ goto out;
+ }
+
+ principal_s = talloc_asprintf(tmp_ctx, "%s@%s", name_user, realm);
+ if (principal_s == NULL) {
+ result = NT_STATUS_NO_MEMORY;
+ goto out;
+ }
+
+ if (user_ccache_file != NULL) {
+
+ if (_krb5ccname != NULL) {
+ *_krb5ccname = talloc_move(mem_ctx,
+ &user_ccache_file);
+ }
+
+ result = add_ccache_to_list(principal_s,
+ cc,
+ user,
+ pass,
+ realm,
+ uid,
+ time(NULL),
+ time(NULL) + lp_winbind_cache_time(),
+ time(NULL) + WINBINDD_PAM_AUTH_KRB5_RENEW_TIME,
+ true,
+ principal_s,
+ realm);
+
+ if (!NT_STATUS_IS_OK(result)) {
+ DEBUG(10,("winbindd_dual_pam_auth_cached: failed "
+ "to add ccache to list: %s\n",
+ nt_errstr(result)));
+ }
+ }
+ }
+#endif /* HAVE_KRB5 */
+ success:
+ /* FIXME: we possibly should handle logon hours as well (does xp when
+ * offline?) see auth/auth_sam.c:sam_account_ok for details */
+
+ unix_to_nt_time(&my_info3->base.logon_time, time(NULL));
+ my_info3->base.bad_password_count = 0;
+
+ result = winbindd_update_creds_by_info3(domain,
+ user,
+ pass,
+ my_info3);
+ if (!NT_STATUS_IS_OK(result)) {
+ DEBUG(1,("winbindd_dual_pam_auth_cached: failed to update creds: %s\n",
+ nt_errstr(result)));
+ goto out;
+ }
+
+ result = map_info3_to_validation(mem_ctx,
+ my_info3,
+ _validation_level,
+ _validation);
+ if (!NT_STATUS_IS_OK(result)) {
+ DBG_ERR("map_info3_to_validation failed: %s\n",
+ nt_errstr(result));
+ goto out;
+ }
+
+ result = NT_STATUS_OK;
+ goto out;
+ }
+
+ /* User does *NOT* know the correct password, modify info3 accordingly, but only if online */
+ if (domain->online == false) {
+ goto failed;
+ }
+
+ /* failure of this is not critical */
+ result = get_max_bad_attempts_from_lockout_policy(domain, tmp_ctx, &max_allowed_bad_attempts);
+ if (!NT_STATUS_IS_OK(result)) {
+ DEBUG(10,("winbindd_dual_pam_auth_cached: failed to get max_allowed_bad_attempts. "
+ "Won't be able to honour account lockout policies\n"));
+ }
+
+ /* increase counter */
+ my_info3->base.bad_password_count++;
+
+ if (max_allowed_bad_attempts == 0) {
+ goto failed;
+ }
+
+ /* lockout user */
+ if (my_info3->base.bad_password_count >= max_allowed_bad_attempts) {
+
+ uint32_t password_properties;
+
+ result = get_pwd_properties(domain, tmp_ctx, &password_properties);
+ if (!NT_STATUS_IS_OK(result)) {
+ DEBUG(10,("winbindd_dual_pam_auth_cached: failed to get password properties.\n"));
+ }
+
+ if ((my_info3->base.rid != DOMAIN_RID_ADMINISTRATOR) ||
+ (password_properties & DOMAIN_PASSWORD_LOCKOUT_ADMINS)) {
+ my_info3->base.acct_flags |= ACB_AUTOLOCK;
+ }
+ }
+
+failed:
+ result = winbindd_update_creds_by_info3(domain, user, NULL, my_info3);
+ if (!NT_STATUS_IS_OK(result)) {
+ DEBUG(0,("winbindd_dual_pam_auth_cached: failed to update creds %s\n",
+ nt_errstr(result)));
+ }
+
+ result = NT_STATUS_LOGON_FAILURE;
+
+out:
+ TALLOC_FREE(tmp_ctx);
+
+ return result;
+}
+
+static NTSTATUS winbindd_dual_pam_auth_kerberos(struct winbindd_domain *domain,
+ const char *user,
+ const char *pass,
+ const char *krb5_cc_type,
+ uid_t uid,
+ TALLOC_CTX *mem_ctx,
+ uint16_t *_validation_level,
+ union netr_Validation **_validation,
+ const char **_krb5ccname)
+{
+ struct netr_SamInfo6 *info6 = NULL;
+ struct winbindd_domain *contact_domain;
+ char *name_namespace = NULL;
+ char *name_domain = NULL;
+ char *name_user = NULL;
+ NTSTATUS result;
+ bool ok;
+
+ DEBUG(10,("winbindd_dual_pam_auth_kerberos\n"));
+
+ /* Parse domain and username */
+
+ ok = parse_domain_user(mem_ctx,
+ user,
+ &name_namespace,
+ &name_domain,
+ &name_user);
+ if (!ok) {
+ result = NT_STATUS_INVALID_PARAMETER;
+ goto done;
+ }
+
+ /* what domain should we contact? */
+
+ if (lp_winbind_use_krb5_enterprise_principals()) {
+ contact_domain = find_auth_domain(0, name_namespace);
+ } else {
+ contact_domain = find_domain_from_name(name_namespace);
+ }
+ if (contact_domain == NULL) {
+ DEBUG(3, ("Authentication for domain for [%s] -> [%s]\\[%s] failed as %s is not a trusted domain\n",
+ user, name_domain, name_user, name_namespace));
+ result = NT_STATUS_NO_SUCH_USER;
+ goto done;
+ }
+
+ if (contact_domain->initialized &&
+ contact_domain->active_directory) {
+ goto try_login;
+ }
+
+ if (!contact_domain->initialized) {
+ init_dc_connection(contact_domain, false);
+ }
+
+ if (!contact_domain->active_directory) {
+ DEBUG(3,("krb5 auth requested but domain (%s) is not Active Directory\n",
+ contact_domain->name));
+ return NT_STATUS_INVALID_LOGON_TYPE;
+ }
+try_login:
+ result = winbindd_raw_kerberos_login(
+ mem_ctx,
+ contact_domain,
+ user,
+ pass,
+ krb5_cc_type,
+ uid,
+ &info6,
+ _krb5ccname);
+ if (!NT_STATUS_IS_OK(result)) {
+ goto done;
+ }
+
+ result = map_info6_to_validation(mem_ctx,
+ info6,
+ _validation_level,
+ _validation);
+ TALLOC_FREE(info6);
+ if (!NT_STATUS_IS_OK(result)) {
+ DBG_ERR("map_info6_to_validation failed: %s\n",
+ nt_errstr(result));
+ }
+
+done:
+ return result;
+}
+
+static NTSTATUS winbindd_dual_auth_passdb(TALLOC_CTX *mem_ctx,
+ uint32_t logon_parameters,
+ const char *domain,
+ const char *user,
+ const uint64_t logon_id,
+ const char *client_name,
+ const int client_pid,
+ const DATA_BLOB *challenge,
+ const DATA_BLOB *lm_resp,
+ const DATA_BLOB *nt_resp,
+ const struct tsocket_address *remote,
+ const struct tsocket_address *local,
+ bool interactive,
+ uint8_t *pauthoritative,
+ struct netr_SamInfo3 **pinfo3)
+{
+ struct auth_context *auth_context;
+ struct auth_serversupplied_info *server_info;
+ struct auth_usersupplied_info *user_info = NULL;
+ struct netr_SamInfo3 *info3;
+ NTSTATUS status;
+ bool ok;
+ TALLOC_CTX *frame = talloc_stackframe();
+
+ /*
+ * We are authoritative by default
+ */
+ *pauthoritative = 1;
+
+ status = make_user_info(frame, &user_info, user, user, domain, domain,
+ lp_netbios_name(), remote, local,
+ "winbind",
+ lm_resp, nt_resp, NULL, NULL,
+ NULL, AUTH_PASSWORD_RESPONSE);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(10, ("make_user_info failed: %s\n", nt_errstr(status)));
+ TALLOC_FREE(frame);
+ return status;
+ }
+
+ user_info->logon_parameters = logon_parameters;
+ user_info->logon_id = logon_id;
+ user_info->auth_description = talloc_asprintf(
+ frame, "PASSDB, %s, %d", client_name, client_pid);
+ if (user_info->auth_description == NULL) {
+ TALLOC_FREE(frame);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ /* We don't want to come back to winbindd or to do PAM account checks */
+ user_info->flags |= USER_INFO_INFO3_AND_NO_AUTHZ;
+
+ if (interactive) {
+ user_info->flags |= USER_INFO_INTERACTIVE_LOGON;
+ }
+
+ status = make_auth3_context_for_winbind(frame, &auth_context);
+ if (!NT_STATUS_IS_OK(status)) {
+ DBG_ERR("make_auth3_context_for_winbind failed: %s\n",
+ nt_errstr(status));
+ TALLOC_FREE(frame);
+ return status;
+ }
+
+ ok = auth3_context_set_challenge(auth_context,
+ challenge->data, "fixed");
+ if (!ok) {
+ TALLOC_FREE(frame);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ status = auth_check_ntlm_password(mem_ctx,
+ auth_context,
+ user_info,
+ &server_info,
+ pauthoritative);
+ if (!NT_STATUS_IS_OK(status)) {
+ TALLOC_FREE(frame);
+ return status;
+ }
+
+ info3 = talloc_zero(mem_ctx, struct netr_SamInfo3);
+ if (info3 == NULL) {
+ TALLOC_FREE(frame);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ status = serverinfo_to_SamInfo3(server_info, info3);
+ if (!NT_STATUS_IS_OK(status)) {
+ TALLOC_FREE(frame);
+ TALLOC_FREE(info3);
+ DEBUG(0, ("serverinfo_to_SamInfo3 failed: %s\n",
+ nt_errstr(status)));
+ return status;
+ }
+
+ *pinfo3 = info3;
+ DBG_DEBUG("Authenticating user %s\\%s returned %s\n",
+ domain,
+ user,
+ nt_errstr(status));
+ TALLOC_FREE(frame);
+ return status;
+}
+
+static NTSTATUS winbind_samlogon_retry_loop(struct winbindd_domain *domain,
+ TALLOC_CTX *mem_ctx,
+ uint32_t logon_parameters,
+ const char *username,
+ const char *password,
+ const char *domainname,
+ const char *workstation,
+ const uint64_t logon_id,
+ bool plaintext_given,
+ DATA_BLOB chal,
+ DATA_BLOB lm_response,
+ DATA_BLOB nt_response,
+ bool interactive,
+ uint8_t *authoritative,
+ uint32_t *flags,
+ uint16_t *_validation_level,
+ union netr_Validation **_validation)
+{
+ int attempts = 0;
+ int netr_attempts = 0;
+ bool retry = false;
+ bool valid_result = false;
+ NTSTATUS result;
+ enum netr_LogonInfoClass logon_type_i;
+ enum netr_LogonInfoClass logon_type_n;
+ uint16_t validation_level = UINT16_MAX;
+ union netr_Validation *validation = NULL;
+ TALLOC_CTX *base_ctx = NULL;
+ struct netr_SamBaseInfo *base_info = NULL;
+
+ do {
+ struct rpc_pipe_client *netlogon_pipe;
+ struct netlogon_creds_cli_context *netlogon_creds_ctx = NULL;
+
+ /*
+ * We should always reset authoritative to 1
+ * before calling a server again.
+ *
+ * Otherwise we could treat a local problem as
+ * non-authoritative.
+ */
+ *authoritative = 1;
+
+ retry = false;
+
+ D_DEBUG("Creating a DCERPC netlogon connection for SAM logon. "
+ "netlogon attempt: %d, samlogon attempt: %d.\n",
+ netr_attempts,
+ attempts);
+ result = cm_connect_netlogon_secure(domain, &netlogon_pipe,
+ &netlogon_creds_ctx);
+
+ if (NT_STATUS_EQUAL(result,
+ NT_STATUS_CANT_ACCESS_DOMAIN_INFO)) {
+ /*
+ * This means we don't have a trust account.
+ */
+ *authoritative = 0;
+ result = NT_STATUS_NO_SUCH_USER;
+ break;
+ }
+
+ if (!NT_STATUS_IS_OK(result)) {
+ DEBUG(3,("Could not open handle to NETLOGON pipe "
+ "(error: %s, attempts: %d)\n",
+ nt_errstr(result), netr_attempts));
+
+ reset_cm_connection_on_error(domain, NULL, result);
+
+ /* After the first retry always close the connection */
+ if (netr_attempts > 0) {
+ DEBUG(3, ("This is again a problem for this "
+ "particular call, forcing the close "
+ "of this connection\n"));
+ invalidate_cm_connection(domain);
+ }
+
+ /* After the second retry failover to the next DC */
+ if (netr_attempts > 1) {
+ /*
+ * If the netlogon server is not reachable then
+ * it is possible that the DC is rebuilding
+ * sysvol and shutdown netlogon for that time.
+ * We should failover to the next dc.
+ */
+ DEBUG(3, ("This is the third problem for this "
+ "particular call, adding DC to the "
+ "negative cache list: %s %s\n", domain->name, domain->dcname));
+ add_failed_connection_entry(domain->name,
+ domain->dcname,
+ result);
+ saf_delete(domain->name);
+ }
+
+ /* Only allow 3 retries */
+ if (netr_attempts < 3) {
+ DEBUG(3, ("The connection to netlogon "
+ "failed, retrying\n"));
+ netr_attempts++;
+ retry = true;
+ continue;
+ }
+ return result;
+ }
+
+ logon_type_i = NetlogonInteractiveInformation;
+ logon_type_n = NetlogonNetworkInformation;
+ if (domain->domain_trust_attribs & LSA_TRUST_ATTRIBUTE_WITHIN_FOREST) {
+ logon_type_i = NetlogonInteractiveTransitiveInformation;
+ logon_type_n = NetlogonNetworkTransitiveInformation;
+ }
+
+ if (domain->domain_trust_attribs & LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE) {
+ logon_type_i = NetlogonInteractiveTransitiveInformation;
+ logon_type_n = NetlogonNetworkTransitiveInformation;
+ }
+
+ if (domain->domain_trust_attribs & LSA_TRUST_ATTRIBUTE_NON_TRANSITIVE) {
+ logon_type_i = NetlogonInteractiveInformation;
+ logon_type_n = NetlogonNetworkInformation;
+ }
+
+ if (domain->domain_trust_attribs & LSA_TRUST_ATTRIBUTE_QUARANTINED_DOMAIN) {
+ logon_type_i = NetlogonInteractiveInformation;
+ logon_type_n = NetlogonNetworkInformation;
+ }
+
+ netr_attempts = 0;
+ if (plaintext_given) {
+ result = rpccli_netlogon_password_logon(
+ netlogon_creds_ctx,
+ netlogon_pipe->binding_handle,
+ mem_ctx,
+ logon_parameters,
+ domainname,
+ username,
+ password,
+ workstation,
+ logon_id,
+ logon_type_i,
+ authoritative,
+ flags,
+ &validation_level,
+ &validation);
+ } else if (interactive) {
+ result = rpccli_netlogon_interactive_logon(
+ netlogon_creds_ctx,
+ netlogon_pipe->binding_handle,
+ mem_ctx,
+ logon_parameters,
+ username,
+ domainname,
+ workstation,
+ logon_id,
+ lm_response,
+ nt_response,
+ logon_type_i,
+ authoritative,
+ flags,
+ &validation_level,
+ &validation);
+ } else {
+ result = rpccli_netlogon_network_logon(
+ netlogon_creds_ctx,
+ netlogon_pipe->binding_handle,
+ mem_ctx,
+ logon_parameters,
+ username,
+ domainname,
+ workstation,
+ logon_id,
+ chal,
+ lm_response,
+ nt_response,
+ logon_type_n,
+ authoritative,
+ flags,
+ &validation_level,
+ &validation);
+ }
+
+ /*
+ * we increment this after the "feature negotiation"
+ * for can_do_samlogon_ex and can_do_validation6
+ */
+ attempts += 1;
+
+ /* We have to try a second time as cm_connect_netlogon
+ might not yet have noticed that the DC has killed
+ our connection. */
+
+ retry = reset_cm_connection_on_error(domain,
+ netlogon_pipe->binding_handle,
+ result);
+ if (retry) {
+ DBG_PREFIX(attempts > 1 ? DBGLVL_NOTICE : DBGLVL_INFO, (
+ "This is problem %d for this "
+ "particular call,"
+ "DOMAIN[%s] DC[%s] - %s\n",
+ attempts,
+ domain->name,
+ domain->dcname,
+ nt_errstr(result)));
+ continue;
+ }
+
+ valid_result = true;
+
+ if (NT_STATUS_EQUAL(result, NT_STATUS_RPC_PROCNUM_OUT_OF_RANGE)) {
+ /*
+ * Got DCERPC_FAULT_OP_RNG_ERROR for SamLogon
+ * (no Ex). This happens against old Samba
+ * DCs, if LogonSamLogonEx() fails with an error
+ * e.g. NT_STATUS_NO_SUCH_USER or NT_STATUS_WRONG_PASSWORD.
+ *
+ * The server will log something like this:
+ * api_net_sam_logon_ex: Failed to marshall NET_R_SAM_LOGON_EX.
+ *
+ * This sets the whole connection into a fault_state mode
+ * and all following request get NT_STATUS_RPC_PROCNUM_OUT_OF_RANGE.
+ *
+ * This also happens to our retry with LogonSamLogonWithFlags()
+ * and LogonSamLogon().
+ *
+ * In order to recover from this situation, we need to
+ * drop the connection.
+ */
+ invalidate_cm_connection(domain);
+ result = NT_STATUS_LOGON_FAILURE;
+ break;
+ }
+
+ } while ( (attempts < 3) && retry );
+
+ if (!valid_result) {
+ /*
+ * This matches what windows does. In a chain of transitive
+ * trusts the ACCESS_DENIED/authoritative=0 is not propagated
+ * instead of NT_STATUS_NO_LOGON_SERVERS/authoritative=1 is
+ * passed along the chain if there's no other DC is available.
+ */
+ DBG_WARNING("Mapping %s/authoritative=%u to "
+ "NT_STATUS_NO_LOGON_SERVERS/authoritative=1 for"
+ "USERNAME[%s] USERDOMAIN[%s] REMOTE-DOMAIN[%s] \n",
+ nt_errstr(result),
+ *authoritative,
+ username,
+ domainname,
+ domain->name);
+ *authoritative = 1;
+ return NT_STATUS_NO_LOGON_SERVERS;
+ }
+
+ if (!NT_STATUS_IS_OK(result)) {
+ return result;
+ }
+
+ switch (validation_level) {
+ case 3:
+ base_ctx = validation->sam3;
+ base_info = &validation->sam3->base;
+ break;
+ case 6:
+ base_ctx = validation->sam6;
+ base_info = &validation->sam6->base;
+ break;
+ default:
+ smb_panic(__location__);
+ }
+
+ if (base_info->acct_flags == 0 || base_info->account_name.string == NULL) {
+ struct dom_sid user_sid;
+ struct dom_sid_buf sid_buf;
+ const char *acct_flags_src = "server";
+ const char *acct_name_src = "server";
+
+ /*
+ * Handle the case where a NT4 DC does not fill in the acct_flags in
+ * the samlogon reply info3. Yes, in 2021, there are still admins
+ * around with real NT4 DCs.
+ *
+ * We used to call dcerpc_samr_QueryUserInfo(level=16) to fetch
+ * acct_flags, but as NT4 DCs reject authentication with workstation
+ * accounts with NT_STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT, even if
+ * MSV1_0_ALLOW_WORKSTATION_TRUST_ACCOUNT is specified, we only ever got
+ * ACB_NORMAL back (maybe with ACB_PWNOEXP in addition).
+ *
+ * For network logons NT4 DCs also skip the
+ * account_name, so we have to fallback to the
+ * one given by the client.
+ */
+
+ if (base_info->acct_flags == 0) {
+ base_info->acct_flags = ACB_NORMAL;
+ if (base_info->force_password_change == NTTIME_MAX) {
+ base_info->acct_flags |= ACB_PWNOEXP;
+ }
+ acct_flags_src = "calculated";
+ }
+
+ if (base_info->account_name.string == NULL) {
+ base_info->account_name.string = talloc_strdup(base_ctx,
+ username);
+ if (base_info->account_name.string == NULL) {
+ TALLOC_FREE(validation);
+ return NT_STATUS_NO_MEMORY;
+ }
+ acct_name_src = "client";
+ }
+
+ sid_compose(&user_sid, base_info->domain_sid, base_info->rid);
+
+ DBG_DEBUG("Fallback to %s_acct_flags[0x%x] %s_acct_name[%s] for %s\n",
+ acct_flags_src,
+ base_info->acct_flags,
+ acct_name_src,
+ base_info->account_name.string,
+ dom_sid_str_buf(&user_sid, &sid_buf));
+ }
+
+ *_validation_level = validation_level;
+ *_validation = validation;
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS nt_dual_auth_passdb(TALLOC_CTX *mem_ctx,
+ fstring name_user,
+ fstring name_domain,
+ const char *pass,
+ uint64_t logon_id,
+ const char *client_name,
+ const int client_pid,
+ const struct tsocket_address *remote,
+ const struct tsocket_address *local,
+ uint8_t *authoritative,
+ struct netr_SamInfo3 **info3)
+{
+ unsigned char local_nt_response[24];
+ uchar chal[8];
+ DATA_BLOB chal_blob;
+ DATA_BLOB lm_resp;
+ DATA_BLOB nt_resp;
+
+ /* do password magic */
+
+ generate_random_buffer(chal, sizeof(chal));
+ chal_blob = data_blob_const(chal, sizeof(chal));
+
+ if (lp_client_ntlmv2_auth()) {
+ DATA_BLOB server_chal;
+ DATA_BLOB names_blob;
+ server_chal = data_blob_const(chal, 8);
+
+ /* note that the 'workgroup' here is for the local
+ machine. The 'server name' must match the
+ 'workstation' passed to the actual SamLogon call.
+ */
+ names_blob = NTLMv2_generate_names_blob(mem_ctx,
+ lp_netbios_name(),
+ lp_workgroup());
+
+ if (!SMBNTLMv2encrypt(mem_ctx, name_user, name_domain,
+ pass, &server_chal, &names_blob,
+ &lm_resp, &nt_resp, NULL, NULL)) {
+ data_blob_free(&names_blob);
+ DEBUG(0, ("SMBNTLMv2encrypt() failed!\n"));
+ return NT_STATUS_NO_MEMORY;
+ }
+ data_blob_free(&names_blob);
+ } else {
+ int rc;
+ lm_resp = data_blob_null;
+
+ rc = SMBNTencrypt(pass, chal, local_nt_response);
+ if (rc != 0) {
+ DEBUG(0, ("SMBNTencrypt() failed!\n"));
+ return gnutls_error_to_ntstatus(rc,
+ NT_STATUS_ACCESS_DISABLED_BY_POLICY_OTHER);
+ }
+
+ nt_resp = data_blob_talloc(mem_ctx, local_nt_response,
+ sizeof(local_nt_response));
+ }
+
+ return winbindd_dual_auth_passdb(talloc_tos(), 0, name_domain,
+ name_user, logon_id, client_name,
+ client_pid, &chal_blob, &lm_resp,
+ &nt_resp, remote, local,
+ true, /* interactive */
+ authoritative, info3);
+}
+
+static NTSTATUS winbindd_dual_pam_auth_samlogon(
+ TALLOC_CTX *mem_ctx,
+ struct winbindd_domain *domain,
+ const char *user,
+ const char *pass,
+ uint64_t logon_id,
+ const char *client_name,
+ const int client_pid,
+ uint32_t request_flags,
+ const struct tsocket_address *remote,
+ const struct tsocket_address *local,
+ uint16_t *_validation_level,
+ union netr_Validation **_validation)
+{
+ char *name_namespace = NULL;
+ char *name_domain = NULL;
+ char *name_user = NULL;
+ NTSTATUS result;
+ uint8_t authoritative = 1;
+ uint32_t flags = 0;
+ uint16_t validation_level = 0;
+ union netr_Validation *validation = NULL;
+ bool ok;
+
+ DEBUG(10,("winbindd_dual_pam_auth_samlogon\n"));
+
+ /* Parse domain and username */
+
+ ok = parse_domain_user(mem_ctx,
+ user,
+ &name_namespace,
+ &name_domain,
+ &name_user);
+ if (!ok) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ /*
+ * We check against domain->name instead of
+ * name_domain, as find_auth_domain() ->
+ * find_domain_from_name_noinit() already decided
+ * that we are in a child for the correct domain.
+ *
+ * name_domain can also be lp_realm()
+ * we need to check against domain->name.
+ */
+ if (strequal(domain->name, get_global_sam_name())) {
+ struct netr_SamInfo3 *info3 = NULL;
+
+ result = nt_dual_auth_passdb(mem_ctx, name_user, name_domain,
+ pass, logon_id, client_name,
+ client_pid, remote, local,
+ &authoritative, &info3);
+
+ /*
+ * We need to try the remote NETLOGON server if this is
+ * not authoritative (for example on the RODC).
+ */
+ if (authoritative != 0) {
+ if (!NT_STATUS_IS_OK(result)) {
+ return result;
+ }
+ result = map_info3_to_validation(mem_ctx,
+ info3,
+ &validation_level,
+ &validation);
+ TALLOC_FREE(info3);
+ if (!NT_STATUS_IS_OK(result)) {
+ return result;
+ }
+
+ goto done;
+ }
+ }
+
+ /* check authentication loop */
+
+ result = winbind_samlogon_retry_loop(domain,
+ mem_ctx,
+ 0,
+ name_user,
+ pass,
+ name_domain,
+ lp_netbios_name(),
+ logon_id,
+ true, /* plaintext_given */
+ data_blob_null,
+ data_blob_null, data_blob_null,
+ true, /* interactive */
+ &authoritative,
+ &flags,
+ &validation_level,
+ &validation);
+ if (!NT_STATUS_IS_OK(result)) {
+ return result;
+ }
+
+done:
+ *_validation_level = validation_level;
+ *_validation = validation;
+
+ return NT_STATUS_OK;
+}
+
+/*
+ * @brief generate an authentication message in the logs.
+ *
+ */
+static void log_authentication(
+ TALLOC_CTX *mem_ctx,
+ const struct winbindd_domain *domain,
+ const char *client_name,
+ pid_t client_pid,
+ uint16_t validation_level,
+ union netr_Validation *validation,
+ const struct timeval start_time,
+ const uint64_t logon_id,
+ const char *command,
+ const char *user_name,
+ const char *domain_name,
+ const char *workstation,
+ const DATA_BLOB lm_resp,
+ const DATA_BLOB nt_resp,
+ const struct tsocket_address *remote,
+ const struct tsocket_address *local,
+ NTSTATUS result)
+{
+ struct auth_usersupplied_info *ui = NULL;
+ struct dom_sid *sid = NULL;
+ struct loadparm_context *lp_ctx = NULL;
+ struct imessaging_context *msg_ctx = NULL;
+ struct netr_SamBaseInfo *base_info = NULL;
+
+ if (validation != NULL) {
+ switch (validation_level) {
+ case 3:
+ base_info = &validation->sam3->base;
+ break;
+ case 6:
+ base_info = &validation->sam6->base;
+ break;
+ default:
+ DBG_WARNING("Unexpected validation level '%d'\n",
+ validation_level);
+ break;
+ }
+ }
+
+ ui = talloc_zero(mem_ctx, struct auth_usersupplied_info);
+ ui->logon_id = logon_id;
+ ui->service_description = "winbind";
+ ui->password.response.nt.length = nt_resp.length;
+ ui->password.response.nt.data = nt_resp.data;
+ ui->password.response.lanman.length = lm_resp.length;
+ ui->password.response.lanman.data = lm_resp.data;
+ if (nt_resp.length == 0 && lm_resp.length == 0) {
+ ui->password_state = AUTH_PASSWORD_PLAIN;
+ } else {
+ ui->password_state = AUTH_PASSWORD_RESPONSE;
+ }
+ /*
+ * In the event of a failure ui->auth_description will be null,
+ * the logging code handles this correctly so it can be ignored.
+ */
+ ui->auth_description = talloc_asprintf(
+ ui,
+ "%s, %s, %d",
+ command,
+ client_name,
+ client_pid);
+ if (ui->auth_description == NULL) {
+ DBG_ERR("OOM Unable to create auth_description\n");
+ }
+ ui->client.account_name = user_name;
+ ui->client.domain_name = domain_name;
+ ui->workstation_name = workstation;
+ ui->remote_host = remote;
+ ui->local_host = local;
+
+ if (base_info != NULL) {
+ sid = dom_sid_dup(ui, base_info->domain_sid);
+ if (sid != NULL) {
+ sid_append_rid(sid, base_info->rid);
+ }
+ }
+
+ if (lp_auth_event_notification()) {
+ lp_ctx = loadparm_init_s3(ui, loadparm_s3_helpers());
+ msg_ctx = imessaging_client_init(
+ ui, lp_ctx, global_event_context());
+ }
+ log_authentication_event(
+ msg_ctx,
+ lp_ctx,
+ &start_time,
+ ui,
+ result,
+ base_info != NULL ? base_info->logon_domain.string : "",
+ base_info != NULL ? base_info->account_name.string : "",
+ sid,
+ NULL /* client_audit_info */,
+ NULL /* server_audit_info */);
+ TALLOC_FREE(ui);
+}
+
+NTSTATUS _wbint_PamAuth(struct pipes_struct *p,
+ struct wbint_PamAuth *r)
+{
+ struct winbindd_domain *domain = wb_child_domain();
+ NTSTATUS result = NT_STATUS_LOGON_FAILURE;
+ NTSTATUS krb5_result = NT_STATUS_OK;
+ char *name_namespace = NULL;
+ char *name_domain = NULL;
+ char *name_user = NULL;
+ char *mapped_user = NULL;
+ const char *domain_user = NULL;
+ uint16_t validation_level = UINT16_MAX;
+ union netr_Validation *validation = NULL;
+ struct netr_SamBaseInfo *base_info = NULL;
+ NTSTATUS name_map_status = NT_STATUS_UNSUCCESSFUL;
+ bool ok;
+ uint64_t logon_id = 0;
+ const struct timeval start_time = timeval_current();
+ const struct tsocket_address *remote = NULL;
+ const struct tsocket_address *local = NULL;
+ const char *krb5ccname = NULL;
+ uid_t uid;
+ pid_t client_pid;
+
+ if (domain == NULL) {
+ return NT_STATUS_REQUEST_NOT_ACCEPTED;
+ }
+
+ /* Cut client_pid to 32bit */
+ client_pid = r->in.client_pid;
+ if ((uint64_t)client_pid != r->in.client_pid) {
+ DBG_DEBUG("pid out of range\n");
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ /* Cut uid to 32bit */
+ uid = r->in.info->uid;
+ if ((uint64_t)uid != r->in.info->uid) {
+ DBG_DEBUG("uid out of range\n");
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ /*
+ * Generate a logon_id for this session.
+ */
+ logon_id = generate_random_u64();
+ remote = dcesrv_connection_get_remote_address(p->dce_call->conn);
+ local = dcesrv_connection_get_local_address(p->dce_call->conn);
+ DEBUG(3, ("[%"PRIu32"]: dual pam auth %s\n", client_pid,
+ r->in.info->username));
+
+ /* Parse domain and username */
+
+ name_map_status = normalize_name_unmap(p->mem_ctx,
+ r->in.info->username,
+ &mapped_user);
+
+ /* If the name normalization didn't actually do anything,
+ just use the original name */
+
+ if (!NT_STATUS_IS_OK(name_map_status) &&
+ !NT_STATUS_EQUAL(name_map_status, NT_STATUS_FILE_RENAMED))
+ {
+ mapped_user = discard_const(r->in.info->username);
+ }
+
+ ok = parse_domain_user(p->mem_ctx,
+ mapped_user,
+ &name_namespace,
+ &name_domain,
+ &name_user);
+ if (!ok) {
+ result = NT_STATUS_INVALID_PARAMETER;
+ goto done;
+ }
+
+ if (mapped_user != r->in.info->username) {
+ domain_user = talloc_asprintf(talloc_tos(),
+ "%s%c%s",
+ name_domain,
+ *lp_winbind_separator(),
+ name_user);
+ if (domain_user == NULL) {
+ result = NT_STATUS_NO_MEMORY;
+ goto done;
+ }
+ r->in.info->username = domain_user;
+ }
+
+ if (!domain->online) {
+ result = NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND;
+ if (domain->startup) {
+ /* Logons are very important to users. If we're offline and
+ we get a request within the first 30 seconds of startup,
+ try very hard to find a DC and go online. */
+
+ DEBUG(10,("winbindd_dual_pam_auth: domain: %s offline and auth "
+ "request in startup mode.\n", domain->name ));
+
+ winbindd_flush_negative_conn_cache(domain);
+ result = init_dc_connection(domain, false);
+ }
+ }
+
+ DEBUG(10,("winbindd_dual_pam_auth: domain: %s last was %s\n", domain->name, domain->online ? "online":"offline"));
+
+ /* Check for Kerberos authentication */
+ if (domain->online && (r->in.flags & WBFLAG_PAM_KRB5)) {
+ result = winbindd_dual_pam_auth_kerberos(
+ domain,
+ r->in.info->username,
+ r->in.info->password,
+ r->in.info->krb5_cc_type,
+ uid,
+ p->mem_ctx,
+ &validation_level,
+ &validation,
+ &krb5ccname);
+
+ /* save for later */
+ krb5_result = result;
+
+ if (NT_STATUS_IS_OK(result)) {
+ DEBUG(10,("winbindd_dual_pam_auth_kerberos succeeded\n"));
+ goto process_result;
+ }
+
+ DBG_DEBUG("winbindd_dual_pam_auth_kerberos failed: %s\n",
+ nt_errstr(result));
+
+ if (NT_STATUS_EQUAL(result, NT_STATUS_NO_LOGON_SERVERS) ||
+ NT_STATUS_EQUAL(result, NT_STATUS_IO_TIMEOUT) ||
+ NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
+ DEBUG(10,("winbindd_dual_pam_auth_kerberos setting domain to offline\n"));
+ set_domain_offline( domain );
+ goto cached_logon;
+ }
+
+ /* there are quite some NT_STATUS errors where there is no
+ * point in retrying with a samlogon, we explicitly have to take
+ * care not to increase the bad logon counter on the DC */
+
+ if (NT_STATUS_EQUAL(result, NT_STATUS_ACCOUNT_DISABLED) ||
+ NT_STATUS_EQUAL(result, NT_STATUS_ACCOUNT_EXPIRED) ||
+ NT_STATUS_EQUAL(result, NT_STATUS_ACCOUNT_LOCKED_OUT) ||
+ NT_STATUS_EQUAL(result, NT_STATUS_INVALID_LOGON_HOURS) ||
+ NT_STATUS_EQUAL(result, NT_STATUS_INVALID_WORKSTATION) ||
+ NT_STATUS_EQUAL(result, NT_STATUS_LOGON_FAILURE) ||
+ NT_STATUS_EQUAL(result, NT_STATUS_NO_SUCH_USER) ||
+ NT_STATUS_EQUAL(result, NT_STATUS_PASSWORD_EXPIRED) ||
+ NT_STATUS_EQUAL(result, NT_STATUS_PASSWORD_MUST_CHANGE) ||
+ NT_STATUS_EQUAL(result, NT_STATUS_WRONG_PASSWORD)) {
+ goto done;
+ }
+
+ if (r->in.flags & WBFLAG_PAM_FALLBACK_AFTER_KRB5) {
+ DEBUG(3,("falling back to samlogon\n"));
+ goto sam_logon;
+ } else {
+ goto cached_logon;
+ }
+ }
+
+sam_logon:
+ /* Check for Samlogon authentication */
+ if (domain->online) {
+ result = winbindd_dual_pam_auth_samlogon(
+ p->mem_ctx,
+ domain,
+ r->in.info->username,
+ r->in.info->password,
+ logon_id,
+ r->in.client_name,
+ client_pid,
+ r->in.flags,
+ remote,
+ local,
+ &validation_level,
+ &validation);
+
+ if (NT_STATUS_IS_OK(result)) {
+ DEBUG(10,("winbindd_dual_pam_auth_samlogon succeeded\n"));
+
+ switch (validation_level) {
+ case 3:
+ base_info = &validation->sam3->base;
+ break;
+ case 6:
+ base_info = &validation->sam6->base;
+ break;
+ default:
+ DBG_ERR("Bad validation level %d\n",
+ validation_level);
+ result = NT_STATUS_INTERNAL_ERROR;
+ goto done;
+ }
+
+ /* add the Krb5 err if we have one */
+ if ( NT_STATUS_EQUAL(krb5_result, NT_STATUS_TIME_DIFFERENCE_AT_DC ) ) {
+ base_info->user_flags |= LOGON_KRB5_FAIL_CLOCK_SKEW;
+ }
+
+ goto process_result;
+ }
+
+ DEBUG(10,("winbindd_dual_pam_auth_samlogon failed: %s\n",
+ nt_errstr(result)));
+
+ if (NT_STATUS_EQUAL(result, NT_STATUS_NO_LOGON_SERVERS) ||
+ NT_STATUS_EQUAL(result, NT_STATUS_IO_TIMEOUT) ||
+ NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND))
+ {
+ DEBUG(10,("winbindd_dual_pam_auth_samlogon setting domain to offline\n"));
+ set_domain_offline( domain );
+ goto cached_logon;
+ }
+
+ if (domain->online) {
+ /* We're still online - fail. */
+ goto done;
+ }
+ }
+
+cached_logon:
+ /* Check for Cached logons */
+ if (!domain->online && (r->in.flags & WBFLAG_PAM_CACHED_LOGIN) &&
+ lp_winbind_offline_logon()) {
+ result = winbindd_dual_pam_auth_cached(domain,
+ (r->in.flags & WBFLAG_PAM_KRB5),
+ r->in.info->username,
+ r->in.info->password,
+ r->in.info->krb5_cc_type,
+ uid,
+ p->mem_ctx,
+ &validation_level,
+ &validation,
+ &krb5ccname);
+
+ if (!NT_STATUS_IS_OK(result)) {
+ DEBUG(10,("winbindd_dual_pam_auth_cached failed: %s\n", nt_errstr(result)));
+ goto done;
+ }
+ DEBUG(10,("winbindd_dual_pam_auth_cached succeeded\n"));
+ }
+
+process_result:
+
+ if (NT_STATUS_IS_OK(result)) {
+ struct dom_sid user_sid;
+ TALLOC_CTX *base_ctx = NULL;
+ struct netr_SamInfo3 *info3 = NULL;
+
+ switch (validation_level) {
+ case 3:
+ base_ctx = validation->sam3;
+ base_info = &validation->sam3->base;
+ break;
+ case 6:
+ base_ctx = validation->sam6;
+ base_info = &validation->sam6->base;
+ break;
+ default:
+ DBG_ERR("Bad validation level %d\n", validation_level);
+ result = NT_STATUS_INTERNAL_ERROR;
+ goto done;
+ }
+
+ sid_compose(&user_sid, base_info->domain_sid, base_info->rid);
+
+ if (base_info->full_name.string == NULL) {
+ struct netr_SamInfo3 *cached_info3;
+
+ cached_info3 = netsamlogon_cache_get(p->mem_ctx,
+ &user_sid);
+ if (cached_info3 != NULL &&
+ cached_info3->base.full_name.string != NULL) {
+ base_info->full_name.string = talloc_strdup(
+ base_ctx,
+ cached_info3->base.full_name.string);
+ if (base_info->full_name.string == NULL) {
+ result = NT_STATUS_NO_MEMORY;
+ goto done;
+ }
+ } else {
+
+ /* this might fail so we don't check the return code */
+ wcache_query_user_fullname(domain,
+ base_ctx,
+ &user_sid,
+ &base_info->full_name.string);
+ }
+ }
+
+ result = map_validation_to_info3(talloc_tos(),
+ validation_level,
+ validation,
+ &info3);
+ if (!NT_STATUS_IS_OK(result)) {
+ goto done;
+ }
+
+ wcache_invalidate_samlogon(find_domain_from_name(name_domain),
+ &user_sid);
+ netsamlogon_cache_store(name_user, info3);
+
+ /* save name_to_sid info as early as possible (only if
+ this is our primary domain so we don't invalidate
+ the cache entry by storing the seq_num for the wrong
+ domain). */
+ if ( domain->primary ) {
+ cache_name2sid(domain, name_domain, name_user,
+ SID_NAME_USER, &user_sid);
+ }
+
+ /* Check if the user is in the right group */
+
+ result = check_info3_in_group(info3,
+ r->in.require_membership_of_sid);
+ if (!NT_STATUS_IS_OK(result)) {
+ char *s = NDR_PRINT_STRUCT_STRING(p->mem_ctx,
+ wbint_SidArray,
+ r->in.require_membership_of_sid);
+ DBG_NOTICE("User %s is not in the required groups:\n",
+ r->in.info->username);
+ DEBUGADD(DBGLVL_NOTICE, ("%s", s));
+ DEBUGADD(DBGLVL_NOTICE,
+ ("Plaintext authentication is rejected\n"));
+ goto done;
+ }
+
+ if (!is_allowed_domain(info3->base.logon_domain.string)) {
+ DBG_NOTICE("Authentication failed for user [%s] "
+ "from firewalled domain [%s]\n",
+ info3->base.account_name.string,
+ info3->base.logon_domain.string);
+ result = NT_STATUS_AUTHENTICATION_FIREWALL_FAILED;
+ goto done;
+ }
+
+ r->out.validation = talloc_zero(p->mem_ctx,
+ struct wbint_Validation);
+ if (r->out.validation == NULL) {
+ result = NT_STATUS_NO_MEMORY;
+ goto done;
+ }
+
+ r->out.validation->level = validation_level;
+ r->out.validation->validation = talloc_steal(r->out.validation,
+ validation);
+ r->out.validation->krb5ccname = talloc_steal(r->out.validation,
+ krb5ccname);
+ if ((r->in.flags & WBFLAG_PAM_CACHED_LOGIN)
+ && lp_winbind_offline_logon()) {
+
+ result = winbindd_store_creds(domain,
+ r->in.info->username,
+ r->in.info->password,
+ info3);
+ }
+
+ result = NT_STATUS_OK;
+ }
+
+done:
+ /* give us a more useful (more correct?) error code */
+ if ((NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) ||
+ (NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL)))) {
+ result = NT_STATUS_NO_LOGON_SERVERS;
+ }
+
+ DBG_PREFIX(NT_STATUS_IS_OK(result) ? 5 : 2,
+ ("Plain-text authentication for user %s returned %s"
+ " (PAM: %d)\n",
+ r->in.info->username,
+ nt_errstr(result),
+ nt_status_to_pam(result)));
+
+ /*
+ * Log the winbind pam authentication, the logon_id will tie this to
+ * any of the logons invoked from this request.
+ */
+
+ log_authentication(
+ p->mem_ctx,
+ domain,
+ r->in.client_name,
+ client_pid,
+ validation_level,
+ validation,
+ start_time,
+ logon_id,
+ "PAM_AUTH",
+ name_user,
+ name_domain,
+ NULL,
+ data_blob_null,
+ data_blob_null,
+ remote,
+ local,
+ result);
+
+ if (NT_STATUS_IS_OK(result)) {
+ gpupdate_user_init(r->in.info->username);
+ }
+
+ return result;
+}
+
+NTSTATUS winbind_dual_SamLogon(struct winbindd_domain *domain,
+ TALLOC_CTX *mem_ctx,
+ bool interactive,
+ uint32_t logon_parameters,
+ const char *name_user,
+ const char *name_domain,
+ const char *workstation,
+ const uint64_t logon_id,
+ const char* client_name,
+ const int client_pid,
+ DATA_BLOB chal_blob,
+ DATA_BLOB lm_response,
+ DATA_BLOB nt_response,
+ const struct tsocket_address *remote,
+ const struct tsocket_address *local,
+ uint8_t *authoritative,
+ bool skip_sam,
+ uint32_t *flags,
+ uint16_t *_validation_level,
+ union netr_Validation **_validation)
+{
+ uint16_t validation_level = 0;
+ union netr_Validation *validation = NULL;
+ NTSTATUS result;
+
+ /*
+ * We check against domain->name instead of
+ * name_domain, as find_auth_domain() ->
+ * find_domain_from_name_noinit() already decided
+ * that we are in a child for the correct domain.
+ *
+ * name_domain can also be lp_realm()
+ * we need to check against domain->name.
+ */
+ if (!skip_sam && strequal(domain->name, get_global_sam_name())) {
+ struct netr_SamInfo3 *info3 = NULL;
+
+ result = winbindd_dual_auth_passdb(
+ talloc_tos(),
+ logon_parameters,
+ name_domain, name_user,
+ logon_id,
+ client_name,
+ client_pid,
+ &chal_blob, &lm_response, &nt_response,
+ remote,
+ local,
+ interactive,
+ authoritative,
+ &info3);
+ if (NT_STATUS_IS_OK(result)) {
+ result = map_info3_to_validation(mem_ctx,
+ info3,
+ &validation_level,
+ &validation);
+ TALLOC_FREE(info3);
+ if (!NT_STATUS_IS_OK(result)) {
+ goto done;
+ }
+ }
+
+ /*
+ * We need to try the remote NETLOGON server if this is
+ * not authoritative.
+ */
+ if (*authoritative != 0) {
+ *flags = 0;
+ goto process_result;
+ }
+ }
+
+ result = winbind_samlogon_retry_loop(domain,
+ mem_ctx,
+ logon_parameters,
+ name_user,
+ NULL, /* password */
+ name_domain,
+ /* Bug #3248 - found by Stefan Burkei. */
+ workstation, /* We carefully set this above so use it... */
+ logon_id,
+ false, /* plaintext_given */
+ chal_blob,
+ lm_response,
+ nt_response,
+ interactive,
+ authoritative,
+ flags,
+ &validation_level,
+ &validation);
+ if (!NT_STATUS_IS_OK(result)) {
+ goto done;
+ }
+
+process_result:
+
+ if (NT_STATUS_IS_OK(result)) {
+ struct dom_sid user_sid;
+ TALLOC_CTX *base_ctx = NULL;
+ struct netr_SamBaseInfo *base_info = NULL;
+ struct netr_SamInfo3 *info3 = NULL;
+
+ switch (validation_level) {
+ case 3:
+ base_ctx = validation->sam3;
+ base_info = &validation->sam3->base;
+ break;
+ case 6:
+ base_ctx = validation->sam6;
+ base_info = &validation->sam6->base;
+ break;
+ default:
+ result = NT_STATUS_INTERNAL_ERROR;
+ goto done;
+ }
+
+ sid_compose(&user_sid, base_info->domain_sid, base_info->rid);
+
+ if (base_info->full_name.string == NULL) {
+ struct netr_SamInfo3 *cached_info3;
+
+ cached_info3 = netsamlogon_cache_get(mem_ctx,
+ &user_sid);
+ if (cached_info3 != NULL &&
+ cached_info3->base.full_name.string != NULL)
+ {
+ base_info->full_name.string = talloc_strdup(
+ base_ctx,
+ cached_info3->base.full_name.string);
+ } else {
+
+ /* this might fail so we don't check the return code */
+ wcache_query_user_fullname(domain,
+ base_ctx,
+ &user_sid,
+ &base_info->full_name.string);
+ }
+ }
+
+ result = map_validation_to_info3(talloc_tos(),
+ validation_level,
+ validation,
+ &info3);
+ if (!NT_STATUS_IS_OK(result)) {
+ goto done;
+ }
+ wcache_invalidate_samlogon(find_domain_from_name(name_domain),
+ &user_sid);
+ netsamlogon_cache_store(name_user, info3);
+ TALLOC_FREE(info3);
+ }
+
+done:
+
+ /* give us a more useful (more correct?) error code */
+ if ((NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) ||
+ (NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL)))) {
+ result = NT_STATUS_NO_LOGON_SERVERS;
+ }
+
+ DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2,
+ ("NTLM CRAP authentication for user [%s]\\[%s] returned %s\n",
+ name_domain,
+ name_user,
+ nt_errstr(result)));
+
+ if (!NT_STATUS_IS_OK(result)) {
+ return result;
+ }
+
+ *_validation_level = validation_level;
+ *_validation = validation;
+ return NT_STATUS_OK;
+}
+
+NTSTATUS _wbint_PamAuthCrap(struct pipes_struct *p, struct wbint_PamAuthCrap *r)
+{
+ struct winbindd_domain *domain = wb_child_domain();
+ NTSTATUS result;
+ uint64_t logon_id = 0;
+ uint8_t authoritative = 1;
+ uint32_t flags = 0;
+ uint16_t validation_level = UINT16_MAX;
+ union netr_Validation *validation = NULL;
+ const struct timeval start_time = timeval_current();
+ const struct tsocket_address *remote = NULL;
+ const struct tsocket_address *local = NULL;
+ struct netr_SamInfo3 *info3 = NULL;
+ pid_t client_pid;
+
+ if (domain == NULL) {
+ return NT_STATUS_REQUEST_NOT_ACCEPTED;
+ }
+
+ /* Cut client_pid to 32bit */
+ client_pid = r->in.client_pid;
+ if ((uint64_t)client_pid != r->in.client_pid) {
+ DBG_DEBUG("pid out of range\n");
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ logon_id = generate_random_u64();
+ remote = dcesrv_connection_get_remote_address(p->dce_call->conn);
+ local = dcesrv_connection_get_local_address(p->dce_call->conn);
+
+ DBG_NOTICE("[%"PRIu32"]: pam auth crap domain: %s user: %s\n",
+ client_pid, r->in.domain, r->in.user);
+
+ result = winbind_dual_SamLogon(domain,
+ p->mem_ctx,
+ false, /* interactive */
+ r->in.logon_parameters,
+ r->in.user,
+ r->in.domain,
+ r->in.workstation,
+ logon_id,
+ r->in.client_name,
+ client_pid,
+ r->in.chal,
+ r->in.lm_resp,
+ r->in.nt_resp,
+ remote,
+ local,
+ &authoritative,
+ false,
+ &flags,
+ &validation_level,
+ &validation);
+ if (!NT_STATUS_IS_OK(result)) {
+ goto done;
+ }
+
+ result = map_validation_to_info3(p->mem_ctx,
+ validation_level,
+ validation,
+ &info3);
+ if (!NT_STATUS_IS_OK(result)) {
+ goto done;
+ }
+
+ /* Check if the user is in the right group */
+ result = check_info3_in_group(info3, r->in.require_membership_of_sid);
+ if (!NT_STATUS_IS_OK(result)) {
+ char *s = NDR_PRINT_STRUCT_STRING(p->mem_ctx,
+ wbint_SidArray,
+ r->in.require_membership_of_sid);
+ DBG_NOTICE("User %s is not in the required groups:\n",
+ r->in.user);
+ DEBUGADD(DBGLVL_NOTICE, ("%s", s));
+ DEBUGADD(DBGLVL_NOTICE,
+ ("CRAP authentication is rejected\n"));
+ goto done;
+ }
+
+ if (!is_allowed_domain(info3->base.logon_domain.string)) {
+ DBG_NOTICE("Authentication failed for user [%s] "
+ "from firewalled domain [%s]\n",
+ info3->base.account_name.string,
+ info3->base.logon_domain.string);
+ result = NT_STATUS_AUTHENTICATION_FIREWALL_FAILED;
+ goto done;
+ }
+
+ r->out.validation = talloc_zero(p->mem_ctx,
+ struct wbint_PamAuthCrapValidation);
+ if (r->out.validation == NULL) {
+ result = NT_STATUS_NO_MEMORY;
+ goto done;
+ }
+
+ r->out.validation->level = validation_level;
+ r->out.validation->validation = talloc_move(r->out.validation,
+ &validation);
+done:
+
+ if (r->in.flags & WBFLAG_PAM_NT_STATUS_SQUASH) {
+ result = nt_status_squash(result);
+ }
+
+ *r->out.authoritative = authoritative;
+
+ /*
+ * Log the winbind pam authentication, the logon_id will tie this to
+ * any of the logons invoked from this request.
+ */
+ log_authentication(
+ p->mem_ctx,
+ domain,
+ r->in.client_name,
+ client_pid,
+ r->out.validation->level,
+ r->out.validation->validation,
+ start_time,
+ logon_id,
+ "NTLM_AUTH",
+ r->in.user,
+ r->in.domain,
+ r->in.workstation,
+ r->in.lm_resp,
+ r->in.nt_resp,
+ remote,
+ local,
+ result);
+
+ return result;
+}
+
+NTSTATUS _wbint_PamAuthChangePassword(struct pipes_struct *p,
+ struct wbint_PamAuthChangePassword *r)
+{
+ struct winbindd_domain *contact_domain = wb_child_domain();
+ struct policy_handle dom_pol;
+ struct rpc_pipe_client *cli = NULL;
+ bool got_info = false;
+ struct samr_DomInfo1 *info = NULL;
+ struct userPwdChangeFailureInformation *reject = NULL;
+ NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+ char *namespace = NULL;
+ char *domain = NULL;
+ char *user = NULL;
+ struct dcerpc_binding_handle *b = NULL;
+ bool ok;
+ pid_t client_pid;
+
+ ZERO_STRUCT(dom_pol);
+
+ if (contact_domain == NULL) {
+ return NT_STATUS_REQUEST_NOT_ACCEPTED;
+ }
+
+ /* Cut client_pid to 32bit */
+ client_pid = r->in.client_pid;
+ if ((uint64_t)client_pid != r->in.client_pid) {
+ DBG_DEBUG("pid out of range\n");
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ DBG_NOTICE("[%"PRIu32"]: dual pam chauthtok %s\n",
+ client_pid, r->in.user);
+
+ ok = parse_domain_user(p->mem_ctx,
+ r->in.user,
+ &namespace,
+ &domain,
+ &user);
+ if (!ok) {
+ goto done;
+ }
+
+ if (!is_allowed_domain(domain)) {
+ DBG_NOTICE("Authentication failed for user [%s] "
+ "from firewalled domain [%s]\n",
+ user, domain);
+ result = NT_STATUS_AUTHENTICATION_FIREWALL_FAILED;
+ goto done;
+ }
+
+ /* Initialize reject reason */
+ *r->out.reject_reason = Undefined;
+
+ /* Get sam handle */
+
+ result = cm_connect_sam(contact_domain,
+ p->mem_ctx,
+ true,
+ &cli,
+ &dom_pol);
+ if (!NT_STATUS_IS_OK(result)) {
+ DEBUG(1, ("could not get SAM handle on DC for %s\n", domain));
+ goto done;
+ }
+
+ b = cli->binding_handle;
+
+ status = dcerpc_samr_chgpasswd_user4(cli->binding_handle,
+ p->mem_ctx,
+ cli->srv_name_slash,
+ user,
+ r->in.old_password,
+ r->in.new_password,
+ &result);
+ if (NT_STATUS_IS_OK(status) && NT_STATUS_IS_OK(result)) {
+ /* Password successfully changed. */
+ goto done;
+ }
+ if (!NT_STATUS_IS_OK(status)) {
+ if (NT_STATUS_EQUAL(status, NT_STATUS_RPC_PROCNUM_OUT_OF_RANGE) ||
+ NT_STATUS_EQUAL(status, NT_STATUS_NOT_SUPPORTED) ||
+ NT_STATUS_EQUAL(status, NT_STATUS_NOT_IMPLEMENTED)) {
+ /* DO NOT FALLBACK TO RC4 */
+ if (lp_weak_crypto() == SAMBA_WEAK_CRYPTO_DISALLOWED) {
+ result = NT_STATUS_STRONG_CRYPTO_NOT_SUPPORTED;
+ goto process_result;
+ }
+ }
+ } else {
+ /* Password change was unsuccessful. */
+ if (!NT_STATUS_IS_OK(result)) {
+ goto done;
+ }
+ }
+
+ result = rpccli_samr_chgpasswd_user3(cli,
+ p->mem_ctx,
+ user,
+ r->in.new_password,
+ r->in.old_password,
+ &info,
+ &reject);
+
+ /* Windows 2003 returns NT_STATUS_PASSWORD_RESTRICTION */
+
+ if (NT_STATUS_EQUAL(result, NT_STATUS_PASSWORD_RESTRICTION) ) {
+
+ *r->out.dominfo = talloc_steal(p->mem_ctx, info);
+ *r->out.reject_reason = reject->extendedFailureReason;
+
+ got_info = true;
+ }
+
+ /* atm the pidl generated rpccli_samr_ChangePasswordUser3 function will
+ * return with NT_STATUS_BUFFER_TOO_SMALL for w2k dcs as w2k just
+ * returns with 4byte error code (NT_STATUS_NOT_SUPPORTED) which is too
+ * short to comply with the samr_ChangePasswordUser3 idl - gd */
+
+ /* only fallback when the chgpasswd_user3 call is not supported */
+ if (NT_STATUS_EQUAL(result, NT_STATUS_RPC_PROCNUM_OUT_OF_RANGE) ||
+ NT_STATUS_EQUAL(result, NT_STATUS_NOT_SUPPORTED) ||
+ NT_STATUS_EQUAL(result, NT_STATUS_BUFFER_TOO_SMALL) ||
+ NT_STATUS_EQUAL(result, NT_STATUS_NOT_IMPLEMENTED)) {
+
+ DEBUG(10,("Password change with chgpasswd_user3 failed with: %s, retrying chgpasswd_user2\n",
+ nt_errstr(result)));
+
+ result = rpccli_samr_chgpasswd_user2(cli,
+ p->mem_ctx,
+ user,
+ r->in.new_password,
+ r->in.old_password);
+
+ /* Windows 2000 returns NT_STATUS_ACCOUNT_RESTRICTION.
+ Map to the same status code as Windows 2003. */
+
+ if ( NT_STATUS_EQUAL(NT_STATUS_ACCOUNT_RESTRICTION, result ) ) {
+ result = NT_STATUS_PASSWORD_RESTRICTION;
+ }
+ }
+
+done:
+
+ if (NT_STATUS_IS_OK(result)
+ && (r->in.flags & WBFLAG_PAM_CACHED_LOGIN)
+ && lp_winbind_offline_logon()) {
+ result = winbindd_update_creds_by_name(contact_domain, user,
+ r->in.new_password);
+ /* Again, this happens when we login from gdm or xdm
+ * and the password expires, *BUT* cached credentials
+ * don't exist. winbindd_update_creds_by_name()
+ * returns NT_STATUS_NO_SUCH_USER.
+ * This is not a failure.
+ * --- BoYang
+ * */
+ if (NT_STATUS_EQUAL(result, NT_STATUS_NO_SUCH_USER)) {
+ result = NT_STATUS_OK;
+ }
+
+ if (!NT_STATUS_IS_OK(result)) {
+ DEBUG(10, ("Failed to store creds: %s\n",
+ nt_errstr(result)));
+ goto process_result;
+ }
+ }
+
+ if (!NT_STATUS_IS_OK(result) && !got_info && contact_domain) {
+
+ NTSTATUS policy_ret;
+
+ policy_ret = get_password_policy(contact_domain,
+ p->mem_ctx,
+ &info);
+
+ /* failure of this is non critical, it will just provide no
+ * additional information to the client why the change has
+ * failed - Guenther */
+
+ if (!NT_STATUS_IS_OK(policy_ret)) {
+ DEBUG(10,("Failed to get password policies: %s\n", nt_errstr(policy_ret)));
+ goto process_result;
+ }
+
+ *r->out.dominfo = talloc_steal(p->mem_ctx, info);
+ }
+
+process_result:
+
+ if (strequal(contact_domain->name, get_global_sam_name())) {
+ /* FIXME: internal rpc pipe does not cache handles yet */
+ if (b) {
+ if (is_valid_policy_hnd(&dom_pol)) {
+ NTSTATUS _result;
+ dcerpc_samr_Close(b,
+ p->mem_ctx,
+ &dom_pol,
+ &_result);
+ }
+ TALLOC_FREE(cli);
+ }
+ }
+
+ DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2,
+ ("Password change for user [%s]\\[%s] returned %s (PAM: %d)\n",
+ domain,
+ user,
+ nt_errstr(result),
+ nt_status_to_pam(result)));
+
+ return result;
+}
+
+NTSTATUS _wbint_PamLogOff(struct pipes_struct *p, struct wbint_PamLogOff *r)
+{
+ struct winbindd_domain *domain = wb_child_domain();
+ NTSTATUS result = NT_STATUS_NOT_SUPPORTED;
+ pid_t client_pid;
+ uid_t user_uid;
+
+ if (domain == NULL) {
+ return NT_STATUS_REQUEST_NOT_ACCEPTED;
+ }
+
+ /* Cut client_pid to 32bit */
+ client_pid = r->in.client_pid;
+ if ((uint64_t)client_pid != r->in.client_pid) {
+ DBG_DEBUG("pid out of range\n");
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ /* Cut uid to 32bit */
+ user_uid = r->in.uid;
+ if ((uint64_t)user_uid != r->in.uid) {
+ DBG_DEBUG("uid out of range\n");
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ DBG_NOTICE("[%"PRIu32"]: pam dual logoff %s\n", client_pid, r->in.user);
+
+ if (!(r->in.flags & WBFLAG_PAM_KRB5)) {
+ result = NT_STATUS_OK;
+ goto process_result;
+ }
+
+ if ((r->in.krb5ccname == NULL) || (strlen(r->in.krb5ccname) == 0)) {
+ result = NT_STATUS_OK;
+ goto process_result;
+ }
+
+#ifdef HAVE_KRB5
+
+ if (user_uid == (uid_t)-1) {
+ DBG_DEBUG("Invalid uid for user '%s'\n", r->in.user);
+ goto process_result;
+ }
+
+ /* what we need here is to find the corresponding krb5 ccache name *we*
+ * created for a given username and destroy it */
+
+ if (!ccache_entry_exists(r->in.user)) {
+ result = NT_STATUS_OK;
+ DBG_DEBUG("No entry found for user '%s'.\n", r->in.user);
+ goto process_result;
+ }
+
+ if (!ccache_entry_identical(r->in.user, user_uid, r->in.krb5ccname)) {
+ DBG_DEBUG("Cached entry differs for user '%s'\n", r->in.user);
+ goto process_result;
+ }
+
+ result = remove_ccache(r->in.user);
+ if (!NT_STATUS_IS_OK(result)) {
+ DBG_DEBUG("Failed to remove ccache for user '%s': %s\n",
+ r->in.user, nt_errstr(result));
+ goto process_result;
+ }
+
+ /*
+ * Remove any mlock'ed memory creds in the child
+ * we might be using for krb5 ticket renewal.
+ */
+
+ winbindd_delete_memory_creds(r->in.user);
+
+#else
+ result = NT_STATUS_NOT_SUPPORTED;
+#endif
+
+process_result:
+
+ return result;
+}
+
+/* Change user password with auth crap*/
+
+NTSTATUS _wbint_PamAuthCrapChangePassword(struct pipes_struct *p,
+ struct wbint_PamAuthCrapChangePassword *r)
+{
+ NTSTATUS result;
+ char *namespace = NULL;
+ char *domain = NULL;
+ char *user = NULL;
+ struct policy_handle dom_pol;
+ struct winbindd_domain *contact_domain = wb_child_domain();
+ struct rpc_pipe_client *cli = NULL;
+ struct dcerpc_binding_handle *b = NULL;
+ TALLOC_CTX *frame = talloc_stackframe();
+ pid_t client_pid;
+
+ ZERO_STRUCT(dom_pol);
+
+ if (contact_domain == NULL) {
+ return NT_STATUS_REQUEST_NOT_ACCEPTED;
+ }
+
+ /* Cut client_pid to 32bit */
+ client_pid = r->in.client_pid;
+ if ((uint64_t)client_pid != r->in.client_pid) {
+ DBG_DEBUG("pid out of range\n");
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ DBG_NOTICE("[%"PRIu32"]: pam change pswd auth crap domain: %s "
+ "user: %s\n", client_pid, r->in.domain, r->in.user);
+
+ if (lp_winbind_offline_logon()) {
+ DEBUG(0,("Refusing password change as winbind offline logons are enabled. "));
+ DEBUGADD(0,("Changing passwords here would risk inconsistent logons\n"));
+ result = NT_STATUS_ACCESS_DENIED;
+ goto done;
+ }
+
+ if (r->in.domain != NULL && strlen(r->in.domain) > 0) {
+ user = talloc_strdup(frame, "");
+ namespace = talloc_strdup(frame, "");
+ domain = talloc_strdup(frame, r->in.domain);
+ if (domain == NULL || user == NULL || namespace == NULL) {
+ result = NT_STATUS_NO_MEMORY;
+ goto done;
+ }
+
+ } else {
+ bool ok;
+
+ ok = parse_domain_user(frame,
+ r->in.user,
+ &namespace,
+ &domain,
+ &user);
+ if (!ok) {
+ result = NT_STATUS_INVALID_PARAMETER;
+ goto done;
+ }
+
+ if (strlen(domain) == 0) {
+ DBG_NOTICE("no domain specified with username (%s) - "
+ "failing auth\n", r->in.user);
+ result = NT_STATUS_NO_SUCH_USER;
+ goto done;
+ }
+ }
+
+ if (!*domain && lp_winbind_use_default_domain()) {
+ TALLOC_FREE(domain);
+ domain = talloc_strdup(frame, lp_workgroup());
+ if (domain == NULL) {
+ result = NT_STATUS_NO_MEMORY;
+ goto done;
+ }
+ }
+
+ if (!is_allowed_domain(domain)) {
+ DBG_NOTICE("Authentication failed for user [%s] "
+ "from firewalled domain [%s]\n",
+ r->in.user,
+ domain);
+ result = NT_STATUS_AUTHENTICATION_FIREWALL_FAILED;
+ goto done;
+ }
+
+ if(!*user) {
+ TALLOC_FREE(user);
+ user = talloc_strdup(frame, r->in.user);
+ if (user == NULL) {
+ result = NT_STATUS_NO_SUCH_USER;
+ goto done;
+ }
+ }
+
+ /* Get sam handle */
+
+ result = cm_connect_sam(contact_domain,
+ p->mem_ctx,
+ true,
+ &cli,
+ &dom_pol);
+ if (!NT_STATUS_IS_OK(result)) {
+ DEBUG(1, ("could not get SAM handle on DC for %s\n", domain));
+ goto done;
+ }
+
+ b = cli->binding_handle;
+
+ result = rpccli_samr_chng_pswd_auth_crap(cli,
+ p->mem_ctx,
+ user,
+ r->in.new_nt_pswd,
+ r->in.old_nt_hash_enc,
+ r->in.new_lm_pswd,
+ r->in.old_lm_hash_enc);
+
+ done:
+
+ if (strequal(contact_domain->name, get_global_sam_name())) {
+ /* FIXME: internal rpc pipe does not cache handles yet */
+ if (b) {
+ if (is_valid_policy_hnd(&dom_pol)) {
+ NTSTATUS _result;
+ dcerpc_samr_Close(b,
+ p->mem_ctx,
+ &dom_pol,
+ &_result);
+ }
+ TALLOC_FREE(cli);
+ }
+ }
+
+ DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2,
+ ("Password change for user [%s]\\[%s] returned %s (PAM: %d)\n",
+ domain, user,
+ nt_errstr(result),
+ nt_status_to_pam(result)));
+ TALLOC_FREE(frame);
+ return result;
+}
+
+#ifdef HAVE_KRB5
+static NTSTATUS extract_pac_vrfy_sigs(TALLOC_CTX *mem_ctx, DATA_BLOB pac_blob,
+ struct PAC_DATA **p_pac_data)
+{
+ krb5_context krbctx = NULL;
+ krb5_error_code k5ret;
+ krb5_keytab keytab;
+ krb5_kt_cursor cursor;
+ krb5_keytab_entry entry;
+ NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
+
+ ZERO_STRUCT(entry);
+ ZERO_STRUCT(cursor);
+
+ k5ret = smb_krb5_init_context_common(&krbctx);
+ if (k5ret) {
+ DBG_ERR("kerberos init context failed (%s)\n",
+ error_message(k5ret));
+ status = krb5_to_nt_status(k5ret);
+ goto out;
+ }
+
+ k5ret = gse_krb5_get_server_keytab(krbctx, &keytab);
+ if (k5ret) {
+ DEBUG(1, ("Failed to get keytab: %s\n",
+ error_message(k5ret)));
+ status = krb5_to_nt_status(k5ret);
+ goto out_free;
+ }
+
+ k5ret = krb5_kt_start_seq_get(krbctx, keytab, &cursor);
+ if (k5ret) {
+ DEBUG(1, ("Failed to start seq: %s\n",
+ error_message(k5ret)));
+ status = krb5_to_nt_status(k5ret);
+ goto out_keytab;
+ }
+
+ k5ret = krb5_kt_next_entry(krbctx, keytab, &entry, &cursor);
+ while (k5ret == 0) {
+ status = kerberos_decode_pac(mem_ctx,
+ pac_blob,
+ krbctx,
+ NULL, /* krbtgt_keyblock */
+ KRB5_KT_KEY(&entry), /* service_keyblock */
+ NULL, /* client_principal */
+ 0, /* tgs_authtime */
+ p_pac_data);
+ if (NT_STATUS_IS_OK(status)) {
+ break;
+ }
+ k5ret = smb_krb5_kt_free_entry(krbctx, &entry);
+ k5ret = krb5_kt_next_entry(krbctx, keytab, &entry, &cursor);
+ }
+
+ k5ret = krb5_kt_end_seq_get(krbctx, keytab, &cursor);
+ if (k5ret) {
+ DEBUG(1, ("Failed to end seq: %s\n",
+ error_message(k5ret)));
+ }
+out_keytab:
+ k5ret = krb5_kt_close(krbctx, keytab);
+ if (k5ret) {
+ DEBUG(1, ("Failed to close keytab: %s\n",
+ error_message(k5ret)));
+ }
+out_free:
+ krb5_free_context(krbctx);
+out:
+ return status;
+}
+
+NTSTATUS winbindd_pam_auth_pac_verify(struct winbindd_cli_state *state,
+ TALLOC_CTX *mem_ctx,
+ bool *p_is_trusted,
+ uint16_t *p_validation_level,
+ union netr_Validation **p_validation)
+{
+ struct winbindd_request *req = state->request;
+ DATA_BLOB pac_blob;
+ struct PAC_DATA *pac_data = NULL;
+ struct PAC_LOGON_INFO *logon_info = NULL;
+ struct PAC_UPN_DNS_INFO *upn_dns_info = NULL;
+ struct netr_SamInfo6 *info6 = NULL;
+ uint16_t validation_level = 0;
+ union netr_Validation *validation = NULL;
+ struct netr_SamInfo3 *info3_copy = NULL;
+ NTSTATUS result;
+ bool is_trusted = false;
+ uint32_t i;
+ TALLOC_CTX *tmp_ctx = NULL;
+
+ tmp_ctx = talloc_new(mem_ctx);
+ if (tmp_ctx == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ *p_is_trusted = false;
+ *p_validation_level = 0;
+ *p_validation = NULL;
+
+ pac_blob = data_blob_const(req->extra_data.data, req->extra_len);
+ result = extract_pac_vrfy_sigs(tmp_ctx, pac_blob, &pac_data);
+ if (NT_STATUS_IS_OK(result)) {
+ is_trusted = true;
+ }
+ if (NT_STATUS_EQUAL(result, NT_STATUS_ACCESS_DENIED)) {
+ /* Try without signature verification */
+ result = kerberos_decode_pac(tmp_ctx,
+ pac_blob,
+ NULL, /* krb5_context */
+ NULL, /* krbtgt_keyblock */
+ NULL, /* service_keyblock */
+ NULL, /* client_principal */
+ 0, /* tgs_authtime */
+ &pac_data);
+ }
+ if (!NT_STATUS_IS_OK(result)) {
+ DEBUG(1, ("Error during PAC signature verification: %s\n",
+ nt_errstr(result)));
+ goto out;
+ }
+
+ for (i=0; i < pac_data->num_buffers; i++) {
+ if (pac_data->buffers[i].type == PAC_TYPE_LOGON_INFO) {
+ logon_info = pac_data->buffers[i].info->logon_info.info;
+ continue;
+ }
+ if (pac_data->buffers[i].type == PAC_TYPE_UPN_DNS_INFO) {
+ upn_dns_info = &pac_data->buffers[i].info->upn_dns_info;
+ continue;
+ }
+ }
+
+ result = create_info6_from_pac(tmp_ctx,
+ logon_info,
+ upn_dns_info,
+ &info6);
+ if (!NT_STATUS_IS_OK(result)) {
+ goto out;
+ }
+
+ if (!is_allowed_domain(info6->base.logon_domain.string)) {
+ DBG_NOTICE("Authentication failed for user [%s] "
+ "from firewalled domain [%s]\n",
+ info6->base.account_name.string,
+ info6->base.logon_domain.string);
+ result = NT_STATUS_AUTHENTICATION_FIREWALL_FAILED;
+ goto out;
+ }
+
+ result = map_info6_to_validation(tmp_ctx,
+ info6,
+ &validation_level,
+ &validation);
+ if (!NT_STATUS_IS_OK(result)) {
+ goto out;
+ }
+
+ result = map_validation_to_info3(tmp_ctx,
+ validation_level,
+ validation,
+ &info3_copy);
+ if (!NT_STATUS_IS_OK(result)) {
+ goto out;
+ }
+
+ if (is_trusted) {
+ /*
+ * Signature verification succeeded, we can
+ * trust the PAC and prime the netsamlogon
+ * and name2sid caches. DO NOT DO THIS
+ * in the signature verification failed
+ * code path.
+ */
+ struct winbindd_domain *domain = NULL;
+
+ netsamlogon_cache_store(NULL, info3_copy);
+
+ /*
+ * We're in the parent here, so find the child
+ * pointer from the PAC domain name.
+ */
+ domain = find_lookup_domain_from_name(
+ info3_copy->base.logon_domain.string);
+ if (domain && domain->primary ) {
+ struct dom_sid user_sid;
+ struct dom_sid_buf buf;
+
+ sid_compose(&user_sid,
+ info3_copy->base.domain_sid,
+ info3_copy->base.rid);
+
+ cache_name2sid_trusted(domain,
+ info3_copy->base.logon_domain.string,
+ info3_copy->base.account_name.string,
+ SID_NAME_USER,
+ &user_sid);
+
+ DBG_INFO("PAC for user %s\\%s SID %s primed cache\n",
+ info3_copy->base.logon_domain.string,
+ info3_copy->base.account_name.string,
+ dom_sid_str_buf(&user_sid, &buf));
+ }
+ }
+
+ *p_is_trusted = is_trusted;
+ *p_validation_level = validation_level;
+ *p_validation = talloc_move(mem_ctx, &validation);
+
+ result = NT_STATUS_OK;
+out:
+ TALLOC_FREE(tmp_ctx);
+ return result;
+}
+#else /* HAVE_KRB5 */
+NTSTATUS winbindd_pam_auth_pac_verify(struct winbindd_cli_state *state,
+ TALLOC_CTX *mem_ctx,
+ bool *p_is_trusted,
+ uint16_t *p_validation_level,
+ union netr_Validation **p_validation);
+{
+
+ *p_is_trusted = false;
+ *p_validation_level = 0;
+ *p_validation = NULL;
+ return NT_STATUS_NO_SUCH_USER;
+}
+#endif /* HAVE_KRB5 */
diff --git a/source3/winbindd/winbindd_pam_auth.c b/source3/winbindd/winbindd_pam_auth.c
new file mode 100644
index 0000000..431da09
--- /dev/null
+++ b/source3/winbindd/winbindd_pam_auth.c
@@ -0,0 +1,291 @@
+/*
+ Unix SMB/CIFS implementation.
+ async implementation of WINBINDD_PAM_AUTH
+ Copyright (C) Volker Lendecke 2010
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "winbindd.h"
+#include "libcli/security/dom_sid.h"
+#include "lib/util/string_wrappers.h"
+#include "lib/global_contexts.h"
+#include "librpc/gen_ndr/ndr_winbind_c.h"
+
+static NTSTATUS fake_password_policy(struct winbindd_response *r,
+ uint16_t validation_level,
+ union netr_Validation *validation)
+{
+ const struct netr_SamBaseInfo *bi = NULL;
+ NTTIME min_password_age;
+ NTTIME max_password_age;
+
+ switch (validation_level) {
+ case 3:
+ bi = &validation->sam3->base;
+ break;
+ case 6:
+ bi = &validation->sam6->base;
+ break;
+ default:
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+
+ if (bi->allow_password_change > bi->last_password_change) {
+ min_password_age = bi->allow_password_change -
+ bi->last_password_change;
+ } else {
+ min_password_age = 0;
+ }
+
+ if (bi->force_password_change > bi->last_password_change) {
+ max_password_age = bi->force_password_change -
+ bi->last_password_change;
+ } else {
+ max_password_age = 0;
+ }
+
+ r->data.auth.policy.min_length_password = 0;
+ r->data.auth.policy.password_history = 0;
+ r->data.auth.policy.password_properties = 0;
+ r->data.auth.policy.expire =
+ nt_time_to_unix_abs(&max_password_age);
+ r->data.auth.policy.min_passwordage =
+ nt_time_to_unix_abs(&min_password_age);
+
+ return NT_STATUS_OK;
+}
+
+struct winbindd_pam_auth_state {
+ struct wbint_PamAuth *r;
+ char *name_namespace;
+ char *name_domain;
+ char *name_user;
+};
+
+static void winbindd_pam_auth_done(struct tevent_req *subreq);
+
+struct tevent_req *winbindd_pam_auth_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct winbindd_cli_state *cli,
+ struct winbindd_request *request)
+{
+ struct tevent_req *req, *subreq;
+ struct winbindd_pam_auth_state *state;
+ struct winbindd_domain *domain;
+ char *mapped = NULL;
+ char *auth_user = NULL;
+ NTSTATUS status;
+ bool ok;
+
+ req = tevent_req_create(mem_ctx, &state,
+ struct winbindd_pam_auth_state);
+ if (req == NULL) {
+ return NULL;
+ }
+
+ D_NOTICE("[%s (%u)] Winbind external command PAM_AUTH start.\n"
+ "Authenticating user '%s'.\n",
+ cli->client_name,
+ (unsigned int)cli->pid,
+ request->data.auth.user);
+
+ if (!check_request_flags(request->flags)) {
+ tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER_MIX);
+ return tevent_req_post(req, ev);
+ }
+
+ /* Parse domain and username */
+
+ status = normalize_name_unmap(state, request->data.auth.user, &mapped);
+
+ /* If the name normalization changed something, copy it over the given
+ name */
+
+ if (NT_STATUS_IS_OK(status)
+ || NT_STATUS_EQUAL(status, NT_STATUS_FILE_RENAMED)) {
+ fstrcpy(request->data.auth.user, mapped);
+ }
+
+ auth_user = request->data.auth.user;
+ ok = canonicalize_username(state,
+ &auth_user,
+ &state->name_namespace,
+ &state->name_domain,
+ &state->name_user);
+ if (!ok) {
+ tevent_req_nterror(req, NT_STATUS_NO_SUCH_USER);
+ return tevent_req_post(req, ev);
+ }
+
+ fstrcpy(request->data.auth.user, auth_user);
+
+ domain = find_auth_domain(request->flags, state->name_namespace);
+ if (domain == NULL) {
+ tevent_req_nterror(req, NT_STATUS_NO_SUCH_USER);
+ return tevent_req_post(req, ev);
+ }
+
+ state->r = talloc_zero(state, struct wbint_PamAuth);
+ if (tevent_req_nomem(state->r, req)) {
+ return tevent_req_post(req, ev);
+ }
+
+ state->r->in.client_name = talloc_strdup(
+ state->r, request->client_name);
+ if (tevent_req_nomem(state->r, req)) {
+ return tevent_req_post(req, ev);
+ }
+
+ state->r->in.client_pid = request->pid;
+ state->r->in.flags = request->flags;
+
+ state->r->in.info = talloc_zero(state->r, struct wbint_AuthUserInfo);
+ if (tevent_req_nomem(state->r, req)) {
+ return tevent_req_post(req, ev);
+ }
+
+ state->r->in.info->krb5_cc_type = talloc_strdup(
+ state->r, request->data.auth.krb5_cc_type);
+ if (tevent_req_nomem(state->r, req)) {
+ return tevent_req_post(req, ev);
+ }
+
+ state->r->in.info->password = talloc_strdup(
+ state->r, request->data.auth.pass);
+ if (tevent_req_nomem(state->r, req)) {
+ return tevent_req_post(req, ev);
+ }
+
+ state->r->in.info->username = talloc_strdup(
+ state->r, request->data.auth.user);
+ if (tevent_req_nomem(state->r, req)) {
+ return tevent_req_post(req, ev);
+ }
+
+ state->r->in.info->uid = request->data.auth.uid;
+
+ status = extra_data_to_sid_array(
+ request->data.auth.require_membership_of_sid,
+ state->r,
+ &state->r->in.require_membership_of_sid);
+ if (tevent_req_nterror(req, status)) {
+ return tevent_req_post(req, ev);
+ }
+
+ subreq = dcerpc_wbint_PamAuth_r_send(state,
+ global_event_context(),
+ dom_child_handle(domain),
+ state->r);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, winbindd_pam_auth_done, req);
+ return req;
+}
+
+static void winbindd_pam_auth_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct winbindd_pam_auth_state *state = tevent_req_data(
+ req, struct winbindd_pam_auth_state);
+ NTSTATUS status;
+
+ status = dcerpc_wbint_PamAuth_r_recv(subreq, state);
+ TALLOC_FREE(subreq);
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+
+ if (tevent_req_nterror(req, state->r->out.result)) {
+ return;
+ }
+
+ tevent_req_done(req);
+}
+
+NTSTATUS winbindd_pam_auth_recv(struct tevent_req *req,
+ struct winbindd_response *response)
+{
+ struct winbindd_pam_auth_state *state = tevent_req_data(
+ req, struct winbindd_pam_auth_state);
+ NTSTATUS status;
+
+ D_NOTICE("Winbind external command PAM_AUTH end.\n");
+ if (tevent_req_is_nterror(req, &status)) {
+ set_auth_errors(response, status);
+ return status;
+ }
+
+ response->result = WINBINDD_PENDING;
+
+ status = append_auth_data(response,
+ response,
+ state->r->in.flags,
+ state->r->out.validation->level,
+ state->r->out.validation->validation,
+ state->name_domain,
+ state->name_user);
+ fstrcpy(response->data.auth.krb5ccname,
+ state->r->out.validation->krb5ccname);
+
+ if (state->r->in.flags & WBFLAG_PAM_INFO3_TEXT) {
+ bool ok;
+
+ ok = add_trusted_domain_from_auth(
+ state->r->out.validation->level,
+ &response->data.auth.info3,
+ &response->data.auth.info6);
+ if (!ok) {
+ DBG_ERR("add_trusted_domain_from_auth failed\n");
+ set_auth_errors(response, NT_STATUS_LOGON_FAILURE);
+ return NT_STATUS_LOGON_FAILURE;
+ }
+ }
+
+ if (state->r->in.flags & WBFLAG_PAM_CACHED_LOGIN) {
+
+ /* Store in-memory creds for single-signon using ntlm_auth. */
+
+ status = winbindd_add_memory_creds(
+ state->r->in.info->username,
+ state->r->in.info->uid,
+ state->r->in.info->password);
+ D_DEBUG("winbindd_add_memory_creds returned: %s\n",
+ nt_errstr(status));
+ }
+
+ if (state->r->in.flags & WBFLAG_PAM_GET_PWD_POLICY) {
+ /*
+ * WBFLAG_PAM_GET_PWD_POLICY is not used within
+ * any Samba caller anymore.
+ *
+ * We just fake this based on the effective values
+ * for the user, for legacy callers.
+ */
+ status = fake_password_policy(response,
+ state->r->out.validation->level,
+ state->r->out.validation->validation);
+ if (!NT_STATUS_IS_OK(status)) {
+ DBG_ERR("Failed to fake password policy: %s\n",
+ nt_errstr(status));
+ set_auth_errors(response, status);
+ return status;
+ }
+ }
+
+ return NT_STATUS_OK;
+}
diff --git a/source3/winbindd/winbindd_pam_auth_crap.c b/source3/winbindd/winbindd_pam_auth_crap.c
new file mode 100644
index 0000000..e6a32c7
--- /dev/null
+++ b/source3/winbindd/winbindd_pam_auth_crap.c
@@ -0,0 +1,285 @@
+/*
+ Unix SMB/CIFS implementation.
+ async implementation of WINBINDD_PAM_AUTH_CRAP
+ Copyright (C) Volker Lendecke 2010
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "winbindd.h"
+#include "rpc_client/util_netlogon.h"
+#include "libcli/security/dom_sid.h"
+#include "lib/util/string_wrappers.h"
+#include "lib/global_contexts.h"
+#include "librpc/gen_ndr/ndr_winbind_c.h"
+
+struct winbindd_pam_auth_crap_state {
+ uint8_t authoritative;
+ uint32_t flags;
+ bool pac_is_trusted;
+ char *domain;
+ char *user;
+ struct wbint_PamAuthCrapValidation validation;
+ NTSTATUS result;
+};
+
+static void winbindd_pam_auth_crap_done(struct tevent_req *subreq);
+
+struct tevent_req *winbindd_pam_auth_crap_send(
+ TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct winbindd_cli_state *cli,
+ struct winbindd_request *request)
+{
+ struct tevent_req *req, *subreq;
+ struct winbindd_pam_auth_crap_state *state;
+ struct winbindd_domain *domain;
+ const char *auth_domain = NULL;
+ DATA_BLOB lm_resp = data_blob_null;
+ DATA_BLOB nt_resp = data_blob_null;
+ DATA_BLOB chal = data_blob_null;
+ struct wbint_SidArray *require_membership_of_sid = NULL;
+ NTSTATUS status;
+ bool lmlength_ok = false;
+ bool ntlength_ok = false;
+ bool pwlength_ok = false;
+
+ req = tevent_req_create(mem_ctx, &state,
+ struct winbindd_pam_auth_crap_state);
+ if (req == NULL) {
+ return NULL;
+ }
+ state->authoritative = 1;
+ state->flags = request->flags;
+
+ if (state->flags & WBFLAG_PAM_AUTH_PAC) {
+ state->result = winbindd_pam_auth_pac_verify(cli,
+ state,
+ &state->pac_is_trusted,
+ &state->validation.level,
+ &state->validation.validation);
+ if (tevent_req_nterror(req, state->result)) {
+ return tevent_req_post(req, ev);
+ }
+
+ tevent_req_done(req);
+ return tevent_req_post(req, ev);
+ }
+
+ /* Ensure null termination */
+ request->data.auth_crap.user[
+ sizeof(request->data.auth_crap.user)-1] = '\0';
+ request->data.auth_crap.domain[
+ sizeof(request->data.auth_crap.domain)-1] = '\0';
+ request->data.auth_crap.workstation[
+ sizeof(request->data.auth_crap.workstation)-1] = '\0';
+
+ DBG_NOTICE("[%5lu]: pam auth crap domain: [%s] user: [%s] "
+ "workstation: [%s]\n",
+ (unsigned long)cli->pid,
+ request->data.auth_crap.domain,
+ request->data.auth_crap.user,
+ request->data.auth_crap.workstation);
+
+ if (!check_request_flags(request->flags)) {
+ tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER_MIX);
+ return tevent_req_post(req, ev);
+ }
+
+ auth_domain = request->data.auth_crap.domain;
+ if (auth_domain[0] == '\0') {
+ auth_domain = lp_workgroup();
+ }
+
+ domain = find_auth_domain(request->flags, auth_domain);
+ if (domain == NULL) {
+ /*
+ * We don't know the domain so
+ * we're not authoritative
+ */
+ state->authoritative = 0;
+ tevent_req_nterror(req, NT_STATUS_NO_SUCH_USER);
+ return tevent_req_post(req, ev);
+ }
+
+ if (request->data.auth_crap.workstation[0] == '\0') {
+ fstrcpy(request->data.auth_crap.workstation, lp_netbios_name());
+ }
+
+ lmlength_ok = (request->data.auth_crap.lm_resp_len <=
+ sizeof(request->data.auth_crap.lm_resp));
+
+ ntlength_ok = (request->data.auth_crap.nt_resp_len <=
+ sizeof(request->data.auth_crap.nt_resp));
+
+ ntlength_ok |=
+ ((request->flags & WBFLAG_BIG_NTLMV2_BLOB) &&
+ (request->extra_len == request->data.auth_crap.nt_resp_len));
+
+ pwlength_ok = lmlength_ok && ntlength_ok;
+
+ if (!pwlength_ok) {
+ DBG_ERR("Invalid password length %u/%u\n",
+ request->data.auth_crap.lm_resp_len,
+ request->data.auth_crap.nt_resp_len);
+ tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
+ return tevent_req_post(req, ev);
+ }
+
+ state->domain = talloc_strdup(state, request->data.auth_crap.domain);
+ if (tevent_req_nomem(state->domain, req)) {
+ return tevent_req_post(req, ev);
+ }
+
+ state->user = talloc_strdup(state, request->data.auth_crap.user);
+ if (tevent_req_nomem(state->user, req)) {
+ return tevent_req_post(req, ev);
+ }
+
+ status = extra_data_to_sid_array(
+ request->data.auth_crap.require_membership_of_sid,
+ state,
+ &require_membership_of_sid);
+ if (tevent_req_nterror(req, status)) {
+ return tevent_req_post(req, ev);
+ }
+
+ lm_resp = data_blob_talloc(state,
+ request->data.auth_crap.lm_resp,
+ request->data.auth_crap.lm_resp_len);
+ if (tevent_req_nomem(lm_resp.data, req)) {
+ return tevent_req_post(req, ev);
+ }
+
+ if (request->flags & WBFLAG_BIG_NTLMV2_BLOB) {
+ nt_resp = data_blob_talloc(state,
+ request->extra_data.data,
+ request->data.auth_crap.nt_resp_len);
+ } else {
+ nt_resp = data_blob_talloc(state,
+ request->data.auth_crap.nt_resp,
+ request->data.auth_crap.nt_resp_len);
+ }
+ if (tevent_req_nomem(nt_resp.data, req)) {
+ return tevent_req_post(req, ev);
+ }
+
+ chal = data_blob_talloc(state,
+ request->data.auth_crap.chal,
+ 8);
+ if (tevent_req_nomem(chal.data, req)) {
+ return tevent_req_post(req, ev);
+ }
+
+ subreq = dcerpc_wbint_PamAuthCrap_send(state,
+ global_event_context(),
+ dom_child_handle(domain),
+ request->client_name,
+ request->pid,
+ request->flags,
+ request->data.auth_crap.user,
+ request->data.auth_crap.domain,
+ request->data.auth_crap.workstation,
+ lm_resp,
+ nt_resp,
+ chal,
+ request->data.auth_crap.logon_parameters,
+ require_membership_of_sid,
+ &state->authoritative,
+ &state->validation);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, winbindd_pam_auth_crap_done, req);
+ return req;
+}
+
+static void winbindd_pam_auth_crap_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct winbindd_pam_auth_crap_state *state = tevent_req_data(
+ req, struct winbindd_pam_auth_crap_state);
+ NTSTATUS status;
+
+ status = dcerpc_wbint_PamAuthCrap_recv(subreq, state, &state->result);
+ TALLOC_FREE(subreq);
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+
+ tevent_req_done(req);
+}
+
+NTSTATUS winbindd_pam_auth_crap_recv(struct tevent_req *req,
+ struct winbindd_response *response)
+{
+ struct winbindd_pam_auth_crap_state *state = tevent_req_data(
+ req, struct winbindd_pam_auth_crap_state);
+ NTSTATUS status;
+
+ if (tevent_req_is_nterror(req, &status)) {
+ goto out;
+ }
+
+ if (NT_STATUS_IS_ERR(state->result)) {
+ status = state->result;
+ goto out;
+ }
+
+ status = append_auth_data(response,
+ response,
+ state->flags,
+ state->validation.level,
+ state->validation.validation,
+ state->domain,
+ state->user);
+ if (NT_STATUS_IS_ERR(status)) {
+ goto out;
+ }
+
+ if (state->flags & WBFLAG_PAM_AUTH_PAC && !state->pac_is_trusted) {
+ /*
+ * Clear the flag just in state to do no add the domain
+ * from auth below.
+ */
+ state->flags &= ~WBFLAG_PAM_INFO3_TEXT;
+ }
+
+ if (state->flags & WBFLAG_PAM_INFO3_TEXT) {
+ bool ok;
+
+ ok = add_trusted_domain_from_auth(
+ response->data.auth.validation_level,
+ &response->data.auth.info3,
+ &response->data.auth.info6);
+ if (!ok) {
+ status = NT_STATUS_LOGON_FAILURE;
+ DBG_ERR("add_trusted_domain_from_auth failed\n");
+ set_auth_errors(response, status);
+ response->data.auth.authoritative =
+ state->authoritative;
+ return status;
+ }
+ }
+
+ status = NT_STATUS_OK;
+
+out:
+ set_auth_errors(response, status);
+ response->data.auth.authoritative = state->authoritative;
+ response->result = WINBINDD_PENDING;
+ return NT_STATUS(response->data.auth.nt_status);
+}
diff --git a/source3/winbindd/winbindd_pam_chauthtok.c b/source3/winbindd/winbindd_pam_chauthtok.c
new file mode 100644
index 0000000..e778df8
--- /dev/null
+++ b/source3/winbindd/winbindd_pam_chauthtok.c
@@ -0,0 +1,204 @@
+/*
+ Unix SMB/CIFS implementation.
+ async implementation of WINBINDD_PAM_CHAUTHTOK
+ Copyright (C) Volker Lendecke 2010
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "winbindd.h"
+#include "lib/util/string_wrappers.h"
+#include "lib/global_contexts.h"
+#include "librpc/gen_ndr/ndr_winbind_c.h"
+
+static void fill_in_password_policy(struct winbindd_response *r,
+ const struct samr_DomInfo1 *p)
+{
+ r->data.auth.policy.min_length_password =
+ p->min_password_length;
+ r->data.auth.policy.password_history =
+ p->password_history_length;
+ r->data.auth.policy.password_properties =
+ p->password_properties;
+ r->data.auth.policy.expire =
+ nt_time_to_unix_abs((const NTTIME *)&(p->max_password_age));
+ r->data.auth.policy.min_passwordage =
+ nt_time_to_unix_abs((const NTTIME *)&(p->min_password_age));
+}
+
+struct winbindd_pam_chauthtok_state {
+ struct wbint_PamAuthChangePassword r;
+};
+
+static void winbindd_pam_chauthtok_done(struct tevent_req *subreq);
+
+struct tevent_req *winbindd_pam_chauthtok_send(
+ TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct winbindd_cli_state *cli,
+ struct winbindd_request *request)
+{
+ struct tevent_req *req, *subreq;
+ struct winbindd_pam_chauthtok_state *state;
+ struct winbindd_domain *contact_domain;
+ char *namespace = NULL;
+ char *domain = NULL;
+ char *user = NULL;
+ char *chauthtok_user = NULL;
+ char *mapped_user;
+ NTSTATUS status;
+ bool ok;
+
+ req = tevent_req_create(mem_ctx, &state,
+ struct winbindd_pam_chauthtok_state);
+ if (req == NULL) {
+ return NULL;
+ }
+
+ /* Ensure null termination */
+ request->data.chauthtok.user[
+ sizeof(request->data.chauthtok.user)-1]='\0';
+
+ DEBUG(3, ("[%5lu]: pam chauthtok %s\n", (unsigned long)cli->pid,
+ request->data.chauthtok.user));
+
+ status = normalize_name_unmap(state, request->data.chauthtok.user,
+ &mapped_user);
+
+ if (NT_STATUS_IS_OK(status) ||
+ NT_STATUS_EQUAL(status, NT_STATUS_FILE_RENAMED)) {
+ fstrcpy(request->data.chauthtok.user, mapped_user);
+ }
+
+ chauthtok_user = request->data.chauthtok.user;
+ ok = canonicalize_username(req,
+ &chauthtok_user,
+ &namespace,
+ &domain,
+ &user);
+ if (!ok) {
+ DEBUG(10, ("winbindd_pam_chauthtok: canonicalize_username %s "
+ "failed with\n", request->data.chauthtok.user));
+ tevent_req_nterror(req, NT_STATUS_NO_SUCH_USER);
+ return tevent_req_post(req, ev);
+ }
+
+ fstrcpy(request->data.chauthtok.user, chauthtok_user);
+
+ contact_domain = find_domain_from_name(namespace);
+ if (contact_domain == NULL) {
+ DEBUG(3, ("Cannot change password for [%s] -> [%s]\\[%s] "
+ "as %s is not a trusted domain\n",
+ request->data.chauthtok.user, domain, user, domain));
+ tevent_req_nterror(req, NT_STATUS_NO_SUCH_USER);
+ return tevent_req_post(req, ev);
+ }
+
+ state->r.in.client_pid = request->pid;
+ state->r.in.flags = request->flags;
+
+ state->r.in.client_name = talloc_strdup(state, request->client_name);
+ if (tevent_req_nomem(state->r.in.client_name, req)) {
+ return tevent_req_post(req, ev);
+ }
+
+ state->r.in.user = talloc_strdup(state, request->data.chauthtok.user);
+ if (tevent_req_nomem(state->r.in.user, req)) {
+ return tevent_req_post(req, ev);
+ }
+
+ state->r.in.old_password = talloc_strdup(state,
+ request->data.chauthtok.oldpass);
+ if (tevent_req_nomem(state->r.in.old_password, req)) {
+ return tevent_req_post(req, ev);
+ }
+
+ state->r.in.new_password = talloc_strdup(state,
+ request->data.chauthtok.newpass);
+ if (tevent_req_nomem(state->r.in.new_password, req)) {
+ return tevent_req_post(req, ev);
+ }
+
+ subreq = dcerpc_wbint_PamAuthChangePassword_r_send(state,
+ global_event_context(),
+ dom_child_handle(contact_domain),
+ &state->r);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, winbindd_pam_chauthtok_done, req);
+ return req;
+}
+
+static void winbindd_pam_chauthtok_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct winbindd_pam_chauthtok_state *state = tevent_req_data(
+ req, struct winbindd_pam_chauthtok_state);
+ NTSTATUS status;
+
+ status = dcerpc_wbint_PamAuthChangePassword_r_recv(subreq, state);
+ TALLOC_FREE(subreq);
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+
+ tevent_req_done(req);
+}
+
+NTSTATUS winbindd_pam_chauthtok_recv(struct tevent_req *req,
+ struct winbindd_response *response)
+{
+ struct winbindd_pam_chauthtok_state *state = tevent_req_data(
+ req, struct winbindd_pam_chauthtok_state);
+ NTSTATUS status = NT_STATUS_OK;
+
+ if (tevent_req_is_nterror(req, &status)) {
+ set_auth_errors(response, status);
+ return status;
+ }
+
+ response->result = WINBINDD_PENDING;
+
+ set_auth_errors(response, state->r.out.result);
+ if (*state->r.out.dominfo != NULL) {
+ fill_in_password_policy(response, *state->r.out.dominfo);
+ }
+ response->data.auth.reject_reason = *state->r.out.reject_reason;
+
+ if (state->r.in.flags & WBFLAG_PAM_CACHED_LOGIN) {
+
+ /* Update the single sign-on memory creds. */
+ status = winbindd_replace_memory_creds(
+ state->r.in.user, state->r.in.new_password);
+
+ DEBUG(10, ("winbindd_replace_memory_creds returned %s\n",
+ nt_errstr(status)));
+
+ /*
+ * When we login from gdm or xdm and password expires,
+ * we change password, but there are no memory
+ * credentials. So, winbindd_replace_memory_creds()
+ * returns NT_STATUS_OBJECT_NAME_NOT_FOUND. This is
+ * not a failure. --- BoYang
+ */
+ if (NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND)) {
+ status = NT_STATUS_OK;
+ }
+ }
+
+ return NT_STATUS(response->data.auth.nt_status);
+}
diff --git a/source3/winbindd/winbindd_pam_chng_pswd_auth_crap.c b/source3/winbindd/winbindd_pam_chng_pswd_auth_crap.c
new file mode 100644
index 0000000..8b69f02
--- /dev/null
+++ b/source3/winbindd/winbindd_pam_chng_pswd_auth_crap.c
@@ -0,0 +1,171 @@
+/*
+ Unix SMB/CIFS implementation.
+ async implementation of WINBINDD_PAM_CHNG_PSWD_AUTH_CRAP
+ Copyright (C) Volker Lendecke 2010
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "winbindd.h"
+#include "lib/global_contexts.h"
+#include "librpc/gen_ndr/ndr_winbind_c.h"
+
+struct winbindd_pam_chng_pswd_auth_crap_state {
+ struct wbint_PamAuthCrapChangePassword r;
+};
+
+static void winbindd_pam_chng_pswd_auth_crap_done(struct tevent_req *subreq);
+
+struct tevent_req *winbindd_pam_chng_pswd_auth_crap_send(
+ TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct winbindd_cli_state *cli,
+ struct winbindd_request *request)
+{
+ struct tevent_req *req, *subreq;
+ struct winbindd_pam_chng_pswd_auth_crap_state *state;
+ struct winbindd_domain *domain;
+ const char *domain_name;
+
+ req = tevent_req_create(mem_ctx, &state,
+ struct winbindd_pam_chng_pswd_auth_crap_state);
+ if (req == NULL) {
+ return NULL;
+ }
+
+ /* Ensure null termination */
+ request->data.chng_pswd_auth_crap.user[
+ sizeof(request->data.chng_pswd_auth_crap.user)-1]='\0';
+ request->data.chng_pswd_auth_crap.domain[
+ sizeof(request->data.chng_pswd_auth_crap.domain)-1]=0;
+
+ DEBUG(3, ("[%5lu]: pam change pswd auth crap domain: %s user: %s\n",
+ (unsigned long)cli->pid,
+ request->data.chng_pswd_auth_crap.domain,
+ request->data.chng_pswd_auth_crap.user));
+
+ domain_name = NULL;
+ if (*request->data.chng_pswd_auth_crap.domain != '\0') {
+ domain_name = request->data.chng_pswd_auth_crap.domain;
+ } else if (lp_winbind_use_default_domain()) {
+ domain_name = lp_workgroup();
+ }
+
+ domain = NULL;
+ if (domain_name != NULL) {
+ domain = find_domain_from_name(domain_name);
+ }
+
+ if (domain == NULL) {
+ tevent_req_nterror(req, NT_STATUS_NO_SUCH_USER);
+ return tevent_req_post(req, ev);
+ }
+
+ state->r.in.client_pid = request->pid;
+ state->r.in.client_name = talloc_strdup(state, request->client_name);
+ if (tevent_req_nomem(state->r.in.client_name, req)) {
+ return tevent_req_post(req, ev);
+ }
+
+ state->r.in.domain = talloc_strdup(state, domain_name);
+ if (tevent_req_nomem(state->r.in.domain, req)) {
+ return tevent_req_post(req, ev);
+ }
+ state->r.in.user = talloc_strdup(state,
+ request->data.chng_pswd_auth_crap.user);
+ if (tevent_req_nomem(state->r.in.user, req)) {
+ return tevent_req_post(req, ev);
+ }
+
+ state->r.in.new_nt_pswd = data_blob_talloc(state,
+ request->data.chng_pswd_auth_crap.new_nt_pswd,
+ request->data.chng_pswd_auth_crap.new_nt_pswd_len);
+ if (tevent_req_nomem(state->r.in.new_nt_pswd.data, req)) {
+ return tevent_req_post(req, ev);
+ }
+
+ state->r.in.old_nt_hash_enc = data_blob_talloc(state,
+ request->data.chng_pswd_auth_crap.old_nt_hash_enc,
+ request->data.chng_pswd_auth_crap.old_nt_hash_enc_len);
+ if (tevent_req_nomem(state->r.in.old_nt_hash_enc.data, req)) {
+ return tevent_req_post(req, ev);
+ }
+
+ if (request->data.chng_pswd_auth_crap.new_lm_pswd_len > 0) {
+ state->r.in.new_lm_pswd = data_blob_talloc(state,
+ request->data.chng_pswd_auth_crap.new_lm_pswd,
+ request->data.chng_pswd_auth_crap.new_lm_pswd_len);
+ if (tevent_req_nomem(state->r.in.new_lm_pswd.data, req)) {
+ return tevent_req_post(req, ev);
+ }
+
+ state->r.in.old_lm_hash_enc = data_blob_talloc(state,
+ request->data.chng_pswd_auth_crap.old_lm_hash_enc,
+ request->data.chng_pswd_auth_crap.old_lm_hash_enc_len);
+ if (tevent_req_nomem(state->r.in.old_lm_hash_enc.data, req)) {
+ return tevent_req_post(req, ev);
+ }
+ } else {
+ state->r.in.new_lm_pswd = data_blob_null;
+ state->r.in.old_lm_hash_enc = data_blob_null;
+ }
+
+ subreq = dcerpc_wbint_PamAuthCrapChangePassword_r_send(state,
+ global_event_context(),
+ dom_child_handle(domain),
+ &state->r);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, winbindd_pam_chng_pswd_auth_crap_done,
+ req);
+ return req;
+}
+
+static void winbindd_pam_chng_pswd_auth_crap_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct winbindd_pam_chng_pswd_auth_crap_state *state = tevent_req_data(
+ req, struct winbindd_pam_chng_pswd_auth_crap_state);
+ NTSTATUS status;
+
+ status = dcerpc_wbint_PamAuthCrapChangePassword_r_recv(subreq, state);
+ TALLOC_FREE(subreq);
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+
+ tevent_req_done(req);
+}
+
+NTSTATUS winbindd_pam_chng_pswd_auth_crap_recv(
+ struct tevent_req *req,
+ struct winbindd_response *response)
+{
+ struct winbindd_pam_chng_pswd_auth_crap_state *state = tevent_req_data(
+ req, struct winbindd_pam_chng_pswd_auth_crap_state);
+ NTSTATUS status = NT_STATUS_OK;
+
+ if (tevent_req_is_nterror(req, &status)) {
+ set_auth_errors(response, status);
+ return status;
+ }
+
+ response->result = WINBINDD_PENDING;
+ set_auth_errors(response, state->r.out.result);
+
+ return NT_STATUS(response->data.auth.nt_status);
+}
diff --git a/source3/winbindd/winbindd_pam_logoff.c b/source3/winbindd/winbindd_pam_logoff.c
new file mode 100644
index 0000000..c799eb5
--- /dev/null
+++ b/source3/winbindd/winbindd_pam_logoff.c
@@ -0,0 +1,183 @@
+/*
+ Unix SMB/CIFS implementation.
+ async implementation of WINBINDD_PAM_LOGOFF
+ Copyright (C) Volker Lendecke 2010
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "util/debug.h"
+#include "winbindd.h"
+#include "lib/global_contexts.h"
+#include "librpc/gen_ndr/ndr_winbind_c.h"
+#include "lib/util/string_wrappers.h"
+
+struct winbindd_pam_logoff_state {
+ struct wbint_PamLogOff r;
+};
+
+static void winbindd_pam_logoff_done(struct tevent_req *subreq);
+
+struct tevent_req *winbindd_pam_logoff_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct winbindd_cli_state *cli,
+ struct winbindd_request *request)
+{
+ struct tevent_req *req, *subreq;
+ struct winbindd_pam_logoff_state *state;
+ struct winbindd_domain *domain;
+ char *name_namespace = NULL;
+ char *name_domain = NULL;
+ char *user = NULL;
+ char *logoff_user = NULL;
+
+ uid_t caller_uid;
+ gid_t caller_gid;
+ int res;
+ bool ok;
+
+ req = tevent_req_create(mem_ctx, &state,
+ struct winbindd_pam_logoff_state);
+ if (req == NULL) {
+ return NULL;
+ }
+ D_NOTICE("[%s (%u)] Winbind external command PAM_LOGOFF start.\n"
+ "Username '%s' is used during logoff.\n",
+ cli->client_name,
+ (unsigned int)cli->pid,
+ request->data.auth.user);
+ /* Ensure null termination */
+ /* Ensure null termination */
+ request->data.logoff.user[sizeof(request->data.logoff.user)-1]='\0';
+ request->data.logoff.krb5ccname[
+ sizeof(request->data.logoff.krb5ccname)-1]='\0';
+
+ if (request->data.logoff.uid == (uid_t)-1) {
+ goto failed;
+ }
+
+ logoff_user = request->data.logoff.user;
+
+ ok = canonicalize_username(req,
+ &logoff_user,
+ &name_namespace,
+ &name_domain,
+ &user);
+ if (!ok) {
+ goto failed;
+ }
+
+ fstrcpy(request->data.logoff.user, logoff_user);
+
+ domain = find_auth_domain(request->flags, name_namespace);
+ if (domain == NULL) {
+ goto failed;
+ }
+
+ caller_uid = (uid_t)-1;
+
+ res = getpeereid(cli->sock, &caller_uid, &caller_gid);
+ if (res != 0) {
+ D_WARNING("winbindd_pam_logoff: failed to check peerid: %s\n",
+ strerror(errno));
+ goto failed;
+ }
+
+ switch (caller_uid) {
+ case -1:
+ goto failed;
+ case 0:
+ /* root must be able to logoff any user - gd */
+ break;
+ default:
+ if (caller_uid != request->data.logoff.uid) {
+ D_WARNING("caller requested invalid uid\n");
+ goto failed;
+ }
+ break;
+ }
+
+ state->r.in.client_name = talloc_strdup(state, request->client_name);
+ if (tevent_req_nomem(state->r.in.client_name, req)) {
+ return tevent_req_post(req, ev);
+ }
+ state->r.in.client_pid = request->pid;
+
+ state->r.in.flags = request->flags;
+ state->r.in.user = talloc_strdup(state, request->data.logoff.user);
+ if (tevent_req_nomem(state->r.in.user, req)) {
+ return tevent_req_post(req, ev);
+ }
+ state->r.in.uid = request->data.logoff.uid;
+ state->r.in.krb5ccname = talloc_strdup(state,
+ request->data.logoff.krb5ccname);
+ if (tevent_req_nomem(state->r.in.krb5ccname, req)) {
+ return tevent_req_post(req, ev);
+ }
+
+ subreq = dcerpc_wbint_PamLogOff_r_send(state,
+ global_event_context(),
+ dom_child_handle(domain),
+ &state->r);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, winbindd_pam_logoff_done, req);
+ return req;
+
+failed:
+ tevent_req_nterror(req, NT_STATUS_NO_SUCH_USER);
+ return tevent_req_post(req, ev);
+}
+
+static void winbindd_pam_logoff_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct winbindd_pam_logoff_state *state = tevent_req_data(
+ req, struct winbindd_pam_logoff_state);
+ NTSTATUS status;
+
+ status = dcerpc_wbint_PamLogOff_r_recv(subreq, state);
+ TALLOC_FREE(subreq);
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+
+ tevent_req_done(req);
+}
+
+NTSTATUS winbindd_pam_logoff_recv(struct tevent_req *req,
+ struct winbindd_response *response)
+{
+ struct winbindd_pam_logoff_state *state = tevent_req_data(
+ req, struct winbindd_pam_logoff_state);
+ NTSTATUS status = NT_STATUS_OK;
+
+ D_NOTICE("Winbind external command PAM_LOGOFF end.\n");
+ if (tevent_req_is_nterror(req, &status)) {
+ set_auth_errors(response, status);
+ return status;
+ }
+
+ response->result = WINBINDD_PENDING;
+ set_auth_errors(response, state->r.out.result);
+
+ if (NT_STATUS_IS_OK(state->r.out.result)) {
+ winbindd_delete_memory_creds(state->r.in.user);
+ }
+
+ return NT_STATUS(response->data.auth.nt_status);
+}
diff --git a/source3/winbindd/winbindd_ping_dc.c b/source3/winbindd/winbindd_ping_dc.c
new file mode 100644
index 0000000..8f56a9e
--- /dev/null
+++ b/source3/winbindd/winbindd_ping_dc.c
@@ -0,0 +1,140 @@
+/*
+ Unix SMB/CIFS implementation.
+ async implementation of WINBINDD_PING_DC
+ Copyright (C) Volker Lendecke 2009
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "winbindd.h"
+#include "librpc/gen_ndr/ndr_winbind_c.h"
+
+struct winbindd_ping_dc_state {
+ const char *dcname;
+ NTSTATUS result;
+};
+
+static void winbindd_ping_dc_done(struct tevent_req *subreq);
+
+struct tevent_req *winbindd_ping_dc_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct winbindd_cli_state *cli,
+ struct winbindd_request *request)
+{
+ struct tevent_req *req, *subreq;
+ struct winbindd_ping_dc_state *state;
+ struct winbindd_domain *domain;
+
+ req = tevent_req_create(mem_ctx, &state,
+ struct winbindd_ping_dc_state);
+ if (req == NULL) {
+ return NULL;
+ }
+
+ if (request->domain_name[0] == '\0') {
+ /* preserve old behavior, when no domain name is given */
+ domain = find_our_domain();
+ } else {
+ domain = find_trust_from_name_noinit(request->domain_name);
+ }
+ if (domain == NULL) {
+ tevent_req_nterror(req, NT_STATUS_NO_SUCH_DOMAIN);
+ return tevent_req_post(req, ev);
+ }
+ if (domain->internal) {
+ const char *d = lp_dnsdomain();
+ const char *n = lp_netbios_name();
+
+ /*
+ * Internal domains are passdb based, we can always
+ * contact them.
+ */
+
+ if (d != NULL) {
+ char *h;
+ h = strlower_talloc(mem_ctx, n);
+ if (tevent_req_nomem(h, req)) {
+ return tevent_req_post(req, ev);
+ }
+
+ state->dcname = talloc_asprintf(state, "%s.%s", h, d);
+ TALLOC_FREE(h);
+
+ if (tevent_req_nomem(state->dcname, req)) {
+ return tevent_req_post(req, ev);
+ }
+ } else {
+ state->dcname = talloc_strdup(state, n);
+ if (tevent_req_nomem(state->dcname, req)) {
+ return tevent_req_post(req, ev);
+ }
+ }
+
+ tevent_req_done(req);
+ return tevent_req_post(req, ev);
+ }
+
+ subreq = dcerpc_wbint_PingDc_send(state, ev, dom_child_handle(domain),
+ &state->dcname);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, winbindd_ping_dc_done, req);
+ return req;
+}
+
+static void winbindd_ping_dc_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct winbindd_ping_dc_state *state = tevent_req_data(
+ req, struct winbindd_ping_dc_state);
+ NTSTATUS status, result;
+
+ status = dcerpc_wbint_PingDc_recv(subreq, state, &result);
+ state->result = result;
+ if (any_nt_status_not_ok(status, result, &status)) {
+ tevent_req_nterror(req, status);
+ return;
+ }
+ tevent_req_done(req);
+}
+
+NTSTATUS winbindd_ping_dc_recv(struct tevent_req *req,
+ struct winbindd_response *presp)
+{
+ struct winbindd_ping_dc_state *state = tevent_req_data(
+ req, struct winbindd_ping_dc_state);
+ NTSTATUS status;
+
+ if (tevent_req_is_nterror(req, &status)) {
+ return status;
+ }
+
+ if (!NT_STATUS_IS_OK(state->result)) {
+ set_auth_errors(presp, state->result);
+ }
+
+ if (state->dcname) {
+ presp->extra_data.data = talloc_strdup(presp, state->dcname);
+ if (presp->extra_data.data == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ presp->length += strlen((char *)presp->extra_data.data) + 1;
+ }
+
+ tevent_req_received(req);
+ return NT_STATUS_OK;
+}
diff --git a/source3/winbindd/winbindd_proto.h b/source3/winbindd/winbindd_proto.h
new file mode 100644
index 0000000..a5e8c8b
--- /dev/null
+++ b/source3/winbindd/winbindd_proto.h
@@ -0,0 +1,1059 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * collected prototypes header
+ *
+ * frozen from "make proto" in May 2008
+ *
+ * Copyright (C) Michael Adam 2008
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef _WINBINDD_PROTO_H_
+#define _WINBINDD_PROTO_H_
+
+/* The following definitions come from winbindd/winbindd.c */
+struct imessaging_context *winbind_imessaging_context(void);
+void winbindd_terminate(bool is_parent);
+bool winbindd_setup_sig_term_handler(bool parent);
+bool winbindd_setup_stdin_handler(bool parent, bool foreground);
+bool winbindd_setup_sig_hup_handler(const char *lfile);
+bool winbindd_use_idmap_cache(void);
+bool winbindd_use_cache(void);
+void winbindd_set_use_cache(bool use_cache);
+char *get_winbind_priv_pipe_dir(void);
+void winbindd_flush_caches(void);
+void winbind_debug_call_depth_setup(size_t *depth);
+void winbind_call_flow(void *private_data,
+ enum tevent_thread_call_depth_cmd cmd,
+ struct tevent_req *req,
+ size_t depth,
+ const char *fname);
+bool winbindd_reload_services_file(const char *lfile);
+
+/* The following definitions come from winbindd/winbindd_ads.c */
+
+/* The following definitions come from winbindd/winbindd_rpc.c */
+
+NTSTATUS winbindd_lookup_sids(TALLOC_CTX *mem_ctx,
+ struct winbindd_domain *domain,
+ uint32_t num_sids,
+ const struct dom_sid *sids,
+ char ***domains,
+ char ***names,
+ enum lsa_SidType **types);
+NTSTATUS rpc_lookup_sids(TALLOC_CTX *mem_ctx,
+ struct winbindd_domain *domain,
+ struct lsa_SidArray *sids,
+ struct lsa_RefDomainList **pdomains,
+ struct lsa_TransNameArray **pnames);
+
+/* The following definitions come from winbindd/winbindd_cache.c */
+
+NTSTATUS wb_cache_query_user_list(struct winbindd_domain *domain,
+ TALLOC_CTX *mem_ctx,
+ uint32_t **prids);
+NTSTATUS wb_cache_enum_dom_groups(struct winbindd_domain *domain,
+ TALLOC_CTX *mem_ctx,
+ uint32_t *num_entries,
+ struct wb_acct_info **info);
+NTSTATUS wb_cache_enum_local_groups(struct winbindd_domain *domain,
+ TALLOC_CTX *mem_ctx,
+ uint32_t *num_entries,
+ struct wb_acct_info **info);
+NTSTATUS wb_cache_name_to_sid(struct winbindd_domain *domain,
+ TALLOC_CTX *mem_ctx,
+ const char *domain_name,
+ const char *name,
+ uint32_t flags,
+ struct dom_sid *sid,
+ enum lsa_SidType *type);
+NTSTATUS wb_cache_sid_to_name(struct winbindd_domain *domain,
+ TALLOC_CTX *mem_ctx,
+ const struct dom_sid *sid,
+ char **domain_name,
+ char **name,
+ enum lsa_SidType *type);
+NTSTATUS wb_cache_rids_to_names(struct winbindd_domain *domain,
+ TALLOC_CTX *mem_ctx,
+ const struct dom_sid *domain_sid,
+ uint32_t *rids,
+ size_t num_rids,
+ char **domain_name,
+ char ***names,
+ enum lsa_SidType **types);
+NTSTATUS wb_cache_lookup_usergroups(struct winbindd_domain *domain,
+ TALLOC_CTX *mem_ctx,
+ const struct dom_sid *user_sid,
+ uint32_t *pnum_sids,
+ struct dom_sid **psids);
+NTSTATUS wb_cache_lookup_useraliases(struct winbindd_domain *domain,
+ TALLOC_CTX *mem_ctx,
+ uint32_t num_sids,
+ const struct dom_sid *sids,
+ uint32_t *num_aliases,
+ uint32_t **alias_rids);
+NTSTATUS wb_cache_lookup_groupmem(struct winbindd_domain *domain,
+ TALLOC_CTX *mem_ctx,
+ const struct dom_sid *group_sid,
+ enum lsa_SidType type,
+ uint32_t *num_names,
+ struct dom_sid **sid_mem,
+ char ***names,
+ uint32_t **name_types);
+NTSTATUS wb_cache_lookup_aliasmem(struct winbindd_domain *domain,
+ TALLOC_CTX *mem_ctx,
+ const struct dom_sid *group_sid,
+ enum lsa_SidType type,
+ uint32_t *num_names,
+ struct dom_sid **sid_mem);
+NTSTATUS wb_cache_sequence_number(struct winbindd_domain *domain,
+ uint32_t *seq);
+NTSTATUS wb_cache_lockout_policy(struct winbindd_domain *domain,
+ TALLOC_CTX *mem_ctx,
+ struct samr_DomInfo12 *policy);
+NTSTATUS wb_cache_password_policy(struct winbindd_domain *domain,
+ TALLOC_CTX *mem_ctx,
+ struct samr_DomInfo1 *policy);
+NTSTATUS wb_cache_trusted_domains(struct winbindd_domain *domain,
+ TALLOC_CTX *mem_ctx,
+ struct netr_DomainTrustList *trusts);
+
+NTSTATUS wcache_cached_creds_exist(struct winbindd_domain *domain, const struct dom_sid *sid);
+NTSTATUS wcache_get_creds(struct winbindd_domain *domain,
+ TALLOC_CTX *mem_ctx,
+ const struct dom_sid *sid,
+ const uint8_t **cached_nt_pass,
+ const uint8_t **cached_salt);
+NTSTATUS wcache_save_creds(struct winbindd_domain *domain,
+ const struct dom_sid *sid,
+ const uint8_t nt_pass[NT_HASH_LEN]);
+void wcache_invalidate_samlogon(struct winbindd_domain *domain,
+ const struct dom_sid *user_sid);
+bool wcache_invalidate_cache(void);
+bool wcache_invalidate_cache_noinit(void);
+bool initialize_winbindd_cache(void);
+void close_winbindd_cache(void);
+bool lookup_cached_sid(TALLOC_CTX *mem_ctx, const struct dom_sid *sid,
+ char **domain_name, char **name,
+ enum lsa_SidType *type);
+bool lookup_cached_name(const char *namespace,
+ const char *domain_name,
+ const char *name,
+ struct dom_sid *sid,
+ enum lsa_SidType *type);
+void cache_name2sid_trusted(struct winbindd_domain *domain,
+ const char *domain_name,
+ const char *name,
+ enum lsa_SidType type,
+ const struct dom_sid *sid);
+void cache_name2sid(struct winbindd_domain *domain,
+ const char *domain_name, const char *name,
+ enum lsa_SidType type, const struct dom_sid *sid);
+NTSTATUS wcache_query_user_fullname(struct winbindd_domain *domain,
+ TALLOC_CTX *mem_ctx,
+ const struct dom_sid *user_sid,
+ const char **full_name);
+
+NTSTATUS wcache_count_cached_creds(struct winbindd_domain *domain, int *count);
+NTSTATUS wcache_remove_oldest_cached_creds(struct winbindd_domain *domain, const struct dom_sid *sid) ;
+bool set_global_winbindd_state_offline(void);
+void set_global_winbindd_state_online(void);
+bool get_global_winbindd_state_offline(void);
+int winbindd_validate_cache(void);
+int winbindd_validate_cache_nobackup(void);
+bool winbindd_cache_validate_and_initialize(void);
+bool wcache_tdc_fetch_list( struct winbindd_tdc_domain **domains, size_t *num_domains );
+bool wcache_tdc_add_domain( struct winbindd_domain *domain );
+struct winbindd_tdc_domain * wcache_tdc_fetch_domain( TALLOC_CTX *ctx, const char *name );
+void wcache_tdc_clear( void );
+bool wcache_store_seqnum(const char *domain_name, uint32_t seqnum,
+ time_t last_seq_check);
+bool wcache_fetch_ndr(TALLOC_CTX *mem_ctx, struct winbindd_domain *domain,
+ uint32_t opnum, const DATA_BLOB *req, DATA_BLOB *resp);
+void wcache_store_ndr(struct winbindd_domain *domain, uint32_t opnum,
+ const DATA_BLOB *req, const DATA_BLOB *resp);
+
+/* The following definitions come from winbindd/winbindd_ccache_access.c */
+
+bool winbindd_ccache_ntlm_auth(struct winbindd_cli_state *state);
+enum winbindd_result winbindd_dual_ccache_ntlm_auth(struct winbindd_domain *domain,
+ struct winbindd_cli_state *state);
+bool winbindd_ccache_save(struct winbindd_cli_state *state);
+
+/* The following definitions come from winbindd/winbindd_cm.c */
+void winbind_msg_domain_offline(struct messaging_context *msg_ctx,
+ void *private_data,
+ uint32_t msg_type,
+ struct server_id server_id,
+ DATA_BLOB *data);
+void winbind_msg_domain_online(struct messaging_context *msg_ctx,
+ void *private_data,
+ uint32_t msg_type,
+ struct server_id server_id,
+ DATA_BLOB *data);
+
+void set_domain_offline(struct winbindd_domain *domain);
+void set_domain_online_request(struct winbindd_domain *domain);
+
+struct ndr_interface_table;
+NTSTATUS wb_open_internal_pipe(TALLOC_CTX *mem_ctx,
+ const struct ndr_interface_table *table,
+ struct rpc_pipe_client **ret_pipe);
+void invalidate_cm_connection(struct winbindd_domain *domain);
+void close_conns_after_fork(void);
+NTSTATUS init_dc_connection(struct winbindd_domain *domain, bool need_rw_dc);
+NTSTATUS cm_connect_sam(struct winbindd_domain *domain, TALLOC_CTX *mem_ctx,
+ bool need_rw_dc,
+ struct rpc_pipe_client **cli, struct policy_handle *sam_handle);
+NTSTATUS cm_connect_lsa(struct winbindd_domain *domain, TALLOC_CTX *mem_ctx,
+ struct rpc_pipe_client **cli, struct policy_handle *lsa_policy);
+NTSTATUS cm_connect_lsat(struct winbindd_domain *domain,
+ TALLOC_CTX *mem_ctx,
+ struct rpc_pipe_client **cli,
+ struct policy_handle *lsa_policy);
+NTSTATUS cm_connect_netlogon(struct winbindd_domain *domain,
+ struct rpc_pipe_client **cli);
+NTSTATUS cm_connect_netlogon_secure(struct winbindd_domain *domain,
+ struct rpc_pipe_client **cli,
+ struct netlogon_creds_cli_context **ppdc);
+bool fetch_current_dc_from_gencache(TALLOC_CTX *mem_ctx,
+ const char *domain_name,
+ char **p_dc_name, char **p_dc_ip);
+
+/* The following definitions come from winbindd/winbindd_cred_cache.c */
+
+bool ccache_entry_exists(const char *username);
+bool ccache_entry_identical(const char *username,
+ uid_t uid,
+ const char *ccname);
+void ccache_remove_all_after_fork(void);
+void ccache_regain_all_now(void);
+NTSTATUS add_ccache_to_list(const char *princ_name,
+ const char *ccname,
+ const char *username,
+ const char *password,
+ const char *realm,
+ uid_t uid,
+ time_t create_time,
+ time_t ticket_end,
+ time_t renew_until,
+ bool postponed_request,
+ const char *canon_principal,
+ const char *canon_realm);
+NTSTATUS remove_ccache(const char *username);
+struct WINBINDD_MEMORY_CREDS *find_memory_creds_by_name(const char *username);
+NTSTATUS winbindd_add_memory_creds(const char *username,
+ uid_t uid,
+ const char *pass);
+NTSTATUS winbindd_delete_memory_creds(const char *username);
+NTSTATUS winbindd_replace_memory_creds(const char *username,
+ const char *pass);
+
+/* The following definitions come from winbindd/winbindd_creds.c */
+
+NTSTATUS winbindd_get_creds(struct winbindd_domain *domain,
+ TALLOC_CTX *mem_ctx,
+ const struct dom_sid *sid,
+ struct netr_SamInfo3 **info3,
+ const uint8_t **cached_nt_pass,
+ const uint8_t **cred_salt);
+NTSTATUS winbindd_store_creds(struct winbindd_domain *domain,
+ const char *user,
+ const char *pass,
+ struct netr_SamInfo3 *info3);
+NTSTATUS winbindd_update_creds_by_info3(struct winbindd_domain *domain,
+ const char *user,
+ const char *pass,
+ struct netr_SamInfo3 *info3);
+NTSTATUS winbindd_update_creds_by_name(struct winbindd_domain *domain,
+ const char *user,
+ const char *pass);
+
+/* The following definitions come from winbindd/winbindd_domain.c */
+
+void setup_domain_child(struct winbindd_domain *domain);
+
+/* The following definitions come from winbindd/winbindd_dual.c */
+
+struct dcerpc_binding_handle *dom_child_handle(struct winbindd_domain *domain);
+
+struct tevent_req *wb_child_request_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct winbindd_child *child,
+ struct winbindd_request *request);
+int wb_child_request_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx,
+ struct winbindd_response **presponse, int *err);
+struct tevent_req *wb_domain_request_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct winbindd_domain *domain,
+ struct winbindd_request *request);
+int wb_domain_request_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx,
+ struct winbindd_response **presponse, int *err);
+
+void setup_child(struct winbindd_domain *domain, struct winbindd_child *child,
+ const char *logprefix,
+ const char *logname);
+void winbind_child_died(pid_t pid);
+void winbindd_flush_negative_conn_cache(struct winbindd_domain *domain);
+void winbind_msg_debug(struct messaging_context *msg_ctx,
+ void *private_data,
+ uint32_t msg_type,
+ struct server_id server_id,
+ DATA_BLOB *data);
+void winbind_disconnect_dc_parent(struct messaging_context *msg_ctx,
+ void *private_data,
+ uint32_t msg_type,
+ struct server_id server_id,
+ DATA_BLOB *data);
+void winbind_msg_offline(struct messaging_context *msg_ctx,
+ void *private_data,
+ uint32_t msg_type,
+ struct server_id server_id,
+ DATA_BLOB *data);
+void winbind_msg_online(struct messaging_context *msg_ctx,
+ void *private_data,
+ uint32_t msg_type,
+ struct server_id server_id,
+ DATA_BLOB *data);
+void winbind_msg_onlinestatus(struct messaging_context *msg_ctx,
+ void *private_data,
+ uint32_t msg_type,
+ struct server_id server_id,
+ DATA_BLOB *data);
+void winbind_msg_dump_event_list(struct messaging_context *msg_ctx,
+ void *private_data,
+ uint32_t msg_type,
+ struct server_id server_id,
+ DATA_BLOB *data);
+void winbind_msg_dump_domain_list(struct messaging_context *msg_ctx,
+ void *private_data,
+ uint32_t msg_type,
+ struct server_id server_id,
+ DATA_BLOB *data);
+void winbind_msg_ip_dropped(struct messaging_context *msg_ctx,
+ void *private_data,
+ uint32_t msg_type,
+ struct server_id server_id,
+ DATA_BLOB *data);
+void winbind_msg_disconnect_dc(struct messaging_context *msg_ctx,
+ void *private_data,
+ uint32_t msg_type,
+ struct server_id server_id,
+ DATA_BLOB *data);
+void winbind_msg_ip_dropped_parent(struct messaging_context *msg_ctx,
+ void *private_data,
+ uint32_t msg_type,
+ struct server_id server_id,
+ DATA_BLOB *data);
+void winbindd_msg_reload_services_parent(struct messaging_context *msg,
+ void *private_data,
+ uint32_t msg_type,
+ struct server_id server_id,
+ DATA_BLOB *data);
+NTSTATUS winbindd_reinit_after_fork(const struct winbindd_child *myself,
+ const char *logfilename);
+struct winbindd_domain *wb_child_domain(void);
+bool add_trusted_domains_dc(void);
+
+/* The following definitions come from winbindd/winbindd_group.c */
+bool fill_grent(TALLOC_CTX *mem_ctx, struct winbindd_gr *gr,
+ const char *dom_name, const char *gr_name, gid_t unix_gid);
+
+struct db_context;
+NTSTATUS winbindd_print_groupmembers(struct db_context *members,
+ TALLOC_CTX *mem_ctx,
+ int *num_members, char **result);
+
+
+/* The following definitions come from winbindd/winbindd_idmap.c */
+
+struct tevent_req *wb_parent_idmap_setup_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev);
+NTSTATUS wb_parent_idmap_setup_recv(struct tevent_req *req,
+ const struct wb_parent_idmap_config **_cfg);
+
+NTSTATUS init_idmap_child(TALLOC_CTX *mem_ctx);
+struct winbindd_child *idmap_child(void);
+bool is_idmap_child(const struct winbindd_child *child);
+pid_t idmap_child_pid(void);
+struct dcerpc_binding_handle *idmap_child_handle(void);
+struct idmap_domain *idmap_find_domain_with_sid(const char *domname,
+ const struct dom_sid *sid);
+const char *idmap_config_const_string(const char *domname, const char *option,
+ const char *def);
+bool idmap_config_bool(const char *domname, const char *option, bool def);
+int idmap_config_int(const char *domname, const char *option, int def);
+const char **idmap_config_string_list(const char *domname,
+ const char *option,
+ const char **def);
+bool domain_has_idmap_config(const char *domname);
+bool lp_scan_idmap_domains(bool (*fn)(const char *domname,
+ void *private_data),
+ void *private_data);
+
+/* The following definitions come from winbindd/winbindd_locator.c */
+
+NTSTATUS init_locator_child(TALLOC_CTX *mem_ctx);
+struct winbindd_child *locator_child(void);
+struct dcerpc_binding_handle *locator_child_handle(void);
+
+/* The following definitions come from winbindd/winbindd_misc.c */
+
+bool winbindd_list_trusted_domains(struct winbindd_cli_state *state);
+bool winbindd_dc_info(struct winbindd_cli_state *state);
+bool winbindd_ping(struct winbindd_cli_state *state);
+bool winbindd_info(struct winbindd_cli_state *state);
+bool winbindd_interface_version(struct winbindd_cli_state *state);
+bool winbindd_domain_name(struct winbindd_cli_state *state);
+bool winbindd_netbios_name(struct winbindd_cli_state *state);
+bool winbindd_priv_pipe_dir(struct winbindd_cli_state *state);
+
+/* The following definitions come from winbindd/winbindd_ndr.c */
+struct ndr_print;
+void ndr_print_winbindd_child(struct ndr_print *ndr,
+ const char *name,
+ const struct winbindd_child *r);
+void ndr_print_winbindd_cm_conn(struct ndr_print *ndr,
+ const char *name,
+ const struct winbindd_cm_conn *r);
+void ndr_print_winbindd_methods(struct ndr_print *ndr,
+ const char *name,
+ const struct winbindd_methods *r);
+void ndr_print_winbindd_domain(struct ndr_print *ndr,
+ const char *name,
+ const struct winbindd_domain *r);
+
+/* The following definitions come from winbindd/winbindd_pam.c */
+
+bool check_request_flags(uint32_t flags);
+NTSTATUS append_auth_data(TALLOC_CTX *mem_ctx,
+ struct winbindd_response *resp,
+ uint32_t request_flags,
+ uint16_t validation_level,
+ union netr_Validation *validation,
+ const char *name_domain,
+ const char *name_user);
+NTSTATUS extra_data_to_sid_array(const char *group_sid,
+ TALLOC_CTX *mem_ctx,
+ struct wbint_SidArray **_sid_array);
+uid_t get_uid_from_request(struct winbindd_request *request);
+struct winbindd_domain *find_auth_domain(uint8_t flags,
+ const char *domain_name);
+struct pipes_struct;
+struct wbint_PamAuth;
+NTSTATUS _wbint_PamAuth(struct pipes_struct *p,
+ struct wbint_PamAuth *r);
+NTSTATUS _wbint_PamAuthCrap(struct pipes_struct *p,
+ struct wbint_PamAuthCrap *r);
+NTSTATUS _wbint_PamAuthChangePassword(struct pipes_struct *p,
+ struct wbint_PamAuthChangePassword *r);
+NTSTATUS _wbint_PamLogOff(struct pipes_struct *p,
+ struct wbint_PamLogOff *r);
+NTSTATUS _wbint_PamAuthCrapChangePassword(struct pipes_struct *p,
+ struct wbint_PamAuthCrapChangePassword *r);
+NTSTATUS winbindd_pam_auth_pac_verify(struct winbindd_cli_state *state,
+ TALLOC_CTX *mem_ctx,
+ bool *p_is_trusted,
+ uint16_t *p_validation_level,
+ union netr_Validation **p_validation);
+
+NTSTATUS winbind_dual_SamLogon(struct winbindd_domain *domain,
+ TALLOC_CTX *mem_ctx,
+ bool interactive,
+ uint32_t logon_parameters,
+ const char *name_user,
+ const char *name_domain,
+ const char *workstation,
+ const uint64_t logon_id,
+ const char *client_name,
+ const int pid,
+ DATA_BLOB chal,
+ DATA_BLOB lm_response,
+ DATA_BLOB nt_response,
+ const struct tsocket_address *remote,
+ const struct tsocket_address *local,
+ uint8_t *authoritative,
+ bool skip_sam,
+ uint32_t *flags,
+ uint16_t *_validation_level,
+ union netr_Validation **_validation);
+
+/* The following definitions come from winbindd/winbindd_util.c */
+
+struct winbindd_domain *domain_list(void);
+struct winbindd_domain *wb_next_domain(struct winbindd_domain *domain);
+bool set_routing_domain(struct winbindd_domain *domain,
+ struct winbindd_domain *routing_domain);
+bool add_trusted_domain_from_auth(uint16_t validation_level,
+ struct info3_text *info3,
+ struct info6_text *info6);
+bool domain_is_forest_root(const struct winbindd_domain *domain);
+void rescan_trusted_domains(struct tevent_context *ev, struct tevent_timer *te,
+ struct timeval now, void *private_data);
+void winbindd_ping_offline_domains(struct tevent_context *ev,
+ struct tevent_timer *te,
+ struct timeval now,
+ void *private_data);
+bool init_domain_list(void);
+struct winbindd_domain *find_domain_from_name_noinit(const char *domain_name);
+struct winbindd_domain *find_trust_from_name_noinit(const char *domain_name);
+struct winbindd_domain *find_domain_from_name(const char *domain_name);
+struct winbindd_domain *find_domain_from_sid_noinit(const struct dom_sid *sid);
+struct winbindd_domain *find_trust_from_sid_noinit(const struct dom_sid *sid);
+struct winbindd_domain *find_domain_from_sid(const struct dom_sid *sid);
+struct winbindd_domain *find_our_domain(void);
+struct winbindd_domain *find_default_route_domain(void);
+struct winbindd_domain *find_lookup_domain_from_sid(const struct dom_sid *sid);
+struct winbindd_domain *find_lookup_domain_from_name(const char *domain_name);
+/**
+ * Parse a DOMAIN\user or UPN string into a domain, namespace and a user
+ *
+ * @param[in] ctx talloc context
+ * @param[in] domuser a DOMAIN\user or UPN string
+ * @param[out] namespace
+ * @param[out] domain
+ * @param[out] user
+ * @return bool indicating success or failure
+ */
+bool parse_domain_user(TALLOC_CTX *ctx,
+ const char *domuser,
+ char **namespace,
+ char **domain,
+ char **user);
+/**
+ * Ensure an incoming username from NSS is fully qualified. Replace the
+ * incoming username with DOMAIN <separator> user. Additionally returns
+ * the same values as parse_domain_user() as out params.
+ * Used to ensure all names are fully qualified within winbindd.
+ * Used by the NSS protocols of auth, chauthtok, logoff and ccache_ntlm_auth.
+ * The protocol definitions of auth_crap, chng_pswd_auth_crap
+ * really should be changed to use this instead of doing things
+ * by hand. JRA.
+ *
+ * @param[in] mem_ctx talloc context
+ * @param[in,out] username_inout populated with fully qualified name
+ with format 'DOMAIN <separator> user' where DOMAIN and
+ user are determined by the output of parse_domain_user()
+ * @param[out] namespace populated with namespace returned from
+ parse_domain_user()
+ * @param[out] domain populated with domain returned from
+ parse_domain_user()
+ * @param[out] populated with user returned from
+ parse_domain_user()
+ * @return bool indicating success or failure
+ */
+bool canonicalize_username(TALLOC_CTX *mem_ctx,
+ char **username_inout,
+ char **namespace,
+ char **domain,
+ char **user);
+char *fill_domain_username_talloc(TALLOC_CTX *ctx,
+ const char *domain,
+ const char *user,
+ bool can_assume);
+struct winbindd_cli_state *winbindd_client_list(void);
+struct winbindd_cli_state *winbindd_client_list_tail(void);
+struct winbindd_cli_state *
+winbindd_client_list_prev(struct winbindd_cli_state *cli);
+void winbindd_add_client(struct winbindd_cli_state *cli);
+void winbindd_remove_client(struct winbindd_cli_state *cli);
+void winbindd_promote_client(struct winbindd_cli_state *cli);
+int winbindd_num_clients(void);
+NTSTATUS lookup_usergroups_cached(TALLOC_CTX *mem_ctx,
+ const struct dom_sid *user_sid,
+ uint32_t *p_num_groups, struct dom_sid **user_sids);
+
+NTSTATUS normalize_name_map(TALLOC_CTX *mem_ctx,
+ const char *domain_name,
+ const char *name,
+ char **normalized);
+NTSTATUS normalize_name_unmap(TALLOC_CTX *mem_ctx,
+ const char *name,
+ char **normalized);
+
+NTSTATUS resolve_username_to_alias(TALLOC_CTX *mem_ctx,
+ struct winbindd_domain *domain,
+ const char *name, char **alias);
+NTSTATUS resolve_alias_to_username(TALLOC_CTX *mem_ctx,
+ struct winbindd_domain *domain,
+ const char *alias, char **name);
+
+bool winbindd_can_contact_domain(struct winbindd_domain *domain);
+void winbindd_set_locator_kdc_envs(const struct winbindd_domain *domain);
+void winbindd_unset_locator_kdc_env(const struct winbindd_domain *domain);
+void winbindd_set_locator_kdc_envs(const struct winbindd_domain *domain);
+void winbindd_unset_locator_kdc_env(const struct winbindd_domain *domain);
+void set_auth_errors(struct winbindd_response *resp, NTSTATUS result);
+bool is_domain_offline(const struct winbindd_domain *domain);
+bool is_domain_online(const struct winbindd_domain *domain);
+bool parse_sidlist(TALLOC_CTX *mem_ctx, const char *sidstr,
+ struct dom_sid **sids, uint32_t *num_sids);
+bool parse_xidlist(TALLOC_CTX *mem_ctx, const char *xidstr,
+ struct unixid **pxids, uint32_t *pnum_xids);
+
+/* The following definitions come from winbindd/winbindd_wins.c */
+
+void winbindd_wins_byname(struct winbindd_cli_state *state);
+
+struct dcerpc_binding_handle *wbint_binding_handle(TALLOC_CTX *mem_ctx,
+ struct winbindd_domain *domain,
+ struct winbindd_child *child);
+enum winbindd_result winbindd_dual_ndrcmd(struct winbindd_domain *domain,
+ struct winbindd_cli_state *state);
+
+struct tevent_req *wb_lookupsid_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ const struct dom_sid *sid);
+NTSTATUS wb_lookupsid_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx,
+ enum lsa_SidType *type, const char **domain,
+ const char **name);
+
+struct tevent_req *winbindd_lookupsid_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct winbindd_cli_state *cli,
+ struct winbindd_request *request);
+NTSTATUS winbindd_lookupsid_recv(struct tevent_req *req,
+ struct winbindd_response *response);
+
+struct tevent_req *winbindd_lookupsids_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct winbindd_cli_state *cli,
+ struct winbindd_request *request);
+NTSTATUS winbindd_lookupsids_recv(struct tevent_req *req,
+ struct winbindd_response *response);
+
+struct tevent_req *wb_lookupname_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ const char *namespace,
+ const char *dom_name,
+ const char *name,
+ uint32_t flags);
+NTSTATUS wb_lookupname_recv(struct tevent_req *req, struct dom_sid *sid,
+ enum lsa_SidType *type);
+
+struct tevent_req *winbindd_lookupname_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct winbindd_cli_state *cli,
+ struct winbindd_request *request);
+NTSTATUS winbindd_lookupname_recv(struct tevent_req *req,
+ struct winbindd_response *response);
+
+struct tevent_req *winbindd_allocate_uid_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct winbindd_cli_state *cli,
+ struct winbindd_request *request);
+NTSTATUS winbindd_allocate_uid_recv(struct tevent_req *req,
+ struct winbindd_response *response);
+
+struct tevent_req *winbindd_allocate_gid_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct winbindd_cli_state *cli,
+ struct winbindd_request *request);
+NTSTATUS winbindd_allocate_gid_recv(struct tevent_req *req,
+ struct winbindd_response *response);
+
+struct tevent_req *wb_queryuser_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ const struct dom_sid *user_sid);
+NTSTATUS wb_queryuser_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx,
+ struct wbint_userinfo **pinfo);
+
+struct tevent_req *wb_getpwsid_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ const struct dom_sid *user_sid,
+ struct winbindd_pw *pw);
+NTSTATUS wb_getpwsid_recv(struct tevent_req *req);
+
+struct tevent_req *winbindd_getpwsid_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct winbindd_cli_state *cli,
+ struct winbindd_request *request);
+NTSTATUS winbindd_getpwsid_recv(struct tevent_req *req,
+ struct winbindd_response *response);
+
+struct tevent_req *winbindd_getpwnam_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct winbindd_cli_state *cli,
+ struct winbindd_request *request);
+NTSTATUS winbindd_getpwnam_recv(struct tevent_req *req,
+ struct winbindd_response *response);
+
+struct tevent_req *winbindd_getpwuid_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct winbindd_cli_state *cli,
+ struct winbindd_request *request);
+NTSTATUS winbindd_getpwuid_recv(struct tevent_req *req,
+ struct winbindd_response *response);
+struct tevent_req *wb_lookupuseraliases_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct winbindd_domain *domain,
+ uint32_t num_sids,
+ const struct dom_sid *sids);
+NTSTATUS wb_lookupuseraliases_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx,
+ uint32_t *num_aliases, uint32_t **aliases);
+struct tevent_req *winbindd_getsidaliases_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct winbindd_cli_state *cli,
+ struct winbindd_request *request);
+NTSTATUS winbindd_getsidaliases_recv(struct tevent_req *req,
+ struct winbindd_response *response);
+struct tevent_req *wb_lookupusergroups_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ const struct dom_sid *sid);
+NTSTATUS wb_lookupusergroups_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx,
+ uint32_t *num_sids, struct dom_sid **sids);
+
+struct tevent_req *winbindd_getuserdomgroups_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct winbindd_cli_state *cli,
+ struct winbindd_request *request);
+NTSTATUS winbindd_getuserdomgroups_recv(struct tevent_req *req,
+ struct winbindd_response *response);
+struct tevent_req *wb_gettoken_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ const struct dom_sid *sid,
+ bool expand_local_aliases);
+NTSTATUS wb_gettoken_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx,
+ uint32_t *num_sids, struct dom_sid **sids);
+struct tevent_req *winbindd_getgroups_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct winbindd_cli_state *cli,
+ struct winbindd_request *request);
+NTSTATUS winbindd_getgroups_recv(struct tevent_req *req,
+ struct winbindd_response *response);
+
+struct tevent_req *wb_seqnum_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct winbindd_domain *domain);
+NTSTATUS wb_seqnum_recv(struct tevent_req *req, uint32_t *seqnum);
+
+struct tevent_req *wb_seqnums_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev);
+NTSTATUS wb_seqnums_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx,
+ int *num_domains, struct winbindd_domain ***domains,
+ NTSTATUS **statuses, uint32_t **seqnums);
+
+struct tevent_req *winbindd_show_sequence_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct winbindd_cli_state *cli,
+ struct winbindd_request *request);
+NTSTATUS winbindd_show_sequence_recv(struct tevent_req *req,
+ struct winbindd_response *response);
+
+struct tevent_req *wb_group_members_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ const struct dom_sid *sid,
+ uint32_t num_sids,
+ enum lsa_SidType *type,
+ int max_depth);
+NTSTATUS wb_group_members_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx,
+ struct db_context **members);
+
+struct tevent_req *wb_alias_members_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ const struct dom_sid *sid,
+ enum lsa_SidType type,
+ int max_nesting);
+NTSTATUS wb_alias_members_recv(struct tevent_req *req,
+ TALLOC_CTX *mem_ctx,
+ uint32_t *num_sids,
+ struct dom_sid **sids);
+
+NTSTATUS add_member_to_db(struct db_context *db, struct dom_sid *sid,
+ const char *name);
+
+struct tevent_req *wb_getgrsid_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ const struct dom_sid *group_sid,
+ int max_nesting);
+NTSTATUS wb_getgrsid_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx,
+ const char **domname, const char **name, gid_t *gid,
+ struct db_context **members);
+
+struct tevent_req *winbindd_getgrgid_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct winbindd_cli_state *cli,
+ struct winbindd_request *request);
+NTSTATUS winbindd_getgrgid_recv(struct tevent_req *req,
+ struct winbindd_response *response);
+
+struct tevent_req *winbindd_getgrnam_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct winbindd_cli_state *cli,
+ struct winbindd_request *request);
+NTSTATUS winbindd_getgrnam_recv(struct tevent_req *req,
+ struct winbindd_response *response);
+
+struct tevent_req *winbindd_getusersids_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct winbindd_cli_state *cli,
+ struct winbindd_request *request);
+NTSTATUS winbindd_getusersids_recv(struct tevent_req *req,
+ struct winbindd_response *response);
+
+struct tevent_req *winbindd_lookuprids_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct winbindd_cli_state *cli,
+ struct winbindd_request *request);
+NTSTATUS winbindd_lookuprids_recv(struct tevent_req *req,
+ struct winbindd_response *response);
+
+struct tevent_req *wb_query_user_list_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct winbindd_domain *domain);
+NTSTATUS wb_query_user_list_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx,
+ char **users);
+
+struct tevent_req *wb_query_group_list_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct winbindd_domain *domain);
+NTSTATUS wb_query_group_list_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx,
+ uint32_t *num_users,
+ struct wbint_Principal **groups);
+
+struct tevent_req *wb_next_pwent_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct getpwent_state *gstate,
+ struct winbindd_pw *pw);
+NTSTATUS wb_next_pwent_recv(struct tevent_req *req);
+
+struct tevent_req *winbindd_setpwent_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct winbindd_cli_state *cli,
+ struct winbindd_request *request);
+NTSTATUS winbindd_setpwent_recv(struct tevent_req *req,
+ struct winbindd_response *presp);
+
+struct tevent_req *winbindd_getpwent_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct winbindd_cli_state *cli,
+ struct winbindd_request *request);
+NTSTATUS winbindd_getpwent_recv(struct tevent_req *req,
+ struct winbindd_response *response);
+
+struct tevent_req *winbindd_endpwent_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct winbindd_cli_state *cli,
+ struct winbindd_request *request);
+NTSTATUS winbindd_endpwent_recv(struct tevent_req *req,
+ struct winbindd_response *response);
+
+struct tevent_req *winbindd_dsgetdcname_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct winbindd_cli_state *cli,
+ struct winbindd_request *request);
+NTSTATUS winbindd_dsgetdcname_recv(struct tevent_req *req,
+ struct winbindd_response *response);
+
+struct tevent_req *wb_dsgetdcname_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ const char *domain_name,
+ const struct GUID *domain_guid,
+ const char *site_name,
+ uint32_t flags);
+NTSTATUS wb_dsgetdcname_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx,
+ struct netr_DsRGetDCNameInfo **pdcinfo);
+NTSTATUS wb_dsgetdcname_gencache_set(const char *domname,
+ struct netr_DsRGetDCNameInfo *dcinfo);
+NTSTATUS wb_dsgetdcname_gencache_get(TALLOC_CTX *mem_ctx,
+ const char *domname,
+ struct netr_DsRGetDCNameInfo **dcinfo);
+
+struct tevent_req *winbindd_getdcname_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct winbindd_cli_state *cli,
+ struct winbindd_request *request);
+NTSTATUS winbindd_getdcname_recv(struct tevent_req *req,
+ struct winbindd_response *response);
+
+struct tevent_req *wb_next_grent_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ int max_nesting,
+ struct getgrent_state *gstate,
+ struct winbindd_gr *gr);
+NTSTATUS wb_next_grent_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx,
+ struct db_context **members);
+
+struct tevent_req *winbindd_setgrent_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct winbindd_cli_state *cli,
+ struct winbindd_request *request);
+NTSTATUS winbindd_setgrent_recv(struct tevent_req *req,
+ struct winbindd_response *response);
+struct tevent_req *winbindd_getgrent_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct winbindd_cli_state *cli,
+ struct winbindd_request *request);
+NTSTATUS winbindd_getgrent_recv(struct tevent_req *req,
+ struct winbindd_response *response);
+struct tevent_req *winbindd_endgrent_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct winbindd_cli_state *cli,
+ struct winbindd_request *request);
+NTSTATUS winbindd_endgrent_recv(struct tevent_req *req,
+ struct winbindd_response *response);
+
+struct tevent_req *winbindd_list_users_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct winbindd_cli_state *cli,
+ struct winbindd_request *request);
+NTSTATUS winbindd_list_users_recv(struct tevent_req *req,
+ struct winbindd_response *response);
+
+struct tevent_req *winbindd_list_groups_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct winbindd_cli_state *cli,
+ struct winbindd_request *request);
+NTSTATUS winbindd_list_groups_recv(struct tevent_req *req,
+ struct winbindd_response *response);
+
+struct tevent_req *winbindd_check_machine_acct_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct winbindd_cli_state *cli,
+ struct winbindd_request *request);
+NTSTATUS winbindd_check_machine_acct_recv(struct tevent_req *req,
+ struct winbindd_response *presp);
+
+struct tevent_req *winbindd_ping_dc_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct winbindd_cli_state *cli,
+ struct winbindd_request *request);
+NTSTATUS winbindd_ping_dc_recv(struct tevent_req *req,
+ struct winbindd_response *presp);
+
+struct tevent_req *winbindd_change_machine_acct_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct winbindd_cli_state *cli,
+ struct winbindd_request *request);
+NTSTATUS winbindd_change_machine_acct_recv(struct tevent_req *req,
+ struct winbindd_response *presp);
+
+struct tevent_req *winbindd_pam_auth_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct winbindd_cli_state *cli,
+ struct winbindd_request *request);
+NTSTATUS winbindd_pam_auth_recv(struct tevent_req *req,
+ struct winbindd_response *response);
+
+struct tevent_req *winbindd_pam_auth_crap_send(
+ TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct winbindd_cli_state *cli,
+ struct winbindd_request *request);
+NTSTATUS winbindd_pam_auth_crap_recv(struct tevent_req *req,
+ struct winbindd_response *response);
+
+struct tevent_req *winbindd_pam_chauthtok_send(
+ TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct winbindd_cli_state *cli,
+ struct winbindd_request *request);
+NTSTATUS winbindd_pam_chauthtok_recv(struct tevent_req *req,
+ struct winbindd_response *response);
+
+struct tevent_req *winbindd_pam_logoff_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct winbindd_cli_state *cli,
+ struct winbindd_request *request);
+NTSTATUS winbindd_pam_logoff_recv(struct tevent_req *req,
+ struct winbindd_response *response);
+
+struct tevent_req *winbindd_pam_chng_pswd_auth_crap_send(
+ TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct winbindd_cli_state *cli,
+ struct winbindd_request *request);
+NTSTATUS winbindd_pam_chng_pswd_auth_crap_recv(
+ struct tevent_req *req,
+ struct winbindd_response *response);
+
+struct tevent_req *wb_lookupsids_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct dom_sid *sids,
+ uint32_t num_sids);
+NTSTATUS wb_lookupsids_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx,
+ struct lsa_RefDomainList **domains,
+ struct lsa_TransNameArray **names);
+
+struct tevent_req *wb_sids2xids_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ const struct dom_sid *sids,
+ const uint32_t num_sids);
+NTSTATUS wb_sids2xids_recv(struct tevent_req *req,
+ struct unixid xids[], uint32_t num_xids);
+struct tevent_req *winbindd_sids_to_xids_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct winbindd_cli_state *cli,
+ struct winbindd_request *request);
+NTSTATUS winbindd_sids_to_xids_recv(struct tevent_req *req,
+ struct winbindd_response *response);
+struct tevent_req *wb_xids2sids_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ const struct unixid *xids,
+ uint32_t num_xids);
+NTSTATUS wb_xids2sids_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx,
+ struct dom_sid **sids);
+struct tevent_req *winbindd_xids_to_sids_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct winbindd_cli_state *cli,
+ struct winbindd_request *request);
+NTSTATUS winbindd_xids_to_sids_recv(struct tevent_req *req,
+ struct winbindd_response *response);
+struct tevent_req *winbindd_wins_byip_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct winbindd_cli_state *cli,
+ struct winbindd_request *request);
+NTSTATUS winbindd_wins_byip_recv(struct tevent_req *req,
+ struct winbindd_response *presp);
+struct tevent_req *winbindd_wins_byname_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct winbindd_cli_state *cli,
+ struct winbindd_request *request);
+NTSTATUS winbindd_wins_byname_recv(struct tevent_req *req,
+ struct winbindd_response *presp);
+struct tevent_req *winbindd_domain_info_send(
+ TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct winbindd_cli_state *cli,
+ struct winbindd_request *request);
+NTSTATUS winbindd_domain_info_recv(struct tevent_req *req,
+ struct winbindd_response *response);
+
+/* The following definitions come from winbindd/winbindd_samr.c */
+
+NTSTATUS open_internal_samr_conn(TALLOC_CTX *mem_ctx,
+ struct winbindd_domain *domain,
+ struct rpc_pipe_client **samr_pipe,
+ struct policy_handle *samr_domain_hnd);
+NTSTATUS open_internal_lsa_conn(TALLOC_CTX *mem_ctx,
+ struct rpc_pipe_client **lsa_pipe,
+ struct policy_handle *lsa_hnd);
+
+/* The following definitions come from winbindd/winbindd_irpc.c */
+NTSTATUS wb_irpc_register(void);
+
+/* The following definitions come from winbindd/winbindd_reconnect.c */
+bool reconnect_need_retry(NTSTATUS status, struct winbindd_domain *domain);
+
+/* The following definitions come from winbindd/winbindd_gpupdate.c */
+void gpupdate_init(void);
+void gpupdate_user_init(const char *user);
+
+/* The following comes from winbindd/winbindd_dual_srv.c */
+bool reset_cm_connection_on_error(struct winbindd_domain *domain,
+ struct dcerpc_binding_handle *b,
+ NTSTATUS status);
+
+#endif /* _WINBINDD_PROTO_H_ */
diff --git a/source3/winbindd/winbindd_reconnect.c b/source3/winbindd/winbindd_reconnect.c
new file mode 100644
index 0000000..c49831b
--- /dev/null
+++ b/source3/winbindd/winbindd_reconnect.c
@@ -0,0 +1,354 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Wrapper around winbindd_rpc.c to centralize retry logic.
+
+ Copyright (C) Volker Lendecke 2005
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "winbindd.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_WINBIND
+
+extern struct winbindd_methods msrpc_methods;
+
+bool reconnect_need_retry(NTSTATUS status, struct winbindd_domain *domain)
+{
+ if (NT_STATUS_IS_OK(status)) {
+ return false;
+ }
+
+ if (!NT_STATUS_IS_ERR(status)) {
+ return false;
+ }
+
+ if (NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED)) {
+ return false;
+ }
+
+ if (NT_STATUS_EQUAL(status, NT_STATUS_NO_SUCH_USER)) {
+ return false;
+ }
+
+ if (NT_STATUS_EQUAL(status, NT_STATUS_NO_SUCH_GROUP)) {
+ return false;
+ }
+
+ if (NT_STATUS_EQUAL(status, NT_STATUS_NO_SUCH_ALIAS)) {
+ return false;
+ }
+
+ if (NT_STATUS_EQUAL(status, NT_STATUS_NO_SUCH_MEMBER)) {
+ return false;
+ }
+
+ if (NT_STATUS_EQUAL(status, NT_STATUS_NO_SUCH_DOMAIN)) {
+ return false;
+ }
+
+ if (NT_STATUS_EQUAL(status, NT_STATUS_NO_SUCH_PRIVILEGE)) {
+ return false;
+ }
+
+ if (NT_STATUS_EQUAL(status, NT_STATUS_NO_MEMORY)) {
+ return false;
+ }
+
+ reset_cm_connection_on_error(domain, NULL, status);
+
+ return true;
+}
+
+/* List all users */
+static NTSTATUS query_user_list(struct winbindd_domain *domain,
+ TALLOC_CTX *mem_ctx,
+ uint32_t **rids)
+{
+ NTSTATUS result;
+
+ result = msrpc_methods.query_user_list(domain, mem_ctx, rids);
+
+ if (reconnect_need_retry(result, domain))
+ result = msrpc_methods.query_user_list(domain, mem_ctx, rids);
+
+ return result;
+}
+
+/* list all domain groups */
+static NTSTATUS enum_dom_groups(struct winbindd_domain *domain,
+ TALLOC_CTX *mem_ctx,
+ uint32_t *num_entries,
+ struct wb_acct_info **info)
+{
+ NTSTATUS result;
+
+ result = msrpc_methods.enum_dom_groups(domain, mem_ctx,
+ num_entries, info);
+
+ if (reconnect_need_retry(result, domain))
+ result = msrpc_methods.enum_dom_groups(domain, mem_ctx,
+ num_entries, info);
+ return result;
+}
+
+/* List all domain groups */
+
+static NTSTATUS enum_local_groups(struct winbindd_domain *domain,
+ TALLOC_CTX *mem_ctx,
+ uint32_t *num_entries,
+ struct wb_acct_info **info)
+{
+ NTSTATUS result;
+
+ result = msrpc_methods.enum_local_groups(domain, mem_ctx,
+ num_entries, info);
+
+ if (reconnect_need_retry(result, domain))
+ result = msrpc_methods.enum_local_groups(domain, mem_ctx,
+ num_entries, info);
+
+ return result;
+}
+
+/* convert a single name to a sid in a domain */
+static NTSTATUS name_to_sid(struct winbindd_domain *domain,
+ TALLOC_CTX *mem_ctx,
+ const char *domain_name,
+ const char *name,
+ uint32_t flags,
+ const char **pdom_name,
+ struct dom_sid *sid,
+ enum lsa_SidType *type)
+{
+ NTSTATUS result;
+
+ result = msrpc_methods.name_to_sid(domain, mem_ctx, domain_name, name,
+ flags, pdom_name, sid, type);
+
+ if (reconnect_need_retry(result, domain))
+ result = msrpc_methods.name_to_sid(domain, mem_ctx,
+ domain_name, name, flags,
+ pdom_name, sid, type);
+
+ return result;
+}
+
+/*
+ convert a domain SID to a user or group name
+*/
+static NTSTATUS sid_to_name(struct winbindd_domain *domain,
+ TALLOC_CTX *mem_ctx,
+ const struct dom_sid *sid,
+ char **domain_name,
+ char **name,
+ enum lsa_SidType *type)
+{
+ NTSTATUS result;
+
+ result = msrpc_methods.sid_to_name(domain, mem_ctx, sid,
+ domain_name, name, type);
+
+ if (reconnect_need_retry(result, domain))
+ result = msrpc_methods.sid_to_name(domain, mem_ctx, sid,
+ domain_name, name, type);
+
+ return result;
+}
+
+static NTSTATUS rids_to_names(struct winbindd_domain *domain,
+ TALLOC_CTX *mem_ctx,
+ const struct dom_sid *sid,
+ uint32_t *rids,
+ size_t num_rids,
+ char **domain_name,
+ char ***names,
+ enum lsa_SidType **types)
+{
+ NTSTATUS result;
+
+ result = msrpc_methods.rids_to_names(domain, mem_ctx, sid,
+ rids, num_rids,
+ domain_name, names, types);
+ if (reconnect_need_retry(result, domain)) {
+ result = msrpc_methods.rids_to_names(domain, mem_ctx, sid,
+ rids, num_rids,
+ domain_name, names,
+ types);
+ }
+
+ return result;
+}
+
+/* Lookup groups a user is a member of. I wish Unix had a call like this! */
+static NTSTATUS lookup_usergroups(struct winbindd_domain *domain,
+ TALLOC_CTX *mem_ctx,
+ const struct dom_sid *user_sid,
+ uint32_t *num_groups, struct dom_sid **user_gids)
+{
+ NTSTATUS result;
+
+ result = msrpc_methods.lookup_usergroups(domain, mem_ctx,
+ user_sid, num_groups,
+ user_gids);
+
+ if (reconnect_need_retry(result, domain))
+ result = msrpc_methods.lookup_usergroups(domain, mem_ctx,
+ user_sid, num_groups,
+ user_gids);
+
+ return result;
+}
+
+static NTSTATUS lookup_useraliases(struct winbindd_domain *domain,
+ TALLOC_CTX *mem_ctx,
+ uint32_t num_sids, const struct dom_sid *sids,
+ uint32_t *num_aliases, uint32_t **alias_rids)
+{
+ NTSTATUS result;
+
+ result = msrpc_methods.lookup_useraliases(domain, mem_ctx,
+ num_sids, sids,
+ num_aliases,
+ alias_rids);
+
+ if (reconnect_need_retry(result, domain))
+ result = msrpc_methods.lookup_useraliases(domain, mem_ctx,
+ num_sids, sids,
+ num_aliases,
+ alias_rids);
+
+ return result;
+}
+
+/* Lookup alias membership given */
+static NTSTATUS lookup_aliasmem(struct winbindd_domain *domain,
+ TALLOC_CTX *mem_ctx,
+ const struct dom_sid *sid,
+ enum lsa_SidType type,
+ uint32_t *num_sids,
+ struct dom_sid **sids)
+{
+ NTSTATUS result;
+
+ result = msrpc_methods.lookup_aliasmem(domain,
+ mem_ctx,
+ sid,
+ type,
+ num_sids,
+ sids);
+
+ if (reconnect_need_retry(result, domain))
+ result = msrpc_methods.lookup_aliasmem(domain,
+ mem_ctx,
+ sid,
+ type,
+ num_sids,
+ sids);
+
+ return result;
+}
+
+/* Lookup group membership given a rid. */
+static NTSTATUS lookup_groupmem(struct winbindd_domain *domain,
+ TALLOC_CTX *mem_ctx,
+ const struct dom_sid *group_sid,
+ enum lsa_SidType type,
+ uint32_t *num_names,
+ struct dom_sid **sid_mem, char ***names,
+ uint32_t **name_types)
+{
+ NTSTATUS result;
+
+ result = msrpc_methods.lookup_groupmem(domain, mem_ctx,
+ group_sid, type, num_names,
+ sid_mem, names,
+ name_types);
+
+ if (reconnect_need_retry(result, domain))
+ result = msrpc_methods.lookup_groupmem(domain, mem_ctx,
+ group_sid, type,
+ num_names,
+ sid_mem, names,
+ name_types);
+
+ return result;
+}
+
+/* find the lockout policy of a domain */
+static NTSTATUS lockout_policy(struct winbindd_domain *domain,
+ TALLOC_CTX *mem_ctx,
+ struct samr_DomInfo12 *policy)
+{
+ NTSTATUS result;
+
+ result = msrpc_methods.lockout_policy(domain, mem_ctx, policy);
+
+ if (reconnect_need_retry(result, domain))
+ result = msrpc_methods.lockout_policy(domain, mem_ctx, policy);
+
+ return result;
+}
+
+/* find the password policy of a domain */
+static NTSTATUS password_policy(struct winbindd_domain *domain,
+ TALLOC_CTX *mem_ctx,
+ struct samr_DomInfo1 *policy)
+{
+ NTSTATUS result;
+
+ result = msrpc_methods.password_policy(domain, mem_ctx, policy);
+
+ if (reconnect_need_retry(result, domain))
+ result = msrpc_methods.password_policy(domain, mem_ctx, policy);
+
+ return result;
+}
+
+/* get a list of trusted domains */
+static NTSTATUS trusted_domains(struct winbindd_domain *domain,
+ TALLOC_CTX *mem_ctx,
+ struct netr_DomainTrustList *trusts)
+{
+ NTSTATUS result;
+
+ result = msrpc_methods.trusted_domains(domain, mem_ctx, trusts);
+
+ if (reconnect_need_retry(result, domain))
+ result = msrpc_methods.trusted_domains(domain, mem_ctx,
+ trusts);
+
+ return result;
+}
+
+/* the rpc backend methods are exposed via this structure */
+struct winbindd_methods reconnect_methods = {
+ False,
+ query_user_list,
+ enum_dom_groups,
+ enum_local_groups,
+ name_to_sid,
+ sid_to_name,
+ rids_to_names,
+ lookup_usergroups,
+ lookup_useraliases,
+ lookup_groupmem,
+ lookup_aliasmem,
+ lockout_policy,
+ password_policy,
+ trusted_domains,
+};
diff --git a/source3/winbindd/winbindd_reconnect_ads.c b/source3/winbindd/winbindd_reconnect_ads.c
new file mode 100644
index 0000000..367f4c6
--- /dev/null
+++ b/source3/winbindd/winbindd_reconnect_ads.c
@@ -0,0 +1,362 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Wrapper around winbindd_ads.c to centralize retry logic.
+ Copyright (C) Christof Schmitt 2016
+
+ Based on winbindd_reconnect.c
+ Copyright (C) Volker Lendecke 2005
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "winbindd.h"
+
+#ifdef HAVE_ADS
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_WINBIND
+
+extern struct winbindd_methods ads_methods;
+
+static bool ldap_reconnect_need_retry(NTSTATUS status,
+ struct winbindd_domain *domain)
+{
+ if (NT_STATUS_IS_OK(status)) {
+ return false;
+ }
+
+ if (!NT_STATUS_IS_ERR(status)) {
+ return false;
+ }
+
+ if (NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED)) {
+ return false;
+ }
+
+ if (NT_STATUS_EQUAL(status, NT_STATUS_NO_SUCH_USER)) {
+ return false;
+ }
+
+ if (NT_STATUS_EQUAL(status, NT_STATUS_NO_SUCH_GROUP)) {
+ return false;
+ }
+
+ if (NT_STATUS_EQUAL(status, NT_STATUS_NO_SUCH_ALIAS)) {
+ return false;
+ }
+
+ if (NT_STATUS_EQUAL(status, NT_STATUS_NO_SUCH_MEMBER)) {
+ return false;
+ }
+
+ if (NT_STATUS_EQUAL(status, NT_STATUS_NO_SUCH_DOMAIN)) {
+ return false;
+ }
+
+ if (NT_STATUS_EQUAL(status, NT_STATUS_NO_SUCH_PRIVILEGE)) {
+ return false;
+ }
+
+ if (NT_STATUS_EQUAL(status, NT_STATUS_NO_MEMORY)) {
+ return false;
+ }
+
+ return true;
+}
+
+/* List all users */
+static NTSTATUS query_user_list(struct winbindd_domain *domain,
+ TALLOC_CTX *mem_ctx,
+ uint32_t **rids)
+{
+ NTSTATUS result;
+
+ result = ads_methods.query_user_list(domain, mem_ctx, rids);
+
+ if (ldap_reconnect_need_retry(result, domain)) {
+ result = ads_methods.query_user_list(domain, mem_ctx, rids);
+ }
+
+ return result;
+}
+
+/* list all domain groups */
+static NTSTATUS enum_dom_groups(struct winbindd_domain *domain,
+ TALLOC_CTX *mem_ctx,
+ uint32_t *num_entries,
+ struct wb_acct_info **info)
+{
+ NTSTATUS result;
+
+ result = ads_methods.enum_dom_groups(domain, mem_ctx,
+ num_entries, info);
+
+ if (ldap_reconnect_need_retry(result, domain)) {
+ result = ads_methods.enum_dom_groups(domain, mem_ctx,
+ num_entries, info);
+ }
+
+ return result;
+}
+
+/* List all domain groups */
+static NTSTATUS enum_local_groups(struct winbindd_domain *domain,
+ TALLOC_CTX *mem_ctx,
+ uint32_t *num_entries,
+ struct wb_acct_info **info)
+{
+ NTSTATUS result;
+
+ result = ads_methods.enum_local_groups(domain, mem_ctx,
+ num_entries, info);
+
+ if (ldap_reconnect_need_retry(result, domain)) {
+ result = ads_methods.enum_local_groups(domain, mem_ctx,
+ num_entries, info);
+ }
+
+ return result;
+}
+
+/* convert a single name to a sid in a domain */
+static NTSTATUS name_to_sid(struct winbindd_domain *domain,
+ TALLOC_CTX *mem_ctx,
+ const char *domain_name,
+ const char *name,
+ uint32_t flags,
+ const char **pdom_name,
+ struct dom_sid *sid,
+ enum lsa_SidType *type)
+{
+ NTSTATUS result;
+
+ result = ads_methods.name_to_sid(domain, mem_ctx, domain_name, name,
+ flags, pdom_name, sid, type);
+
+ if (reconnect_need_retry(result, domain)) {
+ result = ads_methods.name_to_sid(domain, mem_ctx,
+ domain_name, name, flags,
+ pdom_name, sid, type);
+ }
+
+ return result;
+}
+
+/*
+ convert a domain SID to a user or group name
+*/
+static NTSTATUS sid_to_name(struct winbindd_domain *domain,
+ TALLOC_CTX *mem_ctx,
+ const struct dom_sid *sid,
+ char **domain_name,
+ char **name,
+ enum lsa_SidType *type)
+{
+ NTSTATUS result;
+
+ result = ads_methods.sid_to_name(domain, mem_ctx, sid,
+ domain_name, name, type);
+
+ if (reconnect_need_retry(result, domain))
+ result = ads_methods.sid_to_name(domain, mem_ctx, sid,
+ domain_name, name, type);
+
+ return result;
+}
+
+static NTSTATUS rids_to_names(struct winbindd_domain *domain,
+ TALLOC_CTX *mem_ctx,
+ const struct dom_sid *sid,
+ uint32_t *rids,
+ size_t num_rids,
+ char **domain_name,
+ char ***names,
+ enum lsa_SidType **types)
+{
+ NTSTATUS result;
+
+ result = ads_methods.rids_to_names(domain, mem_ctx, sid,
+ rids, num_rids,
+ domain_name, names, types);
+ if (reconnect_need_retry(result, domain)) {
+ result = ads_methods.rids_to_names(domain, mem_ctx, sid,
+ rids, num_rids, domain_name,
+ names, types);
+ }
+
+ return result;
+}
+
+/* Lookup groups a user is a member of. I wish Unix had a call like this! */
+static NTSTATUS lookup_usergroups(struct winbindd_domain *domain,
+ TALLOC_CTX *mem_ctx,
+ const struct dom_sid *user_sid,
+ uint32_t *num_groups,
+ struct dom_sid **user_gids)
+{
+ NTSTATUS result;
+
+ result = ads_methods.lookup_usergroups(domain, mem_ctx, user_sid,
+ num_groups, user_gids);
+
+ if (ldap_reconnect_need_retry(result, domain)) {
+ result = ads_methods.lookup_usergroups(domain, mem_ctx,
+ user_sid, num_groups,
+ user_gids);
+ }
+
+ return result;
+}
+
+static NTSTATUS lookup_useraliases(struct winbindd_domain *domain,
+ TALLOC_CTX *mem_ctx,
+ uint32_t num_sids,
+ const struct dom_sid *sids,
+ uint32_t *num_aliases, uint32_t **alias_rids)
+{
+ NTSTATUS result;
+
+ result = ads_methods.lookup_useraliases(domain, mem_ctx, num_sids, sids,
+ num_aliases, alias_rids);
+
+ if (reconnect_need_retry(result, domain)) {
+ result = ads_methods.lookup_useraliases(domain, mem_ctx,
+ num_sids, sids,
+ num_aliases,
+ alias_rids);
+ }
+
+ return result;
+}
+
+/* Lookup group membership given a rid. */
+static NTSTATUS lookup_groupmem(struct winbindd_domain *domain,
+ TALLOC_CTX *mem_ctx,
+ const struct dom_sid *group_sid,
+ enum lsa_SidType type,
+ uint32_t *num_names,
+ struct dom_sid **sid_mem, char ***names,
+ uint32_t **name_types)
+{
+ NTSTATUS result;
+
+ result = ads_methods.lookup_groupmem(domain, mem_ctx, group_sid, type,
+ num_names, sid_mem, names,
+ name_types);
+
+ if (ldap_reconnect_need_retry(result, domain)) {
+ result = ads_methods.lookup_groupmem(domain, mem_ctx, group_sid,
+ type, num_names, sid_mem,
+ names, name_types);
+ }
+
+ return result;
+}
+
+static NTSTATUS lookup_aliasmem(struct winbindd_domain *domain,
+ TALLOC_CTX *mem_ctx,
+ const struct dom_sid *group_sid,
+ enum lsa_SidType type,
+ uint32_t *num_names,
+ struct dom_sid **sid_mem)
+{
+ NTSTATUS result = NT_STATUS_OK;
+
+ result = ads_methods.lookup_aliasmem(domain,
+ mem_ctx,
+ group_sid,
+ type,
+ num_names,
+ sid_mem);
+
+ if (ldap_reconnect_need_retry(result, domain)) {
+ result = ads_methods.lookup_aliasmem(domain,
+ mem_ctx,
+ group_sid,
+ type,
+ num_names,
+ sid_mem);
+ }
+ return result;
+}
+
+/* find the lockout policy of a domain */
+static NTSTATUS lockout_policy(struct winbindd_domain *domain,
+ TALLOC_CTX *mem_ctx,
+ struct samr_DomInfo12 *policy)
+{
+ NTSTATUS result;
+
+ result = ads_methods.lockout_policy(domain, mem_ctx, policy);
+
+ if (reconnect_need_retry(result, domain)) {
+ result = ads_methods.lockout_policy(domain, mem_ctx, policy);
+ }
+
+ return result;
+}
+
+/* find the password policy of a domain */
+static NTSTATUS password_policy(struct winbindd_domain *domain,
+ TALLOC_CTX *mem_ctx,
+ struct samr_DomInfo1 *policy)
+{
+ NTSTATUS result;
+
+ result = ads_methods.password_policy(domain, mem_ctx, policy);
+
+ if (reconnect_need_retry(result, domain)) {
+ result = ads_methods.password_policy(domain, mem_ctx, policy);
+ }
+
+ return result;
+}
+
+/* get a list of trusted domains */
+static NTSTATUS trusted_domains(struct winbindd_domain *domain,
+ TALLOC_CTX *mem_ctx,
+ struct netr_DomainTrustList *trusts)
+{
+ NTSTATUS result;
+
+ result = ads_methods.trusted_domains(domain, mem_ctx, trusts);
+
+ if (reconnect_need_retry(result, domain)) {
+ result = ads_methods.trusted_domains(domain, mem_ctx, trusts);
+ }
+
+ return result;
+}
+
+/* the rpc backend methods are exposed via this structure */
+struct winbindd_methods reconnect_ads_methods = {
+ true,
+ query_user_list,
+ enum_dom_groups,
+ enum_local_groups,
+ name_to_sid,
+ sid_to_name,
+ rids_to_names,
+ lookup_usergroups,
+ lookup_useraliases,
+ lookup_groupmem,
+ lookup_aliasmem,
+ lockout_policy,
+ password_policy,
+ trusted_domains,
+};
+
+#endif
diff --git a/source3/winbindd/winbindd_rpc.c b/source3/winbindd/winbindd_rpc.c
new file mode 100644
index 0000000..2b4a47e
--- /dev/null
+++ b/source3/winbindd/winbindd_rpc.c
@@ -0,0 +1,855 @@
+/*
+ * Unix SMB/CIFS implementation.
+ *
+ * Winbind rpc backend functions
+ *
+ * Copyright (c) 2000-2003 Tim Potter
+ * Copyright (c) 2001 Andrew Tridgell
+ * Copyright (c) 2005 Volker Lendecke
+ * Copyright (c) 2008 Guenther Deschner (pidl conversion)
+ * Copyright (c) 2010 Andreas Schneider <asn@samba.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "includes.h"
+#include "winbindd.h"
+#include "winbindd_rpc.h"
+#include "rpc_client/rpc_client.h"
+#include "librpc/gen_ndr/ndr_samr_c.h"
+#include "librpc/gen_ndr/ndr_lsa_c.h"
+#include "rpc_client/cli_samr.h"
+#include "rpc_client/cli_lsarpc.h"
+#include "../libcli/security/security.h"
+#include "lsa.h"
+
+/* Query display info for a domain */
+NTSTATUS rpc_query_user_list(TALLOC_CTX *mem_ctx,
+ struct rpc_pipe_client *samr_pipe,
+ struct policy_handle *samr_policy,
+ const struct dom_sid *domain_sid,
+ uint32_t **prids)
+{
+ struct dcerpc_binding_handle *b = samr_pipe->binding_handle;
+ uint32_t *rids = NULL;
+ uint32_t num_rids = 0;
+ uint32_t i = 0;
+ uint32_t resume_handle = 0;
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+ NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
+ TALLOC_CTX *tmp_ctx;
+
+ *prids = NULL;
+
+ tmp_ctx = talloc_stackframe();
+ if (tmp_ctx == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ do {
+ struct samr_SamArray *sam_array = NULL;
+ uint32_t count = 0;
+ uint32_t *tmp;
+
+ status = dcerpc_samr_EnumDomainUsers(
+ b, tmp_ctx, samr_policy, &resume_handle,
+ ACB_NORMAL, &sam_array, 0xffff, &count, &result);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto done;
+ }
+ if (!NT_STATUS_IS_OK(result)) {
+ if (!NT_STATUS_EQUAL(result, STATUS_MORE_ENTRIES)) {
+ DBG_WARNING("EnumDomainUsers failed: %s\n",
+ nt_errstr(result));
+ status = result;
+ goto done;
+ }
+ }
+
+ if (num_rids + count < num_rids) {
+ status = NT_STATUS_INTEGER_OVERFLOW;
+ goto done;
+ }
+
+ tmp = talloc_realloc(tmp_ctx, rids, uint32_t, num_rids+count);
+ if (tmp == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ goto done;
+ }
+ rids = tmp;
+
+ for (i=0; i<count; i++) {
+ rids[num_rids++] = sam_array->entries[i].idx;
+ }
+
+ TALLOC_FREE(sam_array);
+ } while (NT_STATUS_EQUAL(result, STATUS_MORE_ENTRIES));
+
+ *prids = talloc_steal(mem_ctx, rids);
+ status = NT_STATUS_OK;
+
+done:
+ TALLOC_FREE(tmp_ctx);
+ return status;
+}
+
+/* List all domain groups */
+NTSTATUS rpc_enum_dom_groups(TALLOC_CTX *mem_ctx,
+ struct rpc_pipe_client *samr_pipe,
+ struct policy_handle *samr_policy,
+ uint32_t *pnum_info,
+ struct wb_acct_info **pinfo)
+{
+ struct wb_acct_info *info = NULL;
+ uint32_t start = 0;
+ uint32_t num_info = 0;
+ NTSTATUS status, result;
+ struct dcerpc_binding_handle *b = samr_pipe->binding_handle;
+
+ *pnum_info = 0;
+
+ do {
+ struct samr_SamArray *sam_array = NULL;
+ uint32_t count = 0;
+ uint32_t g;
+
+ /* start is updated by this call. */
+ status = dcerpc_samr_EnumDomainGroups(b,
+ mem_ctx,
+ samr_policy,
+ &start,
+ &sam_array,
+ 0xFFFF, /* buffer size? */
+ &count,
+ &result);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ if (!NT_STATUS_IS_OK(result)) {
+ if (!NT_STATUS_EQUAL(result, STATUS_MORE_ENTRIES)) {
+ DEBUG(2,("query_user_list: failed to enum domain groups: %s\n",
+ nt_errstr(result)));
+ return result;
+ }
+ }
+
+ info = talloc_realloc(mem_ctx,
+ info,
+ struct wb_acct_info,
+ num_info + count);
+ if (info == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ for (g = 0; g < count; g++) {
+ struct wb_acct_info *i = &info[num_info + g];
+
+ i->acct_name = talloc_strdup(info,
+ sam_array->entries[g].name.string);
+ if (i->acct_name == NULL) {
+ TALLOC_FREE(info);
+ return NT_STATUS_NO_MEMORY;
+ }
+ i->acct_desc = NULL;
+ i->rid = sam_array->entries[g].idx;
+ }
+
+ num_info += count;
+ } while (NT_STATUS_EQUAL(result, STATUS_MORE_ENTRIES));
+
+ *pnum_info = num_info;
+ *pinfo = info;
+
+ return NT_STATUS_OK;
+}
+
+NTSTATUS rpc_enum_local_groups(TALLOC_CTX *mem_ctx,
+ struct rpc_pipe_client *samr_pipe,
+ struct policy_handle *samr_policy,
+ uint32_t *pnum_info,
+ struct wb_acct_info **pinfo)
+{
+ struct wb_acct_info *info = NULL;
+ uint32_t num_info = 0;
+ NTSTATUS status, result;
+ struct dcerpc_binding_handle *b = samr_pipe->binding_handle;
+
+ *pnum_info = 0;
+
+ do {
+ struct samr_SamArray *sam_array = NULL;
+ uint32_t count = 0;
+ uint32_t start = num_info;
+ uint32_t g;
+
+ status = dcerpc_samr_EnumDomainAliases(b,
+ mem_ctx,
+ samr_policy,
+ &start,
+ &sam_array,
+ 0xFFFF, /* buffer size? */
+ &count,
+ &result);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ if (!NT_STATUS_IS_OK(result)) {
+ if (!NT_STATUS_EQUAL(result, STATUS_MORE_ENTRIES)) {
+ return result;
+ }
+ }
+
+ info = talloc_realloc(mem_ctx,
+ info,
+ struct wb_acct_info,
+ num_info + count);
+ if (info == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ for (g = 0; g < count; g++) {
+ struct wb_acct_info *i = &info[num_info + g];
+
+ i->acct_name = talloc_strdup(info,
+ sam_array->entries[g].name.string);
+ if (i->acct_name == NULL) {
+ TALLOC_FREE(info);
+ return NT_STATUS_NO_MEMORY;
+ }
+ i->acct_desc = NULL;
+ i->rid = sam_array->entries[g].idx;
+ }
+
+ num_info += count;
+ } while (NT_STATUS_EQUAL(result, STATUS_MORE_ENTRIES));
+
+ *pnum_info = num_info;
+ *pinfo = info;
+
+ return NT_STATUS_OK;
+}
+
+/* Lookup groups a user is a member of. */
+NTSTATUS rpc_lookup_usergroups(TALLOC_CTX *mem_ctx,
+ struct rpc_pipe_client *samr_pipe,
+ struct policy_handle *samr_policy,
+ const struct dom_sid *domain_sid,
+ const struct dom_sid *user_sid,
+ uint32_t *pnum_groups,
+ struct dom_sid **puser_grpsids)
+{
+ struct policy_handle user_policy;
+ struct samr_RidWithAttributeArray *rid_array = NULL;
+ struct dom_sid *user_grpsids = NULL;
+ uint32_t num_groups = 0, i;
+ uint32_t user_rid;
+ NTSTATUS status, result;
+ struct dcerpc_binding_handle *b = samr_pipe->binding_handle;
+
+ if (!sid_peek_check_rid(domain_sid, user_sid, &user_rid)) {
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ /* Get user handle */
+ status = dcerpc_samr_OpenUser(b,
+ mem_ctx,
+ samr_policy,
+ SEC_FLAG_MAXIMUM_ALLOWED,
+ user_rid,
+ &user_policy,
+ &result);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ if (!NT_STATUS_IS_OK(result)) {
+ return result;
+ }
+
+ /* Query user rids */
+ status = dcerpc_samr_GetGroupsForUser(b,
+ mem_ctx,
+ &user_policy,
+ &rid_array,
+ &result);
+ {
+ NTSTATUS _result;
+ dcerpc_samr_Close(b, mem_ctx, &user_policy, &_result);
+ }
+
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ if (!NT_STATUS_IS_OK(result)) {
+ return result;
+ }
+
+ num_groups = rid_array->count;
+
+ user_grpsids = talloc_array(mem_ctx, struct dom_sid, num_groups);
+ if (user_grpsids == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ return status;
+ }
+
+ for (i = 0; i < num_groups; i++) {
+ sid_compose(&(user_grpsids[i]), domain_sid,
+ rid_array->rids[i].rid);
+ }
+
+ *pnum_groups = num_groups;
+
+ *puser_grpsids = user_grpsids;
+
+ return NT_STATUS_OK;
+}
+
+NTSTATUS rpc_lookup_useraliases(TALLOC_CTX *mem_ctx,
+ struct rpc_pipe_client *samr_pipe,
+ struct policy_handle *samr_policy,
+ uint32_t num_sids,
+ const struct dom_sid *sids,
+ uint32_t *pnum_aliases,
+ uint32_t **palias_rids)
+{
+#define MAX_SAM_ENTRIES_W2K 0x400 /* 1024 */
+ uint32_t num_query_sids = 0;
+ uint32_t num_queries = 1;
+ uint32_t num_aliases = 0;
+ uint32_t total_sids = 0;
+ uint32_t *alias_rids = NULL;
+ uint32_t rangesize = MAX_SAM_ENTRIES_W2K;
+ uint32_t i;
+ struct samr_Ids alias_rids_query;
+ NTSTATUS status, result;
+ struct dcerpc_binding_handle *b = samr_pipe->binding_handle;
+
+ do {
+ /* prepare query */
+ struct lsa_SidArray sid_array;
+
+ ZERO_STRUCT(sid_array);
+
+ num_query_sids = MIN(num_sids - total_sids, rangesize);
+
+ DEBUG(10,("rpc: lookup_useraliases: entering query %d for %d sids\n",
+ num_queries, num_query_sids));
+
+ if (num_query_sids) {
+ sid_array.sids = talloc_zero_array(mem_ctx, struct lsa_SidPtr, num_query_sids);
+ if (sid_array.sids == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ } else {
+ sid_array.sids = NULL;
+ }
+
+ for (i = 0; i < num_query_sids; i++) {
+ sid_array.sids[i].sid = dom_sid_dup(mem_ctx, &sids[total_sids++]);
+ if (sid_array.sids[i].sid == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ }
+ sid_array.num_sids = num_query_sids;
+
+ /* do request */
+ status = dcerpc_samr_GetAliasMembership(b,
+ mem_ctx,
+ samr_policy,
+ &sid_array,
+ &alias_rids_query,
+ &result);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ if (!NT_STATUS_IS_OK(result)) {
+ return result;
+ }
+
+ /* process output */
+ for (i = 0; i < alias_rids_query.count; i++) {
+ size_t na = num_aliases;
+
+ if (!add_rid_to_array_unique(mem_ctx,
+ alias_rids_query.ids[i],
+ &alias_rids,
+ &na)) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ num_aliases = na;
+ }
+
+ num_queries++;
+
+ } while (total_sids < num_sids);
+
+ DEBUG(10,("rpc: rpc_lookup_useraliases: got %d aliases in %d queries "
+ "(rangesize: %d)\n", num_aliases, num_queries, rangesize));
+
+ *pnum_aliases = num_aliases;
+ *palias_rids = alias_rids;
+
+ return NT_STATUS_OK;
+#undef MAX_SAM_ENTRIES_W2K
+}
+
+/* Lookup group membership given a rid. */
+NTSTATUS rpc_lookup_groupmem(TALLOC_CTX *mem_ctx,
+ struct rpc_pipe_client *samr_pipe,
+ struct policy_handle *samr_policy,
+ const char *domain_name,
+ const struct dom_sid *domain_sid,
+ const struct dom_sid *group_sid,
+ enum lsa_SidType type,
+ uint32_t *pnum_names,
+ struct dom_sid **psid_mem,
+ char ***pnames,
+ uint32_t **pname_types)
+{
+ struct policy_handle group_policy;
+ uint32_t group_rid;
+ uint32_t *rid_mem = NULL;
+
+ uint32_t num_names = 0;
+ uint32_t total_names = 0;
+ struct dom_sid *sid_mem = NULL;
+ char **names = NULL;
+ uint32_t *name_types = NULL;
+
+ struct lsa_Strings tmp_names;
+ struct samr_Ids tmp_types;
+
+ uint32_t j, r;
+ NTSTATUS status, result;
+ struct dcerpc_binding_handle *b = samr_pipe->binding_handle;
+
+ if (!sid_peek_check_rid(domain_sid, group_sid, &group_rid)) {
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ switch(type) {
+ case SID_NAME_DOM_GRP:
+ {
+ struct samr_RidAttrArray *rids = NULL;
+
+ status = dcerpc_samr_OpenGroup(b,
+ mem_ctx,
+ samr_policy,
+ SEC_FLAG_MAXIMUM_ALLOWED,
+ group_rid,
+ &group_policy,
+ &result);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ if (!NT_STATUS_IS_OK(result)) {
+ return result;
+ }
+
+ /*
+ * Step #1: Get a list of user rids that are the members of the group.
+ */
+ status = dcerpc_samr_QueryGroupMember(b,
+ mem_ctx,
+ &group_policy,
+ &rids,
+ &result);
+ {
+ NTSTATUS _result;
+ dcerpc_samr_Close(b, mem_ctx, &group_policy, &_result);
+ }
+
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ if (!NT_STATUS_IS_OK(result)) {
+ return result;
+ }
+
+
+ if (rids == NULL || rids->count == 0) {
+ pnum_names = 0;
+ pnames = NULL;
+ pname_types = NULL;
+ psid_mem = NULL;
+
+ return NT_STATUS_OK;
+ }
+
+ num_names = rids->count;
+ rid_mem = rids->rids;
+
+ break;
+ }
+ default:
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ /*
+ * Step #2: Convert list of rids into list of usernames.
+ */
+ if (num_names > 0) {
+ names = talloc_zero_array(mem_ctx, char *, num_names);
+ name_types = talloc_zero_array(mem_ctx, uint32_t, num_names);
+ sid_mem = talloc_zero_array(mem_ctx, struct dom_sid, num_names);
+ if (names == NULL || name_types == NULL || sid_mem == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ }
+
+ for (j = 0; j < num_names; j++) {
+ sid_compose(&sid_mem[j], domain_sid, rid_mem[j]);
+ }
+
+ status = dcerpc_samr_LookupRids(b,
+ mem_ctx,
+ samr_policy,
+ num_names,
+ rid_mem,
+ &tmp_names,
+ &tmp_types,
+ &result);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ if (!NT_STATUS_IS_OK(result)) {
+ if (!NT_STATUS_EQUAL(result, STATUS_SOME_UNMAPPED)) {
+ return result;
+ }
+ }
+
+ /* Copy result into array. The talloc system will take
+ care of freeing the temporary arrays later on. */
+ if (tmp_names.count != num_names) {
+ return NT_STATUS_INVALID_NETWORK_RESPONSE;
+ }
+ if (tmp_types.count != num_names) {
+ return NT_STATUS_INVALID_NETWORK_RESPONSE;
+ }
+
+ for (r = 0; r < tmp_names.count; r++) {
+ if (tmp_types.ids[r] == SID_NAME_UNKNOWN) {
+ continue;
+ }
+ if (total_names >= num_names) {
+ break;
+ }
+ names[total_names] = fill_domain_username_talloc(names,
+ domain_name,
+ tmp_names.names[r].string,
+ true);
+ if (names[total_names] == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ name_types[total_names] = tmp_types.ids[r];
+ total_names++;
+ }
+
+ *pnum_names = total_names;
+ *pnames = names;
+ *pname_types = name_types;
+ *psid_mem = sid_mem;
+
+ return NT_STATUS_OK;
+}
+
+/* Lookup alias membership using a rid taken from alias_sid. */
+NTSTATUS rpc_lookup_aliasmem(TALLOC_CTX *mem_ctx,
+ struct rpc_pipe_client *samr_pipe,
+ struct policy_handle *samr_policy,
+ const struct dom_sid *domain_sid,
+ const struct dom_sid *alias_sid,
+ enum lsa_SidType type,
+ uint32_t *pnum_sids,
+ struct dom_sid **psids)
+{
+ uint32_t alias_rid;
+ struct dom_sid *sid_mem = NULL;
+ struct lsa_SidArray sid_array;
+ uint32_t i;
+ NTSTATUS status, result;
+ struct dcerpc_binding_handle *b = samr_pipe->binding_handle;
+
+ if (!sid_peek_check_rid(domain_sid, alias_sid, &alias_rid)) {
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ switch (type) {
+ case SID_NAME_ALIAS: {
+ struct policy_handle alias_policy;
+
+ status = dcerpc_samr_OpenAlias(b,
+ mem_ctx,
+ samr_policy,
+ SEC_FLAG_MAXIMUM_ALLOWED,
+ alias_rid,
+ &alias_policy,
+ &result);
+ if (any_nt_status_not_ok(status, result, &status)) {
+ return status;
+ }
+
+ status = dcerpc_samr_GetMembersInAlias(b,
+ mem_ctx,
+ &alias_policy,
+ &sid_array,
+ &result);
+ {
+ NTSTATUS _result;
+ dcerpc_samr_Close(b, mem_ctx, &alias_policy, &_result);
+ }
+ if (any_nt_status_not_ok(status, result, &status)) {
+ return status;
+ }
+
+ sid_mem = talloc_zero_array(mem_ctx,
+ struct dom_sid,
+ sid_array.num_sids);
+ if (sid_mem == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ /*
+ * We cannot just simply assign '*psids = sid_array.sids;'
+ * we need to copy every sid since these are incompatible types:
+ * 'struct dom_sid *' vs 'struct lsa_SidPtr *'
+ */
+ for (i = 0; i < sid_array.num_sids; i++) {
+ sid_copy(&sid_mem[i], sid_array.sids[i].sid);
+ }
+
+ *pnum_sids = sid_array.num_sids;
+ *psids = sid_mem;
+
+ return NT_STATUS_OK;
+ }
+ default:
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+}
+
+/* Get a list of trusted domains */
+NTSTATUS rpc_trusted_domains(TALLOC_CTX *mem_ctx,
+ struct rpc_pipe_client *lsa_pipe,
+ struct policy_handle *lsa_policy,
+ uint32_t *pnum_trusts,
+ struct netr_DomainTrust **ptrusts)
+{
+ struct netr_DomainTrust *array = NULL;
+ uint32_t enum_ctx = 0;
+ uint32_t count = 0;
+ NTSTATUS status, result;
+ struct dcerpc_binding_handle *b = lsa_pipe->binding_handle;
+
+ do {
+ struct lsa_DomainList dom_list;
+ struct lsa_DomainListEx dom_list_ex;
+ bool has_ex = false;
+ uint32_t i;
+
+ /*
+ * We don't run into deadlocks here, cause winbind_off() is
+ * called in the main function.
+ */
+ status = dcerpc_lsa_EnumTrustedDomainsEx(b,
+ mem_ctx,
+ lsa_policy,
+ &enum_ctx,
+ &dom_list_ex,
+ (uint32_t) -1,
+ &result);
+ if (NT_STATUS_IS_OK(status) && !NT_STATUS_IS_ERR(result) &&
+ dom_list_ex.count > 0) {
+ count += dom_list_ex.count;
+ has_ex = true;
+ } else {
+ status = dcerpc_lsa_EnumTrustDom(b,
+ mem_ctx,
+ lsa_policy,
+ &enum_ctx,
+ &dom_list,
+ (uint32_t) -1,
+ &result);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ if (!NT_STATUS_IS_OK(result)) {
+ if (!NT_STATUS_EQUAL(result, STATUS_MORE_ENTRIES)) {
+ return result;
+ }
+ }
+
+ count += dom_list.count;
+ }
+
+ array = talloc_realloc(mem_ctx,
+ array,
+ struct netr_DomainTrust,
+ count);
+ if (array == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ for (i = 0; i < count; i++) {
+ struct netr_DomainTrust *trust = &array[i];
+ struct dom_sid *sid;
+
+ ZERO_STRUCTP(trust);
+
+ sid = talloc(array, struct dom_sid);
+ if (sid == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ if (dom_list_ex.domains[i].sid == NULL) {
+ DBG_ERR("Trusted domain %s has no SID, "
+ "skipping!\n",
+ trust->dns_name);
+ continue;
+ }
+
+ if (has_ex) {
+ trust->netbios_name = talloc_move(array,
+ &dom_list_ex.domains[i].netbios_name.string);
+ trust->dns_name = talloc_move(array,
+ &dom_list_ex.domains[i].domain_name.string);
+ sid_copy(sid, dom_list_ex.domains[i].sid);
+ } else {
+ trust->netbios_name = talloc_move(array,
+ &dom_list.domains[i].name.string);
+ trust->dns_name = NULL;
+
+ sid_copy(sid, dom_list.domains[i].sid);
+ }
+
+ trust->sid = sid;
+ }
+ } while (NT_STATUS_EQUAL(result, STATUS_MORE_ENTRIES));
+
+ *pnum_trusts = count;
+ *ptrusts = array;
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS rpc_try_lookup_sids3(TALLOC_CTX *mem_ctx,
+ struct winbindd_domain *domain,
+ struct rpc_pipe_client *cli,
+ struct lsa_SidArray *sids,
+ struct lsa_RefDomainList **pdomains,
+ struct lsa_TransNameArray **pnames)
+{
+ struct lsa_TransNameArray2 lsa_names2;
+ struct lsa_TransNameArray *names = *pnames;
+ uint32_t i, count = 0;
+ NTSTATUS status, result;
+
+ ZERO_STRUCT(lsa_names2);
+ status = dcerpc_lsa_LookupSids3(cli->binding_handle,
+ mem_ctx,
+ sids,
+ pdomains,
+ &lsa_names2,
+ LSA_LOOKUP_NAMES_ALL,
+ &count,
+ LSA_LOOKUP_OPTION_SEARCH_ISOLATED_NAMES,
+ LSA_CLIENT_REVISION_2,
+ &result);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ if (NT_STATUS_LOOKUP_ERR(result)) {
+ return result;
+ }
+ if (sids->num_sids != lsa_names2.count) {
+ return NT_STATUS_INVALID_NETWORK_RESPONSE;
+ }
+
+ names->count = lsa_names2.count;
+ names->names = talloc_array(names, struct lsa_TranslatedName,
+ names->count);
+ if (names->names == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ for (i=0; i<names->count; i++) {
+ names->names[i].sid_type = lsa_names2.names[i].sid_type;
+ names->names[i].name.string = talloc_move(
+ names->names, &lsa_names2.names[i].name.string);
+ names->names[i].sid_index = lsa_names2.names[i].sid_index;
+
+ if (names->names[i].sid_index == UINT32_MAX) {
+ continue;
+ }
+ if ((*pdomains) == NULL) {
+ return NT_STATUS_INVALID_NETWORK_RESPONSE;
+ }
+ if (names->names[i].sid_index >= (*pdomains)->count) {
+ return NT_STATUS_INVALID_NETWORK_RESPONSE;
+ }
+ }
+ return NT_STATUS_OK;
+}
+
+NTSTATUS rpc_lookup_sids(TALLOC_CTX *mem_ctx,
+ struct winbindd_domain *domain,
+ struct lsa_SidArray *sids,
+ struct lsa_RefDomainList **pdomains,
+ struct lsa_TransNameArray **pnames)
+{
+ struct lsa_TransNameArray *names = *pnames;
+ struct rpc_pipe_client *cli = NULL;
+ struct policy_handle lsa_policy;
+ uint32_t count;
+ uint32_t i;
+ NTSTATUS status, result;
+
+ status = cm_connect_lsat(domain, mem_ctx, &cli, &lsa_policy);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ if (cli->transport->transport == NCACN_IP_TCP) {
+ return rpc_try_lookup_sids3(mem_ctx, domain, cli, sids,
+ pdomains, pnames);
+ }
+
+ status = dcerpc_lsa_LookupSids(cli->binding_handle, mem_ctx,
+ &lsa_policy, sids, pdomains,
+ names, LSA_LOOKUP_NAMES_ALL,
+ &count, &result);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ if (NT_STATUS_LOOKUP_ERR(result)) {
+ return result;
+ }
+
+ if (sids->num_sids != names->count) {
+ return NT_STATUS_INVALID_NETWORK_RESPONSE;
+ }
+
+ for (i=0; i < names->count; i++) {
+ if (names->names[i].sid_index == UINT32_MAX) {
+ continue;
+ }
+ if ((*pdomains) == NULL) {
+ return NT_STATUS_INVALID_NETWORK_RESPONSE;
+ }
+ if (names->names[i].sid_index >= (*pdomains)->count) {
+ return NT_STATUS_INVALID_NETWORK_RESPONSE;
+ }
+ }
+
+ return NT_STATUS_OK;
+}
diff --git a/source3/winbindd/winbindd_rpc.h b/source3/winbindd/winbindd_rpc.h
new file mode 100644
index 0000000..020958f
--- /dev/null
+++ b/source3/winbindd/winbindd_rpc.h
@@ -0,0 +1,95 @@
+/*
+ * Unix SMB/CIFS implementation.
+ *
+ * Winbind rpc backend functions
+ *
+ * Copyright (c) 2000-2003 Tim Potter
+ * Copyright (c) 2001 Andrew Tridgell
+ * Copyright (c) 2005 Volker Lendecke
+ * Copyright (c) 2008 Guenther Deschner (pidl conversion)
+ * Copyright (c) 2010 Andreas Schneider <asn@samba.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef _WINBINDD_RPC_H_
+#define _WINBINDD_RPC_H_
+
+/* Query display info for a domain */
+NTSTATUS rpc_query_user_list(TALLOC_CTX *mem_ctx,
+ struct rpc_pipe_client *samr_pipe,
+ struct policy_handle *samr_policy,
+ const struct dom_sid *domain_sid,
+ uint32_t **prids);
+
+NTSTATUS rpc_enum_dom_groups(TALLOC_CTX *mem_ctx,
+ struct rpc_pipe_client *samr_pipe,
+ struct policy_handle *sam_policy,
+ uint32_t *pnum_info,
+ struct wb_acct_info **pinfo);
+
+/* List all domain groups */
+NTSTATUS rpc_enum_local_groups(TALLOC_CTX *mem_ctx,
+ struct rpc_pipe_client *samr_pipe,
+ struct policy_handle *samr_policy,
+ uint32_t *pnum_info,
+ struct wb_acct_info **pinfo);
+
+/* Lookup groups a user is a member of. */
+NTSTATUS rpc_lookup_usergroups(TALLOC_CTX *mem_ctx,
+ struct rpc_pipe_client *samr_pipe,
+ struct policy_handle *samr_policy,
+ const struct dom_sid *domain_sid,
+ const struct dom_sid *user_sid,
+ uint32_t *pnum_groups,
+ struct dom_sid **puser_grpsids);
+
+NTSTATUS rpc_lookup_useraliases(TALLOC_CTX *mem_ctx,
+ struct rpc_pipe_client *samr_pipe,
+ struct policy_handle *samr_policy,
+ uint32_t num_sids,
+ const struct dom_sid *sids,
+ uint32_t *pnum_aliases,
+ uint32_t **palias_rids);
+
+/* Lookup group membership given a rid. */
+NTSTATUS rpc_lookup_groupmem(TALLOC_CTX *mem_ctx,
+ struct rpc_pipe_client *samr_pipe,
+ struct policy_handle *samr_policy,
+ const char *domain_name,
+ const struct dom_sid *domain_sid,
+ const struct dom_sid *group_sid,
+ enum lsa_SidType type,
+ uint32_t *pnum_names,
+ struct dom_sid **psid_mem,
+ char ***pnames,
+ uint32_t **pname_types);
+
+NTSTATUS rpc_lookup_aliasmem(TALLOC_CTX *mem_ctx,
+ struct rpc_pipe_client *samr_pipe,
+ struct policy_handle *samr_policy,
+ const struct dom_sid *domain_sid,
+ const struct dom_sid *group_sid,
+ enum lsa_SidType type,
+ uint32_t *pnum_sids,
+ struct dom_sid **psids);
+
+/* Get a list of trusted domains */
+NTSTATUS rpc_trusted_domains(TALLOC_CTX *mem_ctx,
+ struct rpc_pipe_client *lsa_pipe,
+ struct policy_handle *lsa_policy,
+ uint32_t *pnum_trusts,
+ struct netr_DomainTrust **ptrusts);
+
+#endif /* _WINBINDD_RPC_H_ */
diff --git a/source3/winbindd/winbindd_samr.c b/source3/winbindd/winbindd_samr.c
new file mode 100644
index 0000000..65e9932
--- /dev/null
+++ b/source3/winbindd/winbindd_samr.c
@@ -0,0 +1,1424 @@
+/*
+ * Unix SMB/CIFS implementation.
+ *
+ * Winbind rpc backend functions
+ *
+ * Copyright (c) 2000-2003 Tim Potter
+ * Copyright (c) 2001 Andrew Tridgell
+ * Copyright (c) 2005 Volker Lendecke
+ * Copyright (c) 2008 Guenther Deschner (pidl conversion)
+ * Copyright (c) 2010 Andreas Schneider <asn@samba.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "includes.h"
+#include "winbindd.h"
+#include "winbindd_rpc.h"
+#include "lib/util_unixsids.h"
+#include "rpc_client/rpc_client.h"
+#include "rpc_client/cli_pipe.h"
+#include "../librpc/gen_ndr/ndr_samr_c.h"
+#include "rpc_client/cli_samr.h"
+#include "../librpc/gen_ndr/ndr_lsa_c.h"
+#include "rpc_client/cli_lsarpc.h"
+#include "rpc_server/rpc_ncacn_np.h"
+#include "../libcli/security/security.h"
+#include "passdb/machine_sid.h"
+#include "auth.h"
+#include "source3/lib/global_contexts.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_WINBIND
+
+/*
+ * The other end of this won't go away easily, so we can trust it
+ *
+ * It is either a long-lived process with the same lifetime as
+ * winbindd or a part of this process
+ */
+struct winbind_internal_pipes {
+ struct tevent_timer *shutdown_timer;
+ struct rpc_pipe_client *samr_pipe;
+ struct policy_handle samr_domain_hnd;
+ struct rpc_pipe_client *lsa_pipe;
+ struct policy_handle lsa_hnd;
+};
+
+
+NTSTATUS open_internal_samr_conn(TALLOC_CTX *mem_ctx,
+ struct winbindd_domain *domain,
+ struct rpc_pipe_client **samr_pipe,
+ struct policy_handle *samr_domain_hnd)
+{
+ NTSTATUS status, result;
+ struct policy_handle samr_connect_hnd;
+ struct dcerpc_binding_handle *b;
+
+ status = wb_open_internal_pipe(mem_ctx, &ndr_table_samr, samr_pipe);
+ if (!NT_STATUS_IS_OK(status)) {
+ DBG_ERR("Could not connect to %s pipe: %s\n",
+ ndr_table_samr.name, nt_errstr(status));
+ return status;
+ }
+
+ b = (*samr_pipe)->binding_handle;
+
+ status = dcerpc_samr_Connect2(b, mem_ctx,
+ (*samr_pipe)->desthost,
+ SEC_FLAG_MAXIMUM_ALLOWED,
+ &samr_connect_hnd,
+ &result);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ if (!NT_STATUS_IS_OK(result)) {
+ return result;
+ }
+
+ status = dcerpc_samr_OpenDomain(b, mem_ctx,
+ &samr_connect_hnd,
+ SEC_FLAG_MAXIMUM_ALLOWED,
+ &domain->sid,
+ samr_domain_hnd,
+ &result);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ return result;
+}
+
+NTSTATUS open_internal_lsa_conn(TALLOC_CTX *mem_ctx,
+ struct rpc_pipe_client **lsa_pipe,
+ struct policy_handle *lsa_hnd)
+{
+ NTSTATUS status;
+
+ status = wb_open_internal_pipe(mem_ctx, &ndr_table_lsarpc, lsa_pipe);
+ if (!NT_STATUS_IS_OK(status)) {
+ DBG_ERR("Could not connect to %s pipe: %s\n",
+ ndr_table_lsarpc.name, nt_errstr(status));
+ return status;
+ }
+
+ status = rpccli_lsa_open_policy((*lsa_pipe),
+ mem_ctx,
+ true,
+ SEC_FLAG_MAXIMUM_ALLOWED,
+ lsa_hnd);
+
+ return status;
+}
+
+static void cached_internal_pipe_close(
+ struct tevent_context *ev,
+ struct tevent_timer *te,
+ struct timeval current_time,
+ void *private_data)
+{
+ struct winbindd_domain *domain = talloc_get_type_abort(
+ private_data, struct winbindd_domain);
+ /*
+ * Freeing samr_pipes closes the cached pipes.
+ *
+ * We can do a hard close because at the time of this commit
+ * we only use synchronous calls to external pipes. So we can't
+ * have any outstanding requests. Also, we don't set
+ * dcerpc_binding_handle_set_sync_ev in winbind, so we don't
+ * get nested event loops. Once we start to get async in
+ * winbind children, we need to check for outstanding calls
+ */
+ TALLOC_FREE(domain->backend_data.samr_pipes);
+}
+
+static NTSTATUS open_cached_internal_pipe_conn(
+ struct winbindd_domain *domain,
+ struct rpc_pipe_client **samr_pipe,
+ struct policy_handle *samr_domain_hnd,
+ struct rpc_pipe_client **lsa_pipe,
+ struct policy_handle *lsa_hnd)
+{
+ struct winbind_internal_pipes *internal_pipes =
+ domain->backend_data.samr_pipes;
+
+ if (internal_pipes == NULL) {
+ TALLOC_CTX *frame = talloc_stackframe();
+ NTSTATUS status;
+
+ internal_pipes = talloc_zero(frame,
+ struct winbind_internal_pipes);
+
+ status = open_internal_samr_conn(
+ internal_pipes,
+ domain,
+ &internal_pipes->samr_pipe,
+ &internal_pipes->samr_domain_hnd);
+ if (!NT_STATUS_IS_OK(status)) {
+ TALLOC_FREE(frame);
+ return status;
+ }
+
+ status = open_internal_lsa_conn(internal_pipes,
+ &internal_pipes->lsa_pipe,
+ &internal_pipes->lsa_hnd);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ TALLOC_FREE(frame);
+ return status;
+ }
+
+ internal_pipes->shutdown_timer = tevent_add_timer(
+ global_event_context(),
+ internal_pipes,
+ timeval_current_ofs(5, 0),
+ cached_internal_pipe_close,
+ domain);
+ if (internal_pipes->shutdown_timer == NULL) {
+ TALLOC_FREE(frame);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ domain->backend_data.samr_pipes =
+ talloc_steal(domain, internal_pipes);
+
+ TALLOC_FREE(frame);
+ }
+
+ if (samr_domain_hnd) {
+ *samr_domain_hnd = internal_pipes->samr_domain_hnd;
+ }
+
+ if (samr_pipe) {
+ *samr_pipe = internal_pipes->samr_pipe;
+ }
+
+ if (lsa_hnd) {
+ *lsa_hnd = internal_pipes->lsa_hnd;
+ }
+
+ if (lsa_pipe) {
+ *lsa_pipe = internal_pipes->lsa_pipe;
+ }
+
+ tevent_update_timer(
+ internal_pipes->shutdown_timer,
+ timeval_current_ofs(5, 0));
+
+ return NT_STATUS_OK;
+}
+
+static bool reset_connection_on_error(struct winbindd_domain *domain,
+ struct rpc_pipe_client *p,
+ NTSTATUS status)
+{
+ struct dcerpc_binding_handle *b = p->binding_handle;
+
+ if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
+ NT_STATUS_EQUAL(status, NT_STATUS_IO_DEVICE_ERROR))
+ {
+ TALLOC_FREE(domain->backend_data.samr_pipes);
+ return true;
+ }
+
+ if (!dcerpc_binding_handle_is_connected(b)) {
+ TALLOC_FREE(domain->backend_data.samr_pipes);
+ return true;
+ }
+
+ return false;
+}
+
+/*********************************************************************
+ SAM specific functions.
+*********************************************************************/
+
+/* List all domain groups */
+static NTSTATUS sam_enum_dom_groups(struct winbindd_domain *domain,
+ TALLOC_CTX *mem_ctx,
+ uint32_t *pnum_info,
+ struct wb_acct_info **pinfo)
+{
+ struct rpc_pipe_client *samr_pipe;
+ struct policy_handle dom_pol = { 0 };
+ struct wb_acct_info *info = NULL;
+ uint32_t num_info = 0;
+ TALLOC_CTX *tmp_ctx = talloc_stackframe();
+ NTSTATUS status;
+ bool retry = false;
+
+ DEBUG(3,("sam_enum_dom_groups\n"));
+
+ if (pnum_info) {
+ *pnum_info = 0;
+ }
+
+again:
+ status = open_cached_internal_pipe_conn(domain,
+ &samr_pipe,
+ &dom_pol,
+ NULL,
+ NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ TALLOC_FREE(tmp_ctx);
+ return status;
+ }
+
+ status = rpc_enum_dom_groups(tmp_ctx,
+ samr_pipe,
+ &dom_pol,
+ &num_info,
+ &info);
+
+ if (!retry && reset_connection_on_error(domain, samr_pipe, status)) {
+ retry = true;
+ goto again;
+ }
+
+ if (!NT_STATUS_IS_OK(status)) {
+ TALLOC_FREE(tmp_ctx);
+ return status;
+ }
+
+ if (pnum_info) {
+ *pnum_info = num_info;
+ }
+
+ if (pinfo) {
+ *pinfo = talloc_move(mem_ctx, &info);
+ }
+
+ TALLOC_FREE(tmp_ctx);
+ return status;
+}
+
+/* Query display info for a domain */
+static NTSTATUS sam_query_user_list(struct winbindd_domain *domain,
+ TALLOC_CTX *mem_ctx,
+ uint32_t **prids)
+{
+ struct rpc_pipe_client *samr_pipe = NULL;
+ struct policy_handle dom_pol = { 0 };
+ uint32_t *rids = NULL;
+ TALLOC_CTX *tmp_ctx = talloc_stackframe();
+ NTSTATUS status;
+ bool retry = false;
+
+ DEBUG(3,("samr_query_user_list\n"));
+
+again:
+ status = open_cached_internal_pipe_conn(domain,
+ &samr_pipe,
+ &dom_pol,
+ NULL,
+ NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto done;
+ }
+
+ status = rpc_query_user_list(tmp_ctx,
+ samr_pipe,
+ &dom_pol,
+ &domain->sid,
+ &rids);
+ if (!retry && reset_connection_on_error(domain, samr_pipe, status)) {
+ retry = true;
+ goto again;
+ }
+
+ if (!NT_STATUS_IS_OK(status)) {
+ goto done;
+ }
+
+ if (prids != NULL) {
+ *prids = talloc_move(mem_ctx, &rids);
+ }
+
+done:
+ TALLOC_FREE(rids);
+ TALLOC_FREE(tmp_ctx);
+ return status;
+}
+
+/* get a list of trusted domains - builtin domain */
+static NTSTATUS sam_trusted_domains(struct winbindd_domain *domain,
+ TALLOC_CTX *mem_ctx,
+ struct netr_DomainTrustList *ptrust_list)
+{
+ struct rpc_pipe_client *lsa_pipe;
+ struct policy_handle lsa_policy = { 0 };
+ struct netr_DomainTrust *trusts = NULL;
+ uint32_t num_trusts = 0;
+ TALLOC_CTX *tmp_ctx = talloc_stackframe();
+ NTSTATUS status;
+ bool retry = false;
+
+ DEBUG(3,("samr: trusted domains\n"));
+
+ if (ptrust_list) {
+ ZERO_STRUCTP(ptrust_list);
+ }
+
+again:
+ status = open_cached_internal_pipe_conn(domain,
+ NULL,
+ NULL,
+ &lsa_pipe,
+ &lsa_policy);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto done;
+ }
+
+ status = rpc_trusted_domains(tmp_ctx,
+ lsa_pipe,
+ &lsa_policy,
+ &num_trusts,
+ &trusts);
+
+ if (!retry && reset_connection_on_error(domain, lsa_pipe, status)) {
+ retry = true;
+ goto again;
+ }
+
+ if (!NT_STATUS_IS_OK(status)) {
+ goto done;
+ }
+
+ if (ptrust_list) {
+ ptrust_list->count = num_trusts;
+ ptrust_list->array = talloc_move(mem_ctx, &trusts);
+ }
+
+done:
+ TALLOC_FREE(tmp_ctx);
+ return status;
+}
+
+/* Lookup group membership given a rid. */
+static NTSTATUS sam_lookup_groupmem(struct winbindd_domain *domain,
+ TALLOC_CTX *mem_ctx,
+ const struct dom_sid *group_sid,
+ enum lsa_SidType type,
+ uint32_t *pnum_names,
+ struct dom_sid **psid_mem,
+ char ***pnames,
+ uint32_t **pname_types)
+{
+ struct rpc_pipe_client *samr_pipe;
+ struct policy_handle dom_pol = { 0 };
+
+ uint32_t num_names = 0;
+ struct dom_sid *sid_mem = NULL;
+ char **names = NULL;
+ uint32_t *name_types = NULL;
+
+ TALLOC_CTX *tmp_ctx = talloc_stackframe();
+ NTSTATUS status;
+ bool retry = false;
+
+ DEBUG(3,("sam_lookup_groupmem\n"));
+
+ /* Paranoia check */
+ if (sid_check_is_in_builtin(group_sid) && (type != SID_NAME_ALIAS)) {
+ /* There's no groups, only aliases in BUILTIN */
+ status = NT_STATUS_NO_SUCH_GROUP;
+ goto done;
+ }
+
+ if (pnum_names) {
+ *pnum_names = 0;
+ }
+
+again:
+ status = open_cached_internal_pipe_conn(domain,
+ &samr_pipe,
+ &dom_pol,
+ NULL,
+ NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto done;
+ }
+
+ status = rpc_lookup_groupmem(tmp_ctx,
+ samr_pipe,
+ &dom_pol,
+ domain->name,
+ &domain->sid,
+ group_sid,
+ type,
+ &num_names,
+ &sid_mem,
+ &names,
+ &name_types);
+
+ if (!retry && reset_connection_on_error(domain, samr_pipe, status)) {
+ retry = true;
+ goto again;
+ }
+
+ if (pnum_names) {
+ *pnum_names = num_names;
+ }
+
+ if (pnames) {
+ *pnames = talloc_move(mem_ctx, &names);
+ }
+
+ if (pname_types) {
+ *pname_types = talloc_move(mem_ctx, &name_types);
+ }
+
+ if (psid_mem) {
+ *psid_mem = talloc_move(mem_ctx, &sid_mem);
+ }
+
+done:
+ TALLOC_FREE(tmp_ctx);
+ return status;
+}
+
+/* Lookup alias membership */
+static NTSTATUS sam_lookup_aliasmem(struct winbindd_domain *domain,
+ TALLOC_CTX *mem_ctx,
+ const struct dom_sid *group_sid,
+ enum lsa_SidType type,
+ uint32_t *pnum_sids,
+ struct dom_sid **psid_mem)
+{
+ struct rpc_pipe_client *samr_pipe;
+ struct policy_handle dom_pol = {0};
+
+ uint32_t num_sids = 0;
+ struct dom_sid *sid_mem = NULL;
+
+ TALLOC_CTX *tmp_ctx = talloc_stackframe();
+ NTSTATUS status;
+ bool retry = false;
+
+ DBG_INFO("sam_lookup_aliasmem\n");
+
+ /* Paranoia check */
+ if (type != SID_NAME_ALIAS) {
+ status = NT_STATUS_NO_SUCH_ALIAS;
+ goto done;
+ }
+
+ if (pnum_sids) {
+ *pnum_sids = 0;
+ }
+
+again:
+ status = open_cached_internal_pipe_conn(domain,
+ &samr_pipe,
+ &dom_pol,
+ NULL,
+ NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto done;
+ }
+
+ status = rpc_lookup_aliasmem(tmp_ctx,
+ samr_pipe,
+ &dom_pol,
+ &domain->sid,
+ group_sid,
+ type,
+ &num_sids,
+ &sid_mem);
+
+ if (!retry && reset_connection_on_error(domain, samr_pipe, status)) {
+ retry = true;
+ goto again;
+ }
+
+ if (pnum_sids) {
+ *pnum_sids = num_sids;
+ }
+
+ if (psid_mem) {
+ *psid_mem = talloc_move(mem_ctx, &sid_mem);
+ }
+
+done:
+ TALLOC_FREE(tmp_ctx);
+ return status;
+}
+
+/*********************************************************************
+ BUILTIN specific functions.
+*********************************************************************/
+
+/* List all domain groups */
+static NTSTATUS builtin_enum_dom_groups(struct winbindd_domain *domain,
+ TALLOC_CTX *mem_ctx,
+ uint32_t *num_entries,
+ struct wb_acct_info **info)
+{
+ /* BUILTIN doesn't have domain groups */
+ *num_entries = 0;
+ *info = NULL;
+ return NT_STATUS_OK;
+}
+
+/* Query display info for a domain */
+static NTSTATUS builtin_query_user_list(struct winbindd_domain *domain,
+ TALLOC_CTX *mem_ctx,
+ uint32_t **rids)
+{
+ /* We don't have users */
+ *rids = NULL;
+ return NT_STATUS_OK;
+}
+
+/* get a list of trusted domains - builtin domain */
+static NTSTATUS builtin_trusted_domains(struct winbindd_domain *domain,
+ TALLOC_CTX *mem_ctx,
+ struct netr_DomainTrustList *trusts)
+{
+ ZERO_STRUCTP(trusts);
+ return NT_STATUS_OK;
+}
+
+/*********************************************************************
+ COMMON functions.
+*********************************************************************/
+
+/* List all local groups (aliases) */
+static NTSTATUS sam_enum_local_groups(struct winbindd_domain *domain,
+ TALLOC_CTX *mem_ctx,
+ uint32_t *pnum_info,
+ struct wb_acct_info **pinfo)
+{
+ struct rpc_pipe_client *samr_pipe;
+ struct policy_handle dom_pol = { 0 };
+ struct wb_acct_info *info = NULL;
+ uint32_t num_info = 0;
+ TALLOC_CTX *tmp_ctx = talloc_stackframe();
+ NTSTATUS status;
+ bool retry = false;
+
+ DEBUG(3,("samr: enum local groups\n"));
+
+ if (pnum_info) {
+ *pnum_info = 0;
+ }
+
+again:
+ status = open_cached_internal_pipe_conn(domain,
+ &samr_pipe,
+ &dom_pol,
+ NULL,
+ NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto done;
+ }
+
+ status = rpc_enum_local_groups(mem_ctx,
+ samr_pipe,
+ &dom_pol,
+ &num_info,
+
+ &info);
+ if (!retry && reset_connection_on_error(domain, samr_pipe, status)) {
+ retry = true;
+ goto again;
+ }
+
+ if (!NT_STATUS_IS_OK(status)) {
+ goto done;
+ }
+
+ if (pnum_info) {
+ *pnum_info = num_info;
+ }
+
+ if (pinfo) {
+ *pinfo = talloc_move(mem_ctx, &info);
+ }
+
+done:
+ TALLOC_FREE(tmp_ctx);
+ return status;
+}
+
+/* convert a single name to a sid in a domain */
+static NTSTATUS sam_name_to_sid(struct winbindd_domain *domain,
+ TALLOC_CTX *mem_ctx,
+ const char *domain_name,
+ const char *name,
+ uint32_t flags,
+ const char **pdom_name,
+ struct dom_sid *psid,
+ enum lsa_SidType *ptype)
+{
+ struct rpc_pipe_client *samr_pipe = NULL;
+ struct dcerpc_binding_handle *h = NULL;
+ struct policy_handle dom_pol = { .handle_type = 0, };
+ struct dom_sid sid;
+ const char *dom_name = domain_name;
+ struct lsa_String lsa_name = { .string = name };
+ struct samr_Ids rids = { .count = 0 };
+ struct samr_Ids types = { .count = 0 };
+ enum lsa_SidType type;
+ TALLOC_CTX *tmp_ctx = talloc_stackframe();
+ NTSTATUS status = NT_STATUS_NONE_MAPPED;
+ NTSTATUS result;
+ bool retry = false;
+ bool ok;
+
+ DBG_NOTICE("%s\\%s\n", domain_name, name);
+
+ if (strequal(domain_name, unix_users_domain_name())) {
+ struct passwd *pwd = NULL;
+
+ if (name[0] == '\0') {
+ sid_copy(&sid, &global_sid_Unix_Users);
+ type = SID_NAME_DOMAIN;
+ goto done;
+ }
+
+ pwd = Get_Pwnam_alloc(tmp_ctx, name);
+ if (pwd == NULL) {
+ goto fail;
+ }
+ ok = sid_compose(&sid, &global_sid_Unix_Users, pwd->pw_uid);
+ if (!ok) {
+ status = NT_STATUS_INTERNAL_ERROR;
+ goto fail;
+ }
+ type = SID_NAME_USER;
+ goto done;
+ }
+
+ if (strequal(domain_name, unix_groups_domain_name())) {
+ struct group *grp = NULL;
+
+ if (name[0] == '\0') {
+ sid_copy(&sid, &global_sid_Unix_Groups);
+ type = SID_NAME_DOMAIN;
+ goto done;
+ }
+
+ grp = getgrnam(name);
+ if (grp == NULL) {
+ goto fail;
+ }
+ ok = sid_compose(&sid, &global_sid_Unix_Groups, grp->gr_gid);
+ if (!ok) {
+ status = NT_STATUS_INTERNAL_ERROR;
+ goto fail;
+ }
+ type = SID_NAME_DOM_GRP;
+ goto done;
+ }
+
+ if (name[0] == '\0') {
+ sid_copy(&sid, &domain->sid);
+ type = SID_NAME_DOMAIN;
+ goto done;
+ }
+
+ ok = lookup_wellknown_name(tmp_ctx, name, &sid, &dom_name);
+ if (ok) {
+ type = SID_NAME_WKN_GRP;
+ goto done;
+ }
+
+ {
+ char *normalized = NULL;
+ NTSTATUS nstatus = normalize_name_unmap(
+ tmp_ctx, name, &normalized);
+ if (NT_STATUS_IS_OK(nstatus) ||
+ NT_STATUS_EQUAL(nstatus, NT_STATUS_FILE_RENAMED)) {
+ lsa_name.string = normalized;
+ }
+ }
+
+again:
+ status = open_cached_internal_pipe_conn(
+ domain, &samr_pipe, &dom_pol, NULL, NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto fail;
+ }
+ h = samr_pipe->binding_handle;
+
+ status = dcerpc_samr_LookupNames(
+ h, tmp_ctx, &dom_pol, 1, &lsa_name, &rids, &types, &result);
+
+ if (!retry && reset_connection_on_error(domain, samr_pipe, status)) {
+ retry = true;
+ goto again;
+ }
+
+ if (!NT_STATUS_IS_OK(status)) {
+ DBG_DEBUG("dcerpc_samr_LookupNames returned %s\n",
+ nt_errstr(status));
+ goto fail;
+ }
+ if (!NT_STATUS_IS_OK(result)) {
+ DBG_DEBUG("dcerpc_samr_LookupNames resulted in %s\n",
+ nt_errstr(status));
+ status = result;
+ goto fail;
+ }
+
+ sid_compose(&sid, &domain->sid, rids.ids[0]);
+ type = types.ids[0];
+
+done:
+ if (pdom_name != NULL) {
+ *pdom_name = talloc_strdup(mem_ctx, dom_name);
+ if (*pdom_name == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ goto fail;
+ }
+ }
+
+ if (psid) {
+ sid_copy(psid, &sid);
+ }
+ if (ptype) {
+ *ptype = type;
+ }
+
+ status = NT_STATUS_OK;
+fail:
+ TALLOC_FREE(tmp_ctx);
+ return status;
+}
+
+/* convert a domain SID to a user or group name */
+static NTSTATUS sam_sid_to_name(struct winbindd_domain *domain,
+ TALLOC_CTX *mem_ctx,
+ const struct dom_sid *sid,
+ char **pdomain_name,
+ char **pname,
+ enum lsa_SidType *ptype)
+{
+ struct rpc_pipe_client *samr_pipe = NULL;
+ struct dcerpc_binding_handle *h = NULL;
+ struct policy_handle dom_pol = { .handle_type = 0, };
+ const char *domain_name = "";
+ const char *name = "";
+ enum lsa_SidType type = SID_NAME_USE_NONE;
+ struct lsa_Strings names = { .count = 0, };
+ struct samr_Ids types = { .count = 0 };
+ struct dom_sid domain_sid;
+ uint32_t rid;
+ TALLOC_CTX *tmp_ctx = talloc_stackframe();
+ NTSTATUS status = NT_STATUS_NONE_MAPPED;
+ NTSTATUS result;
+ bool retry = false;
+ bool ok;
+
+ DEBUG(3,("sam_sid_to_name\n"));
+
+ if (sid_check_is_unix_users(sid)) {
+ domain_name = unix_users_domain_name();
+ type = SID_NAME_DOMAIN;
+ goto done;
+ }
+ if (sid_check_is_in_unix_users(sid)) {
+ struct passwd *pwd = NULL;
+
+ ok = sid_peek_rid(sid, &rid);
+ if (!ok) {
+ goto fail;
+ }
+ pwd = getpwuid(rid);
+ if (pwd == NULL) {
+ goto fail;
+ }
+
+ domain_name = unix_users_domain_name();
+ name = talloc_strdup(tmp_ctx, pwd->pw_name);
+ if (name == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ goto fail;
+ }
+ type = SID_NAME_USER;
+ goto done;
+ }
+
+ if (sid_check_is_unix_groups(sid)) {
+ domain_name = unix_groups_domain_name();
+ type = SID_NAME_DOMAIN;
+ goto done;
+ }
+ if (sid_check_is_in_unix_groups(sid)) {
+ struct group *grp = NULL;
+
+ ok = sid_peek_rid(sid, &rid);
+ if (!ok) {
+ goto fail;
+ }
+ grp = getgrgid(rid);
+ if (grp == NULL) {
+ goto fail;
+ }
+
+ domain_name = unix_groups_domain_name();
+ name = talloc_strdup(tmp_ctx, grp->gr_name);
+ if (name == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ goto fail;
+ }
+ type = SID_NAME_DOM_GRP;
+ goto done;
+ }
+
+ ok = lookup_wellknown_sid(tmp_ctx, sid, &domain_name, &name);
+ if (ok) {
+ type = SID_NAME_WKN_GRP;
+ goto done;
+ }
+
+ if (dom_sid_equal(sid, &domain->sid)) {
+ domain_name = domain->name;
+ type = SID_NAME_DOMAIN;
+ goto done;
+ }
+
+ sid_copy(&domain_sid, sid);
+ ok = sid_split_rid(&domain_sid, &rid);
+ if (!ok) {
+ goto fail;
+ }
+
+ if (!dom_sid_equal(&domain_sid, &domain->sid)) {
+ goto fail;
+ }
+
+again:
+ status = open_cached_internal_pipe_conn(
+ domain, &samr_pipe, &dom_pol, NULL, NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto fail;
+ }
+ h = samr_pipe->binding_handle;
+
+ status = dcerpc_samr_LookupRids(
+ h, tmp_ctx, &dom_pol, 1, &rid, &names, &types, &result);
+
+ if (!retry && reset_connection_on_error(domain, samr_pipe, status)) {
+ retry = true;
+ goto again;
+ }
+
+ if (!NT_STATUS_IS_OK(status)) {
+ DBG_DEBUG("dcerpc_samr_LookupRids failed: %s\n",
+ nt_errstr(status));
+ goto fail;
+ }
+ if (!NT_STATUS_IS_OK(result)) {
+ DBG_DEBUG("dcerpc_samr_LookupRids resulted in %s\n",
+ nt_errstr(result));
+ status = result;
+ goto fail;
+ }
+
+ domain_name = domain->name;
+ name = names.names[0].string;
+ type = types.ids[0];
+
+ if (name != NULL) {
+ char *normalized = NULL;
+ NTSTATUS nstatus = normalize_name_map(
+ tmp_ctx, domain_name, name, &normalized);
+ if (NT_STATUS_IS_OK(nstatus) ||
+ NT_STATUS_EQUAL(nstatus, NT_STATUS_FILE_RENAMED)) {
+ name = normalized;
+ }
+ }
+
+done:
+ if (ptype) {
+ *ptype = type;
+ }
+
+ if (pname) {
+ *pname = talloc_strdup(mem_ctx, name);
+ if (*pname == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ goto fail;
+ }
+ }
+
+ if (pdomain_name) {
+ *pdomain_name = talloc_strdup(mem_ctx, domain_name);
+ if (*pdomain_name == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ goto fail;
+ }
+ }
+
+ status = NT_STATUS_OK;
+fail:
+ TALLOC_FREE(tmp_ctx);
+ return status;
+}
+
+static NTSTATUS sam_rids_to_names(struct winbindd_domain *domain,
+ TALLOC_CTX *mem_ctx,
+ const struct dom_sid *domain_sid,
+ uint32_t *rids,
+ size_t num_rids,
+ char **pdomain_name,
+ char ***pnames,
+ enum lsa_SidType **ptypes)
+{
+ struct rpc_pipe_client *samr_pipe = NULL;
+ struct dcerpc_binding_handle *h = NULL;
+ struct policy_handle dom_pol = { .handle_type = 0, };
+ enum lsa_SidType *types = NULL;
+ char **names = NULL;
+ const char *domain_name = NULL;
+ TALLOC_CTX *tmp_ctx = talloc_stackframe();
+ NTSTATUS status = NT_STATUS_NO_MEMORY;
+ NTSTATUS result;
+ bool retry = false;
+ uint32_t i;
+
+ DEBUG(3,("sam_rids_to_names for %s\n", domain->name));
+
+ types = talloc_array(tmp_ctx, enum lsa_SidType, num_rids);
+ if (types == NULL) {
+ goto fail;
+ }
+
+ names = talloc_array(tmp_ctx, char *, num_rids);
+ if (names == NULL) {
+ goto fail;
+ }
+
+ if (sid_check_is_unix_users(domain_sid)) {
+ domain_name = unix_users_domain_name();
+ domain_sid = &global_sid_Unix_Users;
+ }
+ if (sid_check_is_unix_groups(domain_sid)) {
+ domain_name = unix_groups_domain_name();
+ domain_sid = &global_sid_Unix_Groups;
+ }
+
+ /* Here we're only interested in the domain name being set */
+ sid_check_is_wellknown_domain(domain_sid, &domain_name);
+
+ if (domain_name != NULL) {
+ uint32_t num_mapped = 0;
+
+ /*
+ * Do unix users/groups and wkn in a loop. There is no
+ * getpwuids() call & friends anyway
+ */
+
+ for (i=0; i<num_rids; i++) {
+ struct dom_sid sid;
+ char *name = NULL;
+
+ sid_compose(&sid, domain_sid, rids[i]);
+
+ types[i] = SID_NAME_UNKNOWN;
+ names[i] = NULL;
+
+ status = sam_sid_to_name(
+ domain,
+ tmp_ctx,
+ &sid,
+ NULL,
+ &name,
+ &types[i]);
+ if (NT_STATUS_IS_OK(status)) {
+ names[i] = talloc_move(names, &name);
+ num_mapped += 1;
+ }
+ }
+
+ status = NT_STATUS_NONE_MAPPED;
+ if (num_mapped > 0) {
+ status = (num_mapped == num_rids) ?
+ NT_STATUS_OK : STATUS_SOME_UNMAPPED;
+ }
+ goto done;
+ }
+
+ domain_name = domain->name;
+
+again:
+ status = open_cached_internal_pipe_conn(
+ domain, &samr_pipe, &dom_pol, NULL, NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto done;
+ }
+ h = samr_pipe->binding_handle;
+
+ /*
+ * Magic number 1000 comes from samr.idl
+ */
+
+ for (i = 0; i < num_rids; i += 1000) {
+ uint32_t num_lookup_rids = MIN(num_rids - i, 1000);
+ struct lsa_Strings lsa_names = {
+ .count = 0,
+ };
+ struct samr_Ids samr_types = {
+ .count = 0,
+ };
+ uint32_t j;
+
+ status = dcerpc_samr_LookupRids(h,
+ tmp_ctx,
+ &dom_pol,
+ num_lookup_rids,
+ &rids[i],
+ &lsa_names,
+ &samr_types,
+ &result);
+
+ if (!retry &&
+ reset_connection_on_error(domain, samr_pipe, status)) {
+ retry = true;
+ goto again;
+ }
+
+ if (!NT_STATUS_IS_OK(status)) {
+ DBG_DEBUG("dcerpc_samr_LookupRids failed: %s\n",
+ nt_errstr(status));
+ goto fail;
+ }
+ if (!NT_STATUS_IS_OK(result) &&
+ !NT_STATUS_EQUAL(result, STATUS_SOME_UNMAPPED)) {
+ DBG_DEBUG("dcerpc_samr_LookupRids resulted in %s\n",
+ nt_errstr(result));
+ status = result;
+ goto fail;
+ }
+
+ for (j = 0; j < num_lookup_rids; j++) {
+ uint32_t dst = i + j;
+
+ types[dst] = samr_types.ids[j];
+ names[dst] = talloc_move(
+ names,
+ discard_const_p(char *,
+ &lsa_names.names[j].string));
+ if (names[dst] != NULL) {
+ char *normalized = NULL;
+ NTSTATUS nstatus =
+ normalize_name_map(names,
+ domain_name,
+ names[dst],
+ &normalized);
+ if (NT_STATUS_IS_OK(nstatus) ||
+ NT_STATUS_EQUAL(nstatus,
+ NT_STATUS_FILE_RENAMED)) {
+ names[dst] = normalized;
+ }
+ }
+ }
+
+ TALLOC_FREE(samr_types.ids);
+ TALLOC_FREE(lsa_names.names);
+ }
+
+done:
+ if (pdomain_name) {
+ *pdomain_name = talloc_strdup(mem_ctx, domain_name);
+ if (*pdomain_name == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ goto fail;
+ }
+ }
+
+ if (ptypes) {
+ *ptypes = talloc_move(mem_ctx, &types);
+ }
+
+ if (pnames) {
+ *pnames = talloc_move(mem_ctx, &names);
+ }
+
+fail:
+ TALLOC_FREE(tmp_ctx);
+ return status;
+}
+
+static NTSTATUS sam_lockout_policy(struct winbindd_domain *domain,
+ TALLOC_CTX *mem_ctx,
+ struct samr_DomInfo12 *lockout_policy)
+{
+ struct rpc_pipe_client *samr_pipe;
+ struct policy_handle dom_pol = { 0 };
+ union samr_DomainInfo *info = NULL;
+ TALLOC_CTX *tmp_ctx = talloc_stackframe();
+ NTSTATUS status, result;
+ struct dcerpc_binding_handle *b = NULL;
+ bool retry = false;
+
+ DEBUG(3,("sam_lockout_policy\n"));
+
+again:
+ status = open_cached_internal_pipe_conn(domain,
+ &samr_pipe,
+ &dom_pol,
+ NULL,
+ NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto error;
+ }
+
+ b = samr_pipe->binding_handle;
+
+ status = dcerpc_samr_QueryDomainInfo(b,
+ mem_ctx,
+ &dom_pol,
+ DomainLockoutInformation,
+ &info,
+ &result);
+
+ if (!retry && reset_connection_on_error(domain, samr_pipe, status)) {
+ retry = true;
+ goto again;
+ }
+
+ if (!NT_STATUS_IS_OK(status)) {
+ goto error;
+ }
+ if (!NT_STATUS_IS_OK(result)) {
+ status = result;
+ goto error;
+ }
+
+ *lockout_policy = info->info12;
+
+error:
+ TALLOC_FREE(tmp_ctx);
+ return status;
+}
+
+static NTSTATUS sam_password_policy(struct winbindd_domain *domain,
+ TALLOC_CTX *mem_ctx,
+ struct samr_DomInfo1 *passwd_policy)
+{
+ struct rpc_pipe_client *samr_pipe;
+ struct policy_handle dom_pol = { 0 };
+ union samr_DomainInfo *info = NULL;
+ TALLOC_CTX *tmp_ctx = talloc_stackframe();
+ NTSTATUS status, result;
+ struct dcerpc_binding_handle *b = NULL;
+ bool retry = false;
+
+ DEBUG(3,("sam_password_policy\n"));
+
+again:
+ status = open_cached_internal_pipe_conn(domain,
+ &samr_pipe,
+ &dom_pol,
+ NULL,
+ NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto error;
+ }
+
+ b = samr_pipe->binding_handle;
+
+ status = dcerpc_samr_QueryDomainInfo(b,
+ mem_ctx,
+ &dom_pol,
+ DomainPasswordInformation,
+ &info,
+ &result);
+
+ if (!retry && reset_connection_on_error(domain, samr_pipe, status)) {
+ retry = true;
+ goto again;
+ }
+
+ if (!NT_STATUS_IS_OK(status)) {
+ goto error;
+ }
+ if (!NT_STATUS_IS_OK(result)) {
+ status = result;
+ goto error;
+ }
+
+ *passwd_policy = info->info1;
+
+error:
+ TALLOC_FREE(tmp_ctx);
+ return status;
+}
+
+/* Lookup groups a user is a member of. */
+static NTSTATUS sam_lookup_usergroups(struct winbindd_domain *domain,
+ TALLOC_CTX *mem_ctx,
+ const struct dom_sid *user_sid,
+ uint32_t *pnum_groups,
+ struct dom_sid **puser_grpsids)
+{
+ struct rpc_pipe_client *samr_pipe;
+ struct policy_handle dom_pol;
+ struct dom_sid *user_grpsids = NULL;
+ uint32_t num_groups = 0;
+ TALLOC_CTX *tmp_ctx = talloc_stackframe();
+ NTSTATUS status;
+ bool retry = false;
+
+ DEBUG(3,("sam_lookup_usergroups\n"));
+
+ ZERO_STRUCT(dom_pol);
+
+ if (pnum_groups) {
+ *pnum_groups = 0;
+ }
+
+again:
+ status = open_cached_internal_pipe_conn(domain,
+ &samr_pipe,
+ &dom_pol,
+ NULL,
+ NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto done;
+ }
+
+ status = rpc_lookup_usergroups(tmp_ctx,
+ samr_pipe,
+ &dom_pol,
+ &domain->sid,
+ user_sid,
+ &num_groups,
+ &user_grpsids);
+
+ if (!retry && reset_connection_on_error(domain, samr_pipe, status)) {
+ retry = true;
+ goto again;
+ }
+
+ if (!NT_STATUS_IS_OK(status)) {
+ goto done;
+ }
+
+ if (pnum_groups) {
+ *pnum_groups = num_groups;
+ }
+
+ if (puser_grpsids) {
+ *puser_grpsids = talloc_move(mem_ctx, &user_grpsids);
+ }
+
+done:
+
+ TALLOC_FREE(tmp_ctx);
+ return status;
+}
+
+static NTSTATUS sam_lookup_useraliases(struct winbindd_domain *domain,
+ TALLOC_CTX *mem_ctx,
+ uint32_t num_sids,
+ const struct dom_sid *sids,
+ uint32_t *pnum_aliases,
+ uint32_t **palias_rids)
+{
+ struct rpc_pipe_client *samr_pipe;
+ struct policy_handle dom_pol = { 0 };
+ uint32_t num_aliases = 0;
+ uint32_t *alias_rids = NULL;
+ TALLOC_CTX *tmp_ctx = talloc_stackframe();
+ NTSTATUS status;
+ bool retry = false;
+
+ DEBUG(3,("sam_lookup_useraliases\n"));
+
+ if (pnum_aliases) {
+ *pnum_aliases = 0;
+ }
+
+again:
+ status = open_cached_internal_pipe_conn(domain,
+ &samr_pipe,
+ &dom_pol,
+ NULL,
+ NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto done;
+ }
+
+ status = rpc_lookup_useraliases(tmp_ctx,
+ samr_pipe,
+ &dom_pol,
+ num_sids,
+ sids,
+ &num_aliases,
+ &alias_rids);
+
+ if (!retry && reset_connection_on_error(domain, samr_pipe, status)) {
+ retry = true;
+ goto again;
+ }
+
+ if (!NT_STATUS_IS_OK(status)) {
+ goto done;
+ }
+
+ if (pnum_aliases) {
+ *pnum_aliases = num_aliases;
+ }
+
+ if (palias_rids) {
+ *palias_rids = talloc_move(mem_ctx, &alias_rids);
+ }
+
+done:
+
+ TALLOC_FREE(tmp_ctx);
+ return status;
+}
+
+/* the rpc backend methods are exposed via this structure */
+struct winbindd_methods builtin_passdb_methods = {
+ .consistent = false,
+
+ .query_user_list = builtin_query_user_list,
+ .enum_dom_groups = builtin_enum_dom_groups,
+ .enum_local_groups = sam_enum_local_groups,
+ .name_to_sid = sam_name_to_sid,
+ .sid_to_name = sam_sid_to_name,
+ .rids_to_names = sam_rids_to_names,
+ .lookup_usergroups = sam_lookup_usergroups,
+ .lookup_useraliases = sam_lookup_useraliases,
+ .lookup_groupmem = sam_lookup_groupmem,
+ .lookup_aliasmem = sam_lookup_aliasmem,
+ .lockout_policy = sam_lockout_policy,
+ .password_policy = sam_password_policy,
+ .trusted_domains = builtin_trusted_domains
+};
+
+/* the rpc backend methods are exposed via this structure */
+struct winbindd_methods sam_passdb_methods = {
+ .consistent = false,
+
+ .query_user_list = sam_query_user_list,
+ .enum_dom_groups = sam_enum_dom_groups,
+ .enum_local_groups = sam_enum_local_groups,
+ .name_to_sid = sam_name_to_sid,
+ .sid_to_name = sam_sid_to_name,
+ .rids_to_names = sam_rids_to_names,
+ .lookup_usergroups = sam_lookup_usergroups,
+ .lookup_useraliases = sam_lookup_useraliases,
+ .lookup_groupmem = sam_lookup_groupmem,
+ .lookup_aliasmem = sam_lookup_aliasmem,
+ .lockout_policy = sam_lockout_policy,
+ .password_policy = sam_password_policy,
+ .trusted_domains = sam_trusted_domains
+};
diff --git a/source3/winbindd/winbindd_setgrent.c b/source3/winbindd/winbindd_setgrent.c
new file mode 100644
index 0000000..f4f2498
--- /dev/null
+++ b/source3/winbindd/winbindd_setgrent.c
@@ -0,0 +1,67 @@
+/*
+ Unix SMB/CIFS implementation.
+ async implementation of WINBINDD_SETGRENT
+ Copyright (C) Volker Lendecke 2009
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "winbindd.h"
+
+struct winbindd_setgrent_state {
+ uint8_t dummy;
+};
+
+struct tevent_req *winbindd_setgrent_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct winbindd_cli_state *cli,
+ struct winbindd_request *request)
+{
+ struct tevent_req *req;
+ struct winbindd_setgrent_state *state;
+
+ req = tevent_req_create(mem_ctx, &state,
+ struct winbindd_setgrent_state);
+ if (req == NULL) {
+ return NULL;
+ }
+ TALLOC_FREE(cli->grent_state);
+
+ D_NOTICE("[%s (%u)] Winbind external command SETGRENT start.\n"
+ "winbind enum groups = %d\n",
+ cli->client_name,
+ (unsigned int)cli->pid,
+ lp_winbind_enum_groups());
+
+ if (!lp_winbind_enum_groups()) {
+ tevent_req_done(req);
+ return tevent_req_post(req, ev);
+ }
+
+ cli->grent_state = talloc_zero(cli, struct getgrent_state);
+ if (tevent_req_nomem(cli->grent_state, req)) {
+ return tevent_req_post(req, ev);
+ }
+
+ tevent_req_done(req);
+ return tevent_req_post(req, ev);
+}
+
+NTSTATUS winbindd_setgrent_recv(struct tevent_req *req,
+ struct winbindd_response *presp)
+{
+ D_NOTICE("Winbind external command SETGRENT end.\n");
+ return NT_STATUS_OK;
+}
diff --git a/source3/winbindd/winbindd_setpwent.c b/source3/winbindd/winbindd_setpwent.c
new file mode 100644
index 0000000..3c98e5a
--- /dev/null
+++ b/source3/winbindd/winbindd_setpwent.c
@@ -0,0 +1,67 @@
+/*
+ Unix SMB/CIFS implementation.
+ async implementation of WINBINDD_SETPWENT
+ Copyright (C) Volker Lendecke 2009
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "winbindd.h"
+
+struct winbindd_setpwent_state {
+ uint8_t dummy;
+};
+
+struct tevent_req *winbindd_setpwent_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct winbindd_cli_state *cli,
+ struct winbindd_request *request)
+{
+ struct tevent_req *req;
+ struct winbindd_setpwent_state *state;
+
+ req = tevent_req_create(mem_ctx, &state,
+ struct winbindd_setpwent_state);
+ if (req == NULL) {
+ return NULL;
+ }
+ TALLOC_FREE(cli->pwent_state);
+
+ D_NOTICE("[%s (%u)] Winbind external command SETPWENT start.\n"
+ "winbind enum users = %d\n",
+ cli->client_name,
+ (unsigned int)cli->pid,
+ lp_winbind_enum_users());
+
+ if (!lp_winbind_enum_users()) {
+ tevent_req_done(req);
+ return tevent_req_post(req, ev);
+ }
+
+ cli->pwent_state = talloc_zero(cli, struct getpwent_state);
+ if (tevent_req_nomem(cli->pwent_state, req)) {
+ return tevent_req_post(req, ev);
+ }
+
+ tevent_req_done(req);
+ return tevent_req_post(req, ev);
+}
+
+NTSTATUS winbindd_setpwent_recv(struct tevent_req *req,
+ struct winbindd_response *presp)
+{
+ D_NOTICE("Winbind external command SETPWENT end.\n");
+ return NT_STATUS_OK;
+}
diff --git a/source3/winbindd/winbindd_show_sequence.c b/source3/winbindd/winbindd_show_sequence.c
new file mode 100644
index 0000000..e12f476
--- /dev/null
+++ b/source3/winbindd/winbindd_show_sequence.c
@@ -0,0 +1,167 @@
+/*
+ Unix SMB/CIFS implementation.
+ async implementation of WINBINDD_SHOW_SEQUENCE
+ Copyright (C) Volker Lendecke 2009
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "winbindd.h"
+
+struct winbindd_show_sequence_state {
+ bool one_domain;
+ /* One domain */
+ uint32_t seqnum;
+
+ /* All domains */
+ int num_domains;
+ NTSTATUS *statuses;
+ struct winbindd_domain **domains;
+ uint32_t *seqnums;
+};
+
+static void winbindd_show_sequence_done_one(struct tevent_req *subreq);
+static void winbindd_show_sequence_done_all(struct tevent_req *subreq);
+
+struct tevent_req *winbindd_show_sequence_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct winbindd_cli_state *cli,
+ struct winbindd_request *request)
+{
+ struct tevent_req *req, *subreq;
+ struct winbindd_show_sequence_state *state;
+
+ req = tevent_req_create(mem_ctx, &state,
+ struct winbindd_show_sequence_state);
+ if (req == NULL) {
+ return NULL;
+ }
+ state->one_domain = false;
+ state->domains = NULL;
+ state->statuses = NULL;
+ state->seqnums = NULL;
+
+ /* Ensure null termination */
+ request->domain_name[sizeof(request->domain_name)-1]='\0';
+
+ DEBUG(3, ("show_sequence %s\n", request->domain_name));
+
+ if (request->domain_name[0] != '\0') {
+ struct winbindd_domain *domain;
+
+ state->one_domain = true;
+
+ domain = find_domain_from_name_noinit(
+ request->domain_name);
+ if (domain == NULL) {
+ tevent_req_nterror(req, NT_STATUS_NO_SUCH_DOMAIN);
+ return tevent_req_post(req, ev);
+ }
+
+ subreq = wb_seqnum_send(state, ev, domain);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(
+ subreq, winbindd_show_sequence_done_one, req);
+ return req;
+ }
+
+ subreq = wb_seqnums_send(state, ev);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, winbindd_show_sequence_done_all, req);
+ return req;
+}
+
+static void winbindd_show_sequence_done_one(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct winbindd_show_sequence_state *state = tevent_req_data(
+ req, struct winbindd_show_sequence_state);
+ NTSTATUS status;
+
+ status = wb_seqnum_recv(subreq, &state->seqnum);
+ TALLOC_FREE(subreq);
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+ tevent_req_done(req);
+}
+
+static void winbindd_show_sequence_done_all(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct winbindd_show_sequence_state *state = tevent_req_data(
+ req, struct winbindd_show_sequence_state);
+ NTSTATUS status;
+
+ status = wb_seqnums_recv(subreq, state, &state->num_domains,
+ &state->domains, &state->statuses,
+ &state->seqnums);
+ TALLOC_FREE(subreq);
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+ tevent_req_done(req);
+}
+
+NTSTATUS winbindd_show_sequence_recv(struct tevent_req *req,
+ struct winbindd_response *response)
+{
+ struct winbindd_show_sequence_state *state = tevent_req_data(
+ req, struct winbindd_show_sequence_state);
+ NTSTATUS status;
+ char *extra_data;
+ int i;
+
+ if (tevent_req_is_nterror(req, &status)) {
+ return status;
+ }
+
+ if (state->one_domain) {
+ response->data.sequence_number = state->seqnum;
+ return NT_STATUS_OK;
+ }
+
+ extra_data = talloc_strdup(response, "");
+ if (extra_data == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ for (i=0; i<state->num_domains; i++) {
+ if (!NT_STATUS_IS_OK(state->statuses[i])
+ || (state->seqnums[i] == DOM_SEQUENCE_NONE)) {
+ extra_data = talloc_asprintf_append_buffer(
+ extra_data, "%s : DISCONNECTED\n",
+ state->domains[i]->name);
+ } else {
+ extra_data = talloc_asprintf_append_buffer(
+ extra_data, "%s : %d\n",
+ state->domains[i]->name,
+ (int)state->seqnums[i]);
+ }
+ if (extra_data == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ }
+
+ response->extra_data.data = extra_data;
+ response->length += talloc_get_size(extra_data);
+ return NT_STATUS_OK;
+}
diff --git a/source3/winbindd/winbindd_sids_to_xids.c b/source3/winbindd/winbindd_sids_to_xids.c
new file mode 100644
index 0000000..187b299
--- /dev/null
+++ b/source3/winbindd/winbindd_sids_to_xids.c
@@ -0,0 +1,164 @@
+/*
+ Unix SMB/CIFS implementation.
+ async implementation of WINBINDD_SIDS_TO_XIDS
+ Copyright (C) Volker Lendecke 2011
+ Copyright (C) Michael Adam 2012
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "winbindd.h"
+#include "../libcli/security/security.h"
+
+
+struct winbindd_sids_to_xids_state {
+ struct tevent_context *ev;
+ struct dom_sid *sids;
+ uint32_t num_sids;
+ struct unixid *xids;
+};
+
+static void winbindd_sids_to_xids_done(struct tevent_req *subreq);
+
+struct tevent_req *winbindd_sids_to_xids_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct winbindd_cli_state *cli,
+ struct winbindd_request *request)
+{
+ struct tevent_req *req, *subreq;
+ struct winbindd_sids_to_xids_state *state;
+
+ req = tevent_req_create(mem_ctx, &state,
+ struct winbindd_sids_to_xids_state);
+ if (req == NULL) {
+ return NULL;
+ }
+ state->ev = ev;
+
+ D_NOTICE("[%s (%u)] Winbind external command SIDS_TO_XIDS start.\n",
+ cli->client_name,
+ (unsigned int)cli->pid);
+
+ if (request->extra_len == 0) {
+ tevent_req_done(req);
+ return tevent_req_post(req, ev);
+ }
+ if (request->extra_data.data[request->extra_len-1] != '\0') {
+ D_DEBUG("Got invalid sids list\n");
+ tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
+ return tevent_req_post(req, ev);
+ }
+ if (!parse_sidlist(state, request->extra_data.data,
+ &state->sids, &state->num_sids)) {
+ D_DEBUG("parse_sidlist failed\n");
+ tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
+ return tevent_req_post(req, ev);
+ }
+
+ D_DEBUG("Resolving %"PRIu32" SID(s).\n", state->num_sids);
+
+ subreq = wb_sids2xids_send(state, ev, state->sids, state->num_sids);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+
+ tevent_req_set_callback(subreq, winbindd_sids_to_xids_done, req);
+ return req;
+}
+
+static void winbindd_sids_to_xids_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct winbindd_sids_to_xids_state *state = tevent_req_data(
+ req, struct winbindd_sids_to_xids_state);
+ NTSTATUS status;
+
+ state->xids = talloc_zero_array(state, struct unixid, state->num_sids);
+ if (tevent_req_nomem(state->xids, req)) {
+ return;
+ }
+
+ status = wb_sids2xids_recv(subreq, state->xids, state->num_sids);
+ TALLOC_FREE(subreq);
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+ tevent_req_done(req);
+}
+
+NTSTATUS winbindd_sids_to_xids_recv(struct tevent_req *req,
+ struct winbindd_response *response)
+{
+ struct winbindd_sids_to_xids_state *state = tevent_req_data(
+ req, struct winbindd_sids_to_xids_state);
+ NTSTATUS status;
+ char *result = NULL;
+ uint32_t i;
+
+ D_NOTICE("Winbind external command SIDS_TO_XIDS end.\n");
+ if (tevent_req_is_nterror(req, &status)) {
+ D_WARNING("Could not convert sids: %s\n", nt_errstr(status));
+ return status;
+ }
+
+ result = talloc_strdup(response, "");
+
+ for (i=0; i<state->num_sids; i++) {
+ char type = '\0';
+ bool found = true;
+ struct unixid xid;
+
+ xid = state->xids[i];
+
+ switch (xid.type) {
+ case ID_TYPE_UID:
+ type = 'U';
+ break;
+ case ID_TYPE_GID:
+ type = 'G';
+ break;
+ case ID_TYPE_BOTH:
+ type = 'B';
+ break;
+ default:
+ found = false;
+ break;
+ }
+
+ if (xid.id == UINT32_MAX) {
+ found = false;
+ }
+
+ if (found) {
+ talloc_asprintf_addbuf(
+ &result,
+ "%c%lu\n",
+ type,
+ (unsigned long)xid.id);
+ } else {
+ talloc_asprintf_addbuf(&result, "\n");
+ }
+ }
+
+ if (result == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ response->extra_data.data = result;
+ response->length += talloc_get_size(result);
+
+ return NT_STATUS_OK;
+}
diff --git a/source3/winbindd/winbindd_traceid.c b/source3/winbindd/winbindd_traceid.c
new file mode 100644
index 0000000..acf16be
--- /dev/null
+++ b/source3/winbindd/winbindd_traceid.c
@@ -0,0 +1,147 @@
+/*
+ Authors:
+ Pavel Březina <pbrezina@redhat.com>
+
+ Copyright (C) 2021 Red Hat
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "lib/util/debug.h"
+#include "winbindd_traceid.h"
+#include "tevent.h"
+
+static void debug_traceid_trace_fde(struct tevent_fd *fde,
+ enum tevent_event_trace_point point,
+ void *private_data)
+{
+ switch (point) {
+ case TEVENT_EVENT_TRACE_ATTACH:
+ /* Assign the current traceid id when the event is created. */
+ tevent_fd_set_tag(fde, debug_traceid_get());
+ break;
+ case TEVENT_EVENT_TRACE_BEFORE_HANDLER:
+ /* Set the traceid id when a handler is being called. */
+ debug_traceid_set(tevent_fd_get_tag(fde));
+ break;
+ default:
+ /* Do nothing. */
+ break;
+ }
+}
+
+static void debug_traceid_trace_signal(struct tevent_signal *se,
+ enum tevent_event_trace_point point,
+ void *private_data)
+{
+ switch (point) {
+ case TEVENT_EVENT_TRACE_ATTACH:
+ /* Assign the current traceid id when the event is created. */
+ tevent_signal_set_tag(se, debug_traceid_get());
+ break;
+ case TEVENT_EVENT_TRACE_BEFORE_HANDLER:
+ /* Set the traceid id when a handler is being called. */
+ debug_traceid_set(tevent_signal_get_tag(se));
+ break;
+ default:
+ /* Do nothing. */
+ break;
+ }
+}
+
+static void debug_traceid_trace_timer(struct tevent_timer *timer,
+ enum tevent_event_trace_point point,
+ void *private_data)
+{
+ switch (point) {
+ case TEVENT_EVENT_TRACE_ATTACH:
+ /* Assign the current traceid id when the event is created. */
+ tevent_timer_set_tag(timer, debug_traceid_get());
+ break;
+ case TEVENT_EVENT_TRACE_BEFORE_HANDLER:
+ /* Set the traceid id when a handler is being called. */
+ debug_traceid_set(tevent_timer_get_tag(timer));
+ break;
+ default:
+ /* Do nothing. */
+ break;
+ }
+}
+
+static void debug_traceid_trace_immediate(struct tevent_immediate *im,
+ enum tevent_event_trace_point point,
+ void *private_data)
+{
+ switch (point) {
+ case TEVENT_EVENT_TRACE_ATTACH:
+ /* Assign the current traceid id when the event is created. */
+ tevent_immediate_set_tag(im, debug_traceid_get());
+ break;
+ case TEVENT_EVENT_TRACE_BEFORE_HANDLER:
+ /* Set the traceid id when a handler is being called. */
+ debug_traceid_set(tevent_immediate_get_tag(im));
+ break;
+ default:
+ /* Do nothing. */
+ break;
+ }
+}
+
+static void debug_traceid_trace_queue(struct tevent_queue_entry *qe,
+ enum tevent_event_trace_point point,
+ void *private_data)
+{
+ switch (point) {
+ case TEVENT_EVENT_TRACE_ATTACH:
+ /* Assign the current traceid id when the event is created. */
+ tevent_queue_entry_set_tag(qe, debug_traceid_get());
+ break;
+ case TEVENT_EVENT_TRACE_BEFORE_HANDLER:
+ /* Set the traceid id when a handler is being called. */
+ debug_traceid_set(tevent_queue_entry_get_tag(qe));
+ break;
+ default:
+ /* Do nothing. */
+ break;
+ }
+}
+
+static void debug_traceid_trace_loop(enum tevent_trace_point point,
+ void *private_data)
+{
+ switch (point) {
+ case TEVENT_TRACE_AFTER_LOOP_ONCE:
+ /* Reset traceid id when we got back to the loop. An event handler
+ * that set traceid id was fired. This tracepoint represents a place
+ * after the event handler was finished, we need to restore traceid
+ * id to 1 (out of request). 0 means not initialized.
+ */
+ debug_traceid_set(1);
+ break;
+ default:
+ /* Do nothing. */
+ break;
+ }
+}
+
+void winbind_debug_traceid_setup(struct tevent_context *ev)
+{
+ tevent_set_trace_callback(ev, debug_traceid_trace_loop, NULL);
+ tevent_set_trace_fd_callback(ev, debug_traceid_trace_fde, NULL);
+ tevent_set_trace_signal_callback(ev, debug_traceid_trace_signal, NULL);
+ tevent_set_trace_timer_callback(ev, debug_traceid_trace_timer, NULL);
+ tevent_set_trace_immediate_callback(ev, debug_traceid_trace_immediate, NULL);
+ tevent_set_trace_queue_callback(ev, debug_traceid_trace_queue, NULL);
+ debug_traceid_set(1);
+}
diff --git a/source3/winbindd/winbindd_traceid.h b/source3/winbindd/winbindd_traceid.h
new file mode 100644
index 0000000..d06e3ef
--- /dev/null
+++ b/source3/winbindd/winbindd_traceid.h
@@ -0,0 +1,29 @@
+/*
+ Authors:
+ Pavel Březina <pbrezina@redhat.com>
+
+ Copyright (C) 2021 Red Hat
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef _DEBUG_TRACE_ID_
+#define _DEBUG_TRACE_ID_
+
+#include <tevent.h>
+
+/* Setup traceid tracking on tevent context. */
+void winbind_debug_traceid_setup(struct tevent_context *ev);
+
+#endif /* _DEBUG_TRACE_ID_ */
diff --git a/source3/winbindd/winbindd_util.c b/source3/winbindd/winbindd_util.c
new file mode 100644
index 0000000..7527a78
--- /dev/null
+++ b/source3/winbindd/winbindd_util.c
@@ -0,0 +1,2243 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Winbind daemon for ntdom nss module
+
+ Copyright (C) Tim Potter 2000-2001
+ Copyright (C) 2001 by Martin Pool <mbp@samba.org>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "winbindd.h"
+#include "lib/util_unixsids.h"
+#include "secrets.h"
+#include "../libcli/security/security.h"
+#include "../libcli/auth/pam_errors.h"
+#include "passdb/machine_sid.h"
+#include "passdb.h"
+#include "source4/lib/messaging/messaging.h"
+#include "librpc/gen_ndr/ndr_lsa.h"
+#include "librpc/gen_ndr/ndr_drsblobs.h"
+#include "auth/credentials/credentials.h"
+#include "libsmb/samlogon_cache.h"
+#include "lib/util/smb_strtox.h"
+#include "lib/util/string_wrappers.h"
+#include "lib/global_contexts.h"
+#include "librpc/gen_ndr/ndr_winbind_c.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_WINBIND
+
+/**
+ * @file winbindd_util.c
+ *
+ * Winbind daemon for NT domain authentication nss module.
+ **/
+
+/* The list of trusted domains. Note that the list can be deleted and
+ recreated using the init_domain_list() function so pointers to
+ individual winbindd_domain structures cannot be made. Keep a copy of
+ the domain name instead. */
+
+static struct winbindd_domain *_domain_list = NULL;
+
+struct winbindd_domain *domain_list(void)
+{
+ /* Initialise list */
+
+ if ((!_domain_list) && (!init_domain_list())) {
+ smb_panic("Init_domain_list failed");
+ }
+
+ return _domain_list;
+}
+
+/* Free all entries in the trusted domain list */
+
+static void free_domain_list(void)
+{
+ struct winbindd_domain *domain = _domain_list;
+
+ while(domain) {
+ struct winbindd_domain *next = domain->next;
+
+ DLIST_REMOVE(_domain_list, domain);
+ TALLOC_FREE(domain);
+ domain = next;
+ }
+}
+
+/**
+ * Iterator for winbindd's domain list.
+ * To be used (e.g.) in tevent based loops.
+ */
+struct winbindd_domain *wb_next_domain(struct winbindd_domain *domain)
+{
+ if (domain == NULL) {
+ domain = domain_list();
+ } else {
+ domain = domain->next;
+ }
+
+ if ((domain != NULL) &&
+ (lp_server_role() != ROLE_ACTIVE_DIRECTORY_DC) &&
+ sid_check_is_our_sam(&domain->sid))
+ {
+ domain = domain->next;
+ }
+
+ return domain;
+}
+
+static bool is_internal_domain(const struct dom_sid *sid)
+{
+ if (sid == NULL)
+ return False;
+
+ return (sid_check_is_our_sam(sid) || sid_check_is_builtin(sid));
+}
+
+/* Add a trusted domain to our list of domains.
+ If the domain already exists in the list,
+ return it and don't re-initialize. */
+
+static NTSTATUS add_trusted_domain(const char *domain_name,
+ const char *dns_name,
+ const struct dom_sid *sid,
+ uint32_t trust_type,
+ uint32_t trust_flags,
+ uint32_t trust_attribs,
+ enum netr_SchannelType secure_channel_type,
+ struct winbindd_domain *routing_domain,
+ struct winbindd_domain **_d)
+{
+ struct winbindd_domain *domain = NULL;
+ int role = lp_server_role();
+ struct dom_sid_buf buf;
+
+ if (is_null_sid(sid)) {
+ DBG_ERR("Got null SID for domain [%s]\n", domain_name);
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ if (secure_channel_type == SEC_CHAN_NULL && !is_allowed_domain(domain_name)) {
+ return NT_STATUS_NO_SUCH_DOMAIN;
+ }
+
+ /*
+ * We can't call domain_list() as this function is called from
+ * init_domain_list() and we'll get stuck in a loop.
+ */
+ for (domain = _domain_list; domain; domain = domain->next) {
+ if (strequal(domain_name, domain->name)) {
+ break;
+ }
+ }
+
+ if (domain != NULL) {
+ struct winbindd_domain *check_domain = NULL;
+
+ for (check_domain = _domain_list;
+ check_domain != NULL;
+ check_domain = check_domain->next)
+ {
+ if (check_domain == domain) {
+ continue;
+ }
+
+ if (dom_sid_equal(&check_domain->sid, sid)) {
+ break;
+ }
+ }
+
+ if (check_domain != NULL) {
+ DBG_ERR("SID [%s] already used by domain [%s], "
+ "expected [%s]\n",
+ dom_sid_str_buf(sid, &buf),
+ check_domain->name,
+ domain->name);
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+ }
+
+ if ((domain != NULL) && (dns_name != NULL)) {
+ struct winbindd_domain *check_domain = NULL;
+
+ for (check_domain = _domain_list;
+ check_domain != NULL;
+ check_domain = check_domain->next)
+ {
+ if (check_domain == domain) {
+ continue;
+ }
+
+ if (strequal(check_domain->alt_name, dns_name)) {
+ break;
+ }
+ }
+
+ if (check_domain != NULL) {
+ DBG_ERR("DNS name [%s] used by domain [%s], "
+ "expected [%s]\n",
+ dns_name, check_domain->name,
+ domain->name);
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+ }
+
+ if (domain != NULL) {
+ *_d = domain;
+ return NT_STATUS_OK;
+ }
+
+ /* Create new domain entry */
+ domain = talloc_zero(NULL, struct winbindd_domain);
+ if (domain == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ domain->children = talloc_zero_array(domain,
+ struct winbindd_child,
+ lp_winbind_max_domain_connections());
+ if (domain->children == NULL) {
+ TALLOC_FREE(domain);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ domain->queue = tevent_queue_create(domain, "winbind_domain");
+ if (domain->queue == NULL) {
+ TALLOC_FREE(domain);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ domain->binding_handle = wbint_binding_handle(domain, domain, NULL);
+ if (domain->binding_handle == NULL) {
+ TALLOC_FREE(domain);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ domain->name = talloc_strdup(domain, domain_name);
+ if (domain->name == NULL) {
+ TALLOC_FREE(domain);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ if (dns_name != NULL) {
+ domain->alt_name = talloc_strdup(domain, dns_name);
+ if (domain->alt_name == NULL) {
+ TALLOC_FREE(domain);
+ return NT_STATUS_NO_MEMORY;
+ }
+ }
+
+ domain->backend = NULL;
+ domain->internal = is_internal_domain(sid);
+ domain->secure_channel_type = secure_channel_type;
+ domain->sequence_number = DOM_SEQUENCE_NONE;
+ domain->last_seq_check = 0;
+ domain->initialized = false;
+ domain->online = is_internal_domain(sid);
+ domain->domain_flags = trust_flags;
+ domain->domain_type = trust_type;
+ domain->domain_trust_attribs = trust_attribs;
+ domain->secure_channel_type = secure_channel_type;
+ domain->routing_domain = routing_domain;
+ sid_copy(&domain->sid, sid);
+
+ /* Is this our primary domain ? */
+ if (role == ROLE_DOMAIN_MEMBER) {
+ domain->primary = strequal(domain_name, lp_workgroup());
+ } else {
+ domain->primary = strequal(domain_name, get_global_sam_name());
+ }
+
+ if (domain->primary) {
+ if (role == ROLE_ACTIVE_DIRECTORY_DC) {
+ domain->active_directory = true;
+ }
+ if (lp_security() == SEC_ADS) {
+ domain->active_directory = true;
+ }
+ } else if (!domain->internal) {
+ if (domain->domain_type == LSA_TRUST_TYPE_UPLEVEL) {
+ domain->active_directory = true;
+ }
+ }
+
+ domain->can_do_ncacn_ip_tcp = domain->active_directory;
+
+ /* Link to domain list */
+ DLIST_ADD_END(_domain_list, domain);
+
+ wcache_tdc_add_domain( domain );
+
+ setup_domain_child(domain);
+
+ DBG_NOTICE("Added domain [%s] [%s] [%s]\n",
+ domain->name, domain->alt_name,
+ dom_sid_str_buf(&domain->sid, &buf));
+
+ *_d = domain;
+ return NT_STATUS_OK;
+}
+
+bool set_routing_domain(struct winbindd_domain *domain,
+ struct winbindd_domain *routing_domain)
+{
+ if (domain->routing_domain == NULL) {
+ domain->routing_domain = routing_domain;
+ return true;
+ }
+ if (domain->routing_domain != routing_domain) {
+ return false;
+ }
+ return true;
+}
+
+bool add_trusted_domain_from_auth(uint16_t validation_level,
+ struct info3_text *info3,
+ struct info6_text *info6)
+{
+ struct winbindd_domain *domain = NULL;
+ struct dom_sid domain_sid;
+ const char *dns_domainname = NULL;
+ NTSTATUS status;
+ bool ok;
+
+ /*
+ * We got a successful auth from a domain that might not yet be in our
+ * domain list. If we're a member we trust our DC who authenticated the
+ * user from that domain and add the domain to our list on-the-fly. If
+ * we're a DC we rely on configured trusts and don't add on-the-fly.
+ */
+
+ if (IS_DC) {
+ return true;
+ }
+
+ ok = dom_sid_parse(info3->dom_sid, &domain_sid);
+ if (!ok) {
+ DBG_NOTICE("dom_sid_parse [%s] failed\n", info3->dom_sid);
+ return false;
+ }
+
+ if (validation_level == 6) {
+ if (!strequal(info6->dns_domainname, "")) {
+ dns_domainname = info6->dns_domainname;
+ }
+ }
+
+ status = add_trusted_domain(info3->logon_dom,
+ dns_domainname,
+ &domain_sid,
+ 0,
+ NETR_TRUST_FLAG_OUTBOUND,
+ 0,
+ SEC_CHAN_NULL,
+ find_default_route_domain(),
+ &domain);
+ if (!NT_STATUS_IS_OK(status) &&
+ !NT_STATUS_EQUAL(status, NT_STATUS_NO_SUCH_DOMAIN))
+ {
+ DBG_DEBUG("Adding domain [%s] with sid [%s] failed\n",
+ info3->logon_dom, info3->dom_sid);
+ return false;
+ }
+
+ return true;
+}
+
+bool domain_is_forest_root(const struct winbindd_domain *domain)
+{
+ const uint32_t fr_flags =
+ (NETR_TRUST_FLAG_TREEROOT|NETR_TRUST_FLAG_IN_FOREST);
+
+ return ((domain->domain_flags & fr_flags) == fr_flags);
+}
+
+/********************************************************************
+ rescan our domains looking for new trusted domains
+********************************************************************/
+
+struct trustdom_state {
+ struct winbindd_domain *domain;
+ struct netr_DomainTrustList trusts;
+};
+
+static void trustdom_list_done(struct tevent_req *req);
+static void rescan_forest_root_trusts( void );
+static void rescan_forest_trusts( void );
+
+static void add_trusted_domains( struct winbindd_domain *domain )
+{
+ struct tevent_context *ev = global_event_context();
+ struct trustdom_state *state;
+ struct tevent_req *req;
+ const char *client_name = NULL;
+ pid_t client_pid;
+
+ state = talloc_zero(NULL, struct trustdom_state);
+ if (state == NULL) {
+ DEBUG(0, ("talloc failed\n"));
+ return;
+ }
+ state->domain = domain;
+
+ /* Called from timer, not from a real client */
+ client_name = getprogname();
+ client_pid = getpid();
+
+ req = dcerpc_wbint_ListTrustedDomains_send(state,
+ ev,
+ dom_child_handle(domain),
+ client_name,
+ client_pid,
+ &state->trusts);
+ if (req == NULL) {
+ DBG_ERR("dcerpc_wbint_ListTrustedDomains_send failed\n");
+ TALLOC_FREE(state);
+ return;
+ }
+ tevent_req_set_callback(req, trustdom_list_done, state);
+}
+
+static void trustdom_list_done(struct tevent_req *req)
+{
+ struct trustdom_state *state = tevent_req_callback_data(
+ req, struct trustdom_state);
+ bool within_forest = false;
+ NTSTATUS status, result;
+ uint32_t i;
+
+ /*
+ * Only when we enumerate our primary domain
+ * or our forest root domain, we should keep
+ * the NETR_TRUST_FLAG_IN_FOREST flag, in
+ * all other cases we need to clear it as the domain
+ * is not part of our forest.
+ */
+ if (state->domain->primary) {
+ within_forest = true;
+ } else if (domain_is_forest_root(state->domain)) {
+ within_forest = true;
+ }
+
+ status = dcerpc_wbint_ListTrustedDomains_recv(req, state, &result);
+ if (any_nt_status_not_ok(status, result, &status)) {
+ DBG_WARNING("Could not receive trusts for domain %s: %s-%s\n",
+ state->domain->name, nt_errstr(status),
+ nt_errstr(result));
+ TALLOC_FREE(state);
+ return;
+ }
+
+ for (i=0; i<state->trusts.count; i++) {
+ struct netr_DomainTrust *trust = &state->trusts.array[i];
+ struct winbindd_domain *domain = NULL;
+
+ if (!within_forest) {
+ trust->trust_flags &= ~NETR_TRUST_FLAG_IN_FOREST;
+ }
+
+ if (!state->domain->primary) {
+ trust->trust_flags &= ~NETR_TRUST_FLAG_PRIMARY;
+ }
+
+ /*
+ * We always call add_trusted_domain() cause on an existing
+ * domain structure, it will update the SID if necessary.
+ * This is important because we need the SID for sibling
+ * domains.
+ */
+ status = add_trusted_domain(trust->netbios_name,
+ trust->dns_name,
+ trust->sid,
+ trust->trust_type,
+ trust->trust_flags,
+ trust->trust_attributes,
+ SEC_CHAN_NULL,
+ find_default_route_domain(),
+ &domain);
+ if (!NT_STATUS_IS_OK(status) &&
+ !NT_STATUS_EQUAL(status, NT_STATUS_NO_SUCH_DOMAIN))
+ {
+ DBG_NOTICE("add_trusted_domain returned %s\n",
+ nt_errstr(status));
+ return;
+ }
+ }
+
+ /*
+ Cases to consider when scanning trusts:
+ (a) we are calling from a child domain (primary && !forest_root)
+ (b) we are calling from the root of the forest (primary && forest_root)
+ (c) we are calling from a trusted forest domain (!primary
+ && !forest_root)
+ */
+
+ if (state->domain->primary) {
+ /* If this is our primary domain and we are not in the
+ forest root, we have to scan the root trusts first */
+
+ if (!domain_is_forest_root(state->domain))
+ rescan_forest_root_trusts();
+ else
+ rescan_forest_trusts();
+
+ } else if (domain_is_forest_root(state->domain)) {
+ /* Once we have done root forest trust search, we can
+ go on to search the trusted forests */
+
+ rescan_forest_trusts();
+ }
+
+ TALLOC_FREE(state);
+
+ return;
+}
+
+/********************************************************************
+ Scan the trusts of our forest root
+********************************************************************/
+
+static void rescan_forest_root_trusts( void )
+{
+ struct winbindd_tdc_domain *dom_list = NULL;
+ size_t num_trusts = 0;
+ size_t i;
+ NTSTATUS status;
+
+ /* The only transitive trusts supported by Windows 2003 AD are
+ (a) Parent-Child, (b) Tree-Root, and (c) Forest. The
+ first two are handled in forest and listed by
+ DsEnumerateDomainTrusts(). Forest trusts are not so we
+ have to do that ourselves. */
+
+ if ( !wcache_tdc_fetch_list( &dom_list, &num_trusts ) )
+ return;
+
+ for ( i=0; i<num_trusts; i++ ) {
+ struct winbindd_domain *d = NULL;
+
+ /* Find the forest root. Don't necessarily trust
+ the domain_list() as our primary domain may not
+ have been initialized. */
+
+ if ( !(dom_list[i].trust_flags & NETR_TRUST_FLAG_TREEROOT) ) {
+ continue;
+ }
+
+ /* Here's the forest root */
+
+ d = find_domain_from_name_noinit( dom_list[i].domain_name );
+ if (d == NULL) {
+ status = add_trusted_domain(dom_list[i].domain_name,
+ dom_list[i].dns_name,
+ &dom_list[i].sid,
+ dom_list[i].trust_type,
+ dom_list[i].trust_flags,
+ dom_list[i].trust_attribs,
+ SEC_CHAN_NULL,
+ find_default_route_domain(),
+ &d);
+
+ if (!NT_STATUS_IS_OK(status) &&
+ NT_STATUS_EQUAL(status, NT_STATUS_NO_SUCH_DOMAIN))
+ {
+ DBG_ERR("add_trusted_domain returned %s\n",
+ nt_errstr(status));
+ return;
+ }
+ }
+ if (d == NULL) {
+ continue;
+ }
+
+ DEBUG(10,("rescan_forest_root_trusts: Following trust path "
+ "for domain tree root %s (%s)\n",
+ d->name, d->alt_name ));
+
+ d->domain_flags = dom_list[i].trust_flags;
+ d->domain_type = dom_list[i].trust_type;
+ d->domain_trust_attribs = dom_list[i].trust_attribs;
+
+ add_trusted_domains( d );
+
+ break;
+ }
+
+ TALLOC_FREE( dom_list );
+
+ return;
+}
+
+/********************************************************************
+ scan the transitive forest trusts (not our own)
+********************************************************************/
+
+
+static void rescan_forest_trusts( void )
+{
+ struct winbindd_domain *d = NULL;
+ struct winbindd_tdc_domain *dom_list = NULL;
+ size_t num_trusts = 0;
+ size_t i;
+ NTSTATUS status;
+
+ /* The only transitive trusts supported by Windows 2003 AD are
+ (a) Parent-Child, (b) Tree-Root, and (c) Forest. The
+ first two are handled in forest and listed by
+ DsEnumerateDomainTrusts(). Forest trusts are not so we
+ have to do that ourselves. */
+
+ if ( !wcache_tdc_fetch_list( &dom_list, &num_trusts ) )
+ return;
+
+ for ( i=0; i<num_trusts; i++ ) {
+ uint32_t flags = dom_list[i].trust_flags;
+ uint32_t type = dom_list[i].trust_type;
+ uint32_t attribs = dom_list[i].trust_attribs;
+
+ d = find_domain_from_name_noinit( dom_list[i].domain_name );
+
+ /* ignore our primary and internal domains */
+
+ if ( d && (d->internal || d->primary ) )
+ continue;
+
+ if ( (flags & NETR_TRUST_FLAG_INBOUND) &&
+ (type == LSA_TRUST_TYPE_UPLEVEL) &&
+ (attribs & LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE) )
+ {
+ /* add the trusted domain if we don't know
+ about it */
+
+ if (d == NULL) {
+ status = add_trusted_domain(
+ dom_list[i].domain_name,
+ dom_list[i].dns_name,
+ &dom_list[i].sid,
+ type,
+ flags,
+ attribs,
+ SEC_CHAN_NULL,
+ find_default_route_domain(),
+ &d);
+ if (!NT_STATUS_IS_OK(status) &&
+ NT_STATUS_EQUAL(status,
+ NT_STATUS_NO_SUCH_DOMAIN))
+ {
+ DBG_ERR("add_trusted_domain: %s\n",
+ nt_errstr(status));
+ return;
+ }
+ }
+
+ if (d == NULL) {
+ continue;
+ }
+
+ DEBUG(10,("Following trust path for domain %s (%s)\n",
+ d->name, d->alt_name ));
+ add_trusted_domains( d );
+ }
+ }
+
+ TALLOC_FREE( dom_list );
+
+ return;
+}
+
+/*********************************************************************
+ The process of updating the trusted domain list is a three step
+ async process:
+ (a) ask our domain
+ (b) ask the root domain in our forest
+ (c) ask a DC in any Win2003 trusted forests
+*********************************************************************/
+
+void rescan_trusted_domains(struct tevent_context *ev, struct tevent_timer *te,
+ struct timeval now, void *private_data)
+{
+ TALLOC_FREE(te);
+
+ /* I used to clear the cache here and start over but that
+ caused problems in child processes that needed the
+ trust dom list early on. Removing it means we
+ could have some trusted domains listed that have been
+ removed from our primary domain's DC until a full
+ restart. This should be ok since I think this is what
+ Windows does as well. */
+
+ /* this will only add new domains we didn't already know about
+ in the domain_list()*/
+
+ add_trusted_domains( find_our_domain() );
+
+ te = tevent_add_timer(
+ ev, NULL, timeval_current_ofs(WINBINDD_RESCAN_FREQ, 0),
+ rescan_trusted_domains, NULL);
+ /*
+ * If te == NULL, there's not much we can do here. Don't fail, the
+ * only thing we miss is new trusted domains.
+ */
+
+ return;
+}
+
+static void wbd_ping_dc_done(struct tevent_req *subreq);
+
+void winbindd_ping_offline_domains(struct tevent_context *ev,
+ struct tevent_timer *te,
+ struct timeval now,
+ void *private_data)
+{
+ struct winbindd_domain *domain = NULL;
+
+ TALLOC_FREE(te);
+
+ for (domain = domain_list(); domain != NULL; domain = domain->next) {
+ DBG_DEBUG("Domain %s is %s\n",
+ domain->name,
+ domain->online ? "online" : "offline");
+
+ if (get_global_winbindd_state_offline()) {
+ DBG_DEBUG("We are globally offline, do nothing.\n");
+ break;
+ }
+
+ if (domain->online ||
+ domain->check_online_event != NULL ||
+ domain->secure_channel_type == SEC_CHAN_NULL) {
+ continue;
+ }
+
+ winbindd_flush_negative_conn_cache(domain);
+
+ domain->check_online_event =
+ dcerpc_wbint_PingDc_send(domain,
+ ev,
+ dom_child_handle(domain),
+ &domain->ping_dcname);
+ if (domain->check_online_event == NULL) {
+ DBG_WARNING("Failed to schedule ping, no-memory\n");
+ continue;
+ }
+
+ tevent_req_set_callback(domain->check_online_event,
+ wbd_ping_dc_done, domain);
+ }
+
+ te = tevent_add_timer(ev,
+ NULL,
+ timeval_current_ofs(lp_winbind_reconnect_delay(),
+ 0),
+ winbindd_ping_offline_domains,
+ NULL);
+ if (te == NULL) {
+ DBG_ERR("Failed to schedule winbindd_ping_offline_domains()\n");
+ }
+
+ return;
+}
+
+static void wbd_ping_dc_done(struct tevent_req *subreq)
+{
+ struct winbindd_domain *domain =
+ tevent_req_callback_data(subreq,
+ struct winbindd_domain);
+ NTSTATUS status, result;
+
+ SMB_ASSERT(subreq == domain->check_online_event);
+ domain->check_online_event = NULL;
+
+ status = dcerpc_wbint_PingDc_recv(subreq, domain, &result);
+ TALLOC_FREE(subreq);
+ if (any_nt_status_not_ok(status, result, &status)) {
+ DBG_WARNING("dcerpc_wbint_PingDc_recv failed for domain: "
+ "%s - %s\n",
+ domain->name,
+ nt_errstr(status));
+ return;
+ }
+
+ DBG_DEBUG("dcerpc_wbint_PingDc_recv() succeeded, "
+ "domain: %s, dc-name: %s\n",
+ domain->name,
+ domain->ping_dcname);
+
+ talloc_free(discard_const(domain->ping_dcname));
+ domain->ping_dcname = NULL;
+
+ return;
+}
+
+static void wb_imsg_new_trusted_domain(struct imessaging_context *msg,
+ void *private_data,
+ uint32_t msg_type,
+ struct server_id server_id,
+ size_t num_fds,
+ int *fds,
+ DATA_BLOB *data)
+{
+ bool ok;
+
+ if (num_fds != 0) {
+ DBG_WARNING("Received %zu fds, ignoring message\n", num_fds);
+ return;
+ }
+
+ DBG_NOTICE("Rescanning trusted domains\n");
+
+ ok = add_trusted_domains_dc();
+ if (!ok) {
+ DBG_ERR("Failed to reload trusted domains\n");
+ }
+}
+
+/*
+ * We did not get the secret when we queried secrets.tdb, so read it
+ * from secrets.tdb and re-sync the databases
+ */
+static bool migrate_secrets_tdb_to_ldb(struct winbindd_domain *domain)
+{
+ bool ok;
+ struct cli_credentials *creds;
+ NTSTATUS can_migrate = pdb_get_trust_credentials(domain->name,
+ NULL, domain, &creds);
+ if (!NT_STATUS_IS_OK(can_migrate)) {
+ DEBUG(0, ("Failed to fetch our own local AD domain join "
+ "password for winbindd's internal use, both from "
+ "secrets.tdb and secrets.ldb: %s\n",
+ nt_errstr(can_migrate)));
+ return false;
+ }
+
+ /*
+ * NOTE: It is very unlikely we end up here if there is an
+ * oldpass, because a new password is created at
+ * classicupgrade, so this is not a concern.
+ */
+ ok = secrets_store_machine_pw_sync(cli_credentials_get_password(creds),
+ NULL /* oldpass */,
+ cli_credentials_get_domain(creds),
+ cli_credentials_get_realm(creds),
+ cli_credentials_get_salt_principal(creds),
+ 0, /* Supported enc types, unused */
+ &domain->sid,
+ cli_credentials_get_password_last_changed_time(creds),
+ cli_credentials_get_secure_channel_type(creds),
+ false /* do_delete: Do not delete */);
+ TALLOC_FREE(creds);
+ if (ok == false) {
+ DEBUG(0, ("Failed to write our own "
+ "local AD domain join password for "
+ "winbindd's internal use into secrets.tdb\n"));
+ return false;
+ }
+ return true;
+}
+
+bool add_trusted_domains_dc(void)
+{
+ struct winbindd_domain *domain = NULL;
+ struct pdb_trusted_domain **domains = NULL;
+ uint32_t num_domains = 0;
+ uint32_t i;
+ NTSTATUS status;
+
+ if (!(pdb_capabilities() & PDB_CAP_TRUSTED_DOMAINS_EX)) {
+ struct trustdom_info **ti = NULL;
+
+ status = pdb_enum_trusteddoms(talloc_tos(), &num_domains, &ti);
+ if (!NT_STATUS_IS_OK(status)) {
+ DBG_ERR("pdb_enum_trusteddoms() failed - %s\n",
+ nt_errstr(status));
+ return false;
+ }
+
+ for (i = 0; i < num_domains; i++) {
+ status = add_trusted_domain(ti[i]->name,
+ NULL,
+ &ti[i]->sid,
+ LSA_TRUST_TYPE_DOWNLEVEL,
+ NETR_TRUST_FLAG_OUTBOUND,
+ 0,
+ SEC_CHAN_DOMAIN,
+ NULL,
+ &domain);
+ if (!NT_STATUS_IS_OK(status)) {
+ DBG_NOTICE("add_trusted_domain returned %s\n",
+ nt_errstr(status));
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ status = pdb_enum_trusted_domains(talloc_tos(), &num_domains, &domains);
+ if (!NT_STATUS_IS_OK(status)) {
+ DBG_ERR("pdb_enum_trusted_domains() failed - %s\n",
+ nt_errstr(status));
+ return false;
+ }
+
+ for (i = 0; i < num_domains; i++) {
+ enum netr_SchannelType sec_chan_type = SEC_CHAN_DOMAIN;
+ uint32_t trust_flags = 0;
+
+ if (domains[i]->trust_type == LSA_TRUST_TYPE_UPLEVEL) {
+ sec_chan_type = SEC_CHAN_DNS_DOMAIN;
+ }
+
+ if (!(domains[i]->trust_direction & LSA_TRUST_DIRECTION_OUTBOUND)) {
+ sec_chan_type = SEC_CHAN_NULL;
+ }
+
+ if (domains[i]->trust_direction & LSA_TRUST_DIRECTION_INBOUND) {
+ trust_flags |= NETR_TRUST_FLAG_INBOUND;
+ }
+ if (domains[i]->trust_direction & LSA_TRUST_DIRECTION_OUTBOUND) {
+ trust_flags |= NETR_TRUST_FLAG_OUTBOUND;
+ }
+ if (domains[i]->trust_attributes & LSA_TRUST_ATTRIBUTE_WITHIN_FOREST) {
+ trust_flags |= NETR_TRUST_FLAG_IN_FOREST;
+ }
+
+ if (domains[i]->trust_attributes & LSA_TRUST_ATTRIBUTE_CROSS_ORGANIZATION) {
+ /*
+ * We don't support selective authentication yet.
+ */
+ DBG_WARNING("Ignoring CROSS_ORGANIZATION trust to "
+ "domain[%s/%s]\n",
+ domains[i]->netbios_name,
+ domains[i]->domain_name);
+ continue;
+ }
+
+ status = add_trusted_domain(domains[i]->netbios_name,
+ domains[i]->domain_name,
+ &domains[i]->security_identifier,
+ domains[i]->trust_type,
+ trust_flags,
+ domains[i]->trust_attributes,
+ sec_chan_type,
+ NULL,
+ &domain);
+ if (!NT_STATUS_IS_OK(status)) {
+ DBG_NOTICE("add_trusted_domain returned %s\n",
+ nt_errstr(status));
+ return false;
+ }
+
+ if (domains[i]->trust_type == LSA_TRUST_TYPE_UPLEVEL) {
+ domain->active_directory = true;
+ }
+ domain->domain_type = domains[i]->trust_type;
+ domain->domain_trust_attribs = domains[i]->trust_attributes;
+ }
+
+ for (i = 0; i < num_domains; i++) {
+ struct ForestTrustInfo fti;
+ uint32_t fi;
+ enum ndr_err_code ndr_err;
+ struct winbindd_domain *routing_domain = NULL;
+
+ if (domains[i]->trust_type != LSA_TRUST_TYPE_UPLEVEL) {
+ continue;
+ }
+
+ if (!(domains[i]->trust_attributes & LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE)) {
+ continue;
+ }
+
+ if (domains[i]->trust_forest_trust_info.length == 0) {
+ continue;
+ }
+
+ routing_domain = find_domain_from_name_noinit(
+ domains[i]->netbios_name);
+ if (routing_domain == NULL) {
+ DBG_ERR("Can't find winbindd domain [%s]\n",
+ domains[i]->netbios_name);
+ return false;
+ }
+
+ ndr_err = ndr_pull_struct_blob_all(
+ &domains[i]->trust_forest_trust_info,
+ talloc_tos(), &fti,
+ (ndr_pull_flags_fn_t)ndr_pull_ForestTrustInfo);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ DBG_ERR("ndr_pull_ForestTrustInfo(%s) - %s\n",
+ domains[i]->netbios_name,
+ ndr_map_error2string(ndr_err));
+ return false;
+ }
+
+ for (fi = 0; fi < fti.count; fi++) {
+ struct ForestTrustInfoRecord *rec =
+ &fti.records[fi].record;
+ struct ForestTrustDataDomainInfo *drec = NULL;
+
+ if (rec->type != FOREST_TRUST_DOMAIN_INFO) {
+ continue;
+ }
+ drec = &rec->data.info;
+
+ if (rec->flags & LSA_NB_DISABLED_MASK) {
+ continue;
+ }
+
+ if (rec->flags & LSA_SID_DISABLED_MASK) {
+ continue;
+ }
+
+ /*
+ * TODO:
+ * also try to find a matching
+ * LSA_TLN_DISABLED_MASK ???
+ */
+
+ domain = find_domain_from_name_noinit(drec->netbios_name.string);
+ if (domain != NULL) {
+ continue;
+ }
+
+ status = add_trusted_domain(drec->netbios_name.string,
+ drec->dns_name.string,
+ &drec->sid,
+ LSA_TRUST_TYPE_UPLEVEL,
+ NETR_TRUST_FLAG_OUTBOUND,
+ 0,
+ SEC_CHAN_NULL,
+ routing_domain,
+ &domain);
+ if (!NT_STATUS_IS_OK(status)) {
+ DBG_NOTICE("add_trusted_domain returned %s\n",
+ nt_errstr(status));
+ return false;
+ }
+ if (domain == NULL) {
+ continue;
+ }
+ }
+ }
+
+ return true;
+}
+
+
+/* Look up global info for the winbind daemon */
+bool init_domain_list(void)
+{
+ int role = lp_server_role();
+ struct pdb_domain_info *pdb_domain_info = NULL;
+ struct winbindd_domain *domain = NULL;
+ NTSTATUS status;
+ bool ok;
+
+ /* Free existing list */
+ free_domain_list();
+
+ /* BUILTIN domain */
+
+ status = add_trusted_domain("BUILTIN",
+ NULL,
+ &global_sid_Builtin,
+ LSA_TRUST_TYPE_DOWNLEVEL,
+ 0, /* trust_flags */
+ 0, /* trust_attribs */
+ SEC_CHAN_LOCAL,
+ NULL,
+ &domain);
+ if (!NT_STATUS_IS_OK(status)) {
+ DBG_ERR("add_trusted_domain BUILTIN returned %s\n",
+ nt_errstr(status));
+ return false;
+ }
+
+ /* Local SAM */
+
+ /*
+ * In case the passdb backend is passdb_dsdb the domain SID comes from
+ * dsdb, not from secrets.tdb. As we use the domain SID in various
+ * places, we must ensure the domain SID is migrated from dsdb to
+ * secrets.tdb before get_global_sam_sid() is called the first time.
+ *
+ * The migration is done as part of the passdb_dsdb initialisation,
+ * calling pdb_get_domain_info() triggers it.
+ */
+ pdb_domain_info = pdb_get_domain_info(talloc_tos());
+
+ if ( role == ROLE_ACTIVE_DIRECTORY_DC ) {
+ uint32_t trust_flags;
+ bool is_root;
+ enum netr_SchannelType sec_chan_type;
+ const char *account_name;
+ struct samr_Password current_nt_hash;
+
+ if (pdb_domain_info == NULL) {
+ DEBUG(0, ("Failed to fetch our own local AD "
+ "domain info from sam.ldb\n"));
+ return false;
+ }
+
+ trust_flags = NETR_TRUST_FLAG_PRIMARY;
+ trust_flags |= NETR_TRUST_FLAG_IN_FOREST;
+ trust_flags |= NETR_TRUST_FLAG_NATIVE;
+ trust_flags |= NETR_TRUST_FLAG_OUTBOUND;
+
+ is_root = strequal(pdb_domain_info->dns_domain,
+ pdb_domain_info->dns_forest);
+ if (is_root) {
+ trust_flags |= NETR_TRUST_FLAG_TREEROOT;
+ }
+
+ status = add_trusted_domain(pdb_domain_info->name,
+ pdb_domain_info->dns_domain,
+ &pdb_domain_info->sid,
+ LSA_TRUST_TYPE_UPLEVEL,
+ trust_flags,
+ LSA_TRUST_ATTRIBUTE_WITHIN_FOREST,
+ SEC_CHAN_BDC,
+ NULL,
+ &domain);
+ TALLOC_FREE(pdb_domain_info);
+ if (!NT_STATUS_IS_OK(status)) {
+ DBG_ERR("Failed to add our own local AD "
+ "domain to winbindd's internal list\n");
+ return false;
+ }
+
+ /*
+ * We need to call this to find out if we are an RODC
+ */
+ ok = get_trust_pw_hash(domain->name,
+ current_nt_hash.hash,
+ &account_name,
+ &sec_chan_type);
+ if (!ok) {
+ /*
+ * If get_trust_pw_hash() fails, then try and
+ * fetch the password from the more recent of
+ * secrets.{ldb,tdb} using the
+ * pdb_get_trust_credentials()
+ */
+ ok = migrate_secrets_tdb_to_ldb(domain);
+
+ if (!ok) {
+ DEBUG(0, ("Failed to migrate our own "
+ "local AD domain join password for "
+ "winbindd's internal use into "
+ "secrets.tdb\n"));
+ return false;
+ }
+ ok = get_trust_pw_hash(domain->name,
+ current_nt_hash.hash,
+ &account_name,
+ &sec_chan_type);
+ if (!ok) {
+ DEBUG(0, ("Failed to find our own just "
+ "written local AD domain join "
+ "password for winbindd's internal "
+ "use in secrets.tdb\n"));
+ return false;
+ }
+ }
+
+ domain->secure_channel_type = sec_chan_type;
+ if (sec_chan_type == SEC_CHAN_RODC) {
+ domain->rodc = true;
+ }
+
+ } else {
+ uint32_t trust_flags;
+ enum netr_SchannelType secure_channel_type;
+
+ trust_flags = NETR_TRUST_FLAG_OUTBOUND;
+ if (role != ROLE_DOMAIN_MEMBER) {
+ trust_flags |= NETR_TRUST_FLAG_PRIMARY;
+ }
+
+ if (role > ROLE_DOMAIN_MEMBER) {
+ secure_channel_type = SEC_CHAN_BDC;
+ } else {
+ secure_channel_type = SEC_CHAN_LOCAL;
+ }
+
+ if ((pdb_domain_info != NULL) && (role == ROLE_IPA_DC)) {
+ /* This is IPA DC that presents itself as
+ * an Active Directory domain controller to trusted AD
+ * forests but in fact is a classic domain controller.
+ */
+ trust_flags = NETR_TRUST_FLAG_PRIMARY;
+ trust_flags |= NETR_TRUST_FLAG_IN_FOREST;
+ trust_flags |= NETR_TRUST_FLAG_NATIVE;
+ trust_flags |= NETR_TRUST_FLAG_OUTBOUND;
+ trust_flags |= NETR_TRUST_FLAG_TREEROOT;
+ status = add_trusted_domain(pdb_domain_info->name,
+ pdb_domain_info->dns_domain,
+ &pdb_domain_info->sid,
+ LSA_TRUST_TYPE_UPLEVEL,
+ trust_flags,
+ LSA_TRUST_ATTRIBUTE_WITHIN_FOREST,
+ secure_channel_type,
+ NULL,
+ &domain);
+ TALLOC_FREE(pdb_domain_info);
+ } else {
+ status = add_trusted_domain(get_global_sam_name(),
+ NULL,
+ get_global_sam_sid(),
+ LSA_TRUST_TYPE_DOWNLEVEL,
+ trust_flags,
+ 0, /* trust_attribs */
+ secure_channel_type,
+ NULL,
+ &domain);
+ }
+ if (!NT_STATUS_IS_OK(status)) {
+ DBG_ERR("Failed to add local SAM to "
+ "domain to winbindd's internal list\n");
+ return false;
+ }
+ }
+
+ if (IS_DC) {
+ ok = add_trusted_domains_dc();
+ if (!ok) {
+ DBG_ERR("init_domain_list_dc failed\n");
+ return false;
+ }
+ }
+
+ if ( role == ROLE_DOMAIN_MEMBER ) {
+ struct dom_sid our_sid;
+ uint32_t trust_type;
+
+ if (!secrets_fetch_domain_sid(lp_workgroup(), &our_sid)) {
+ DEBUG(0, ("Could not fetch our SID - did we join?\n"));
+ return False;
+ }
+
+ if (lp_realm() != NULL) {
+ trust_type = LSA_TRUST_TYPE_UPLEVEL;
+ } else {
+ trust_type = LSA_TRUST_TYPE_DOWNLEVEL;
+ }
+
+ status = add_trusted_domain(lp_workgroup(),
+ lp_realm(),
+ &our_sid,
+ trust_type,
+ NETR_TRUST_FLAG_PRIMARY|
+ NETR_TRUST_FLAG_OUTBOUND,
+ 0, /* trust_attribs */
+ SEC_CHAN_WKSTA,
+ NULL,
+ &domain);
+ if (!NT_STATUS_IS_OK(status)) {
+ DBG_ERR("Failed to add local SAM to "
+ "domain to winbindd's internal list\n");
+ return false;
+ }
+ }
+
+ status = imessaging_register(winbind_imessaging_context(), NULL,
+ MSG_WINBIND_RELOAD_TRUSTED_DOMAINS,
+ wb_imsg_new_trusted_domain);
+ if (!NT_STATUS_IS_OK(status)) {
+ DBG_ERR("imessaging_register failed %s\n", nt_errstr(status));
+ return false;
+ }
+
+ return True;
+}
+
+/**
+ * Given a domain name, return the struct winbindd domain info for it
+ *
+ * @note Do *not* pass lp_workgroup() to this function. domain_list
+ * may modify it's value, and free that pointer. Instead, our local
+ * domain may be found by calling find_our_domain().
+ * directly.
+ *
+ *
+ * @return The domain structure for the named domain, if it is working.
+ */
+
+struct winbindd_domain *find_domain_from_name_noinit(const char *domain_name)
+{
+ struct winbindd_domain *domain;
+
+ /* Search through list */
+
+ for (domain = domain_list(); domain != NULL; domain = domain->next) {
+ if (strequal(domain_name, domain->name)) {
+ return domain;
+ }
+ if (domain->alt_name == NULL) {
+ continue;
+ }
+ if (strequal(domain_name, domain->alt_name)) {
+ return domain;
+ }
+ }
+
+ /* Not found */
+
+ return NULL;
+}
+
+/**
+ * Given a domain name, return the struct winbindd domain if it's a direct
+ * outgoing trust
+ *
+ * @return The domain structure for the named domain, if it is a direct outgoing trust
+ */
+struct winbindd_domain *find_trust_from_name_noinit(const char *domain_name)
+{
+ struct winbindd_domain *domain = NULL;
+
+ domain = find_domain_from_name_noinit(domain_name);
+ if (domain == NULL) {
+ return NULL;
+ }
+
+ if (domain->secure_channel_type != SEC_CHAN_NULL) {
+ return domain;
+ }
+
+ return NULL;
+}
+
+struct winbindd_domain *find_domain_from_name(const char *domain_name)
+{
+ struct winbindd_domain *domain;
+
+ domain = find_domain_from_name_noinit(domain_name);
+
+ if (domain == NULL)
+ return NULL;
+
+ if (!domain->initialized)
+ init_dc_connection(domain, false);
+
+ return domain;
+}
+
+/* Given a domain sid, return the struct winbindd domain info for it */
+
+struct winbindd_domain *find_domain_from_sid_noinit(const struct dom_sid *sid)
+{
+ struct winbindd_domain *domain;
+
+ /* Search through list */
+
+ for (domain = domain_list(); domain != NULL; domain = domain->next) {
+ if (dom_sid_compare_domain(sid, &domain->sid) == 0)
+ return domain;
+ }
+
+ /* Not found */
+
+ return NULL;
+}
+
+/**
+ * Given a domain sid, return the struct winbindd domain if it's a direct
+ * outgoing trust
+ *
+ * @return The domain structure for the specified domain, if it is a direct outgoing trust
+ */
+struct winbindd_domain *find_trust_from_sid_noinit(const struct dom_sid *sid)
+{
+ struct winbindd_domain *domain = NULL;
+
+ domain = find_domain_from_sid_noinit(sid);
+ if (domain == NULL) {
+ return NULL;
+ }
+
+ if (domain->secure_channel_type != SEC_CHAN_NULL) {
+ return domain;
+ }
+
+ return NULL;
+}
+
+/* Given a domain sid, return the struct winbindd domain info for it */
+
+struct winbindd_domain *find_domain_from_sid(const struct dom_sid *sid)
+{
+ struct winbindd_domain *domain;
+
+ domain = find_domain_from_sid_noinit(sid);
+
+ if (domain == NULL)
+ return NULL;
+
+ if (!domain->initialized)
+ init_dc_connection(domain, false);
+
+ return domain;
+}
+
+struct winbindd_domain *find_our_domain(void)
+{
+ struct winbindd_domain *domain;
+
+ /* Search through list */
+
+ for (domain = domain_list(); domain != NULL; domain = domain->next) {
+ if (domain->primary)
+ return domain;
+ }
+
+ smb_panic("Could not find our domain");
+ return NULL;
+}
+
+struct winbindd_domain *find_default_route_domain(void)
+{
+ if (!IS_DC) {
+ return find_our_domain();
+ }
+ DBG_DEBUG("Routing logic not yet implemented on a DC\n");
+ return NULL;
+}
+
+/* Find the appropriate domain to lookup a name or SID */
+
+struct winbindd_domain *find_lookup_domain_from_sid(const struct dom_sid *sid)
+{
+ struct dom_sid_buf buf;
+
+ DBG_DEBUG("SID [%s]\n", dom_sid_str_buf(sid, &buf));
+
+ /*
+ * SIDs in the S-1-22-{1,2} domain and well-known SIDs should be handled
+ * by our passdb.
+ */
+
+ if ( sid_check_is_in_unix_groups(sid) ||
+ sid_check_is_unix_groups(sid) ||
+ sid_check_is_in_unix_users(sid) ||
+ sid_check_is_unix_users(sid) ||
+ sid_check_is_our_sam(sid) ||
+ sid_check_is_in_our_sam(sid) )
+ {
+ return find_domain_from_sid(get_global_sam_sid());
+ }
+
+ if ( sid_check_is_builtin(sid) ||
+ sid_check_is_in_builtin(sid) ||
+ sid_check_is_wellknown_domain(sid, NULL) ||
+ sid_check_is_in_wellknown_domain(sid) )
+ {
+ return find_domain_from_sid(&global_sid_Builtin);
+ }
+
+ if (IS_DC) {
+ struct winbindd_domain *domain = NULL;
+
+ domain = find_domain_from_sid_noinit(sid);
+ if (domain == NULL) {
+ return NULL;
+ }
+
+ if (domain->secure_channel_type != SEC_CHAN_NULL) {
+ return domain;
+ }
+
+ return domain->routing_domain;
+ }
+
+ /* On a member server a query for SID or name can always go to our
+ * primary DC. */
+
+ DEBUG(10, ("calling find_our_domain\n"));
+ return find_our_domain();
+}
+
+struct winbindd_domain *find_lookup_domain_from_name(const char *domain_name)
+{
+ bool predefined;
+
+ if ( strequal(domain_name, unix_users_domain_name() ) ||
+ strequal(domain_name, unix_groups_domain_name() ) )
+ {
+ /*
+ * The "Unix User" and "Unix Group" domain are handled by
+ * passdb
+ */
+ return find_domain_from_name_noinit( get_global_sam_name() );
+ }
+
+ if (strequal(domain_name, "BUILTIN") ||
+ strequal(domain_name, get_global_sam_name())) {
+ return find_domain_from_name_noinit(domain_name);
+ }
+
+ predefined = dom_sid_lookup_is_predefined_domain(domain_name);
+ if (predefined) {
+ return find_domain_from_name_noinit(builtin_domain_name());
+ }
+
+ if (IS_DC) {
+ struct winbindd_domain *domain = NULL;
+
+ domain = find_domain_from_name_noinit(domain_name);
+ if (domain == NULL) {
+ return NULL;
+ }
+
+ if (domain->secure_channel_type != SEC_CHAN_NULL) {
+ return domain;
+ }
+
+ return domain->routing_domain;
+ }
+
+ return find_our_domain();
+}
+
+/* Is this a domain which we may assume no DOMAIN\ prefix? */
+
+static bool assume_domain(const char *domain)
+{
+ /* never assume the domain on a standalone server */
+
+ if ( lp_server_role() == ROLE_STANDALONE )
+ return False;
+
+ /* domain member servers may possibly assume for the domain name */
+
+ if ( lp_server_role() == ROLE_DOMAIN_MEMBER ) {
+ if ( !strequal(lp_workgroup(), domain) )
+ return False;
+
+ if ( lp_winbind_use_default_domain() )
+ return True;
+ }
+
+ /* only left with a domain controller */
+
+ if ( strequal(get_global_sam_name(), domain) ) {
+ return True;
+ }
+
+ return False;
+}
+
+/* Parse a DOMAIN\user or UPN string into a domain, namespace and a user */
+bool parse_domain_user(TALLOC_CTX *ctx,
+ const char *domuser,
+ char **pnamespace,
+ char **pdomain,
+ char **puser)
+{
+ char *p = NULL;
+ char *namespace = NULL;
+ char *domain = NULL;
+ char *user = NULL;
+
+ if (strlen(domuser) == 0) {
+ return false;
+ }
+
+ p = strchr(domuser, *lp_winbind_separator());
+ if (p != NULL) {
+ user = talloc_strdup(ctx, p + 1);
+ if (user == NULL) {
+ goto fail;
+ }
+ domain = talloc_strdup(ctx,
+ domuser);
+ if (domain == NULL) {
+ goto fail;
+ }
+ domain[PTR_DIFF(p, domuser)] = '\0';
+ namespace = talloc_strdup(ctx, domain);
+ if (namespace == NULL) {
+ goto fail;
+ }
+ } else {
+ user = talloc_strdup(ctx, domuser);
+ if (user == NULL) {
+ goto fail;
+ }
+ p = strchr(domuser, '@');
+ if (p != NULL) {
+ /* upn */
+ namespace = talloc_strdup(ctx, p + 1);
+ if (namespace == NULL) {
+ goto fail;
+ }
+ domain = talloc_strdup(ctx, "");
+ if (domain == NULL) {
+ goto fail;
+ }
+
+ } else if (assume_domain(lp_workgroup())) {
+ domain = talloc_strdup(ctx, lp_workgroup());
+ if (domain == NULL) {
+ goto fail;
+ }
+ namespace = talloc_strdup(ctx, domain);
+ if (namespace == NULL) {
+ goto fail;
+ }
+ } else {
+ namespace = talloc_strdup(ctx, lp_netbios_name());
+ if (namespace == NULL) {
+ goto fail;
+ }
+ domain = talloc_strdup(ctx, "");
+ if (domain == NULL) {
+ goto fail;
+ }
+ }
+ }
+
+ if (!strupper_m(domain)) {
+ goto fail;
+ }
+
+ *pnamespace = namespace;
+ *pdomain = domain;
+ *puser = user;
+ return true;
+fail:
+ TALLOC_FREE(user);
+ TALLOC_FREE(domain);
+ TALLOC_FREE(namespace);
+ return false;
+}
+
+bool canonicalize_username(TALLOC_CTX *mem_ctx,
+ char **pusername_inout,
+ char **pnamespace,
+ char **pdomain,
+ char **puser)
+{
+ bool ok;
+ char *namespace = NULL;
+ char *domain = NULL;
+ char *user = NULL;
+ char *username_inout = NULL;
+
+ ok = parse_domain_user(mem_ctx,
+ *pusername_inout,
+ &namespace, &domain, &user);
+
+ if (!ok) {
+ return False;
+ }
+
+ username_inout = talloc_asprintf(mem_ctx, "%s%c%s",
+ domain, *lp_winbind_separator(),
+ user);
+
+ if (username_inout == NULL) {
+ goto fail;
+ }
+
+ *pnamespace = namespace;
+ *puser = user;
+ *pdomain = domain;
+ *pusername_inout = username_inout;
+ return True;
+fail:
+ TALLOC_FREE(username_inout);
+ TALLOC_FREE(namespace);
+ TALLOC_FREE(domain);
+ TALLOC_FREE(user);
+ return false;
+}
+
+/*
+ Fill DOMAIN\\USERNAME entry accounting 'winbind use default domain' and
+ 'winbind separator' options.
+ This means:
+ - omit DOMAIN when 'winbind use default domain = true' and DOMAIN is
+ lp_workgroup()
+
+ If we are a PDC or BDC, and this is for our domain, do likewise.
+
+ On an AD DC we always fill DOMAIN\\USERNAME.
+
+ We always canonicalize as UPPERCASE DOMAIN, lowercase username.
+*/
+/**
+ * talloc version of fill_domain_username()
+ * return NULL on talloc failure.
+ */
+char *fill_domain_username_talloc(TALLOC_CTX *mem_ctx,
+ const char *domain,
+ const char *user,
+ bool can_assume)
+{
+ char *tmp_user, *name;
+
+ if (lp_server_role() == ROLE_ACTIVE_DIRECTORY_DC) {
+ can_assume = false;
+ }
+
+ if (user == NULL) {
+ return NULL;
+ }
+
+ tmp_user = talloc_strdup(mem_ctx, user);
+ if (tmp_user == NULL) {
+ return NULL;
+ }
+ if (!strlower_m(tmp_user)) {
+ TALLOC_FREE(tmp_user);
+ return NULL;
+ }
+
+ if (can_assume && assume_domain(domain)) {
+ name = tmp_user;
+ } else {
+ name = talloc_asprintf(mem_ctx, "%s%c%s",
+ domain,
+ *lp_winbind_separator(),
+ tmp_user);
+ TALLOC_FREE(tmp_user);
+ }
+
+ return name;
+}
+
+/*
+ * Client list accessor functions
+ */
+
+static struct winbindd_cli_state *_client_list;
+static int _num_clients;
+
+/* Return list of all connected clients */
+
+struct winbindd_cli_state *winbindd_client_list(void)
+{
+ return _client_list;
+}
+
+/* Return list-tail of all connected clients */
+
+struct winbindd_cli_state *winbindd_client_list_tail(void)
+{
+ return DLIST_TAIL(_client_list);
+}
+
+/* Return previous (read:newer) client in list */
+
+struct winbindd_cli_state *
+winbindd_client_list_prev(struct winbindd_cli_state *cli)
+{
+ return DLIST_PREV(cli);
+}
+
+/* Add a connection to the list */
+
+void winbindd_add_client(struct winbindd_cli_state *cli)
+{
+ cli->last_access = time(NULL);
+ DLIST_ADD(_client_list, cli);
+ _num_clients++;
+}
+
+/* Remove a client from the list */
+
+void winbindd_remove_client(struct winbindd_cli_state *cli)
+{
+ DLIST_REMOVE(_client_list, cli);
+ _num_clients--;
+}
+
+/* Move a client to head or list */
+
+void winbindd_promote_client(struct winbindd_cli_state *cli)
+{
+ cli->last_access = time(NULL);
+ DLIST_PROMOTE(_client_list, cli);
+}
+
+/* Return number of open clients */
+
+int winbindd_num_clients(void)
+{
+ return _num_clients;
+}
+
+NTSTATUS lookup_usergroups_cached(TALLOC_CTX *mem_ctx,
+ const struct dom_sid *user_sid,
+ uint32_t *p_num_groups, struct dom_sid **user_sids)
+{
+ struct netr_SamInfo3 *info3 = NULL;
+ NTSTATUS status = NT_STATUS_NO_MEMORY;
+ uint32_t num_groups = 0;
+
+ DEBUG(3,(": lookup_usergroups_cached\n"));
+
+ *user_sids = NULL;
+ *p_num_groups = 0;
+
+ info3 = netsamlogon_cache_get(mem_ctx, user_sid);
+
+ if (info3 == NULL) {
+ return NT_STATUS_OBJECT_NAME_NOT_FOUND;
+ }
+
+ /*
+ * Before bug #7843 the "Domain Local" groups were added with a
+ * lookupuseraliases call, but this isn't done anymore for our domain
+ * so we need to resolve resource groups here.
+ *
+ * When to use Resource Groups:
+ * http://technet.microsoft.com/en-us/library/cc753670%28v=WS.10%29.aspx
+ */
+ status = sid_array_from_info3(mem_ctx, info3,
+ user_sids,
+ &num_groups,
+ false);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ TALLOC_FREE(info3);
+ return status;
+ }
+
+ TALLOC_FREE(info3);
+ *p_num_groups = num_groups;
+ status = (user_sids != NULL) ? NT_STATUS_OK : NT_STATUS_NO_MEMORY;
+
+ DEBUG(3,(": lookup_usergroups_cached succeeded\n"));
+
+ return status;
+}
+
+/*********************************************************************
+ We use this to remove spaces from user and group names
+********************************************************************/
+
+NTSTATUS normalize_name_map(TALLOC_CTX *mem_ctx,
+ const char *domain_name,
+ const char *name,
+ char **normalized)
+{
+ struct winbindd_domain *domain = NULL;
+ NTSTATUS nt_status;
+
+ if (!name || !normalized) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ if (!lp_winbind_normalize_names()) {
+ return NT_STATUS_PROCEDURE_NOT_FOUND;
+ }
+
+ domain = find_domain_from_name_noinit(domain_name);
+ if (domain == NULL) {
+ DBG_ERR("Failed to find domain '%s'\n", domain_name);
+ return NT_STATUS_NO_SUCH_DOMAIN;
+ }
+
+ /* Alias support and whitespace replacement are mutually
+ exclusive */
+
+ nt_status = resolve_username_to_alias(mem_ctx, domain,
+ name, normalized );
+ if (NT_STATUS_IS_OK(nt_status)) {
+ /* special return code to let the caller know we
+ mapped to an alias */
+ return NT_STATUS_FILE_RENAMED;
+ }
+
+ /* check for an unreachable domain */
+
+ if (NT_STATUS_EQUAL(nt_status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
+ DEBUG(5,("normalize_name_map: Setting domain %s offline\n",
+ domain->name));
+ set_domain_offline(domain);
+ return nt_status;
+ }
+
+ /* deal with whitespace */
+
+ *normalized = talloc_strdup(mem_ctx, name);
+ if (!(*normalized)) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ all_string_sub( *normalized, " ", "_", 0 );
+
+ return NT_STATUS_OK;
+}
+
+/*********************************************************************
+ We use this to do the inverse of normalize_name_map()
+********************************************************************/
+
+NTSTATUS normalize_name_unmap(TALLOC_CTX *mem_ctx,
+ const char *name,
+ char **normalized)
+{
+ NTSTATUS nt_status;
+ struct winbindd_domain *domain = find_our_domain();
+
+ if (!name || !normalized) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ if (!lp_winbind_normalize_names()) {
+ return NT_STATUS_PROCEDURE_NOT_FOUND;
+ }
+
+ /* Alias support and whitespace replacement are mutally
+ exclusive */
+
+ /* When mapping from an alias to a username, we don't know the
+ domain. But we only need a domain structure to cache
+ a successful lookup , so just our own domain structure for
+ the seqnum. */
+
+ nt_status = resolve_alias_to_username(mem_ctx, domain,
+ name, normalized);
+ if (NT_STATUS_IS_OK(nt_status)) {
+ /* Special return code to let the caller know we mapped
+ from an alias */
+ return NT_STATUS_FILE_RENAMED;
+ }
+
+ /* check for an unreachable domain */
+
+ if (NT_STATUS_EQUAL(nt_status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
+ DEBUG(5,("normalize_name_unmap: Setting domain %s offline\n",
+ domain->name));
+ set_domain_offline(domain);
+ return nt_status;
+ }
+
+ /* deal with whitespace */
+
+ *normalized = talloc_strdup(mem_ctx, name);
+ if (!(*normalized)) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ all_string_sub(*normalized, "_", " ", 0);
+
+ return NT_STATUS_OK;
+}
+
+/*********************************************************************
+ ********************************************************************/
+
+bool winbindd_can_contact_domain(struct winbindd_domain *domain)
+{
+ struct winbindd_tdc_domain *tdc = NULL;
+ TALLOC_CTX *frame = talloc_stackframe();
+ bool ret = false;
+
+ /* We can contact the domain if it is our primary domain */
+
+ if (domain->primary) {
+ ret = true;
+ goto done;
+ }
+
+ /* Trust the TDC cache and not the winbindd_domain flags */
+
+ if ((tdc = wcache_tdc_fetch_domain(frame, domain->name)) == NULL) {
+ DEBUG(10,("winbindd_can_contact_domain: %s not found in cache\n",
+ domain->name));
+ ret = false;
+ goto done;
+ }
+
+ /* Can always contact a domain that is in out forest */
+
+ if (tdc->trust_flags & NETR_TRUST_FLAG_IN_FOREST) {
+ ret = true;
+ goto done;
+ }
+
+ /*
+ * On a _member_ server, we cannot contact the domain if it
+ * is running AD and we have no inbound trust.
+ */
+
+ if (!IS_DC &&
+ domain->active_directory &&
+ ((tdc->trust_flags & NETR_TRUST_FLAG_INBOUND) != NETR_TRUST_FLAG_INBOUND))
+ {
+ DEBUG(10, ("winbindd_can_contact_domain: %s is an AD domain "
+ "and we have no inbound trust.\n", domain->name));
+ goto done;
+ }
+
+ /* Assume everything else is ok (probably not true but what
+ can you do?) */
+
+ ret = true;
+
+done:
+ talloc_destroy(frame);
+
+ return ret;
+}
+
+#ifdef HAVE_KRB5_LOCATE_PLUGIN_H
+
+/*********************************************************************
+ ********************************************************************/
+
+static void winbindd_set_locator_kdc_env(const struct winbindd_domain *domain)
+{
+ char *var = NULL;
+ char addr[INET6_ADDRSTRLEN];
+ const char *kdc = NULL;
+ int lvl = 11;
+
+ if (!domain || !domain->alt_name || !*domain->alt_name) {
+ return;
+ }
+
+ if (domain->initialized && !domain->active_directory) {
+ DEBUG(lvl,("winbindd_set_locator_kdc_env: %s not AD\n",
+ domain->alt_name));
+ return;
+ }
+
+ print_sockaddr(addr, sizeof(addr), &domain->dcaddr);
+ kdc = addr;
+ if (!*kdc) {
+ DEBUG(lvl,("winbindd_set_locator_kdc_env: %s no DC IP\n",
+ domain->alt_name));
+ kdc = domain->dcname;
+ }
+
+ if (!kdc || !*kdc) {
+ DEBUG(lvl,("winbindd_set_locator_kdc_env: %s no DC at all\n",
+ domain->alt_name));
+ return;
+ }
+
+ var = talloc_asprintf_strupper_m(
+ talloc_tos(),
+ "%s_%s",
+ WINBINDD_LOCATOR_KDC_ADDRESS,
+ domain->alt_name);
+ if (var == NULL) {
+ return;
+ }
+
+ DEBUG(lvl,("winbindd_set_locator_kdc_env: setting var: %s to: %s\n",
+ var, kdc));
+
+ setenv(var, kdc, 1);
+ TALLOC_FREE(var);
+}
+
+/*********************************************************************
+ ********************************************************************/
+
+void winbindd_set_locator_kdc_envs(const struct winbindd_domain *domain)
+{
+ struct winbindd_domain *our_dom = find_our_domain();
+
+ winbindd_set_locator_kdc_env(domain);
+
+ if (domain != our_dom) {
+ winbindd_set_locator_kdc_env(our_dom);
+ }
+}
+
+/*********************************************************************
+ ********************************************************************/
+
+void winbindd_unset_locator_kdc_env(const struct winbindd_domain *domain)
+{
+ char *var = NULL;
+
+ if (!domain || !domain->alt_name || !*domain->alt_name) {
+ return;
+ }
+
+ var = talloc_asprintf_strupper_m(
+ talloc_tos(),
+ "%s_%s",
+ WINBINDD_LOCATOR_KDC_ADDRESS,
+ domain->alt_name);
+ if (var == NULL) {
+ return;
+ }
+
+ unsetenv(var);
+ TALLOC_FREE(var);
+}
+#else
+
+void winbindd_set_locator_kdc_envs(const struct winbindd_domain *domain)
+{
+ return;
+}
+
+void winbindd_unset_locator_kdc_env(const struct winbindd_domain *domain)
+{
+ return;
+}
+
+#endif /* HAVE_KRB5_LOCATE_PLUGIN_H */
+
+void set_auth_errors(struct winbindd_response *resp, NTSTATUS result)
+{
+ /*
+ * Make sure we start with authoritative=true,
+ * it will only set to false if we don't know the
+ * domain.
+ */
+ resp->data.auth.authoritative = true;
+
+ resp->data.auth.nt_status = NT_STATUS_V(result);
+ fstrcpy(resp->data.auth.nt_status_string, nt_errstr(result));
+
+ /* we might have given a more useful error above */
+ if (*resp->data.auth.error_string == '\0')
+ fstrcpy(resp->data.auth.error_string,
+ get_friendly_nt_error_msg(result));
+ resp->data.auth.pam_error = nt_status_to_pam(result);
+}
+
+bool is_domain_offline(const struct winbindd_domain *domain)
+{
+ if (get_global_winbindd_state_offline()) {
+ return true;
+ }
+ return !domain->online;
+}
+
+bool is_domain_online(const struct winbindd_domain *domain)
+{
+ return !is_domain_offline(domain);
+}
+
+/**
+ * Parse an char array into a list of sids.
+ *
+ * The input sidstr should consist of 0-terminated strings
+ * representing sids, separated by newline characters '\n'.
+ * The list is terminated by an empty string, i.e.
+ * character '\0' directly following a character '\n'
+ * (or '\0' right at the start of sidstr).
+ */
+bool parse_sidlist(TALLOC_CTX *mem_ctx, const char *sidstr,
+ struct dom_sid **sids, uint32_t *num_sids)
+{
+ const char *p;
+
+ p = sidstr;
+ if (p == NULL)
+ return False;
+
+ while (p[0] != '\0') {
+ struct dom_sid sid;
+ const char *q = NULL;
+
+ if (!dom_sid_parse_endp(p, &sid, &q)) {
+ DEBUG(1, ("Could not parse sid %s\n", p));
+ return false;
+ }
+ if (q[0] != '\n') {
+ DEBUG(1, ("Got invalid sidstr: %s\n", p));
+ return false;
+ }
+ if (!NT_STATUS_IS_OK(add_sid_to_array(mem_ctx, &sid, sids,
+ num_sids)))
+ {
+ return False;
+ }
+ p = q+1;
+ }
+ return True;
+}
+
+bool parse_xidlist(TALLOC_CTX *mem_ctx, const char *xidstr,
+ struct unixid **pxids, uint32_t *pnum_xids)
+{
+ const char *p;
+ struct unixid *xids = NULL;
+ uint32_t num_xids = 0;
+
+ p = xidstr;
+ if (p == NULL) {
+ return false;
+ }
+
+ while (p[0] != '\0') {
+ struct unixid *tmp;
+ struct unixid xid;
+ unsigned long long id;
+ char *endp;
+ int error = 0;
+
+ switch (p[0]) {
+ case 'U':
+ xid = (struct unixid) { .type = ID_TYPE_UID };
+ break;
+ case 'G':
+ xid = (struct unixid) { .type = ID_TYPE_GID };
+ break;
+ default:
+ return false;
+ }
+
+ p += 1;
+
+ id = smb_strtoull(p, &endp, 10, &error, SMB_STR_STANDARD);
+ if (error != 0) {
+ goto fail;
+ }
+ if (*endp != '\n') {
+ goto fail;
+ }
+ p = endp+1;
+
+ xid.id = id;
+ if ((unsigned long long)xid.id != id) {
+ goto fail;
+ }
+
+ tmp = talloc_realloc(mem_ctx, xids, struct unixid, num_xids+1);
+ if (tmp == NULL) {
+ return 0;
+ }
+ xids = tmp;
+
+ xids[num_xids] = xid;
+ num_xids += 1;
+ }
+
+ *pxids = xids;
+ *pnum_xids = num_xids;
+ return true;
+
+fail:
+ TALLOC_FREE(xids);
+ return false;
+}
diff --git a/source3/winbindd/winbindd_wins_byip.c b/source3/winbindd/winbindd_wins_byip.c
new file mode 100644
index 0000000..1b9cdbc
--- /dev/null
+++ b/source3/winbindd/winbindd_wins_byip.c
@@ -0,0 +1,142 @@
+/*
+ Unix SMB/CIFS implementation.
+ async implementation of WINBINDD_WINS_BYIP
+ Copyright (C) Volker Lendecke 2011
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "winbindd.h"
+#include "libsmb/namequery.h"
+#include "librpc/gen_ndr/ndr_winbind_c.h"
+#include "libsmb/nmblib.h"
+#include "lib/util/string_wrappers.h"
+
+struct winbindd_wins_byip_state {
+ struct nmb_name star;
+ struct sockaddr_storage addr;
+ fstring response;
+};
+
+static void winbindd_wins_byip_done(struct tevent_req *subreq);
+
+struct tevent_req *winbindd_wins_byip_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct winbindd_cli_state *cli,
+ struct winbindd_request *request)
+{
+ struct tevent_req *req, *subreq;
+ struct winbindd_wins_byip_state *state;
+
+ req = tevent_req_create(mem_ctx, &state,
+ struct winbindd_wins_byip_state);
+ if (req == NULL) {
+ return NULL;
+ }
+ /* Ensure null termination */
+ request->data.winsreq[sizeof(request->data.winsreq)-1]='\0';
+
+ fstr_sprintf(state->response, "%s\t", request->data.winsreq);
+
+ D_NOTICE("[%s (%u)] Winbind external command WINS_BYIP start.\n"
+ "Resolving wins byip for %s.\n",
+ cli->client_name,
+ (unsigned int)cli->pid,
+ request->data.winsreq);
+
+ make_nmb_name(&state->star, "*", 0);
+
+ if (!interpret_string_addr(&state->addr, request->data.winsreq,
+ AI_NUMERICHOST)) {
+ tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
+ return tevent_req_post(req, ev);
+ }
+
+ subreq = node_status_query_send(state, ev, &state->star,
+ &state->addr);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, winbindd_wins_byip_done, req);
+ return req;
+}
+
+static void winbindd_wins_byip_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct winbindd_wins_byip_state *state = tevent_req_data(
+ req, struct winbindd_wins_byip_state);
+ struct node_status *names;
+ size_t i;
+ size_t num_names = 0;
+ NTSTATUS status;
+
+ status = node_status_query_recv(subreq, talloc_tos(), &names,
+ &num_names, NULL);
+ TALLOC_FREE(subreq);
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+
+ for (i=0; i<num_names; i++) {
+ size_t size;
+ /*
+ * ignore group names
+ */
+ if (names[i].flags & 0x80) {
+ continue;
+ }
+ /*
+ * Only report 0x20
+ */
+ if (names[i].type != 0x20) {
+ continue;
+ }
+
+ D_DEBUG("Got name '%s'.\n", names[i].name);
+
+ size = strlen(names[i].name + strlen(state->response));
+ if (size > sizeof(state->response) - 1) {
+ D_WARNING("Too much data!\n");
+ tevent_req_nterror(req, STATUS_BUFFER_OVERFLOW);
+ return;
+ }
+ fstrcat(state->response, names[i].name);
+ fstrcat(state->response, " ");
+ }
+ state->response[strlen(state->response)-1] = '\n';
+
+
+ TALLOC_FREE(names);
+ tevent_req_done(req);
+}
+
+NTSTATUS winbindd_wins_byip_recv(struct tevent_req *req,
+ struct winbindd_response *presp)
+{
+ struct winbindd_wins_byip_state *state = tevent_req_data(
+ req, struct winbindd_wins_byip_state);
+ NTSTATUS status;
+
+ if (tevent_req_is_nterror(req, &status)) {
+ return status;
+ }
+ D_NOTICE("Winbind external command WINS_BYIP end.\n"
+ "Response: %s",
+ state->response);
+ fstrcpy(presp->data.winsresp, state->response);
+ return NT_STATUS_OK;
+}
diff --git a/source3/winbindd/winbindd_wins_byname.c b/source3/winbindd/winbindd_wins_byname.c
new file mode 100644
index 0000000..ae170b2
--- /dev/null
+++ b/source3/winbindd/winbindd_wins_byname.c
@@ -0,0 +1,154 @@
+/*
+ Unix SMB/CIFS implementation.
+ async implementation of WINBINDD_WINS_BYNAME
+ Copyright (C) Volker Lendecke 2011
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "winbindd.h"
+#include "libsmb/namequery.h"
+#include "librpc/gen_ndr/ndr_winbind_c.h"
+#include "libsmb/nmblib.h"
+#include "lib/util/string_wrappers.h"
+
+struct winbindd_wins_byname_state {
+ struct tevent_context *ev;
+ struct winbindd_request *request;
+ struct sockaddr_storage *addrs;
+ size_t num_addrs;
+};
+
+static void winbindd_wins_byname_wins_done(struct tevent_req *subreq);
+static void winbindd_wins_byname_bcast_done(struct tevent_req *subreq);
+
+struct tevent_req *winbindd_wins_byname_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct winbindd_cli_state *cli,
+ struct winbindd_request *request)
+{
+ struct tevent_req *req, *subreq;
+ struct winbindd_wins_byname_state *state;
+
+ req = tevent_req_create(mem_ctx, &state,
+ struct winbindd_wins_byname_state);
+ if (req == NULL) {
+ return NULL;
+ }
+ state->ev = ev;
+ state->request = request;
+
+ /* Ensure null termination */
+ request->data.winsreq[sizeof(request->data.winsreq)-1]='\0';
+
+ D_NOTICE("[%s (%u)] Winbind external command WINS_BYNAME start.\n"
+ "Resolving wins byname for '%s'.\n",
+ cli->client_name,
+ (unsigned int)cli->pid,
+ request->data.winsreq);
+
+ subreq = resolve_wins_send(state, ev, state->request->data.winsreq,
+ 0x20);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, winbindd_wins_byname_wins_done, req);
+ return req;
+}
+
+static void winbindd_wins_byname_wins_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct winbindd_wins_byname_state *state = tevent_req_data(
+ req, struct winbindd_wins_byname_state);
+ NTSTATUS status;
+
+ status = resolve_wins_recv(subreq, talloc_tos(), &state->addrs,
+ &state->num_addrs, NULL);
+ TALLOC_FREE(subreq);
+ if (NT_STATUS_IS_OK(status)) {
+ tevent_req_done(req);
+ return;
+ }
+ subreq = name_resolve_bcast_send(state, state->ev,
+ state->request->data.winsreq, 0x20);
+ if (tevent_req_nomem(subreq, req)) {
+ return;
+ }
+ tevent_req_set_callback(subreq, winbindd_wins_byname_bcast_done, req);
+}
+
+static void winbindd_wins_byname_bcast_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct winbindd_wins_byname_state *state = tevent_req_data(
+ req, struct winbindd_wins_byname_state);
+ NTSTATUS status;
+
+ status = name_resolve_bcast_recv(subreq, talloc_tos(), &state->addrs,
+ &state->num_addrs);
+ TALLOC_FREE(subreq);
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+ tevent_req_done(req);
+}
+
+NTSTATUS winbindd_wins_byname_recv(struct tevent_req *req,
+ struct winbindd_response *presp)
+{
+ struct winbindd_wins_byname_state *state = tevent_req_data(
+ req, struct winbindd_wins_byname_state);
+ char *response;
+ NTSTATUS status;
+ size_t i;
+
+ if (tevent_req_is_nterror(req, &status)) {
+ return status;
+ }
+
+ response = talloc_strdup(talloc_tos(), "");
+ if (response == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ D_NOTICE("Winbind external command WINS_BYNAME end.\n"
+ "Received %zu address(es).\n",
+ state->num_addrs);
+ for (i=0; i<state->num_addrs; i++) {
+ char addr[INET6_ADDRSTRLEN];
+ print_sockaddr(addr, sizeof(addr), &state->addrs[i]);
+ D_NOTICE("%zu: %s\n", i, addr);
+ talloc_asprintf_addbuf(
+ &response, "%s%s", addr,
+ i < (state->num_addrs-1) ? " " : "");
+ }
+
+ talloc_asprintf_addbuf(
+ &response, "\t%s\n", state->request->data.winsreq);
+ if (response == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ if (talloc_get_size(response) > sizeof(presp->data.winsresp)) {
+ TALLOC_FREE(response);
+ return NT_STATUS_MARSHALL_OVERFLOW;
+ }
+ fstrcpy(presp->data.winsresp, response);
+ TALLOC_FREE(response);
+ return NT_STATUS_OK;
+}
diff --git a/source3/winbindd/winbindd_xids_to_sids.c b/source3/winbindd/winbindd_xids_to_sids.c
new file mode 100644
index 0000000..3420377
--- /dev/null
+++ b/source3/winbindd/winbindd_xids_to_sids.c
@@ -0,0 +1,142 @@
+/*
+ Unix SMB/CIFS implementation.
+ async implementation of WINBINDD_SIDS_TO_XIDS
+ Copyright (C) Volker Lendecke 2011
+ Copyright (C) Michael Adam 2012
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "winbindd.h"
+#include "../libcli/security/security.h"
+
+
+struct winbindd_xids_to_sids_state {
+ struct tevent_context *ev;
+
+ struct unixid *xids;
+ uint32_t num_xids;
+
+ struct dom_sid *sids;
+};
+
+static void winbindd_xids_to_sids_done(struct tevent_req *subreq);
+
+struct tevent_req *winbindd_xids_to_sids_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct winbindd_cli_state *cli,
+ struct winbindd_request *request)
+{
+ struct tevent_req *req, *subreq;
+ struct winbindd_xids_to_sids_state *state;
+
+ req = tevent_req_create(mem_ctx, &state,
+ struct winbindd_xids_to_sids_state);
+ if (req == NULL) {
+ return NULL;
+ }
+ state->ev = ev;
+
+ D_NOTICE("[%s (%u)] Winbind external command XIDS_TO_SIDS start.\n",
+ cli->client_name,
+ (unsigned int)cli->pid);
+
+ if (request->extra_len == 0) {
+ tevent_req_done(req);
+ return tevent_req_post(req, ev);
+ }
+ if (request->extra_data.data[request->extra_len-1] != '\0') {
+ D_DEBUG("Got invalid XID list.\n");
+ tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
+ return tevent_req_post(req, ev);
+ }
+ if (!parse_xidlist(state, request->extra_data.data,
+ &state->xids, &state->num_xids)) {
+ D_DEBUG("parse_sidlist failed\n");
+ tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
+ return tevent_req_post(req, ev);
+ }
+
+ D_DEBUG("Resolving %"PRIu32" XID(s):\n%s\n",
+ state->num_xids,
+ (char *)request->extra_data.data);
+
+ subreq = wb_xids2sids_send(state, ev, state->xids, state->num_xids);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, winbindd_xids_to_sids_done, req);
+ return req;
+}
+
+static void winbindd_xids_to_sids_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct winbindd_xids_to_sids_state *state = tevent_req_data(
+ req, struct winbindd_xids_to_sids_state);
+ NTSTATUS status;
+
+ status = wb_xids2sids_recv(subreq, state, &state->sids);
+ TALLOC_FREE(subreq);
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+ tevent_req_done(req);
+}
+
+NTSTATUS winbindd_xids_to_sids_recv(struct tevent_req *req,
+ struct winbindd_response *response)
+{
+ struct winbindd_xids_to_sids_state *state = tevent_req_data(
+ req, struct winbindd_xids_to_sids_state);
+ NTSTATUS status;
+ char *result = NULL;
+ uint32_t i;
+
+ D_NOTICE("Winbind external command XIDS_TO_SIDS end.\n");
+ if (tevent_req_is_nterror(req, &status)) {
+ D_WARNING("Could not convert xids: %s\n", nt_errstr(status));
+ return status;
+ }
+
+ result = talloc_strdup(response, "");
+ if (result == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ for (i=0; i<state->num_xids; i++) {
+ struct dom_sid_buf sid_buf;
+ const char *str = "-";
+
+ if (!is_null_sid(&state->sids[i])) {
+ dom_sid_str_buf(&state->sids[i], &sid_buf);
+ str = sid_buf.buf;
+ }
+
+ D_NOTICE("%"PRIu32": XID %"PRIu32" mapped to SID %s.\n",
+ i, state->xids[i].id, str);
+ result = talloc_asprintf_append_buffer(
+ result, "%s\n", str);
+ if (result == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ }
+
+ response->extra_data.data = result;
+ response->length += talloc_get_size(result);
+
+ return NT_STATUS_OK;
+}
diff --git a/source3/winbindd/wscript_build b/source3/winbindd/wscript_build
new file mode 100644
index 0000000..4c85876
--- /dev/null
+++ b/source3/winbindd/wscript_build
@@ -0,0 +1,291 @@
+#!/usr/bin/env python
+
+bld.SAMBA3_LIBRARY('idmap',
+ source='idmap.c idmap_util.c',
+ deps='samba-util pdb',
+ allow_undefined_symbols=True,
+ private_library=True)
+
+bld.SAMBA3_SUBSYSTEM('IDMAP_RW',
+ source='idmap_rw.c',
+ deps='samba-util')
+
+bld.SAMBA3_SUBSYSTEM('IDMAP_TDB_COMMON',
+ source='idmap_tdb_common.c',
+ deps='tdb IDMAP_RW')
+
+bld.SAMBA3_SUBSYSTEM('IDMAP_HASH',
+ source='idmap_hash/idmap_hash.c idmap_hash/mapfile.c',
+ deps='samba-util krb5samba',
+ enabled=bld.SAMBA3_IS_ENABLED_MODULE('idmap_hash'))
+
+bld.SAMBA3_SUBSYSTEM('IDMAP_AD',
+ source='idmap_ad.c idmap_ad_nss.c',
+ deps='ads nss_info',
+ enabled=bld.SAMBA3_IS_ENABLED_MODULE('idmap_ad'))
+
+bld.SAMBA3_MODULE('idmap_ad',
+ subsystem='idmap',
+ allow_undefined_symbols=True,
+ source='',
+ deps='IDMAP_AD TLDAP LIBNMB',
+ init_function='',
+ internal_module=bld.SAMBA3_IS_STATIC_MODULE('idmap_ad'),
+ enabled=bld.SAMBA3_IS_ENABLED_MODULE('idmap_ad'))
+
+bld.SAMBA3_MODULE('idmap_rfc2307',
+ subsystem='idmap',
+ allow_undefined_symbols=True,
+ source='idmap_rfc2307.c',
+ init_function='',
+ deps='ads',
+ internal_module=bld.SAMBA3_IS_STATIC_MODULE('idmap_rfc2307'),
+ enabled=bld.SAMBA3_IS_ENABLED_MODULE('idmap_rfc2307'))
+
+bld.SAMBA3_MODULE('idmap_rid',
+ subsystem='idmap',
+ allow_undefined_symbols=True,
+ source='idmap_rid.c',
+ init_function='',
+ internal_module=bld.SAMBA3_IS_STATIC_MODULE('idmap_rid'),
+ enabled=bld.SAMBA3_IS_ENABLED_MODULE('idmap_rid'))
+
+bld.SAMBA3_MODULE('idmap_passdb',
+ subsystem='idmap',
+ source='idmap_passdb.c',
+ deps='samba-util samba-passdb',
+ init_function='',
+ internal_module=bld.SAMBA3_IS_STATIC_MODULE('idmap_passdb'),
+ enabled=bld.SAMBA3_IS_ENABLED_MODULE('idmap_passdb'))
+
+bld.SAMBA3_MODULE('idmap_ldap',
+ subsystem='idmap',
+ source='idmap_ldap.c',
+ deps='smbldap smbldaphelper pdb IDMAP_RW',
+ init_function='',
+ internal_module=bld.SAMBA3_IS_STATIC_MODULE('idmap_ldap'),
+ enabled=bld.SAMBA3_IS_ENABLED_MODULE('idmap_ldap') and bld.CONFIG_SET("HAVE_LDAP"),
+ allow_undefined_symbols=True)
+
+bld.SAMBA3_MODULE('idmap_nss',
+ subsystem='idmap',
+ source='idmap_nss.c',
+ deps='samba-util',
+ init_function='',
+ internal_module=bld.SAMBA3_IS_STATIC_MODULE('idmap_nss'),
+ enabled=bld.SAMBA3_IS_ENABLED_MODULE('idmap_nss'))
+
+bld.SAMBA3_MODULE('idmap_tdb',
+ subsystem='idmap',
+ source='idmap_tdb.c',
+ deps='samba-util tdb IDMAP_TDB_COMMON',
+ init_function='',
+ allow_undefined_symbols=True,
+ internal_module=bld.SAMBA3_IS_STATIC_MODULE('idmap_tdb'),
+ enabled=bld.SAMBA3_IS_ENABLED_MODULE('idmap_tdb'))
+
+bld.SAMBA3_MODULE('idmap_tdb2',
+ subsystem='idmap',
+ source='idmap_tdb2.c',
+ deps='samba-util tdb IDMAP_TDB_COMMON',
+ init_function='',
+ internal_module=bld.SAMBA3_IS_STATIC_MODULE('idmap_tdb2'),
+ enabled=bld.SAMBA3_IS_ENABLED_MODULE('idmap_tdb2'))
+
+bld.SAMBA3_MODULE('idmap_hash',
+ subsystem='idmap',
+ source='',
+ deps='IDMAP_HASH',
+ allow_undefined_symbols=True,
+ init_function='',
+ internal_module=bld.SAMBA3_IS_STATIC_MODULE('idmap_hash'),
+ enabled=bld.SAMBA3_IS_ENABLED_MODULE('idmap_hash'))
+
+bld.SAMBA3_SUBSYSTEM('IDMAP_AUTORID_TDB',
+ source='idmap_autorid_tdb.c',
+ deps='tdb')
+
+bld.SAMBA3_MODULE('idmap_autorid',
+ subsystem='idmap',
+ source='idmap_autorid.c',
+ deps='samba-util tdb IDMAP_TDB_COMMON IDMAP_AUTORID_TDB',
+ init_function='',
+ internal_module=bld.SAMBA3_IS_STATIC_MODULE('idmap_autorid'),
+ enabled=bld.SAMBA3_IS_ENABLED_MODULE('idmap_autorid'),
+ allow_undefined_symbols=True)
+
+bld.SAMBA3_LIBRARY('nss_info',
+ source='nss_info.c',
+ deps='samba-util smbconf samba-modules',
+ private_library=True)
+
+bld.SAMBA3_MODULE('nss_info_template',
+ subsystem='nss_info',
+ source='nss_info_template.c',
+ deps='samba-util krb5samba',
+ init_function='',
+ internal_module=bld.SAMBA3_IS_STATIC_MODULE('nss_info_template'),
+ enabled=bld.SAMBA3_IS_ENABLED_MODULE('nss_info_template'))
+
+bld.SAMBA3_MODULE('nss_info_hash',
+ subsystem='nss_info',
+ source='',
+ deps='IDMAP_HASH',
+ allow_undefined_symbols=True,
+ init_function='',
+ internal_module=bld.SAMBA3_IS_STATIC_MODULE('idmap_hash'),
+ enabled=bld.SAMBA3_IS_ENABLED_MODULE('idmap_hash'))
+
+bld.SAMBA3_MODULE('nss_info_rfc2307',
+ subsystem='nss_info',
+ source='',
+ deps='IDMAP_AD',
+ allow_undefined_symbols=True,
+ init_function='',
+ internal_module=bld.SAMBA3_IS_STATIC_MODULE('idmap_ad'),
+ enabled=bld.SAMBA3_IS_ENABLED_MODULE('idmap_ad'))
+
+bld.SAMBA3_MODULE('nss_info_sfu20',
+ subsystem='nss_info',
+ source='',
+ deps='IDMAP_AD',
+ allow_undefined_symbols=True,
+ init_function='',
+ internal_module=bld.SAMBA3_IS_STATIC_MODULE('idmap_ad'),
+ enabled=bld.SAMBA3_IS_ENABLED_MODULE('idmap_ad'))
+
+bld.SAMBA3_MODULE('nss_info_sfu',
+ subsystem='nss_info',
+ source='',
+ deps='IDMAP_AD',
+ allow_undefined_symbols=True,
+ init_function='',
+ internal_module=bld.SAMBA3_IS_STATIC_MODULE('idmap_ad'),
+ enabled=bld.SAMBA3_IS_ENABLED_MODULE('idmap_ad') and bld.CONFIG_SET("HAVE_LDAP"))
+
+bld.SAMBA3_MODULE('idmap_script',
+ subsystem='idmap',
+ allow_undefined_symbols=True,
+ source='idmap_script.c',
+ init_function='',
+ internal_module=bld.SAMBA3_IS_STATIC_MODULE('idmap_script'),
+ enabled=bld.SAMBA3_IS_ENABLED_MODULE('idmap_script'))
+
+bld.SAMBA3_SUBSYSTEM('winbindd-lib',
+ source='''
+ winbindd_group.c
+ winbindd_util.c
+ winbindd_cache.c
+ winbindd_pam.c
+ winbindd_misc.c
+ winbindd_cm.c
+ winbindd_wins_byip.c
+ winbindd_wins_byname.c
+ winbindd_msrpc.c
+ winbindd_rpc.c
+ winbindd_reconnect.c
+ winbindd_reconnect_ads.c
+ winbindd_ads.c
+ winbindd_samr.c
+ winbindd_dual.c
+ winbindd_dual_ndr.c
+ winbindd_dual_srv.c
+ winbindd_creds.c
+ winbindd_cred_cache.c
+ winbindd_ccache_access.c
+ winbindd_domain.c
+ winbindd_idmap.c
+ winbindd_locator.c
+ winbindd_ndr.c
+ winbindd_traceid.c
+ wb_lookupsid.c
+ wb_lookupsids.c
+ wb_lookupname.c
+ wb_sids2xids.c
+ wb_xids2sids.c
+ wb_queryuser.c
+ wb_lookupuseraliases.c
+ wb_lookupusergroups.c
+ wb_getpwsid.c
+ wb_gettoken.c
+ wb_seqnum.c
+ wb_seqnums.c
+ wb_group_members.c
+ wb_alias_members.c
+ wb_getgrsid.c
+ wb_query_user_list.c
+ wb_query_group_list.c
+ wb_next_pwent.c
+ wb_next_grent.c
+ wb_dsgetdcname.c
+ winbindd_lookupsid.c
+ winbindd_lookupsids.c
+ winbindd_lookupname.c
+ winbindd_sids_to_xids.c
+ winbindd_xids_to_sids.c
+ winbindd_allocate_uid.c
+ winbindd_allocate_gid.c
+ winbindd_getpwsid.c
+ winbindd_getpwnam.c
+ winbindd_getpwuid.c
+ winbindd_getsidaliases.c
+ winbindd_getuserdomgroups.c
+ winbindd_getgroups.c
+ winbindd_show_sequence.c
+ winbindd_getgrgid.c
+ winbindd_getgrnam.c
+ winbindd_getusersids.c
+ winbindd_lookuprids.c
+ winbindd_setpwent.c
+ winbindd_getpwent.c
+ winbindd_endpwent.c
+ winbindd_setgrent.c
+ winbindd_getgrent.c
+ winbindd_endgrent.c
+ winbindd_dsgetdcname.c
+ winbindd_getdcname.c
+ winbindd_list_users.c
+ winbindd_list_groups.c
+ winbindd_check_machine_acct.c
+ winbindd_change_machine_acct.c
+ winbindd_irpc.c
+ winbindd_ping_dc.c
+ winbindd_domain_info.c
+ winbindd_pam_auth.c
+ winbindd_pam_logoff.c
+ winbindd_pam_chauthtok.c
+ winbindd_pam_auth_crap.c
+ winbindd_pam_chng_pswd_auth_crap.c
+ winbindd_gpupdate.c''',
+ deps='''
+ talloc
+ tevent
+ pdb
+ idmap
+ ads
+ msrpc3
+ nss_info
+ LIBAFS
+ LIBADS_SERVER
+ LIBCLI_SAMR
+ SLCACHE
+ RPC_NDR_DSSETUP
+ RPC_NDR_WINBIND
+ TOKEN_UTIL
+ RPC_SERVER
+ WB_REQTRANS
+ TDB_VALIDATE
+ MESSAGING
+ LIBLSA
+ ''')
+
+bld.SAMBA3_BINARY('winbindd',
+ source='''
+ winbindd.c
+ ''',
+ deps='''
+ CMDLINE_S3
+ winbindd-lib
+ ''',
+ enabled=bld.env.build_winbind,
+ install_path='${SBINDIR}')