s3:winbind: fix bug #7894 - sporadic winbind panic in rpc query_user_list
[samba.git] / source3 / winbindd / winbindd_rpc.c
index 5edb0d98b0508423be0e9ccf045480d54e0e739d..b65b126cf03c0c7ecc5b1021d8ce34385a7806b3 100644 (file)
@@ -7,23 +7,25 @@
    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 "../librpc/gen_ndr/cli_samr.h"
+#include "../librpc/gen_ndr/cli_lsa.h"
 
 #undef DBGC_CLASS
 #define DBGC_CLASS DBGC_WINBIND
@@ -35,7 +37,7 @@
 static NTSTATUS query_user_list(struct winbindd_domain *domain,
                               TALLOC_CTX *mem_ctx,
                               uint32 *num_entries, 
-                              WINBIND_USERINFO **info)
+                              struct wbint_userinfo **info)
 {
        NTSTATUS result;
        struct policy_handle dom_pol;
@@ -82,13 +84,21 @@ static NTSTATUS query_user_list(struct winbindd_domain *domain,
                                                      &total_size,
                                                      &returned_size,
                                                      &disp_info);
+
+               if (!NT_STATUS_IS_OK(result)) {
+                       if (!NT_STATUS_EQUAL(result, STATUS_MORE_ENTRIES)) {
+                               return result;
+                       }
+               }
+
                num_dom_users = disp_info.info1.count;
                start_idx += disp_info.info1.count;
                loop_count++;
 
                *num_entries += num_dom_users;
 
-               *info = TALLOC_REALLOC_ARRAY(mem_ctx, *info, WINBIND_USERINFO,
+               *info = TALLOC_REALLOC_ARRAY(mem_ctx, *info,
+                                            struct wbint_userinfo,
                                             *num_entries);
 
                if (!(*info)) {
@@ -106,7 +116,7 @@ static NTSTATUS query_user_list(struct winbindd_domain *domain,
                        (*info)[i].homedir = NULL;
                        (*info)[i].shell = NULL;
                        sid_compose(&(*info)[i].user_sid, &domain->sid, rid);
-                       
+
                        /* For the moment we set the primary group for
                           every user to be the Domain Users group.
                           There are serious problems with determining
@@ -114,7 +124,7 @@ static NTSTATUS query_user_list(struct winbindd_domain *domain,
                           This should really be made into a 'winbind
                           force group' smb.conf parameter or
                           something like that. */
-                          
+
                        sid_compose(&(*info)[i].group_sid, &domain->sid, 
                                    DOMAIN_GROUP_RID_USERS);
                }
@@ -191,7 +201,7 @@ static NTSTATUS enum_dom_groups(struct winbindd_domain *domain,
                talloc_destroy(mem_ctx2);
        } while (NT_STATUS_EQUAL(status, STATUS_MORE_ENTRIES));
 
-       return NT_STATUS_OK;
+       return status;
 }
 
 /* List all domain groups */
@@ -261,15 +271,15 @@ static NTSTATUS enum_local_groups(struct winbindd_domain *domain,
 
        } while (NT_STATUS_EQUAL(result, STATUS_MORE_ENTRIES));
 
-       return NT_STATUS_OK;
+       return result;
 }
 
 /* convert a single name to a sid in a domain */
 static NTSTATUS msrpc_name_to_sid(struct winbindd_domain *domain,
                                  TALLOC_CTX *mem_ctx,
-                                 enum winbindd_cmd original_cmd,
                                  const char *domain_name,
                                  const char *name,
+                                 uint32_t flags,
                                  DOM_SID *sid,
                                  enum lsa_SidType *type)
 {
@@ -277,8 +287,6 @@ static NTSTATUS msrpc_name_to_sid(struct winbindd_domain *domain,
        DOM_SID *sids = NULL;
        enum lsa_SidType *types = NULL;
        char *full_name = NULL;
-       struct rpc_pipe_client *cli;
-       struct policy_handle lsa_policy;
        NTSTATUS name_map_status = NT_STATUS_UNSUCCESSFUL;
        char *mapped_name = NULL;
 
@@ -310,13 +318,9 @@ static NTSTATUS msrpc_name_to_sid(struct winbindd_domain *domain,
        DEBUG(3,("name_to_sid [rpc] %s for domain %s\n",
                 full_name?full_name:"", domain_name ));
 
-       result = cm_connect_lsa(domain, mem_ctx, &cli, &lsa_policy);
-       if (!NT_STATUS_IS_OK(result))
-               return result;
-
-       result = rpccli_lsa_lookup_names(cli, mem_ctx, &lsa_policy, 1, 
-                                        (const char**) &full_name, NULL, 1, &sids, &types);
-        
+       result = winbindd_lookup_names(mem_ctx, domain, 1,
+                                      (const char **)&full_name, NULL,
+                                      &sids, &types);
        if (!NT_STATUS_IS_OK(result))
                return result;
 
@@ -342,29 +346,25 @@ static NTSTATUS msrpc_sid_to_name(struct winbindd_domain *domain,
        char **names;
        enum lsa_SidType *types = NULL;
        NTSTATUS result;
-       struct rpc_pipe_client *cli;
-       struct policy_handle lsa_policy;
        NTSTATUS name_map_status = NT_STATUS_UNSUCCESSFUL;
        char *mapped_name = NULL;
 
        DEBUG(3,("sid_to_name [rpc] %s for domain %s\n", sid_string_dbg(sid),
                 domain->name ));
 
-       result = cm_connect_lsa(domain, mem_ctx, &cli, &lsa_policy);
+       result = winbindd_lookup_sids(mem_ctx,
+                                     domain,
+                                     1,
+                                     sid,
+                                     &domains,
+                                     &names,
+                                     &types);
        if (!NT_STATUS_IS_OK(result)) {
-               DEBUG(2,("msrpc_sid_to_name: cm_connect_lsa() failed (%s)\n",
-                        nt_errstr(result)));           
+               DEBUG(2,("msrpc_sid_to_name: failed to lookup sids: %s\n",
+                       nt_errstr(result)));
                return result;
        }
-       
 
-       result = rpccli_lsa_lookup_sids(cli, mem_ctx, &lsa_policy,
-                                       1, sid, &domains, &names, &types);
-       if (!NT_STATUS_IS_OK(result)) {         
-               DEBUG(2,("msrpc_sid_to_name: rpccli_lsa_lookup_sids()  failed (%s)\n",
-                        nt_errstr(result)));           
-               return result;
-       }
 
        *type = (enum lsa_SidType)types[0];
        *domain_name = domains[0];
@@ -395,8 +395,6 @@ static NTSTATUS msrpc_rids_to_names(struct winbindd_domain *domain,
 {
        char **domains;
        NTSTATUS result;
-       struct rpc_pipe_client *cli;
-       struct policy_handle lsa_policy;
        DOM_SID *sids;
        size_t i;
        char **ret_names;
@@ -418,14 +416,14 @@ static NTSTATUS msrpc_rids_to_names(struct winbindd_domain *domain,
                }
        }
 
-       result = cm_connect_lsa(domain, mem_ctx, &cli, &lsa_policy);
-       if (!NT_STATUS_IS_OK(result)) {
-               return result;
-       }
+       result = winbindd_lookup_sids(mem_ctx,
+                                     domain,
+                                     num_rids,
+                                     sids,
+                                     &domains,
+                                     names,
+                                     types);
 
-       result = rpccli_lsa_lookup_sids(cli, mem_ctx, &lsa_policy,
-                                       num_rids, sids, &domains,
-                                       names, types);
        if (!NT_STATUS_IS_OK(result) &&
            !NT_STATUS_EQUAL(result, STATUS_SOME_UNMAPPED)) {
                return result;
@@ -458,7 +456,7 @@ static NTSTATUS msrpc_rids_to_names(struct winbindd_domain *domain,
 static NTSTATUS query_user(struct winbindd_domain *domain, 
                           TALLOC_CTX *mem_ctx, 
                           const DOM_SID *user_sid, 
-                          WINBIND_USERINFO *user_info)
+                          struct wbint_userinfo *user_info)
 {
        NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
        struct policy_handle dom_pol, user_pol;
@@ -471,53 +469,41 @@ static NTSTATUS query_user(struct winbindd_domain *domain,
 
        if (!sid_peek_check_rid(&domain->sid, user_sid, &user_rid))
                return NT_STATUS_UNSUCCESSFUL;
-       
+
        user_info->homedir = NULL;
        user_info->shell = NULL;
        user_info->primary_gid = (gid_t)-1;
-                                               
+
        /* 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_dbg(user_sid)));
 
                sid_compose(&user_info->user_sid, &domain->sid, user->base.rid);
                sid_compose(&user_info->group_sid, &domain->sid,
                            user->base.primary_gid);
-                               
+
                user_info->acct_name = talloc_strdup(mem_ctx,
                                                     user->base.account_name.string);
                user_info->full_name = talloc_strdup(mem_ctx,
                                                     user->base.full_name.string);
-               
+
                TALLOC_FREE(user);
-                                               
-               return NT_STATUS_OK;
-       }
-                               
-       if ( !winbindd_can_contact_domain( domain ) ) {
-               DEBUG(10,("query_user: No incoming trust for domain %s\n",
-                         domain->name));
-               return NT_STATUS_OK;
-       }
-       
-       if ( !winbindd_can_contact_domain( domain ) ) {
-               DEBUG(10,("query_user: No incoming trust for domain %s\n",
-                         domain->name));
+
                return NT_STATUS_OK;
        }
-       
+
        if ( !winbindd_can_contact_domain( domain ) ) {
                DEBUG(10,("query_user: No incoming trust for domain %s\n",
                          domain->name));
                return NT_STATUS_OK;
        }
-       
+
        /* no cache; hit the wire */
-               
+
        result = cm_connect_sam(domain, mem_ctx, &cli, &dom_pol);
        if (!NT_STATUS_IS_OK(result))
                return result;
@@ -525,7 +511,7 @@ static NTSTATUS query_user(struct winbindd_domain *domain,
        /* Get user handle */
        result = rpccli_samr_OpenUser(cli, mem_ctx,
                                      &dom_pol,
-                                     SEC_RIGHTS_MAXIMUM_ALLOWED,
+                                     SEC_FLAG_MAXIMUM_ALLOWED,
                                      user_rid,
                                      &user_pol);
 
@@ -565,7 +551,7 @@ static NTSTATUS lookup_usergroups(struct winbindd_domain *domain,
 {
        NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
        struct policy_handle dom_pol, user_pol;
-       uint32 des_access = SEC_RIGHTS_MAXIMUM_ALLOWED;
+       uint32 des_access = SEC_FLAG_MAXIMUM_ALLOWED;
        struct samr_RidWithAttributeArray *rid_array = NULL;
        unsigned int i;
        uint32 user_rid;
@@ -597,7 +583,7 @@ static NTSTATUS lookup_usergroups(struct winbindd_domain *domain,
        }
 
        /* no cache; hit the wire */
-       
+
        result = cm_connect_sam(domain, mem_ctx, &cli, &dom_pol);
        if (!NT_STATUS_IS_OK(result))
                return result;
@@ -739,14 +725,16 @@ static NTSTATUS msrpc_lookup_useraliases(struct winbindd_domain *domain,
 /* Lookup group membership given a rid.   */
 static NTSTATUS lookup_groupmem(struct winbindd_domain *domain,
                                TALLOC_CTX *mem_ctx,
-                               const DOM_SID *group_sid, uint32 *num_names, 
+                               const DOM_SID *group_sid,
+                               enum lsa_SidType type,
+                               uint32 *num_names,
                                DOM_SID **sid_mem, char ***names, 
                                uint32 **name_types)
 {
         NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
         uint32 i, total_names = 0;
         struct policy_handle dom_pol, group_pol;
-        uint32 des_access = SEC_RIGHTS_MAXIMUM_ALLOWED;
+        uint32 des_access = SEC_FLAG_MAXIMUM_ALLOWED;
        uint32 *rid_mem = NULL;
        uint32 group_rid;
        unsigned int j, r;
@@ -801,16 +789,16 @@ static NTSTATUS lookup_groupmem(struct winbindd_domain *domain,
         if (!NT_STATUS_IS_OK(result))
                return result;
 
-       *num_names = rids->count;
-       rid_mem = rids->rids;
-
-       if (!*num_names) {
+       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
@@ -824,7 +812,7 @@ static NTSTATUS lookup_groupmem(struct winbindd_domain *domain,
 
        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;
 
@@ -1046,10 +1034,7 @@ static NTSTATUS sequence_number(struct winbindd_domain *domain, uint32 *seq)
 /* get a list of trusted domains */
 static NTSTATUS trusted_domains(struct winbindd_domain *domain,
                                TALLOC_CTX *mem_ctx,
-                               uint32 *num_domains,
-                               char ***names,
-                               char ***alt_names,
-                               DOM_SID **dom_sids)
+                               struct netr_DomainTrustList *trusts)
 {
        NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
        uint32 enum_ctx = 0;
@@ -1058,10 +1043,7 @@ static NTSTATUS trusted_domains(struct winbindd_domain *domain,
 
        DEBUG(3,("rpc: trusted_domains\n"));
 
-       *num_domains = 0;
-       *names = NULL;
-       *alt_names = NULL;
-       *dom_sids = NULL;
+       ZERO_STRUCTP(trusts);
 
        result = cm_connect_lsa(domain, mem_ctx, &cli, &lsa_policy);
        if (!NT_STATUS_IS_OK(result))
@@ -1084,22 +1066,33 @@ static NTSTATUS trusted_domains(struct winbindd_domain *domain,
                    !NT_STATUS_EQUAL(result, STATUS_MORE_ENTRIES))
                        break;
 
-               start_idx = *num_domains;
-               *num_domains += dom_list.count;
-               *names = TALLOC_REALLOC_ARRAY(mem_ctx, *names,
-                                             char *, *num_domains);
-               *dom_sids = TALLOC_REALLOC_ARRAY(mem_ctx, *dom_sids,
-                                                DOM_SID, *num_domains);
-               *alt_names = TALLOC_REALLOC_ARRAY(mem_ctx, *alt_names,
-                                                char *, *num_domains);
-               if ((*names == NULL) || (*dom_sids == NULL) ||
-                   (*alt_names == NULL))
+               start_idx = trusts->count;
+               trusts->count += dom_list.count;
+
+               trusts->array = talloc_realloc(
+                       mem_ctx, trusts->array, struct netr_DomainTrust,
+                       trusts->count);
+               if (trusts->array == NULL) {
                        return NT_STATUS_NO_MEMORY;
+               }
 
                for (i=0; i<dom_list.count; i++) {
-                       (*names)[start_idx+i] = CONST_DISCARD(char *, dom_list.domains[i].name.string);
-                       (*dom_sids)[start_idx+i] = *dom_list.domains[i].sid;
-                       (*alt_names)[start_idx+i] = talloc_strdup(mem_ctx, "");
+                       struct netr_DomainTrust *trust = &trusts->array[i];
+                       struct dom_sid *sid;
+
+                       ZERO_STRUCTP(trust);
+
+                       trust->netbios_name = talloc_move(
+                               trusts->array,
+                               &dom_list.domains[i].name.string);
+                       trust->dns_name = NULL;
+
+                       sid = talloc(trusts->array, struct dom_sid);
+                       if (sid == NULL) {
+                               return NT_STATUS_NO_MEMORY;
+                       }
+                       sid_copy(sid, dom_list.domains[i].sid);
+                       trust->sid = sid;
                }
        }
        return result;
@@ -1187,6 +1180,136 @@ static NTSTATUS msrpc_password_policy(struct winbindd_domain *domain,
        return result;
 }
 
+typedef NTSTATUS (*lookup_sids_fn_t)(struct rpc_pipe_client *cli,
+                                    TALLOC_CTX *mem_ctx,
+                                    struct policy_handle *pol,
+                                    int num_sids,
+                                    const DOM_SID *sids,
+                                    char ***pdomains,
+                                    char ***pnames,
+                                    enum lsa_SidType **ptypes);
+
+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;
+       struct rpc_pipe_client *cli = NULL;
+       struct policy_handle lsa_policy;
+       unsigned int orig_timeout;
+       lookup_sids_fn_t lookup_sids_fn = rpccli_lsa_lookup_sids;
+
+       if (domain->can_do_ncacn_ip_tcp) {
+               status = cm_connect_lsa_tcp(domain, mem_ctx, &cli);
+               if (NT_STATUS_IS_OK(status)) {
+                       lookup_sids_fn = rpccli_lsa_lookup_sids3;
+                       goto lookup;
+               }
+               domain->can_do_ncacn_ip_tcp = false;
+       }
+       status = cm_connect_lsa(domain, mem_ctx, &cli, &lsa_policy);
+
+       if (!NT_STATUS_IS_OK(status)) {
+               return status;
+       }
+
+ lookup:
+       /*
+        * 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 = lookup_sids_fn(cli,
+                               mem_ctx,
+                               &lsa_policy,
+                               num_sids,
+                               sids,
+                               domains,
+                               names,
+                               types);
+
+       /* And restore our original timeout. */
+       rpccli_set_timeout(cli, orig_timeout);
+
+       if (!NT_STATUS_IS_OK(status)) {
+               return status;
+       }
+
+       return status;
+}
+
+typedef NTSTATUS (*lookup_names_fn_t)(struct rpc_pipe_client *cli,
+                                     TALLOC_CTX *mem_ctx,
+                                     struct policy_handle *pol,
+                                     int num_names,
+                                     const char **names,
+                                     const char ***dom_names,
+                                     int level,
+                                     struct dom_sid **sids,
+                                     enum lsa_SidType **types);
+
+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;
+       struct rpc_pipe_client *cli = NULL;
+       struct policy_handle lsa_policy;
+       unsigned int orig_timeout = 0;
+       lookup_names_fn_t lookup_names_fn = rpccli_lsa_lookup_names;
+
+       if (domain->can_do_ncacn_ip_tcp) {
+               status = cm_connect_lsa_tcp(domain, mem_ctx, &cli);
+               if (NT_STATUS_IS_OK(status)) {
+                       lookup_names_fn = rpccli_lsa_lookup_names4;
+                       goto lookup;
+               }
+               domain->can_do_ncacn_ip_tcp = false;
+       }
+       status = cm_connect_lsa(domain, mem_ctx, &cli, &lsa_policy);
+
+       if (!NT_STATUS_IS_OK(status)) {
+               return status;
+       }
+
+ lookup:
+
+       /*
+        * 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 = lookup_names_fn(cli,
+                                mem_ctx,
+                                &lsa_policy,
+                                num_names,
+                                (const char **) names,
+                                domains,
+                                1,
+                                sids,
+                                types);
+
+       /* And restore our original timeout. */
+       rpccli_set_timeout(cli, orig_timeout);
+
+       if (!NT_STATUS_IS_OK(status)) {
+               return status;
+       }
+
+       return status;
+}
 
 /* the rpc backend methods are exposed via this structure */
 struct winbindd_methods msrpc_methods = {