/* 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; }
/* 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; }
ADS_STATUS cell_do_search(struct likewise_cell *c, const char *search_base, int scope, const char *expr, const char **attrs, LDAPMessage ** msg) { int search_count = 0; ADS_STATUS status; NTSTATUS nt_status; /* check for a NULL connection */ if (!c->conn) { nt_status = cell_connect(c); if (!NT_STATUS_IS_OK(nt_status)) { status = ADS_ERROR_NT(nt_status); return status; } } DEBUG(10, ("cell_do_search: Base = %s, Filter = %s, Scope = %d, GC = %s\n", search_base, expr, scope, c->conn->server.gc ? "yes" : "no")); /* we try multiple times in case the ADS_STRUCT is bad and we need to reconnect */ while (search_count < MAX_SEARCH_COUNT) { *msg = NULL; status = ads_do_search(c->conn, search_base, scope, expr, attrs, msg); if (ADS_ERR_OK(status)) { if (DEBUGLEVEL >= 10) { LDAPMessage *e = NULL; int n = ads_count_replies(c->conn, *msg); DEBUG(10,("cell_do_search: Located %d entries\n", n)); for (e=ads_first_entry(c->conn, *msg); e!=NULL; e = ads_next_entry(c->conn, e)) { char *dn = ads_get_dn(c->conn, talloc_tos(), e); DEBUGADD(10,(" dn: %s\n", dn ? dn : "<NULL>")); TALLOC_FREE(dn); } } return status; } DEBUG(5, ("cell_do_search: search[%d] failed (%s)\n", search_count, ads_errstr(status))); search_count++; /* Houston, we have a problem */ if (status.error_type == ENUM_ADS_ERROR_LDAP) { switch (status.err.rc) { case LDAP_TIMELIMIT_EXCEEDED: case LDAP_TIMEOUT: case -1: /* we get this error if we cannot contact the LDAP server */ nt_status = cell_connect(c); if (!NT_STATUS_IS_OK(nt_status)) { status = ADS_ERROR_NT(nt_status); return status; } break; default: /* we're all done here */ return status; } } } DEBUG(5, ("cell_do_search: exceeded maximum search count!\n")); return ADS_ERROR_NT(NT_STATUS_UNSUCCESSFUL); }
/* 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; }
ADS_STATUS ads_sid_to_dn(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const DOM_SID *sid, char **dn) { ADS_STATUS rc; LDAPMessage *msg = NULL; LDAPMessage *entry = NULL; char *ldap_exp; char *sidstr = NULL; int count; char *dn2 = NULL; const char *attr[] = { "dn", NULL }; if (!(sidstr = sid_binstring(sid))) { DEBUG(1,("ads_sid_to_dn: sid_binstring failed!\n")); rc = ADS_ERROR_NT(NT_STATUS_NO_MEMORY); goto done; } if(!(ldap_exp = talloc_asprintf(mem_ctx, "(objectSid=%s)", sidstr))) { DEBUG(1,("ads_sid_to_dn: talloc_asprintf failed!\n")); rc = ADS_ERROR_NT(NT_STATUS_NO_MEMORY); goto done; } rc = ads_search_retry(ads, (void **)&msg, ldap_exp, attr); if (!ADS_ERR_OK(rc)) { DEBUG(1,("ads_sid_to_dn ads_search: %s\n", ads_errstr(rc))); goto done; } if ((count = ads_count_replies(ads, msg)) != 1) { fstring sid_string; DEBUG(1,("ads_sid_to_dn (sid=%s): Not found (count=%d)\n", sid_to_string(sid_string, sid), count)); rc = ADS_ERROR_NT(NT_STATUS_UNSUCCESSFUL); goto done; } entry = ads_first_entry(ads, msg); dn2 = ads_get_dn(ads, entry); if (!dn2) { rc = ADS_ERROR_NT(NT_STATUS_NO_MEMORY); goto done; } *dn = talloc_strdup(mem_ctx, dn2); if (!*dn) { ads_memfree(ads, dn2); rc = ADS_ERROR_NT(NT_STATUS_NO_MEMORY); goto done; } rc = ADS_ERROR_NT(NT_STATUS_OK); DEBUG(3,("ads sid_to_dn mapped %s\n", dn2)); SAFE_FREE(dn2); done: if (msg) ads_msgfree(ads, msg); if (dn2) ads_memfree(ads, dn2); SAFE_FREE(sidstr); return rc; }
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; }
/* 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; }
static int net_ads_gpo_list_all(struct net_context *c, int argc, const char **argv) { ADS_STRUCT *ads; ADS_STATUS status; LDAPMessage *res = NULL; int num_reply = 0; LDAPMessage *msg = NULL; struct GROUP_POLICY_OBJECT gpo; TALLOC_CTX *mem_ctx; char *dn; const char *attrs[] = { "versionNumber", "flags", "gPCFileSysPath", "displayName", "name", "gPCMachineExtensionNames", "gPCUserExtensionNames", "ntSecurityDescriptor", NULL }; if (c->display_usage) { d_printf("Usage:\n" "net ads gpo listall\n" " List all GPOs on the DC\n"); return 0; } mem_ctx = talloc_init("net_ads_gpo_list_all"); if (mem_ctx == NULL) { return -1; } status = ads_startup(c, false, &ads); if (!ADS_ERR_OK(status)) { goto out; } status = ads_do_search_all_sd_flags(ads, ads->config.bind_path, LDAP_SCOPE_SUBTREE, "(objectclass=groupPolicyContainer)", attrs, DACL_SECURITY_INFORMATION, &res); if (!ADS_ERR_OK(status)) { d_printf("search failed: %s\n", ads_errstr(status)); goto out; } num_reply = ads_count_replies(ads, res); d_printf("Got %d replies\n\n", num_reply); /* dump the results */ for (msg = ads_first_entry(ads, res); msg; msg = ads_next_entry(ads, msg)) { if ((dn = ads_get_dn(ads, mem_ctx, msg)) == NULL) { goto out; } status = ads_parse_gpo(ads, mem_ctx, msg, dn, &gpo); if (!ADS_ERR_OK(status)) { d_printf("ads_parse_gpo failed: %s\n", ads_errstr(status)); goto out; } dump_gpo(ads, mem_ctx, &gpo, 0); } out: ads_msgfree(ads, res); TALLOC_FREE(mem_ctx); ads_destroy(&ads); return 0; }