/* 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; 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; } ads = ads_cached_connection(domain); if (!ads) { 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:%s:=%d))", escaped_dn, ADS_LDAP_MATCHING_RULE_BIT_AND, 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) || !res) { DEBUG(1,("lookup_usergroups ads_search member=%s: %s\n", user_dn, ads_errstr(rc))); return ads_ntstatus(rc); } 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; }
/* list all domain groups */ static NTSTATUS enum_dom_groups(struct winbindd_domain *domain, TALLOC_CTX *mem_ctx, uint32 *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:%s:=%d)(!(groupType:dn:%s:=%d))))", ADS_LDAP_MATCHING_RULE_BIT_AND, GROUP_TYPE_SECURITY_ENABLED, ADS_LDAP_MATCHING_RULE_BIT_AND, enum_dom_local_groups ? GROUP_TYPE_BUILTIN_LOCAL_GROUP : GROUP_TYPE_RESOURCE_GROUP); if (filter == NULL) { status = NT_STATUS_NO_MEMORY; goto done; } ads = ads_cached_connection(domain); if (!ads) { domain->last_status = NT_STATUS_SERVER_DISABLED; goto done; } rc = ads_search_retry(ads, &res, filter, attrs); if (!ADS_ERR_OK(rc) || !res) { DEBUG(1,("enum_dom_groups ads_search: %s\n", ads_errstr(rc))); 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 rid; name = ads_pull_username(ads, mem_ctx, msg); gecos = ads_pull_string(ads, mem_ctx, 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; } fstrcpy((*info)[i].acct_name, name); fstrcpy((*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; }
/* 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 *num_entries, struct wbint_userinfo **pinfo) { ADS_STRUCT *ads = NULL; const char *attrs[] = { "*", NULL }; int i, count; ADS_STATUS rc; LDAPMessage *res = NULL; LDAPMessage *msg = NULL; NTSTATUS status = NT_STATUS_UNSUCCESSFUL; *num_entries = 0; 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; } ads = ads_cached_connection(domain); if (!ads) { domain->last_status = NT_STATUS_SERVER_DISABLED; goto done; } rc = ads_search_retry(ads, &res, "(objectCategory=user)", attrs); if (!ADS_ERR_OK(rc) || !res) { DEBUG(1,("query_user_list ads_search: %s\n", ads_errstr(rc))); goto done; } count = ads_count_replies(ads, res); if (count == 0) { DEBUG(1,("query_user_list: No users found\n")); goto done; } (*pinfo) = talloc_zero_array(mem_ctx, struct wbint_userinfo, count); if (!*pinfo) { status = NT_STATUS_NO_MEMORY; goto done; } count = 0; for (msg = ads_first_entry(ads, res); msg; msg = ads_next_entry(ads, msg)) { struct wbint_userinfo *info = &((*pinfo)[count]); uint32 group; uint32 atype; if (!ads_pull_uint32(ads, msg, "sAMAccountType", &atype) || ds_atype_map(atype) != SID_NAME_USER) { DEBUG(1,("Not a user account? atype=0x%x\n", atype)); continue; } info->acct_name = ads_pull_username(ads, mem_ctx, msg); info->full_name = ads_pull_string(ads, mem_ctx, msg, "name"); info->homedir = NULL; info->shell = NULL; info->primary_gid = (gid_t)-1; if (!ads_pull_sid(ads, msg, "objectSid", &info->user_sid)) { DEBUG(1, ("No sid for %s !?\n", info->acct_name)); continue; } if (!ads_pull_uint32(ads, msg, "primaryGroupID", &group)) { DEBUG(1, ("No primary group for %s !?\n", info->acct_name)); continue; } sid_compose(&info->group_sid, &domain->sid, group); count += 1; } (*num_entries) = count; ads_msgfree(ads, res); for (i=0; i<count; i++) { struct wbint_userinfo *info = &((*pinfo)[i]); const char *gecos = NULL; gid_t primary_gid = (gid_t)-1; status = nss_get_info_cached(domain, &info->user_sid, mem_ctx, &info->homedir, &info->shell, &gecos, &primary_gid); if (!NT_STATUS_IS_OK(status)) { /* * Deliberately ignore this error, there might be more * users to fill */ continue; } if (gecos != NULL) { info->full_name = gecos; } info->primary_gid = primary_gid; } status = NT_STATUS_OK; DEBUG(3,("ads query_user_list gave %d entries\n", (*num_entries))); done: return status; }
/* convert a single name to a sid in a domain */ NTSTATUS ads_name_to_sid(ADS_STRUCT *ads, const char *name, DOM_SID *sid, enum SID_NAME_USE *type) { const char *attrs[] = {"objectSid", "sAMAccountType", NULL}; int count; ADS_STATUS rc; void *res = NULL; char *ldap_exp; uint32 t; NTSTATUS status = NT_STATUS_UNSUCCESSFUL; char *escaped_name = escape_ldap_string_alloc(name); char *escaped_realm = escape_ldap_string_alloc(ads->config.realm); if (!escaped_name || !escaped_realm) { status = NT_STATUS_NO_MEMORY; goto done; } if (asprintf(&ldap_exp, "(|(sAMAccountName=%s)(userPrincipalName=%s@%s))", escaped_name, escaped_name, escaped_realm) == -1) { DEBUG(1,("ads_name_to_sid: asprintf failed!\n")); status = NT_STATUS_NO_MEMORY; goto done; } rc = ads_search_retry(ads, &res, ldap_exp, attrs); free(ldap_exp); if (!ADS_ERR_OK(rc)) { DEBUG(1,("name_to_sid ads_search: %s\n", ads_errstr(rc))); goto done; } count = ads_count_replies(ads, res); if (count != 1) { DEBUG(1,("name_to_sid: %s not found\n", name)); goto done; } if (!ads_pull_sid(ads, res, "objectSid", sid)) { DEBUG(1,("No sid for %s !?\n", name)); goto done; } if (!ads_pull_uint32(ads, res, "sAMAccountType", &t)) { DEBUG(1,("No sAMAccountType for %s !?\n", name)); goto done; } *type = ads_atype_map(t); status = NT_STATUS_OK; DEBUG(3,("ads name_to_sid mapped %s\n", name)); done: if (res) ads_msgfree(ads, res); SAFE_FREE(escaped_name); SAFE_FREE(escaped_realm); 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; } ads = ads_cached_connection(domain); if (!ads) { 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)) { fstring sidstr, domstr; DBG_WARNING("Got sid %s in domain %s\n", sid_to_fstring(sidstr, &user_sid), sid_to_fstring(domstr, &domain->sid)); continue; } sid_split_rid(&user_sid, &rids[count]); count += 1; } rids = talloc_realloc(mem_ctx, rids, uint32_t, count); if (prids != NULL) { *prids = rids; } status = NT_STATUS_OK; DBG_NOTICE("ads query_user_list gave %d entries\n", count); done: 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 *num_entries, struct wbint_userinfo **info) { ADS_STRUCT *ads = NULL; const char *attrs[] = { "*", NULL }; int i, count; ADS_STATUS rc; LDAPMessage *res = NULL; LDAPMessage *msg = NULL; NTSTATUS status = NT_STATUS_UNSUCCESSFUL; *num_entries = 0; 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; } ads = ads_cached_connection(domain); if (!ads) { domain->last_status = NT_STATUS_SERVER_DISABLED; goto done; } rc = ads_search_retry(ads, &res, "(objectCategory=user)", attrs); if (!ADS_ERR_OK(rc) || !res) { DEBUG(1,("query_user_list ads_search: %s\n", ads_errstr(rc))); goto done; } count = ads_count_replies(ads, res); if (count == 0) { DEBUG(1,("query_user_list: No users found\n")); goto done; } (*info) = TALLOC_ZERO_ARRAY(mem_ctx, struct wbint_userinfo, 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)) { const char *name; const char *gecos = NULL; const char *homedir = NULL; const char *shell = NULL; uint32 group; uint32 atype; DOM_SID user_sid; gid_t primary_gid = (gid_t)-1; if (!ads_pull_uint32(ads, msg, "sAMAccountType", &atype) || ds_atype_map(atype) != SID_NAME_USER) { DEBUG(1,("Not a user account? atype=0x%x\n", atype)); continue; } name = ads_pull_username(ads, mem_ctx, msg); if ( ads_pull_sid( ads, msg, "objectSid", &user_sid ) ) { status = nss_get_info_cached( domain, &user_sid, mem_ctx, ads, msg, &homedir, &shell, &gecos, &primary_gid ); } if (gecos == NULL) { gecos = ads_pull_string(ads, mem_ctx, msg, "name"); } if (!ads_pull_sid(ads, msg, "objectSid", &(*info)[i].user_sid)) { DEBUG(1,("No sid for %s !?\n", name)); continue; } if (!ads_pull_uint32(ads, msg, "primaryGroupID", &group)) { DEBUG(1,("No primary group for %s !?\n", name)); continue; } (*info)[i].acct_name = name; (*info)[i].full_name = gecos; (*info)[i].homedir = homedir; (*info)[i].shell = shell; (*info)[i].primary_gid = primary_gid; sid_compose(&(*info)[i].group_sid, &domain->sid, group); i++; } (*num_entries) = i; status = NT_STATUS_OK; DEBUG(3,("ads query_user_list gave %d entries\n", (*num_entries))); done: if (res) ads_msgfree(ads, res); return status; }