/* Lookup user information from a rid */ static NTSTATUS query_user(struct winbindd_domain *domain, TALLOC_CTX *mem_ctx, const DOM_SID *user_sid, WINBIND_USERINFO *info) { struct winbind_cache *cache = get_cache(domain); struct cache_entry *centry = NULL; NTSTATUS status; if (!cache->tdb) goto do_query; centry = wcache_fetch(cache, domain, "U/%s", sid_string_static(user_sid)); /* If we have an access denied cache entry and a cached info3 in the samlogon cache then do a query. This will force the rpc back end to return the info3 data. */ if (NT_STATUS_V(domain->last_status) == NT_STATUS_V(NT_STATUS_ACCESS_DENIED) && netsamlogon_cache_have(user_sid)) { DEBUG(10, ("query_user: cached access denied and have cached info3\n")); domain->last_status = NT_STATUS_OK; centry_free(centry); goto do_query; } if (!centry) goto do_query; info->acct_name = centry_string(centry, mem_ctx); info->full_name = centry_string(centry, mem_ctx); info->user_sid = centry_sid(centry, mem_ctx); info->group_sid = centry_sid(centry, mem_ctx); status = centry->status; DEBUG(10,("query_user: [Cached] - cached info for domain %s status %s\n", domain->name, get_friendly_nt_error_msg(status) )); centry_free(centry); return status; do_query: ZERO_STRUCTP(info); /* Return status value returned by seq number check */ if (!NT_STATUS_IS_OK(domain->last_status)) return domain->last_status; DEBUG(10,("sid_to_name: [Cached] - doing backend query for info for domain %s\n", domain->name )); status = domain->backend->query_user(domain, mem_ctx, user_sid, info); /* and save it */ refresh_sequence_number(domain, False); wcache_save_user(domain, status, info); return status; }
/* convert a sid to a user or group name. The sid is guaranteed to be in the domain given */ static NTSTATUS sid_to_name(struct winbindd_domain *domain, TALLOC_CTX *mem_ctx, const DOM_SID *sid, char **domain_name, char **name, enum SID_NAME_USE *type) { struct winbind_cache *cache = get_cache(domain); struct cache_entry *centry = NULL; NTSTATUS status; fstring sid_string; if (!cache->tdb) goto do_query; centry = wcache_fetch(cache, domain, "SN/%s", sid_to_string(sid_string, sid)); if (!centry) goto do_query; if (NT_STATUS_IS_OK(centry->status)) { *type = (enum SID_NAME_USE)centry_uint32(centry); *domain_name = centry_string(centry, mem_ctx); *name = centry_string(centry, mem_ctx); } status = centry->status; DEBUG(10,("sid_to_name: [Cached] - cached name for domain %s status %s\n", domain->name, get_friendly_nt_error_msg(status) )); centry_free(centry); return status; do_query: *name = NULL; *domain_name = NULL; /* If the seq number check indicated that there is a problem * with this DC, then return that status... except for * access_denied. This is special because the dc may be in * "restrict anonymous = 1" mode, in which case it will deny * most unauthenticated operations, but *will* allow the LSA * sid-to-name that we try as a fallback. */ if (!(NT_STATUS_IS_OK(domain->last_status) || NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED))) return domain->last_status; DEBUG(10,("sid_to_name: [Cached] - doing backend query for name for domain %s\n", domain->name )); status = domain->backend->sid_to_name(domain, mem_ctx, sid, domain_name, name, type); /* and save it */ refresh_sequence_number(domain, False); wcache_save_sid_to_name(domain, status, sid, *domain_name, *name, *type); wcache_save_name_to_sid(domain, status, *domain_name, *name, sid, *type); return status; }
static void wcache_save_user(struct winbindd_domain *domain, NTSTATUS status, WINBIND_USERINFO *info) { struct cache_entry *centry; fstring sid_string; centry = centry_start(domain, status); if (!centry) return; centry_put_string(centry, info->acct_name); centry_put_string(centry, info->full_name); centry_put_sid(centry, info->user_sid); centry_put_sid(centry, info->group_sid); centry_end(centry, "U/%s", sid_to_string(sid_string, info->user_sid)); DEBUG(10,("wcache_save_user: %s (acct_name %s)\n", sid_string, info->acct_name)); centry_free(centry); }
static void wcache_save_sid_to_name(struct winbindd_domain *domain, NTSTATUS status, const DOM_SID *sid, const char *name, enum SID_NAME_USE type) { struct cache_entry *centry; fstring sid_string; centry = centry_start(domain, status); if (!centry) return; if (NT_STATUS_IS_OK(status)) { centry_put_uint32(centry, type); centry_put_string(centry, name); } centry_end(centry, "SN/%s", sid_to_string(sid_string, sid)); DEBUG(10,("wcache_save_sid_to_name: %s -> %s\n", sid_string, name)); centry_free(centry); }
static void wcache_save_name_to_sid(struct winbindd_domain *domain, NTSTATUS status, const char *name, const DOM_SID *sid, enum SID_NAME_USE type) { struct cache_entry *centry; fstring uname; fstring sid_string; centry = centry_start(domain, status); if (!centry) return; centry_put_sid(centry, sid); fstrcpy(uname, name); strupper_m(uname); centry_end(centry, "NS/%s", sid_to_string(sid_string, sid)); DEBUG(10,("wcache_save_name_to_sid: %s -> %s\n", uname, sid_string)); centry_free(centry); }
/* 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, DOM_SID *sid, enum SID_NAME_USE *type) { struct winbind_cache *cache = get_cache(domain); struct cache_entry *centry = NULL; NTSTATUS status; fstring uname; DOM_SID *sid2; if (!cache->tdb) goto do_query; fstrcpy(uname, name); strupper_m(uname); centry = wcache_fetch(cache, domain, "NS/%s/%s", domain_name, uname); if (!centry) goto do_query; *type = (enum SID_NAME_USE)centry_uint32(centry); sid2 = centry_sid(centry, mem_ctx); if (!sid2) { ZERO_STRUCTP(sid); } else { sid_copy(sid, sid2); } status = centry->status; DEBUG(10,("name_to_sid: [Cached] - cached name for domain %s status %s\n", domain->name, get_friendly_nt_error_msg(status) )); centry_free(centry); return status; do_query: ZERO_STRUCTP(sid); /* If the seq number check indicated that there is a problem * with this DC, then return that status... except for * access_denied. This is special because the dc may be in * "restrict anonymous = 1" mode, in which case it will deny * most unauthenticated operations, but *will* allow the LSA * name-to-sid that we try as a fallback. */ if (!(NT_STATUS_IS_OK(domain->last_status) || NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED))) return domain->last_status; DEBUG(10,("name_to_sid: [Cached] - doing backend query for name for domain %s\n", domain->name )); status = domain->backend->name_to_sid(domain, mem_ctx, domain_name, name, sid, type); /* and save it */ wcache_save_name_to_sid(domain, status, domain_name, name, sid, *type); /* We can't save the sid to name mapping as we don't know the correct case of the name without looking it up */ return status; }
/* list all domain groups */ static NTSTATUS enum_local_groups(struct winbindd_domain *domain, TALLOC_CTX *mem_ctx, uint32 *num_entries, struct acct_info **info) { struct winbind_cache *cache = get_cache(domain); struct cache_entry *centry = NULL; NTSTATUS status; unsigned int i; if (!cache->tdb) goto do_query; centry = wcache_fetch(cache, domain, "GL/%s/local", domain->name); if (!centry) goto do_query; *num_entries = centry_uint32(centry); if (*num_entries == 0) goto do_cached; (*info) = talloc(mem_ctx, sizeof(**info) * (*num_entries)); if (! (*info)) smb_panic("enum_dom_groups out of memory"); for (i=0; i<(*num_entries); i++) { fstrcpy((*info)[i].acct_name, centry_string(centry, mem_ctx)); fstrcpy((*info)[i].acct_desc, centry_string(centry, mem_ctx)); (*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)) { DEBUG(10, ("enum_local_groups: returning cached user list and server was down\n")); status = NT_STATUS_MORE_PROCESSING_REQUIRED; } else status = centry->status; DEBUG(10,("enum_local_groups: [Cached] - cached list for domain %s status %s\n", domain->name, get_friendly_nt_error_msg(status) )); centry_free(centry); return status; do_query: *num_entries = 0; *info = NULL; /* Return status value returned by seq number check */ if (!NT_STATUS_IS_OK(domain->last_status)) return domain->last_status; DEBUG(10,("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); /* and save it */ refresh_sequence_number(domain, False); 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; }
/* list all domain groups */ static NTSTATUS enum_dom_groups(struct winbindd_domain *domain, TALLOC_CTX *mem_ctx, uint32 *num_entries, struct acct_info **info) { struct winbind_cache *cache = get_cache(domain); struct cache_entry *centry = NULL; NTSTATUS status; unsigned int i; if (!cache->tdb) goto do_query; centry = wcache_fetch(cache, domain, "GL/%s/domain", domain->name); if (!centry) goto do_query; *num_entries = centry_uint32(centry); if (*num_entries == 0) goto do_cached; (*info) = talloc(mem_ctx, sizeof(**info) * (*num_entries)); if (! (*info)) smb_panic("enum_dom_groups out of memory"); for (i=0; i<(*num_entries); i++) { fstrcpy((*info)[i].acct_name, centry_string(centry, mem_ctx)); fstrcpy((*info)[i].acct_desc, centry_string(centry, mem_ctx)); (*info)[i].rid = centry_uint32(centry); } do_cached: status = centry->status; DEBUG(10,("enum_dom_groups: [Cached] - cached list for domain %s status %s\n", domain->name, get_friendly_nt_error_msg(status) )); centry_free(centry); return status; do_query: *num_entries = 0; *info = NULL; /* Return status value returned by seq number check */ if (!NT_STATUS_IS_OK(domain->last_status)) return domain->last_status; DEBUG(10,("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); /* and save it */ refresh_sequence_number(domain, False); 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; }
/* Query display info. This is the basic user list fn */ static NTSTATUS query_user_list(struct winbindd_domain *domain, TALLOC_CTX *mem_ctx, uint32 *num_entries, WINBIND_USERINFO **info) { struct winbind_cache *cache = get_cache(domain); struct cache_entry *centry = NULL; NTSTATUS status; unsigned int i, retry; if (!cache->tdb) goto do_query; centry = wcache_fetch(cache, domain, "UL/%s", domain->name); if (!centry) goto do_query; *num_entries = centry_uint32(centry); if (*num_entries == 0) goto do_cached; (*info) = talloc(mem_ctx, sizeof(**info) * (*num_entries)); if (! (*info)) smb_panic("query_user_list out of memory"); for (i=0; i<(*num_entries); i++) { (*info)[i].acct_name = centry_string(centry, mem_ctx); (*info)[i].full_name = centry_string(centry, mem_ctx); (*info)[i].user_sid = centry_sid(centry, mem_ctx); (*info)[i].group_sid = centry_sid(centry, mem_ctx); } do_cached: status = centry->status; DEBUG(10,("query_user_list: [Cached] - cached list for domain %s status %s\n", domain->name, get_friendly_nt_error_msg(status) )); centry_free(centry); return status; do_query: *num_entries = 0; *info = NULL; /* Return status value returned by seq number check */ if (!NT_STATUS_IS_OK(domain->last_status)) return domain->last_status; /* 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 { DEBUG(10,("query_user_list: [Cached] - doing backend query for list for domain %s\n", domain->name )); status = domain->backend->query_user_list(domain, mem_ctx, num_entries, info); if (!NT_STATUS_IS_OK(status)) DEBUG(3, ("query_user_list: returned 0x%08x, retrying\n", NT_STATUS_V(status))); if (NT_STATUS_V(status) == NT_STATUS_V(NT_STATUS_UNSUCCESSFUL)) { DEBUG(3, ("query_user_list: flushing connection cache\n")); winbindd_cm_flush(); } } while (NT_STATUS_V(status) == NT_STATUS_V(NT_STATUS_UNSUCCESSFUL) && (retry++ < 5)); /* and save it */ refresh_sequence_number(domain, False); 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].full_name); centry_put_sid(centry, (*info)[i].user_sid); centry_put_sid(centry, (*info)[i].group_sid); if (domain->backend->consistent) { /* when the backend is consistent we can pre-prime some mappings */ wcache_save_name_to_sid(domain, NT_STATUS_OK, (*info)[i].acct_name, domain->name, (*info)[i].user_sid, SID_NAME_USER); wcache_save_sid_to_name(domain, NT_STATUS_OK, (*info)[i].user_sid, domain->name, (*info)[i].acct_name, SID_NAME_USER); wcache_save_user(domain, NT_STATUS_OK, &(*info)[i]); } } centry_end(centry, "UL/%s", domain->name); centry_free(centry); skip_save: return status; }
static struct cache_entry *wcache_fetch(struct winbind_cache *cache, struct winbindd_domain *domain, const char *format, ...) { va_list ap; char *kstr; TDB_DATA data; struct cache_entry *centry; TDB_DATA key; refresh_sequence_number(domain, False); va_start(ap, format); smb_xvasprintf(&kstr, format, ap); va_end(ap); key.dptr = kstr; key.dsize = strlen(kstr); data = tdb_fetch(wcache->tdb, key); if (!data.dptr) { /* a cache miss */ free(kstr); return NULL; } centry = smb_xmalloc(sizeof(*centry)); centry->data = (unsigned char *)data.dptr; centry->len = data.dsize; centry->ofs = 0; if (centry->len < 8) { /* huh? corrupt cache? */ DEBUG(10,("wcache_fetch: Corrupt cache for key %s domain %s (len < 8) ?\n", kstr, domain->name )); centry_free(centry); free(kstr); return NULL; } centry->status = NT_STATUS(centry_uint32(centry)); centry->sequence_number = centry_uint32(centry); if (centry_expired(domain, kstr, centry)) { extern BOOL opt_dual_daemon; DEBUG(10,("wcache_fetch: entry %s expired for domain %s\n", kstr, domain->name )); if (opt_dual_daemon) { extern BOOL background_process; background_process = True; DEBUG(10,("wcache_fetch: background processing expired entry %s for domain %s\n", kstr, domain->name )); } else { centry_free(centry); free(kstr); return NULL; } } DEBUG(10,("wcache_fetch: returning entry %s for domain %s\n", kstr, domain->name )); free(kstr); return centry; }
static NTSTATUS lookup_groupmem(struct winbindd_domain *domain, TALLOC_CTX *mem_ctx, const DOM_SID *group_sid, uint32 *num_names, DOM_SID ***sid_mem, char ***names, uint32 **name_types) { struct winbind_cache *cache = get_cache(domain); struct cache_entry *centry = NULL; NTSTATUS status; unsigned int i; fstring sid_string; if (!cache->tdb) goto do_query; centry = wcache_fetch(cache, domain, "GM/%s", sid_to_string(sid_string, group_sid)); if (!centry) goto do_query; *num_names = centry_uint32(centry); if (*num_names == 0) goto do_cached; (*sid_mem) = talloc(mem_ctx, sizeof(**sid_mem) * (*num_names)); (*names) = talloc(mem_ctx, sizeof(**names) * (*num_names)); (*name_types) = talloc(mem_ctx, sizeof(**name_types) * (*num_names)); if (! (*sid_mem) || ! (*names) || ! (*name_types)) { smb_panic("lookup_groupmem out of memory"); } for (i=0; i<(*num_names); i++) { (*sid_mem)[i] = centry_sid(centry, mem_ctx); (*names)[i] = centry_string(centry, mem_ctx); (*name_types)[i] = centry_uint32(centry); } do_cached: status = centry->status; DEBUG(10,("lookup_groupmem: [Cached] - cached info for domain %s status %s\n", domain->name, get_friendly_nt_error_msg(status) )); centry_free(centry); return status; do_query: (*num_names) = 0; (*sid_mem) = NULL; (*names) = NULL; (*name_types) = NULL; /* Return status value returned by seq number check */ if (!NT_STATUS_IS_OK(domain->last_status)) return domain->last_status; DEBUG(10,("lookup_groupmem: [Cached] - doing backend query for info for domain %s\n", domain->name )); status = domain->backend->lookup_groupmem(domain, mem_ctx, group_sid, num_names, sid_mem, names, name_types); /* and save it */ refresh_sequence_number(domain, False); 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", sid_to_string(sid_string, group_sid)); centry_free(centry); skip_save: return status; }
/* Lookup groups a user is a member of. */ static NTSTATUS lookup_usergroups(struct winbindd_domain *domain, TALLOC_CTX *mem_ctx, const DOM_SID *user_sid, uint32 *num_groups, DOM_SID ***user_gids) { struct winbind_cache *cache = get_cache(domain); struct cache_entry *centry = NULL; NTSTATUS status; unsigned int i; fstring sid_string; if (!cache->tdb) goto do_query; centry = wcache_fetch(cache, domain, "UG/%s", sid_to_string(sid_string, user_sid)); /* If we have an access denied cache entry and a cached info3 in the samlogon cache then do a query. This will force the rpc back end to return the info3 data. */ if (NT_STATUS_V(domain->last_status) == NT_STATUS_V(NT_STATUS_ACCESS_DENIED) && netsamlogon_cache_have(user_sid)) { DEBUG(10, ("query_user: cached access denied and have cached info3\n")); domain->last_status = NT_STATUS_OK; centry_free(centry); goto do_query; } if (!centry) goto do_query; *num_groups = centry_uint32(centry); if (*num_groups == 0) goto do_cached; (*user_gids) = talloc(mem_ctx, sizeof(**user_gids) * (*num_groups)); if (! (*user_gids)) smb_panic("lookup_usergroups out of memory"); for (i=0; i<(*num_groups); i++) { (*user_gids)[i] = centry_sid(centry, mem_ctx); } do_cached: status = centry->status; DEBUG(10,("lookup_usergroups: [Cached] - cached info for domain %s status %s\n", domain->name, get_friendly_nt_error_msg(status) )); centry_free(centry); return status; do_query: (*num_groups) = 0; (*user_gids) = NULL; /* Return status value returned by seq number check */ if (!NT_STATUS_IS_OK(domain->last_status)) return domain->last_status; DEBUG(10,("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); /* and save it */ refresh_sequence_number(domain, False); 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", sid_to_string(sid_string, user_sid)); centry_free(centry); skip_save: return status; }