s4-idmap: Add mapping using uidNumber and gidNumber like idmap_ad
[samba.git] / source4 / winbind / idmap.c
index 0c729825dbb8a7bada0b79b2ce702dbed4fe9d7b..af6f66af285330fc6852034efc510a9a0575e3c4 100644 (file)
@@ -4,6 +4,7 @@
    Map SIDs to unixids and back
 
    Copyright (C) Kai Blin 2008
+   Copyright (C) Andrew Bartlett 2012
 
    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
 
 #include "includes.h"
 #include "auth/auth.h"
-#include "librpc/gen_ndr/lsa.h"
-#include "librpc/gen_ndr/samr.h"
 #include "librpc/gen_ndr/ndr_security.h"
-#include "lib/ldb/include/ldb.h"
-#include "lib/ldb/include/ldb_errors.h"
-#include "lib/ldb_wrap.h"
+#include <ldb.h>
+#include "ldb_wrap.h"
 #include "param/param.h"
 #include "winbind/idmap.h"
-#include "libcli/security/proto.h"
+#include "libcli/security/security.h"
 #include "libcli/ldap/ldap_ndr.h"
+#include "dsdb/samdb/samdb.h"
+#include "../libds/common/flags.h"
 
 /**
  * Get uid/gid bounds from idmap database
@@ -54,11 +54,9 @@ static int idmap_get_bounds(struct idmap_context *idmap_ctx, uint32_t *low,
        dn = ldb_dn_new(tmp_ctx, ldb, "CN=CONFIG");
        if (dn == NULL) goto failed;
 
-       ret = ldb_search(ldb, dn, LDB_SCOPE_BASE, NULL, NULL, &res);
+       ret = ldb_search(ldb, tmp_ctx, &res, dn, LDB_SCOPE_BASE, NULL, NULL);
        if (ret != LDB_SUCCESS) goto failed;
 
-       talloc_steal(tmp_ctx, res);
-
        if (res->count != 1) {
                ret = -1;
                goto failed;
@@ -102,9 +100,7 @@ static int idmap_msg_add_dom_sid(struct idmap_context *idmap_ctx,
        struct ldb_val val;
        enum ndr_err_code ndr_err;
 
-       ndr_err = ndr_push_struct_blob(&val, mem_ctx,
-                                      lp_iconv_convenience(idmap_ctx->lp_ctx),
-                                      sid,
+       ndr_err = ndr_push_struct_blob(&val, mem_ctx, sid,
                                       (ndr_push_flags_fn_t)ndr_push_dom_sid);
 
        if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
@@ -139,7 +135,7 @@ static struct dom_sid *idmap_msg_get_dom_sid(TALLOC_CTX *mem_ctx,
                return NULL;
        }
 
-       ndr_err = ndr_pull_struct_blob(val, sid, NULL, sid,
+       ndr_err = ndr_pull_struct_blob(val, sid, sid,
                                       (ndr_pull_flags_fn_t)ndr_pull_dom_sid);
        if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
                talloc_free(sid);
@@ -158,7 +154,8 @@ static struct dom_sid *idmap_msg_get_dom_sid(TALLOC_CTX *mem_ctx,
  * \return allocated idmap_context on success, NULL on error
  */
 struct idmap_context *idmap_init(TALLOC_CTX *mem_ctx,
-               struct loadparm_context *lp_ctx)
+                                struct tevent_context *ev_ctx,
+                                struct loadparm_context *lp_ctx)
 {
        struct idmap_context *idmap_ctx;
 
@@ -169,10 +166,10 @@ struct idmap_context *idmap_init(TALLOC_CTX *mem_ctx,
 
        idmap_ctx->lp_ctx = lp_ctx;
 
-       idmap_ctx->ldb_ctx = ldb_wrap_connect(mem_ctx, lp_ctx,
-                                             lp_idmap_url(lp_ctx),
-                                             system_session(mem_ctx, lp_ctx),
-                                             NULL, 0, NULL);
+       idmap_ctx->ldb_ctx = ldb_wrap_connect(mem_ctx, ev_ctx, lp_ctx,
+                                             "idmap.ldb",
+                                             system_session(lp_ctx),
+                                             NULL, 0);
        if (idmap_ctx->ldb_ctx == NULL) {
                return NULL;
        }
@@ -186,6 +183,12 @@ struct idmap_context *idmap_init(TALLOC_CTX *mem_ctx,
        if (idmap_ctx->unix_users_sid == NULL) {
                return NULL;
        }
+       
+       idmap_ctx->samdb = samdb_connect(idmap_ctx, ev_ctx, lp_ctx, system_session(lp_ctx), 0);
+       if (idmap_ctx->samdb == NULL) {
+               DEBUG(0, ("Failed to load sam.ldb in idmap_init\n"));
+               return NULL;
+       }
 
        return idmap_ctx;
 }
@@ -203,31 +206,97 @@ struct idmap_context *idmap_init(TALLOC_CTX *mem_ctx,
  * possible or some other NTSTATUS that is more descriptive on failure.
  */
 
-NTSTATUS idmap_xid_to_sid(struct idmap_context *idmap_ctx, TALLOC_CTX *mem_ctx,
-               const struct unixid *unixid, struct dom_sid **sid)
+static NTSTATUS idmap_xid_to_sid(struct idmap_context *idmap_ctx,
+                                TALLOC_CTX *mem_ctx,
+                                const struct unixid *unixid,
+                                struct dom_sid **sid)
 {
        int ret;
        NTSTATUS status = NT_STATUS_NONE_MAPPED;
        struct ldb_context *ldb = idmap_ctx->ldb_ctx;
        struct ldb_result *res = NULL;
+       struct ldb_message *msg;
        struct dom_sid *unix_sid, *new_sid;
        TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx);
        const char *id_type;
 
+       const char *sam_attrs[] = {"objectSid", NULL};
+       
+       /* 
+        * First check against our local DB, to see if this user has a
+        * mapping there.  This means that the Samba4 AD DC behaves
+        * much like a winbindd member server running idmap_ad
+        */
+       
        switch (unixid->type) {
                case ID_TYPE_UID:
+                       ret = dsdb_search_one(idmap_ctx->samdb, tmp_ctx, &msg, NULL, LDB_SCOPE_SUBTREE, 
+                                             sam_attrs, 0,
+                                             "(&(sAMaccountType:" LDB_OID_COMPARATOR_AND ":=%u)(uidNumber=%u)(objectSid=*)"
+                                             "(|(objectClass=posixAccount)(objectClass=posixGroup)))",
+                                             ATYPE_ACCOUNT, unixid->id);
+                       if (ret == LDB_ERR_CONSTRAINT_VIOLATION) {
+                               DEBUG(1, ("Search for uidNumber=%lu gave duplicate results, failing to map to a SID!\n",
+                                         (unsigned long)unixid->id));
+                               status = NT_STATUS_NONE_MAPPED;
+                               goto failed;
+                       } else if (ret == LDB_SUCCESS) {
+                               *sid = samdb_result_dom_sid(mem_ctx, msg, "objectSid");
+                               if (*sid) {
+                                       DEBUG(1, ("Search for uidNumber=%lu did not return an objectSid!\n",
+                                                 (unsigned long)unixid->id));
+                                       status = NT_STATUS_NONE_MAPPED;
+                                       goto failed;
+                               }
+                               talloc_free(tmp_ctx);
+                               return NT_STATUS_OK;
+                       } else if (ret != LDB_ERR_NO_SUCH_OBJECT) {
+                               DEBUG(1, ("Search for uidNumber=%lu gave '%s', failing to map to a SID!\n",
+                                         (unsigned long)unixid->id, ldb_errstring(idmap_ctx->samdb)));
+                               status = NT_STATUS_NONE_MAPPED;
+                               goto failed;
+                       }
+
                        id_type = "ID_TYPE_UID";
                        break;
                case ID_TYPE_GID:
+                       ret = dsdb_search_one(idmap_ctx->samdb, tmp_ctx, &msg, NULL, LDB_SCOPE_SUBTREE, 
+                                             sam_attrs, 0,
+                                             "(&(|(sAMaccountType=%u)(sAMaccountType=%u))(gidNumber=%u)"
+                                             "(|(objectClass=posixAccount)(objectClass=posixGroup)))",
+                                             ATYPE_SECURITY_GLOBAL_GROUP, ATYPE_SECURITY_LOCAL_GROUP, unixid->id);
+                       if (ret == LDB_ERR_CONSTRAINT_VIOLATION) {
+                               DEBUG(1, ("Search for gidNumber=%lu gave duplicate results, failing to map to a SID!\n",
+                                         (unsigned long)unixid->id));
+                               status = NT_STATUS_NONE_MAPPED;
+                               goto failed;
+                       } else if (ret == LDB_SUCCESS) {
+                               *sid = samdb_result_dom_sid(mem_ctx, msg, "objectSid");
+                               if (*sid) {
+                                       DEBUG(1, ("Search for gidNumber=%lu did not return an objectSid!\n",
+                                                 (unsigned long)unixid->id));
+                                       status = NT_STATUS_NONE_MAPPED;
+                                       goto failed;
+                               }
+                               talloc_free(tmp_ctx);
+                               return NT_STATUS_OK;
+                       } else if (ret != LDB_ERR_NO_SUCH_OBJECT) {
+                               DEBUG(1, ("Search for gidNumber=%lu gave '%s', failing to map to a SID!\n",
+                                         (unsigned long)unixid->id, ldb_errstring(idmap_ctx->samdb)));
+                               status = NT_STATUS_NONE_MAPPED;
+                               goto failed;
+                       }
+
                        id_type = "ID_TYPE_GID";
                        break;
                default:
-                       DEBUG(1, ("unixid->type must be type gid or uid\n"));
+                       DEBUG(1, ("unixid->type must be type gid or uid (got %u) for lookup with id %lu\n",
+                                 (unsigned)unixid->type, (unsigned long)unixid->id));
                        status = NT_STATUS_NONE_MAPPED;
                        goto failed;
        }
 
-       ret = ldb_search_exp_fmt(ldb, tmp_ctx, &res, NULL, LDB_SCOPE_SUBTREE,
+       ret = ldb_search(ldb, tmp_ctx, &res, NULL, LDB_SCOPE_SUBTREE,
                                 NULL, "(&(|(type=ID_TYPE_BOTH)(type=%s))"
                                 "(xidNumber=%u))", id_type, unixid->id);
        if (ret != LDB_SUCCESS) {
@@ -282,45 +351,46 @@ failed:
  *
  * If no mapping exists, a new mapping will be created.
  *
- * \todo Check if SIDs can be resolved if lp_idmap_trusted_only() == true
+ * \todo Check if SIDs can be resolved if lpcfg_idmap_trusted_only() == true
  * \todo Fix backwards compatibility for Samba3
  *
  * \param idmap_ctx idmap context to use
  * \param mem_ctx talloc context to use
  * \param sid SID to map to an unixid struct
- * \param unixid pointer to a unixid struct pointer
+ * \param unixid pointer to a unixid struct
  * \return NT_STATUS_OK on success, NT_STATUS_INVALID_SID if the sid is not from
  * a trusted domain and idmap trusted only = true, NT_STATUS_NONE_MAPPED if the
  * mapping failed.
  */
-NTSTATUS idmap_sid_to_xid(struct idmap_context *idmap_ctx, TALLOC_CTX *mem_ctx,
-               const struct dom_sid *sid, struct unixid **unixid)
+static NTSTATUS idmap_sid_to_xid(struct idmap_context *idmap_ctx,
+                                TALLOC_CTX *mem_ctx,
+                                const struct dom_sid *sid,
+                                struct unixid *unixid)
 {
        int ret;
        NTSTATUS status = NT_STATUS_NONE_MAPPED;
        struct ldb_context *ldb = idmap_ctx->ldb_ctx;
        struct ldb_dn *dn;
-       struct ldb_message *hwm_msg, *map_msg;
+       struct ldb_message *hwm_msg, *map_msg, *sam_msg;
        struct ldb_result *res = NULL;
        int trans;
        uint32_t low, high, hwm, new_xid;
        char *sid_string, *unixid_string, *hwm_string;
        bool hwm_entry_exists;
        TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx);
+       const char *sam_attrs[] = {"uidNumber", "gidNumber", "samAccountType", NULL};
 
        if (dom_sid_in_domain(idmap_ctx->unix_users_sid, sid)) {
                uint32_t rid;
                DEBUG(6, ("This is a local unix uid, just calculate that.\n"));
                status = dom_sid_split_rid(tmp_ctx, sid, NULL, &rid);
-               if (!NT_STATUS_IS_OK(status)) goto failed;
-
-               *unixid = talloc(mem_ctx, struct unixid);
-               if (*unixid == NULL) {
-                       status = NT_STATUS_NO_MEMORY;
-                       goto failed;
+               if (!NT_STATUS_IS_OK(status)) {
+                       talloc_free(tmp_ctx);
+                       return status;
                }
-               (*unixid)->id = rid;
-               (*unixid)->type = ID_TYPE_UID;
+
+               unixid->id = rid;
+               unixid->type = ID_TYPE_UID;
 
                talloc_free(tmp_ctx);
                return NT_STATUS_OK;
@@ -330,27 +400,72 @@ NTSTATUS idmap_sid_to_xid(struct idmap_context *idmap_ctx, TALLOC_CTX *mem_ctx,
                uint32_t rid;
                DEBUG(6, ("This is a local unix gid, just calculate that.\n"));
                status = dom_sid_split_rid(tmp_ctx, sid, NULL, &rid);
-               if (!NT_STATUS_IS_OK(status)) goto failed;
-
-               *unixid = talloc(mem_ctx, struct unixid);
-               if (*unixid == NULL) {
-                       status = NT_STATUS_NO_MEMORY;
-                       goto failed;
+               if (!NT_STATUS_IS_OK(status)) {
+                       talloc_free(tmp_ctx);
+                       return status;
                }
-               (*unixid)->id = rid;
-               (*unixid)->type = ID_TYPE_GID;
+
+               unixid->id = rid;
+               unixid->type = ID_TYPE_GID;
 
                talloc_free(tmp_ctx);
                return NT_STATUS_OK;
-        }
+       }
+
+       /* 
+        * First check against our local DB, to see if this user has a
+        * mapping there.  This means that the Samba4 AD DC behaves
+        * much like a winbindd member server running idmap_ad
+        */
+       
+       ret = dsdb_search_one(idmap_ctx->samdb, tmp_ctx, &sam_msg, NULL, LDB_SCOPE_SUBTREE, sam_attrs, 0,
+                             "(&(objectSid=%s)"
+                             "(|(sAMaccountType:" LDB_OID_COMPARATOR_AND ":=%u)"
+                             "(sAMaccountType=%u)"
+                             "(sAMaccountType=%u))"
+                             "(|(uidNumber=*)(gidNumber=*))"
+                             "(|(objectClass=posixAccount)(objectClass=posixGroup)))",
+                             dom_sid_string(tmp_ctx, sid), ATYPE_ACCOUNT, ATYPE_SECURITY_GLOBAL_GROUP, ATYPE_SECURITY_LOCAL_GROUP);
+       if (ret == LDB_ERR_CONSTRAINT_VIOLATION) {
+               DEBUG(1, ("Search for objectSid=%s gave duplicate results, failing to map to a unix ID!\n",
+                         dom_sid_string(tmp_ctx, sid)));
+               status = NT_STATUS_NONE_MAPPED;
+               goto failed;
+       } else if (ret == LDB_SUCCESS) {
+               uint32_t account_type = ldb_msg_find_attr_as_uint(sam_msg, "sAMaccountType", 0);
+               if (account_type & ATYPE_ACCOUNT) {
+                       const struct ldb_val *v = ldb_msg_find_ldb_val(sam_msg, "uidNumber");
+                       if (v) {
+                               unixid->type = ID_TYPE_UID;
+                               unixid->id = ldb_msg_find_attr_as_uint(sam_msg, "uidNumber", -1);
+                               talloc_free(tmp_ctx);
+                               return NT_STATUS_OK;
+                       }
+
+               } else if ((account_type == ATYPE_SECURITY_GLOBAL_GROUP) || (account_type == ATYPE_SECURITY_LOCAL_GROUP)) {
+                       const struct ldb_val *v = ldb_msg_find_ldb_val(sam_msg, "gidNumber");
+                       if (v) {
+                               unixid->type = ID_TYPE_GID;
+                               unixid->id = ldb_msg_find_attr_as_uint(sam_msg, "gidNumber", -1);
+                               talloc_free(tmp_ctx);
+                               return NT_STATUS_OK;
+                       }
+               }
+       } else if (ret != LDB_ERR_NO_SUCH_OBJECT) {
+               DEBUG(1, ("Search for objectSid=%s gave '%s', failing to map to a SID!\n",
+                         dom_sid_string(tmp_ctx, sid), ldb_errstring(idmap_ctx->samdb)));
 
-       ret = ldb_search_exp_fmt(ldb, tmp_ctx, &res, NULL, LDB_SCOPE_SUBTREE,
+               status = NT_STATUS_NONE_MAPPED;
+               goto failed;
+       }
+
+       ret = ldb_search(ldb, tmp_ctx, &res, NULL, LDB_SCOPE_SUBTREE,
                                 NULL, "(&(objectClass=sidMap)(objectSid=%s))",
                                 ldap_encode_ndr_dom_sid(tmp_ctx, sid));
        if (ret != LDB_SUCCESS) {
                DEBUG(1, ("Search failed: %s\n", ldb_errstring(ldb)));
-               status = NT_STATUS_NONE_MAPPED;
-               goto failed;
+               talloc_free(tmp_ctx);
+               return NT_STATUS_NONE_MAPPED;
        }
 
        if (res->count == 1) {
@@ -360,30 +475,24 @@ NTSTATUS idmap_sid_to_xid(struct idmap_context *idmap_ctx, TALLOC_CTX *mem_ctx,
                                                    -1);
                if (new_xid == (uint32_t) -1) {
                        DEBUG(1, ("Invalid xid mapping.\n"));
-                       status = NT_STATUS_NONE_MAPPED;
-                       goto failed;
+                       talloc_free(tmp_ctx);
+                       return NT_STATUS_NONE_MAPPED;
                }
 
                if (type == NULL) {
                        DEBUG(1, ("Invalid type for mapping entry.\n"));
-                       status = NT_STATUS_NONE_MAPPED;
-                       goto failed;
-               }
-
-               *unixid = talloc(mem_ctx, struct unixid);
-               if (*unixid == NULL) {
-                       status = NT_STATUS_NO_MEMORY;
-                       goto failed;
+                       talloc_free(tmp_ctx);
+                       return NT_STATUS_NONE_MAPPED;
                }
 
-               (*unixid)->id = new_xid;
+               unixid->id = new_xid;
 
                if (strcmp(type, "ID_TYPE_BOTH") == 0) {
-                       (*unixid)->type = ID_TYPE_BOTH;
+                       unixid->type = ID_TYPE_BOTH;
                } else if (strcmp(type, "ID_TYPE_UID") == 0) {
-                       (*unixid)->type = ID_TYPE_UID;
+                       unixid->type = ID_TYPE_UID;
                } else {
-                       (*unixid)->type = ID_TYPE_GID;
+                       unixid->type = ID_TYPE_GID;
                }
 
                talloc_free(tmp_ctx);
@@ -400,7 +509,7 @@ NTSTATUS idmap_sid_to_xid(struct idmap_context *idmap_ctx, TALLOC_CTX *mem_ctx,
 
        /* Redo the search to make sure noone changed the mapping while we
         * weren't looking */
-       ret = ldb_search_exp_fmt(ldb, tmp_ctx, &res, NULL, LDB_SCOPE_SUBTREE,
+       ret = ldb_search(ldb, tmp_ctx, &res, NULL, LDB_SCOPE_SUBTREE,
                                 NULL, "(&(objectClass=sidMap)(objectSid=%s))",
                                 ldap_encode_ndr_dom_sid(tmp_ctx, sid));
        if (ret != LDB_SUCCESS) {
@@ -415,7 +524,7 @@ NTSTATUS idmap_sid_to_xid(struct idmap_context *idmap_ctx, TALLOC_CTX *mem_ctx,
                goto failed;
        }
 
-       /*FIXME: if lp_idmap_trusted_only() == true, check if SID can be
+       /*FIXME: if lpcfg_idmap_trusted_only() == true, check if SID can be
         * resolved here. */
 
        ret = idmap_get_bounds(idmap_ctx, &low, &high);
@@ -430,15 +539,13 @@ NTSTATUS idmap_sid_to_xid(struct idmap_context *idmap_ctx, TALLOC_CTX *mem_ctx,
                goto failed;
        }
 
-       ret = ldb_search(ldb, dn, LDB_SCOPE_BASE, NULL, NULL, &res);
+       ret = ldb_search(ldb, tmp_ctx, &res, dn, LDB_SCOPE_BASE, NULL, NULL);
        if (ret != LDB_SUCCESS) {
                DEBUG(1, ("Search failed: %s\n", ldb_errstring(ldb)));
                status = NT_STATUS_NONE_MAPPED;
                goto failed;
        }
 
-       talloc_steal(tmp_ctx, res);
-
        if (res->count != 1) {
                DEBUG(1, ("No CN=CONFIG record, idmap database is broken.\n"));
                status = NT_STATUS_NONE_MAPPED;
@@ -608,14 +715,8 @@ NTSTATUS idmap_sid_to_xid(struct idmap_context *idmap_ctx, TALLOC_CTX *mem_ctx,
                goto failed;
        }
 
-       *unixid = talloc(mem_ctx, struct unixid);
-       if (*unixid == NULL) {
-               status = NT_STATUS_NO_MEMORY;
-               goto failed;
-       }
-
-       (*unixid)->id = new_xid;
-       (*unixid)->type = ID_TYPE_BOTH;
+       unixid->id = new_xid;
+       unixid->type = ID_TYPE_BOTH;
        talloc_free(tmp_ctx);
        return NT_STATUS_OK;
 
@@ -639,27 +740,31 @@ failed:
  */
 
 NTSTATUS idmap_xids_to_sids(struct idmap_context *idmap_ctx,
-                           TALLOC_CTX *mem_ctx, int count,
-                           struct id_mapping *id)
+                           TALLOC_CTX *mem_ctx,
+                           struct id_map **id)
 {
-       int i;
-       int error_count = 0;
-
-       for (i = 0; i < count; ++i) {
-               id[i].status = idmap_xid_to_sid(idmap_ctx, mem_ctx,
-                                               id[i].unixid, &id[i].sid);
-               if (NT_STATUS_EQUAL(id[i].status, NT_STATUS_RETRY)) {
-                       id[i].status = idmap_xid_to_sid(idmap_ctx, mem_ctx,
-                                                       id[i].unixid,
-                                                       &id[i].sid);
+       unsigned int i, error_count = 0;
+       NTSTATUS status;
+
+       for (i = 0; id && id[i]; i++) {
+               status = idmap_xid_to_sid(idmap_ctx, mem_ctx,
+                                               &id[i]->xid, &id[i]->sid);
+               if (NT_STATUS_EQUAL(status, NT_STATUS_RETRY)) {
+                       status = idmap_xid_to_sid(idmap_ctx, mem_ctx,
+                                                       &id[i]->xid,
+                                                       &id[i]->sid);
                }
-               if (!NT_STATUS_IS_OK(id[i].status)) {
-                       DEBUG(1, ("idmapping xid_to_sid failed for id[%d]\n", i));
+               if (!NT_STATUS_IS_OK(status)) {
+                       DEBUG(1, ("idmapping xid_to_sid failed for id[%d]=%lu: %s\n",
+                                 i, (unsigned long)id[i]->xid.id, nt_errstr(status)));
                        error_count++;
+                       id[i]->status = ID_UNMAPPED;
+               } else {
+                       id[i]->status = ID_MAPPED;
                }
        }
 
-       if (error_count == count) {
+       if (error_count == i) {
                /* Mapping did not work at all. */
                return NT_STATUS_NONE_MAPPED;
        } else if (error_count > 0) {
@@ -684,27 +789,33 @@ NTSTATUS idmap_xids_to_sids(struct idmap_context *idmap_ctx,
  */
 
 NTSTATUS idmap_sids_to_xids(struct idmap_context *idmap_ctx,
-                           TALLOC_CTX *mem_ctx, int count,
-                           struct id_mapping *id)
+                           TALLOC_CTX *mem_ctx,
+                           struct id_map **id)
 {
-       int i;
-       int error_count = 0;
-
-       for (i = 0; i < count; ++i) {
-               id[i].status = idmap_sid_to_xid(idmap_ctx, mem_ctx,
-                                               id[i].sid, &id[i].unixid);
-               if (NT_STATUS_EQUAL(id[i].status, NT_STATUS_RETRY)) {
-                       id[i].status = idmap_sid_to_xid(idmap_ctx, mem_ctx,
-                                                       id[i].sid,
-                                                       &id[i].unixid);
+       unsigned int i, error_count = 0;
+       NTSTATUS status;
+
+       for (i = 0; id && id[i]; i++) {
+               status = idmap_sid_to_xid(idmap_ctx, mem_ctx,
+                                         id[i]->sid, &id[i]->xid);
+               if (NT_STATUS_EQUAL(status, NT_STATUS_RETRY)) {
+                       status = idmap_sid_to_xid(idmap_ctx, mem_ctx,
+                                                 id[i]->sid,
+                                                 &id[i]->xid);
                }
-               if (!NT_STATUS_IS_OK(id[i].status)) {
-                       DEBUG(1, ("idmapping sid_to_xid failed for id[%d]\n", i));
+               if (!NT_STATUS_IS_OK(status)) {
+                       char *str = dom_sid_string(mem_ctx, id[i]->sid);
+                       DEBUG(1, ("idmapping sid_to_xid failed for id[%d]=%s: %s\n",
+                                 i, str, nt_errstr(status)));
+                       talloc_free(str);
                        error_count++;
+                       id[i]->status = ID_UNMAPPED;
+               } else {
+                       id[i]->status = ID_MAPPED;
                }
        }
 
-       if (error_count == count) {
+       if (error_count == i) {
                /* Mapping did not work at all. */
                return NT_STATUS_NONE_MAPPED;
        } else if (error_count > 0) {