/* 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 DOM_SID *user_sid, uint32 *num_groups, DOM_SID ***user_grpsids) { CLI_POLICY_HND *hnd; NTSTATUS result = NT_STATUS_UNSUCCESSFUL; POLICY_HND dom_pol, user_pol; uint32 des_access = SEC_RIGHTS_MAXIMUM_ALLOWED; BOOL got_dom_pol = False, got_user_pol = False; DOM_GID *user_groups; unsigned int i; unsigned int retry; fstring sid_string; uint32 user_rid; NET_USER_INFO_3 *user; DEBUG(3,("rpc: lookup_usergroups sid=%s\n", sid_to_string(sid_string, user_sid))); *num_groups = 0; *user_grpsids = NULL; /* so lets see if we have a cached user_info_3 */ if ( (user = netsamlogon_cache_get( mem_ctx, user_sid )) != NULL ) { DEBUG(5,("query_user: Cache lookup succeeded for %s\n", sid_string_static(user_sid))); *num_groups = user->num_groups; (*user_grpsids) = TALLOC_ARRAY(mem_ctx, DOM_SID*, *num_groups); for (i=0;i<(*num_groups);i++) { (*user_grpsids)[i] = rid_to_talloced_sid(domain, mem_ctx, user->gids[i].g_rid); } SAFE_FREE(user); return NT_STATUS_OK; }
/* 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 DOM_SID *user_sid, uint32 *num_groups, DOM_SID ***user_grpsids) { CLI_POLICY_HND *hnd; NTSTATUS result = NT_STATUS_UNSUCCESSFUL; POLICY_HND dom_pol, user_pol; uint32 des_access = SEC_RIGHTS_MAXIMUM_ALLOWED; BOOL got_dom_pol = False, got_user_pol = False; DOM_GID *user_groups; unsigned int i; unsigned int retry; fstring sid_string; uint32 user_rid; NET_USER_INFO_3 *user; DEBUG(3,("rpc: lookup_usergroups sid=%s\n", sid_to_string(sid_string, user_sid))); *num_groups = 0; *user_grpsids = NULL; /* so lets see if we have a cached user_info_3 */ if ( (user = netsamlogon_cache_get( mem_ctx, user_sid )) != NULL ) { DEBUG(5,("query_user: Cache lookup succeeded for %s\n", sid_string_static(user_sid))); *num_groups = user->num_groups; (*user_grpsids) = talloc(mem_ctx, sizeof(DOM_SID*) * (*num_groups)); for (i=0;i<(*num_groups);i++) { (*user_grpsids)[i] = rid_to_talloced_sid(domain, mem_ctx, user->gids[i].g_rid); } SAFE_FREE(user); return NT_STATUS_OK; } /* no cache; hit the wire */ retry = 0; do { /* Get sam handle; if we fail here there is no hope */ if (!NT_STATUS_IS_OK(result = cm_get_sam_handle(domain, &hnd))) goto done; /* Get domain handle */ result = cli_samr_open_domain(hnd->cli, mem_ctx, &hnd->pol, des_access, &domain->sid, &dom_pol); } while (!NT_STATUS_IS_OK(result) && (retry++ < 1) && hnd && hnd->cli && hnd->cli->fd == -1); if (!NT_STATUS_IS_OK(result)) goto done; got_dom_pol = True; if (!sid_peek_check_rid(&domain->sid, user_sid, &user_rid)) { goto done; } /* Get user handle */ result = cli_samr_open_user(hnd->cli, mem_ctx, &dom_pol, des_access, user_rid, &user_pol); if (!NT_STATUS_IS_OK(result)) goto done; got_user_pol = True; /* Query user rids */ result = cli_samr_query_usergroups(hnd->cli, mem_ctx, &user_pol, num_groups, &user_groups); if (!NT_STATUS_IS_OK(result) || (*num_groups) == 0) goto done; (*user_grpsids) = talloc(mem_ctx, sizeof(DOM_SID*) * (*num_groups)); if (!(*user_grpsids)) { result = NT_STATUS_NO_MEMORY; goto done; } for (i=0;i<(*num_groups);i++) { (*user_grpsids)[i] = rid_to_talloced_sid(domain, mem_ctx, user_groups[i].g_rid); } done: /* Clean up policy handles */ if (got_user_pol) cli_samr_close(hnd->cli, mem_ctx, &user_pol); if (got_dom_pol) cli_samr_close(hnd->cli, mem_ctx, &dom_pol); return result; }
/* Lookup user information from a rid or username. */ static NTSTATUS query_user(struct winbindd_domain *domain, TALLOC_CTX *mem_ctx, const DOM_SID *user_sid, WINBIND_USERINFO *user_info) { CLI_POLICY_HND *hnd = NULL; NTSTATUS result = NT_STATUS_UNSUCCESSFUL; POLICY_HND dom_pol, user_pol; BOOL got_dom_pol = False, got_user_pol = False; SAM_USERINFO_CTR *ctr; int retry; fstring sid_string; uint32 user_rid; NET_USER_INFO_3 *user; DEBUG(3,("rpc: query_user rid=%s\n", sid_to_string(sid_string, user_sid))); if (!sid_peek_check_rid(&domain->sid, user_sid, &user_rid)) { goto done; } /* try netsamlogon cache first */ if ( (user = netsamlogon_cache_get( mem_ctx, user_sid )) != NULL ) { DEBUG(5,("query_user: Cache lookup succeeded for %s\n", sid_string_static(user_sid))); user_info->user_sid = rid_to_talloced_sid( domain, mem_ctx, user_rid ); user_info->group_sid = rid_to_talloced_sid( domain, mem_ctx, user->group_rid ); user_info->acct_name = unistr2_tdup(mem_ctx, &user->uni_user_name); user_info->full_name = unistr2_tdup(mem_ctx, &user->uni_full_name); SAFE_FREE(user); return NT_STATUS_OK; } /* no cache; hit the wire */ retry = 0; do { /* Get sam handle; if we fail here there is no hope */ if (!NT_STATUS_IS_OK(result = cm_get_sam_handle(domain, &hnd))) goto done; /* Get domain handle */ result = cli_samr_open_domain(hnd->cli, mem_ctx, &hnd->pol, SEC_RIGHTS_MAXIMUM_ALLOWED, &domain->sid, &dom_pol); } while (!NT_STATUS_IS_OK(result) && (retry++ < 1) && hnd && hnd->cli && hnd->cli->fd == -1); if (!NT_STATUS_IS_OK(result)) goto done; got_dom_pol = True; /* Get user handle */ result = cli_samr_open_user(hnd->cli, mem_ctx, &dom_pol, SEC_RIGHTS_MAXIMUM_ALLOWED, user_rid, &user_pol); if (!NT_STATUS_IS_OK(result)) goto done; got_user_pol = True; /* Get user info */ result = cli_samr_query_userinfo(hnd->cli, mem_ctx, &user_pol, 0x15, &ctr); if (!NT_STATUS_IS_OK(result)) goto done; cli_samr_close(hnd->cli, mem_ctx, &user_pol); got_user_pol = False; user_info->user_sid = rid_to_talloced_sid(domain, mem_ctx, user_rid); user_info->group_sid = rid_to_talloced_sid(domain, mem_ctx, ctr->info.id21->group_rid); user_info->acct_name = unistr2_tdup(mem_ctx, &ctr->info.id21->uni_user_name); user_info->full_name = unistr2_tdup(mem_ctx, &ctr->info.id21->uni_full_name); done: /* Clean up policy handles */ if (got_user_pol) cli_samr_close(hnd->cli, mem_ctx, &user_pol); if (got_dom_pol) cli_samr_close(hnd->cli, mem_ctx, &dom_pol); return result; }
/* Lookup user information from a rid */ static NTSTATUS query_user(struct winbindd_domain *domain, TALLOC_CTX *mem_ctx, const struct dom_sid *sid, struct wbint_userinfo *info) { ADS_STRUCT *ads = NULL; const char *attrs[] = { "*", NULL }; ADS_STATUS rc; int count; LDAPMessage *msg = NULL; char *ldap_exp; char *sidstr; uint32 group_rid; NTSTATUS status = NT_STATUS_UNSUCCESSFUL; struct netr_SamInfo3 *user = NULL; gid_t gid = -1; int ret; char *ads_name; DEBUG(3,("ads: query_user\n")); info->homedir = NULL; info->shell = NULL; /* try netsamlogon cache first */ if (winbindd_use_cache() && (user = netsamlogon_cache_get( mem_ctx, sid )) != NULL ) { DEBUG(5,("query_user: Cache lookup succeeded for %s\n", sid_string_dbg(sid))); sid_compose(&info->user_sid, &domain->sid, user->base.rid); sid_compose(&info->group_sid, &domain->sid, user->base.primary_gid); info->acct_name = talloc_strdup(mem_ctx, user->base.account_name.string); info->full_name = talloc_strdup(mem_ctx, user->base.full_name.string); nss_get_info_cached( domain, sid, mem_ctx, &info->homedir, &info->shell, &info->full_name, &gid ); info->primary_gid = gid; TALLOC_FREE(user); return NT_STATUS_OK; } if ( !winbindd_can_contact_domain(domain)) { DEBUG(8,("query_user: No incoming trust from domain %s\n", domain->name)); /* We still need to generate some basic information about the user even if we cannot contact the domain. Most of this stuff we can deduce. */ sid_copy( &info->user_sid, sid ); /* Assume "Domain Users" for the primary group */ sid_compose(&info->group_sid, &domain->sid, DOMAIN_RID_USERS ); /* Try to fill in what the nss_info backend can do */ nss_get_info_cached( domain, sid, mem_ctx, &info->homedir, &info->shell, &info->full_name, &gid); info->primary_gid = gid; return NT_STATUS_OK; } /* no cache...do the query */ if ( (ads = ads_cached_connection(domain)) == NULL ) { domain->last_status = NT_STATUS_SERVER_DISABLED; return NT_STATUS_SERVER_DISABLED; } sidstr = ldap_encode_ndr_dom_sid(talloc_tos(), sid); ret = asprintf(&ldap_exp, "(objectSid=%s)", sidstr); TALLOC_FREE(sidstr); if (ret == -1) { return NT_STATUS_NO_MEMORY; } rc = ads_search_retry(ads, &msg, ldap_exp, attrs); SAFE_FREE(ldap_exp); if (!ADS_ERR_OK(rc) || !msg) { DEBUG(1,("query_user(sid=%s) ads_search: %s\n", sid_string_dbg(sid), ads_errstr(rc))); return ads_ntstatus(rc); } count = ads_count_replies(ads, msg); if (count != 1) { DEBUG(1,("query_user(sid=%s): Not found\n", sid_string_dbg(sid))); ads_msgfree(ads, msg); return NT_STATUS_NO_SUCH_USER; } info->acct_name = ads_pull_username(ads, mem_ctx, msg); if (!ads_pull_uint32(ads, msg, "primaryGroupID", &group_rid)) { DEBUG(1,("No primary group for %s !?\n", sid_string_dbg(sid))); ads_msgfree(ads, msg); return NT_STATUS_NO_SUCH_USER; } sid_copy(&info->user_sid, sid); sid_compose(&info->group_sid, &domain->sid, group_rid); /* * We have to fetch the "name" attribute before doing the * nss_get_info_cached call. nss_get_info_cached might destroy * the ads struct, potentially invalidating the ldap message. */ ads_name = ads_pull_string(ads, mem_ctx, msg, "name"); ads_msgfree(ads, msg); msg = NULL; status = nss_get_info_cached( domain, sid, mem_ctx, &info->homedir, &info->shell, &info->full_name, &gid); info->primary_gid = gid; if (!NT_STATUS_IS_OK(status)) { DEBUG(1, ("nss_get_info_cached failed: %s\n", nt_errstr(status))); return status; } if (info->full_name == NULL) { info->full_name = ads_name; } else { TALLOC_FREE(ads_name); } status = NT_STATUS_OK; DEBUG(3,("ads query_user gave %s\n", info->acct_name)); return NT_STATUS_OK; }
bool netsamlogon_cache_store(const char *username, struct netr_SamInfo3 *info3) { uint8_t dummy = 0; TDB_DATA data = { .dptr = &dummy, .dsize = sizeof(dummy) }; char keystr[DOM_SID_STR_BUFLEN]; bool result = false; struct dom_sid user_sid; TALLOC_CTX *tmp_ctx = talloc_stackframe(); DATA_BLOB blob; enum ndr_err_code ndr_err; struct netsamlogoncache_entry r; int ret; if (!info3) { return false; } if (!netsamlogon_cache_init()) { DEBUG(0,("netsamlogon_cache_store: cannot open %s for write!\n", NETSAMLOGON_TDB)); return false; } /* * First write a record with just the domain sid for * netsamlogon_cache_domain_known. Use TDB_INSERT to avoid * overwriting potentially other data. We're just interested * in the existence of that record. */ dom_sid_string_buf(info3->base.domain_sid, keystr, sizeof(keystr)); ret = tdb_store_bystring(netsamlogon_tdb, keystr, data, TDB_INSERT); if ((ret == -1) && (tdb_error(netsamlogon_tdb) != TDB_ERR_EXISTS)) { DBG_WARNING("Could not store domain marker for %s: %s\n", keystr, tdb_errorstr(netsamlogon_tdb)); TALLOC_FREE(tmp_ctx); return false; } sid_compose(&user_sid, info3->base.domain_sid, info3->base.rid); /* Prepare key as DOMAIN-SID/USER-RID string */ dom_sid_string_buf(&user_sid, keystr, sizeof(keystr)); DEBUG(10,("netsamlogon_cache_store: SID [%s]\n", keystr)); /* Prepare data */ if (info3->base.full_name.string == NULL) { struct netr_SamInfo3 *cached_info3; const char *full_name = NULL; cached_info3 = netsamlogon_cache_get(tmp_ctx, &user_sid); if (cached_info3 != NULL) { full_name = cached_info3->base.full_name.string; } if (full_name != NULL) { info3->base.full_name.string = talloc_strdup(info3, full_name); } } /* only Samba fills in the username, not sure why NT doesn't */ /* so we fill it in since winbindd_getpwnam() makes use of it */ if (!info3->base.account_name.string) { info3->base.account_name.string = talloc_strdup(info3, username); } r.timestamp = time(NULL); r.info3 = *info3; if (DEBUGLEVEL >= 10) { NDR_PRINT_DEBUG(netsamlogoncache_entry, &r); } ndr_err = ndr_push_struct_blob(&blob, tmp_ctx, &r, (ndr_push_flags_fn_t)ndr_push_netsamlogoncache_entry); if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { DEBUG(0,("netsamlogon_cache_store: failed to push entry to cache\n")); TALLOC_FREE(tmp_ctx); return false; } data.dsize = blob.length; data.dptr = blob.data; if (tdb_store_bystring(netsamlogon_tdb, keystr, data, TDB_REPLACE) == 0) { result = true; } TALLOC_FREE(tmp_ctx); return result; } /*********************************************************************** Retrieves a netr_SamInfo3 structure from a tdb. Caller must free the user_info struct (talloced memory) ***********************************************************************/ struct netr_SamInfo3 *netsamlogon_cache_get(TALLOC_CTX *mem_ctx, const struct dom_sid *user_sid) { struct netr_SamInfo3 *info3 = NULL; TDB_DATA data; char keystr[DOM_SID_STR_BUFLEN]; enum ndr_err_code ndr_err; DATA_BLOB blob; struct netsamlogoncache_entry r; if (!netsamlogon_cache_init()) { DEBUG(0,("netsamlogon_cache_get: cannot open %s for write!\n", NETSAMLOGON_TDB)); return NULL; } /* Prepare key as DOMAIN-SID/USER-RID string */ dom_sid_string_buf(user_sid, keystr, sizeof(keystr)); DEBUG(10,("netsamlogon_cache_get: SID [%s]\n", keystr)); data = tdb_fetch_bystring( netsamlogon_tdb, keystr ); if (!data.dptr) { return NULL; } info3 = talloc_zero(mem_ctx, struct netr_SamInfo3); if (!info3) { goto done; } blob = data_blob_const(data.dptr, data.dsize); ndr_err = ndr_pull_struct_blob(&blob, mem_ctx, &r, (ndr_pull_flags_fn_t)ndr_pull_netsamlogoncache_entry); if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { DEBUG(0,("netsamlogon_cache_get: failed to pull entry from cache\n")); tdb_delete_bystring(netsamlogon_tdb, keystr); TALLOC_FREE(info3); goto done; } if (DEBUGLEVEL >= 10) { NDR_PRINT_DEBUG(netsamlogoncache_entry, &r); } info3 = (struct netr_SamInfo3 *)talloc_memdup(mem_ctx, &r.info3, sizeof(r.info3)); done: SAFE_FREE(data.dptr); return info3; } bool netsamlogon_cache_have(const struct dom_sid *sid) { char keystr[DOM_SID_STR_BUFLEN]; bool ok; if (!netsamlogon_cache_init()) { DBG_WARNING("Cannot open %s\n", NETSAMLOGON_TDB); return false; } dom_sid_string_buf(sid, keystr, sizeof(keystr)); ok = tdb_exists(netsamlogon_tdb, string_term_tdb_data(keystr)); return ok; }
/* Lookup user information from a rid */ static NTSTATUS query_user(struct winbindd_domain *domain, TALLOC_CTX *mem_ctx, const DOM_SID *sid, struct wbint_userinfo *info) { ADS_STRUCT *ads = NULL; const char *attrs[] = { "*", NULL }; ADS_STATUS rc; int count; LDAPMessage *msg = NULL; char *ldap_exp; char *sidstr; uint32 group_rid; NTSTATUS status = NT_STATUS_UNSUCCESSFUL; struct netr_SamInfo3 *user = NULL; gid_t gid; DEBUG(3,("ads: query_user\n")); info->homedir = NULL; info->shell = NULL; info->primary_gid = (gid_t)-1; /* try netsamlogon cache first */ if ( (user = netsamlogon_cache_get( mem_ctx, sid )) != NULL ) { DEBUG(5,("query_user: Cache lookup succeeded for %s\n", sid_string_dbg(sid))); sid_compose(&info->user_sid, &domain->sid, user->base.rid); sid_compose(&info->group_sid, &domain->sid, user->base.primary_gid); info->acct_name = talloc_strdup(mem_ctx, user->base.account_name.string); info->full_name = talloc_strdup(mem_ctx, user->base.full_name.string); nss_get_info_cached( domain, sid, mem_ctx, NULL, NULL, &info->homedir, &info->shell, &info->full_name, &gid ); info->primary_gid = gid; TALLOC_FREE(user); return NT_STATUS_OK; } if ( !winbindd_can_contact_domain(domain)) { DEBUG(8,("query_user: No incoming trust from domain %s\n", domain->name)); /* We still need to generate some basic information about the user even if we cannot contact the domain. Most of this stuff we can deduce. */ sid_copy( &info->user_sid, sid ); /* Assume "Domain Users" for the primary group */ sid_compose(&info->group_sid, &domain->sid, DOMAIN_GROUP_RID_USERS ); /* Try to fill in what the nss_info backend can do */ nss_get_info_cached( domain, sid, mem_ctx, NULL, NULL, &info->homedir, &info->shell, &info->full_name, &gid); info->primary_gid = gid; status = NT_STATUS_OK; goto done; } /* no cache...do the query */ if ( (ads = ads_cached_connection(domain)) == NULL ) { domain->last_status = NT_STATUS_SERVER_DISABLED; goto done; } sidstr = sid_binstring(talloc_tos(), sid); if (asprintf(&ldap_exp, "(objectSid=%s)", sidstr) == -1) { status = NT_STATUS_NO_MEMORY; goto done; } rc = ads_search_retry(ads, &msg, ldap_exp, attrs); free(ldap_exp); TALLOC_FREE(sidstr); if (!ADS_ERR_OK(rc) || !msg) { DEBUG(1,("query_user(sid=%s) ads_search: %s\n", sid_string_dbg(sid), ads_errstr(rc))); goto done; } count = ads_count_replies(ads, msg); if (count != 1) { DEBUG(1,("query_user(sid=%s): Not found\n", sid_string_dbg(sid))); goto done; } info->acct_name = ads_pull_username(ads, mem_ctx, msg); nss_get_info_cached( domain, sid, mem_ctx, ads, msg, &info->homedir, &info->shell, &info->full_name, &gid); info->primary_gid = gid; if (info->full_name == NULL) { info->full_name = ads_pull_string(ads, mem_ctx, msg, "name"); } if (!ads_pull_uint32(ads, msg, "primaryGroupID", &group_rid)) { DEBUG(1,("No primary group for %s !?\n", sid_string_dbg(sid))); goto done; } sid_copy(&info->user_sid, sid); sid_compose(&info->group_sid, &domain->sid, group_rid); status = NT_STATUS_OK; DEBUG(3,("ads query_user gave %s\n", info->acct_name)); done: if (msg) ads_msgfree(ads, msg); return status; }