r22173: BUG 4491, 4501: Additional fixes for protecting against
[ira/wip.git] / source3 / nsswitch / idmap_ad.c
index 6195684d96c9e4f30c1e59c8a3d56fbc6b1a9b50..87f73f99f0965dd42691ebae743f8a1a7368c7bb 100644 (file)
@@ -7,7 +7,7 @@
  *
  * Copyright (C) Andrew Tridgell 2001
  * Copyright (C) Andrew Bartlett <abartlet@samba.org> 2003
- * Copyright (C) Gerald (Jerry) Carter 2004
+ * Copyright (C) Gerald (Jerry) Carter 2004-2007
  * Copyright (C) Luke Howard 2001-2004
  *
  * This program is free software; you can redistribute it and/or modify
 
 #define WINBIND_CCACHE_NAME "MEMORY:winbind_ccache"
 
-NTSTATUS init_module(void);
-
-static ADS_STRUCT *ad_idmap_ads = NULL;
-
-static char *attr_uidnumber = NULL;
-static char *attr_gidnumber = NULL;
-
-static ADS_STATUS ad_idmap_check_attr_mapping(ADS_STRUCT *ads)
-{
-       ADS_STATUS status;
-       enum wb_posix_mapping map_type;
-
-       if (attr_uidnumber != NULL && attr_gidnumber != NULL) {
-               return ADS_ERROR(LDAP_SUCCESS);
-       }
-
-       SMB_ASSERT(ads->server.workgroup);
+#define IDMAP_AD_MAX_IDS 30
+#define CHECK_ALLOC_DONE(mem) do { \
+     if (!mem) { \
+           DEBUG(0, ("Out of memory!\n")); \
+           ret = NT_STATUS_NO_MEMORY; \
+           goto done; \
+      } \
+} while (0)
 
-       map_type = get_nss_info(ads->server.workgroup);
+struct idmap_ad_context {
+       uint32_t filter_low_id;
+       uint32_t filter_high_id;
+};
 
-       if ((map_type == WB_POSIX_MAP_SFU) ||
-           (map_type == WB_POSIX_MAP_RFC2307)) {
+NTSTATUS init_module(void);
 
-               status = ads_check_posix_schema_mapping(ads, map_type);
-               if (ADS_ERR_OK(status)) {
-                       attr_uidnumber = SMB_STRDUP(ads->schema.posix_uidnumber_attr);
-                       attr_gidnumber = SMB_STRDUP(ads->schema.posix_gidnumber_attr);
-                       ADS_ERROR_HAVE_NO_MEMORY(attr_uidnumber);
-                       ADS_ERROR_HAVE_NO_MEMORY(attr_gidnumber);
-                       return ADS_ERROR(LDAP_SUCCESS);
-               } else {
-                       DEBUG(0,("ads_check_posix_schema_mapping failed: %s\n", ads_errstr(status)));
-                       /* return status; */
-               }
-       }
-       
-       /* fallback to XAD defaults */
-       attr_uidnumber = SMB_STRDUP("uidNumber");
-       attr_gidnumber = SMB_STRDUP("gidNumber");
-       ADS_ERROR_HAVE_NO_MEMORY(attr_uidnumber);
-       ADS_ERROR_HAVE_NO_MEMORY(attr_gidnumber);
+static ADS_STRUCT *ad_idmap_ads = NULL;
+static struct posix_schema *ad_schema = NULL;
+static enum wb_posix_mapping ad_map_type = WB_POSIX_MAP_UNKNOWN;
 
-       return ADS_ERROR(LDAP_SUCCESS);
-}
+/************************************************************************
+ ***********************************************************************/
 
-static ADS_STRUCT *ad_idmap_cached_connection(void)
+static ADS_STRUCT *ad_idmap_cached_connection_internal(void)
 {
        ADS_STRUCT *ads;
        ADS_STATUS status;
        BOOL local = False;
+       fstring dc_name;
+       struct in_addr dc_ip;   
 
        if (ad_idmap_ads != NULL) {
+
+               time_t expire;
+               time_t now = time(NULL);
+
                ads = ad_idmap_ads;
 
+               expire = MIN(ads->auth.tgt_expire, ads->auth.tgs_expire);
+
                /* check for a valid structure */
+               DEBUG(7, ("Current tickets expire in %d seconds (at %d, time is now %d)\n",
+                         (uint32)expire-(uint32)now, (uint32) expire, (uint32) now));
 
-               DEBUG(7, ("Current tickets expire at %d, time is now %d\n",
-                         (uint32) ads->auth.expire, (uint32) time(NULL)));
-               if ( ads->config.realm && (ads->auth.expire > time(NULL))) {
+               if ( ads->config.realm && (expire > time(NULL))) {
                        return ads;
                } else {
                        /* we own this ADS_STRUCT so make sure it goes away */
+                       DEBUG(7,("Deleting expired krb5 credential cache\n"));
                        ads->is_mine = True;
                        ads_destroy( &ads );
                        ads_kdestroy(WINBIND_CCACHE_NAME);
                        ad_idmap_ads = NULL;
+                       TALLOC_FREE( ad_schema );                       
                }
        }
 
@@ -106,8 +94,7 @@ static ADS_STRUCT *ad_idmap_cached_connection(void)
                setenv("KRB5CCNAME", WINBIND_CCACHE_NAME, 1);
        }
 
-       ads = ads_init(lp_realm(), lp_workgroup(), NULL);
-       if (!ads) {
+       if ( (ads = ads_init(lp_realm(), lp_workgroup(), NULL)) == NULL ) {
                DEBUG(1,("ads_init failed\n"));
                return NULL;
        }
@@ -119,6 +106,10 @@ static ADS_STRUCT *ad_idmap_cached_connection(void)
        SAFE_FREE(ads->auth.realm);
        ads->auth.realm = SMB_STRDUP(lp_realm());
 
+       /* setup server affinity */
+
+       get_dc_name( NULL, ads->auth.realm, dc_name, &dc_ip );
+       
        status = ads_connect(ads);
        if (!ADS_ERR_OK(status)) {
                DEBUG(1, ("ad_idmap_init: failed to connect to AD\n"));
@@ -128,42 +119,60 @@ static ADS_STRUCT *ad_idmap_cached_connection(void)
 
        ads->is_mine = False;
 
-       status = ad_idmap_check_attr_mapping(ads);
-       if (!ADS_ERR_OK(status)) {
-               DEBUG(1, ("ad_idmap_init: failed to check attribute mapping\n"));
+       ad_idmap_ads = ads;
+
+       return ads;
+}
+
+/************************************************************************
+ ***********************************************************************/
+
+static ADS_STRUCT *ad_idmap_cached_connection(void)
+{
+       ADS_STRUCT *ads = ad_idmap_cached_connection_internal();
+       
+       if ( !ads )
                return NULL;
-       }
 
-       ad_idmap_ads = ads;
+       /* if we have a valid ADS_STRUCT and the schema model is
+          defined, then we can return here. */
+
+       if ( ad_schema )
+               return ads;
+
+       /* Otherwise, set the schema model */
+
+       if ( (ad_map_type ==  WB_POSIX_MAP_SFU) ||
+            (ad_map_type ==  WB_POSIX_MAP_RFC2307) ) 
+       {
+               ADS_STATUS schema_status;
+               
+               schema_status = ads_check_posix_schema_mapping( NULL, ads, ad_map_type, &ad_schema);
+               if ( !ADS_ERR_OK(schema_status) ) {
+                       DEBUG(2,("ad_idmap_cached_connection: Failed to obtain schema details!\n"));
+                       return NULL;                    
+               }
+       }
+       
        return ads;
 }
 
-struct idmap_ad_context {
-       uint32_t filter_low_id, filter_high_id;         /* Filter range */
-};
+/************************************************************************
+ ***********************************************************************/
 
-/* Initialize and check conf is appropriate */
 static NTSTATUS idmap_ad_initialize(struct idmap_domain *dom, const char *params)
 {
        struct idmap_ad_context *ctx;
        char *config_option;
-       const char *range;
-       ADS_STRUCT *ads;
+       const char *range = NULL;
+       const char *schema_mode = NULL; 
 
-       /* verify AD is reachable (not critical, we may just be offline at start) */
-       ads = ad_idmap_cached_connection();
-       if (ads == NULL) {
-               DEBUG(1, ("WARNING: Could not init an AD connection! Mapping might not work.\n"));
-       }
-
-       ctx = talloc_zero(dom, struct idmap_ad_context);
-       if ( ! ctx) {
+       if ( (ctx = talloc_zero(dom, struct idmap_ad_context)) == NULL ) {
                DEBUG(0, ("Out of memory!\n"));
                return NT_STATUS_NO_MEMORY;
        }
 
-       config_option = talloc_asprintf(ctx, "idmap config %s", dom->name);
-       if ( ! config_option) {
+       if ( (config_option = talloc_asprintf(ctx, "idmap config %s", dom->name)) == NULL ) {
                DEBUG(0, ("Out of memory!\n"));
                talloc_free(ctx);
                return NT_STATUS_NO_MEMORY;
@@ -180,43 +189,36 @@ static NTSTATUS idmap_ad_initialize(struct idmap_domain *dom, const char *params
                }
        }
 
-       /* idmap AD can work well only if it is the default module (trusts)
-        * with additional BUILTIN and alloc using TDB */
-       if ( ! dom->default_domain) {
-               DEBUG(1, ("WARNING: idmap_ad is not configured as the default domain.\n"
-                         "For best results we suggest you to configure this module as\n"
-                         "default and configure BULTIN to use idmap_tdb\n"
-                         "ex: idmap domains = BUILTIN %s\n"
-                         "    idmap alloc config: range = 5000 - 9999\n"
-                         "    idmap config %s: default = yes\n"
-                         "    idmap config %s: backend = ad\n"
-                         "    idmap config %s: range = 10000 - 10000000  #this is optional\n"
-                         "NOTE: make sure the ranges do not overlap\n",
-                         dom->name, dom->name, dom->name, dom->name));
-       }
-       if ( ! dom->readonly) {
-               DEBUG(1, ("WARNING: forcing to readonly, as idmap_ad can't write on AD.\n"));
-               dom->readonly = true; /* force readonly */
+       /* schema mode */
+       if ( ad_map_type == WB_POSIX_MAP_UNKNOWN )
+               ad_map_type = WB_POSIX_MAP_RFC2307;
+       schema_mode = lp_parm_const_string(-1, config_option, "schema_mode", NULL);
+       if ( schema_mode && schema_mode[0] ) {
+               if ( strequal(schema_mode, "sfu") )
+                       ad_map_type = WB_POSIX_MAP_SFU;
+               else if ( strequal(schema_mode, "rfc2307" ) )
+                       ad_map_type = WB_POSIX_MAP_RFC2307;
+               else
+                       DEBUG(0,("idmap_ad_initialize: Unknown schema_mode (%s)\n",
+                                schema_mode));
        }
 
        dom->private_data = ctx;
 
        talloc_free(config_option);
+
        return NT_STATUS_OK;
 }
 
-#define IDMAP_AD_MAX_IDS 30
-#define CHECK_ALLOC_DONE(mem) do { if (!mem) { DEBUG(0, ("Out of memory!\n")); ret = NT_STATUS_NO_MEMORY; goto done; } } while (0)
+/************************************************************************
+ Search up to IDMAP_AD_MAX_IDS entries in maps for a match.
+ ***********************************************************************/
 
-/* this function searches up to IDMAP_AD_MAX_IDS entries in maps for a match */
 static struct id_map *find_map_by_id(struct id_map **maps, enum id_type type, uint32_t id)
 {
        int i;
 
-       for (i = 0; i < IDMAP_AD_MAX_IDS; i++) {
-               if (maps[i] == NULL) { /* end of the run */
-                       return NULL;
-               }
+       for (i = 0; maps[i] && i<IDMAP_AD_MAX_IDS; i++) {
                if ((maps[i]->xid.type == type) && (maps[i]->xid.id == id)) {
                        return maps[i];
                }
@@ -225,6 +227,26 @@ static struct id_map *find_map_by_id(struct id_map **maps, enum id_type type, ui
        return NULL;    
 }
 
+/************************************************************************
+ Search up to IDMAP_AD_MAX_IDS entries in maps for a match
+ ***********************************************************************/
+
+static struct id_map *find_map_by_sid(struct id_map **maps, DOM_SID *sid)
+{
+       int i;
+
+       for (i = 0; maps[i] && i<IDMAP_AD_MAX_IDS; i++) {
+               if (sid_equal(maps[i]->sid, sid)) {
+                       return maps[i];
+               }
+       }
+
+       return NULL;    
+}
+
+/************************************************************************
+ ***********************************************************************/
+
 static NTSTATUS idmap_ad_unixids_to_sids(struct idmap_domain *dom, struct id_map **ids)
 {
        NTSTATUS ret;
@@ -234,131 +256,89 @@ static NTSTATUS idmap_ad_unixids_to_sids(struct idmap_domain *dom, struct id_map
        ADS_STRUCT *ads;
        const char *attrs[] = { "sAMAccountType", 
                                "objectSid",
-                               NULL, /* attr_uidnumber */
-                               NULL, /* attr_gidnumber */
+                               NULL, /* uidnumber */
+                               NULL, /* gidnumber */
                                NULL };
        LDAPMessage *res = NULL;
+       LDAPMessage *entry = NULL;
        char *filter = NULL;
-       BOOL multi = False;
        int idx = 0;
        int bidx = 0;
        int count;
        int i;
+       char *u_filter = NULL;
+       char *g_filter = NULL;
 
-       ctx = talloc_get_type(dom->private_data, struct idmap_ad_context);      
+       ctx = talloc_get_type(dom->private_data, struct idmap_ad_context);
 
-       memctx = talloc_new(ctx);
-       if ( ! memctx) {
+       if ( (memctx = talloc_new(ctx)) == NULL ) {
                DEBUG(0, ("Out of memory!\n"));
                return NT_STATUS_NO_MEMORY;
        }
 
-       ads = ad_idmap_cached_connection();
-       if (ads == NULL) {
+       if ( (ads = ad_idmap_cached_connection()) == NULL ) {
                DEBUG(1, ("ADS uninitialized\n"));
                ret = NT_STATUS_UNSUCCESSFUL;
                goto done;
        }
 
-       /* attr_uidnumber and attr_gidnumber are surely successfully initialized now */
-       attrs[2] = attr_uidnumber;
-       attrs[3] = attr_gidnumber;
-
-       if ( ! ids[1]) {
-               /* if we are requested just one mapping use the simple filter */
-               switch (ids[0]->xid.type) {
-               case ID_TYPE_UID:
+       attrs[2] = ad_schema->posix_uidnumber_attr;
+       attrs[3] = ad_schema->posix_gidnumber_attr;
 
-                       filter = talloc_asprintf(memctx,
-                               "(&(|(sAMAccountType=%d)(sAMAccountType=%d)(sAMAccountType=%d))(%s=%lu))",
-                               ATYPE_NORMAL_ACCOUNT, ATYPE_WORKSTATION_TRUST, ATYPE_INTERDOMAIN_TRUST,
-                               attr_uidnumber,
-                               (unsigned long)ids[0]->xid.id);
+again:
+       bidx = idx;
+       for (i = 0; (i < IDMAP_AD_MAX_IDS) && ids[idx]; i++, idx++) {
+               switch (ids[idx]->xid.type) {
+               case ID_TYPE_UID:     
+                       if ( ! u_filter) {
+                               u_filter = talloc_asprintf(memctx, "(&(|"
+                                                          "(sAMAccountType=%d)"
+                                                          "(sAMAccountType=%d)"
+                                                          "(sAMAccountType=%d))(|",
+                                                          ATYPE_NORMAL_ACCOUNT,
+                                                          ATYPE_WORKSTATION_TRUST,
+                                                          ATYPE_INTERDOMAIN_TRUST);
+                       }
+                       u_filter = talloc_asprintf_append(u_filter, "(%s=%lu)",
+                                                         ad_schema->posix_uidnumber_attr,
+                                                         (unsigned long)ids[idx]->xid.id);
+                       CHECK_ALLOC_DONE(u_filter);
                        break;
+                               
                case ID_TYPE_GID:
-
-                       filter = talloc_asprintf(memctx,
-                               "(&(|(sAMAccountType=%d)(sAMAccountType=%d))(%s=%lu))",
-                               ATYPE_SECURITY_GLOBAL_GROUP, ATYPE_SECURITY_LOCAL_GROUP,
-                               attr_gidnumber,
-                               (unsigned long)ids[0]->xid.id);
+                       if ( ! g_filter) {
+                               g_filter = talloc_asprintf(memctx, "(&(|"
+                                                          "(sAMAccountType=%d)"
+                                                          "(sAMAccountType=%d))(|",
+                                                          ATYPE_SECURITY_GLOBAL_GROUP,
+                                                          ATYPE_SECURITY_LOCAL_GROUP);
+                       }
+                       g_filter = talloc_asprintf_append(g_filter, "(%s=%lu)",
+                                                         ad_schema->posix_gidnumber_attr,
+                                                         (unsigned long)ids[idx]->xid.id);
+                       CHECK_ALLOC_DONE(g_filter);
                        break;
+
                default:
                        DEBUG(3, ("Unknown ID type\n"));
-                       ret = NT_STATUS_INVALID_PARAMETER;
-                       goto done;
+                       ids[idx]->status = ID_UNKNOWN;
+                       continue;
                }
-               CHECK_ALLOC_DONE(filter);
-               DEBUG(10, ("Filter: [%s]\n", filter));
-       } else {
-               /* multiple mappings */
-               multi = True;
        }
-
-again:
-       if (multi) {
-               char *u_filter = NULL;
-               char *g_filter = NULL;
-
-               bidx = idx;
-               for (i = 0; (i < IDMAP_AD_MAX_IDS) && ids[idx]; i++, idx++) {
-                       switch (ids[idx]->xid.type) {
-                       case ID_TYPE_UID:
-       
-                               if ( ! u_filter) {
-                                       u_filter = talloc_asprintf(memctx, "(&(|"
-                                               "(sAMAccountType=%d)"
-                                               "(sAMAccountType=%d)"
-                                               "(sAMAccountType=%d))(|",
-                                               ATYPE_NORMAL_ACCOUNT,
-                                               ATYPE_WORKSTATION_TRUST,
-                                               ATYPE_INTERDOMAIN_TRUST);
-                               }
-                               u_filter = talloc_asprintf_append(u_filter, "(%s=%lu)",
-                                       attr_uidnumber,
-                                       (unsigned long)ids[idx]->xid.id);
-                               CHECK_ALLOC_DONE(u_filter);
-                               break;
-                               
-                       case ID_TYPE_GID:
-                               if ( ! g_filter) {
-                                       g_filter = talloc_asprintf(memctx, "(&(|"
-                                               "(sAMAccountType=%d)"
-                                               "(sAMAccountType=%d))(|",
-                                               ATYPE_SECURITY_GLOBAL_GROUP,
-                                               ATYPE_SECURITY_LOCAL_GROUP);
-                               }
-                               g_filter = talloc_asprintf_append(g_filter, "(%s=%lu)",
-                                       attr_gidnumber,
-                                       (unsigned long)ids[idx]->xid.id);
-                               CHECK_ALLOC_DONE(g_filter);
-                               break;
-
-                       default:
-                               DEBUG(3, ("Unknown ID type\n"));
-                               ids[idx]->mapped = false;
-                               continue;
-                       }
-               }
-               filter = talloc_asprintf(memctx, "(|");
+       filter = talloc_asprintf(memctx, "(|");
+       CHECK_ALLOC_DONE(filter);
+       if ( u_filter) {
+               filter = talloc_asprintf_append(filter, "%s))", u_filter);
                CHECK_ALLOC_DONE(filter);
-               if ( u_filter) {
-                       filter = talloc_asprintf_append(filter, "%s))", u_filter);
-                       CHECK_ALLOC_DONE(filter);
                        TALLOC_FREE(u_filter);
-               }
-               if ( g_filter) {
-                       filter = talloc_asprintf_append(filter, "%s))", g_filter);
-                       CHECK_ALLOC_DONE(filter);
-                       TALLOC_FREE(g_filter);
-               }
-               filter = talloc_asprintf_append(filter, ")");
+       }
+       if ( g_filter) {
+               filter = talloc_asprintf_append(filter, "%s))", g_filter);
                CHECK_ALLOC_DONE(filter);
-               DEBUG(10, ("Filter: [%s]\n", filter));
-       } else {
-               bidx = 0;
-               idx = 1;
+               TALLOC_FREE(g_filter);
        }
+       filter = talloc_asprintf_append(filter, ")");
+       CHECK_ALLOC_DONE(filter);
 
        rc = ads_search_retry(ads, &res, filter, attrs);
        if (!ADS_ERR_OK(rc)) {
@@ -367,13 +347,12 @@ again:
                goto done;
        }
 
-       count = ads_count_replies(ads, res);
-       if (count == 0) {
+       if ( (count = ads_count_replies(ads, res)) == 0 ) {
                DEBUG(10, ("No IDs found\n"));
        }
 
-       for (i = 0; i < count; i++) {
-               LDAPMessage *entry = NULL;
+       entry = res;
+       for (i = 0; (i < count) && entry; i++) {
                DOM_SID sid;
                enum id_type type;
                struct id_map *map;
@@ -381,13 +360,14 @@ again:
                uint32_t atype;
 
                if (i == 0) { /* first entry */
-                       entry = ads_first_entry(ads, res);
+                       entry = ads_first_entry(ads, entry);
                } else { /* following ones */
                        entry = ads_next_entry(ads, entry);
                }
-               if ( ! entry) {
+
+               if ( !entry ) {
                        DEBUG(2, ("ERROR: Unable to fetch ldap entries from results\n"));
-                       continue;
+                       break;
                }
 
                /* first check if the SID is present */
@@ -417,10 +397,15 @@ again:
                        continue;
                }
 
-               if (!ads_pull_uint32(ads, entry, (type==ID_TYPE_UID)?attr_uidnumber:attr_gidnumber, &id)) {
+               if (!ads_pull_uint32(ads, entry, (type==ID_TYPE_UID) ? 
+                                                ad_schema->posix_uidnumber_attr : 
+                                                ad_schema->posix_gidnumber_attr, 
+                                    &id)) 
+               {
                        DEBUG(1, ("Could not get unix ID\n"));
                        continue;
                }
+
                if ((id == 0) ||
                    (ctx->filter_low_id && (id < ctx->filter_low_id)) ||
                    (ctx->filter_high_id && (id > ctx->filter_high_id))) {
@@ -438,7 +423,7 @@ again:
                sid_copy(map->sid, &sid);
 
                /* mapped */
-               map->mapped = True;
+               map->status = ID_MAPPED;
 
                DEBUG(10, ("Mapped %s -> %lu (%d)\n",
                           sid_string_static(map->sid),
@@ -450,32 +435,25 @@ again:
                ads_msgfree(ads, res);
        }
 
-       if (multi && ids[idx]) { /* still some values to map */
+       if (ids[idx]) { /* still some values to map */
                goto again;
        }
 
        ret = NT_STATUS_OK;
+
+       /* mark all unknown ones as unmapped */
+       for (i = 0; ids[i]; i++) {
+               if (ids[i]->status == ID_UNKNOWN) 
+                       ids[i]->status = ID_UNMAPPED;
+       }
+
 done:
        talloc_free(memctx);
        return ret;
 }
 
-/* this function searches up to IDMAP_AD_MAX_IDS entries in maps for a match */
-static struct id_map *find_map_by_sid(struct id_map **maps, DOM_SID *sid)
-{
-       int i;
-
-       for (i = 0; i < IDMAP_AD_MAX_IDS; i++) {
-               if (maps[i] == NULL) { /* end of the run */
-                       return NULL;
-               }
-               if (sid_equal(maps[i]->sid, sid)) {
-                       return maps[i];
-               }
-       }
-
-       return NULL;    
-}
+/************************************************************************
+ ***********************************************************************/
 
 static NTSTATUS idmap_ad_sids_to_unixids(struct idmap_domain *dom, struct id_map **ids)
 {
@@ -490,86 +468,52 @@ static NTSTATUS idmap_ad_sids_to_unixids(struct idmap_domain *dom, struct id_map
                                NULL, /* attr_gidnumber */
                                NULL };
        LDAPMessage *res = NULL;
+       LDAPMessage *entry = NULL;
        char *filter = NULL;
-       BOOL multi = False;
        int idx = 0;
        int bidx = 0;
        int count;
        int i;
+       char *sidstr;
 
        ctx = talloc_get_type(dom->private_data, struct idmap_ad_context);      
 
-       memctx = talloc_new(ctx);
-       if ( ! memctx) {
+       if ( (memctx = talloc_new(ctx)) == NULL ) {             
                DEBUG(0, ("Out of memory!\n"));
                return NT_STATUS_NO_MEMORY;
        }
 
-       ads = ad_idmap_cached_connection();
-       if (ads == NULL) {
+       if ( (ads = ad_idmap_cached_connection()) == NULL ) {
                DEBUG(1, ("ADS uninitialized\n"));
                ret = NT_STATUS_UNSUCCESSFUL;
                goto done;
        }
 
-       /* attr_uidnumber and attr_gidnumber are surely successfully initialized now */
-       attrs[2] = attr_uidnumber;
-       attrs[3] = attr_gidnumber;
-
-
-       if ( ! ids[1]) {
-               /* if we are requested just one mapping use the simple filter */
-               char *sidstr;
-
-               sidstr = sid_binstring(ids[0]->sid);
-               filter = talloc_asprintf(memctx, "(&(objectSid=%s)(|" /* the requested Sid */
-                                                "(sAMAccountType=%d)(sAMAccountType=%d)(sAMAccountType=%d)" /* user account types */
-                                                "(sAMAccountType=%d)(sAMAccountType=%d)))", /* group account types */
-                                                sidstr,
-                                                ATYPE_NORMAL_ACCOUNT, ATYPE_WORKSTATION_TRUST, ATYPE_INTERDOMAIN_TRUST,
-                                                ATYPE_SECURITY_GLOBAL_GROUP, ATYPE_SECURITY_LOCAL_GROUP);
-               if (! filter) {
-                       free(sidstr);
-                       ret = NT_STATUS_NO_MEMORY;
-                       goto done;
-               }
-               CHECK_ALLOC_DONE(filter);
-               DEBUG(10, ("Filter: [%s]\n", filter));
-       } else {
-               /* multiple mappings */
-               multi = True;
-       }
+       attrs[2] = ad_schema->posix_uidnumber_attr;
+       attrs[3] = ad_schema->posix_gidnumber_attr;
 
 again:
-       if (multi) {
-               char *sidstr;
-
-               filter = talloc_asprintf(memctx,
-                               "(&(|"
-                                   "(sAMAccountType=%d)(sAMAccountType=%d)(sAMAccountType=%d)" /* user account types */
-                                   "(sAMAccountType=%d)(sAMAccountType=%d)" /* group account types */
-                                 ")(|",
-                                       ATYPE_NORMAL_ACCOUNT, ATYPE_WORKSTATION_TRUST, ATYPE_INTERDOMAIN_TRUST,
-                                       ATYPE_SECURITY_GLOBAL_GROUP, ATYPE_SECURITY_LOCAL_GROUP);
+       filter = talloc_asprintf(memctx, "(&(|"
+                                "(sAMAccountType=%d)(sAMAccountType=%d)(sAMAccountType=%d)" /* user account types */
+                                "(sAMAccountType=%d)(sAMAccountType=%d)" /* group account types */
+                                ")(|",
+                                ATYPE_NORMAL_ACCOUNT, ATYPE_WORKSTATION_TRUST, ATYPE_INTERDOMAIN_TRUST,
+                                ATYPE_SECURITY_GLOBAL_GROUP, ATYPE_SECURITY_LOCAL_GROUP);
                
-               CHECK_ALLOC_DONE(filter);
+       CHECK_ALLOC_DONE(filter);
 
-               bidx = idx;
-               for (i = 0; (i < IDMAP_AD_MAX_IDS) && ids[idx]; i++, idx++) {
+       bidx = idx;
+       for (i = 0; (i < IDMAP_AD_MAX_IDS) && ids[idx]; i++, idx++) {
 
-                       sidstr = sid_binstring(ids[idx]->sid);
-                       filter = talloc_asprintf_append(filter, "(objectSid=%s)", sidstr);
+               sidstr = sid_binstring(ids[idx]->sid);
+               filter = talloc_asprintf_append(filter, "(objectSid=%s)", sidstr);
                        
-                       free(sidstr);
-                       CHECK_ALLOC_DONE(filter);
-               }
-               filter = talloc_asprintf_append(filter, "))");
+               free(sidstr);
                CHECK_ALLOC_DONE(filter);
-               DEBUG(10, ("Filter: [%s]\n", filter));
-       } else {
-               bidx = 0;
-               idx = 1;
        }
+       filter = talloc_asprintf_append(filter, "))");
+       CHECK_ALLOC_DONE(filter);
+       DEBUG(10, ("Filter: [%s]\n", filter));
 
        rc = ads_search_retry(ads, &res, filter, attrs);
        if (!ADS_ERR_OK(rc)) {
@@ -578,13 +522,12 @@ again:
                goto done;
        }
 
-       count = ads_count_replies(ads, res);
-       if (count == 0) {
+       if ( (count = ads_count_replies(ads, res)) == 0 ) {
                DEBUG(10, ("No IDs found\n"));
        }
 
-       for (i = 0; i < count; i++) {
-               LDAPMessage *entry = NULL;
+       entry = res;    
+       for (i = 0; (i < count) && entry; i++) {
                DOM_SID sid;
                enum id_type type;
                struct id_map *map;
@@ -592,13 +535,14 @@ again:
                uint32_t atype;
 
                if (i == 0) { /* first entry */
-                       entry = ads_first_entry(ads, res);
+                       entry = ads_first_entry(ads, entry);
                } else { /* following ones */
                        entry = ads_next_entry(ads, entry);
                }
-               if ( ! entry) {
+
+               if ( !entry ) {
                        DEBUG(2, ("ERROR: Unable to fetch ldap entries from results\n"));
-                       continue;
+                       break;
                }
 
                /* first check if the SID is present */
@@ -634,7 +578,11 @@ again:
                        continue;
                }
 
-               if (!ads_pull_uint32(ads, entry, (type==ID_TYPE_UID)?attr_uidnumber:attr_gidnumber, &id)) {
+               if (!ads_pull_uint32(ads, entry, (type==ID_TYPE_UID) ? 
+                                                ad_schema->posix_uidnumber_attr : 
+                                                ad_schema->posix_gidnumber_attr, 
+                                    &id)) 
+               {
                        DEBUG(1, ("Could not get unix ID\n"));
                        continue;
                }
@@ -649,7 +597,7 @@ again:
                /* mapped */
                map->xid.type = type;
                map->xid.id = id;
-               map->mapped = True;
+               map->status = ID_MAPPED;
 
                DEBUG(10, ("Mapped %s -> %lu (%d)\n",
                           sid_string_static(map->sid),
@@ -661,16 +609,26 @@ again:
                ads_msgfree(ads, res);
        }
 
-       if (multi && ids[idx]) { /* still some values to map */
+       if (ids[idx]) { /* still some values to map */
                goto again;
        }
 
        ret = NT_STATUS_OK;
+
+       /* mark all unknwon ones as unmapped */
+       for (i = 0; ids[i]; i++) {
+               if (ids[i]->status == ID_UNKNOWN) 
+                       ids[i]->status = ID_UNMAPPED;
+       }
+
 done:
        talloc_free(memctx);
        return ret;
 }
 
+/************************************************************************
+ ***********************************************************************/
+
 static NTSTATUS idmap_ad_close(struct idmap_domain *dom)
 {
        ADS_STRUCT *ads = ad_idmap_ads;
@@ -682,22 +640,170 @@ static NTSTATUS idmap_ad_close(struct idmap_domain *dom)
                ad_idmap_ads = NULL;
        }
 
-       SAFE_FREE(attr_uidnumber);
-       SAFE_FREE(attr_gidnumber);
+       TALLOC_FREE( ad_schema );
+       
+       return NT_STATUS_OK;
+}
+
+/*
+ * nss_info_{sfu,rfc2307}
+ */
+
+/************************************************************************
+ Initialize the {sfu,rfc2307} state
+ ***********************************************************************/
+
+static NTSTATUS nss_sfu_init( struct nss_domain_entry *e )
+{
+       /* Sanity check if we have previously been called with a
+          different schema model */
+
+       if ( (ad_map_type != WB_POSIX_MAP_UNKNOWN) &&
+            (ad_map_type != WB_POSIX_MAP_SFU) ) 
+       {
+               DEBUG(0,("nss_sfu_init: Posix Map type has already been set.  "
+                        "Mixed schema models not supported!\n"));
+               return NT_STATUS_NOT_SUPPORTED;
+       }
+       
+       ad_map_type =  WB_POSIX_MAP_SFU;        
+
+       if ( !ad_idmap_ads ) 
+               return idmap_ad_initialize( NULL, NULL );       
+
+       return NT_STATUS_OK;
+}
+
+static NTSTATUS nss_rfc2307_init( struct nss_domain_entry *e )
+{
+       /* Sanity check if we have previously been called with a
+          different schema model */
+        
+       if ( (ad_map_type != WB_POSIX_MAP_UNKNOWN) &&
+            (ad_map_type != WB_POSIX_MAP_RFC2307) ) 
+       {
+               DEBUG(0,("nss_rfc2307_init: Posix Map type has already been set.  "
+                        "Mixed schema models not supported!\n"));
+               return NT_STATUS_NOT_SUPPORTED;
+       }
+       
+       ad_map_type =  WB_POSIX_MAP_RFC2307;
+
+       if ( !ad_idmap_ads ) 
+               return idmap_ad_initialize( NULL, NULL );       
+
+       return NT_STATUS_OK;
+}
+
+
+/************************************************************************
+ ***********************************************************************/
+static NTSTATUS nss_ad_get_info( struct nss_domain_entry *e, 
+                                 const DOM_SID *sid, 
+                                 TALLOC_CTX *ctx,
+                                 ADS_STRUCT *ads, 
+                                 LDAPMessage *msg,
+                                 char **homedir,
+                                 char **shell, 
+                                 char **gecos,
+                                 uint32 *gid )
+{
+       ADS_STRUCT *ads_internal = NULL;
+
+       /* We are assuming that the internal ADS_STRUCT is for the 
+          same forest as the incoming *ads pointer */
+
+       ads_internal = ad_idmap_cached_connection();
+
+       if ( !ads_internal || !ad_schema )
+               return NT_STATUS_OBJECT_NAME_NOT_FOUND;
        
+       if ( !homedir || !shell || !gecos )
+               return NT_STATUS_INVALID_PARAMETER;
+
+       *homedir = ads_pull_string( ads, ctx, msg, ad_schema->posix_homedir_attr );
+       *shell   = ads_pull_string( ads, ctx, msg, ad_schema->posix_shell_attr );
+       *gecos   = ads_pull_string( ads, ctx, msg, ad_schema->posix_gecos_attr );
+       
+       if ( gid ) {            
+               if ( !ads_pull_uint32(ads, msg, ad_schema->posix_gidnumber_attr, gid ) )
+                       *gid = 0;               
+       }
+               
        return NT_STATUS_OK;
 }
 
+/************************************************************************
+ ***********************************************************************/
+
+static NTSTATUS nss_ad_close( void )
+{
+       /* nothing to do.  All memory is free()'d by the idmap close_fn() */
+
+       return NT_STATUS_OK;
+}
+
+/************************************************************************
+ Function dispatch tables for the idmap and nss plugins
+ ***********************************************************************/
+
 static struct idmap_methods ad_methods = {
-       .init = idmap_ad_initialize,
+       .init            = idmap_ad_initialize,
        .unixids_to_sids = idmap_ad_unixids_to_sids,
        .sids_to_unixids = idmap_ad_sids_to_unixids,
-       .close_fn = idmap_ad_close
+       .close_fn        = idmap_ad_close
+};
+
+/* The SFU and RFC2307 NSS plugins share everything but the init
+   function which sets the intended schema model to use */
+  
+static struct nss_info_methods nss_rfc2307_methods = {
+       .init         = nss_rfc2307_init,
+       .get_nss_info = nss_ad_get_info,
+       .close_fn     = nss_ad_close
+};
+
+static struct nss_info_methods nss_sfu_methods = {
+       .init         = nss_sfu_init,
+       .get_nss_info = nss_ad_get_info,
+       .close_fn     = nss_ad_close
 };
 
-/* support for new authentication subsystem */
+
+/************************************************************************
+ Initialize the plugins
+ ***********************************************************************/
+
 NTSTATUS idmap_ad_init(void)
 {
-       return smb_register_idmap(SMB_IDMAP_INTERFACE_VERSION, "ad", &ad_methods);
+       static NTSTATUS status_idmap_ad = NT_STATUS_UNSUCCESSFUL;
+       static NTSTATUS status_nss_rfc2307 = NT_STATUS_UNSUCCESSFUL;
+       static NTSTATUS status_nss_sfu = NT_STATUS_UNSUCCESSFUL;
+
+       /* Always register the AD method first in order to get the
+          idmap_domain interface called */
+
+       if ( !NT_STATUS_IS_OK(status_idmap_ad) ) {
+               status_idmap_ad = smb_register_idmap(SMB_IDMAP_INTERFACE_VERSION, 
+                                                    "ad", &ad_methods);
+               if ( !NT_STATUS_IS_OK(status_idmap_ad) )
+                       return status_idmap_ad;         
+       }
+       
+       if ( !NT_STATUS_IS_OK( status_nss_rfc2307 ) ) {
+               status_nss_rfc2307 = smb_register_idmap_nss(SMB_NSS_INFO_INTERFACE_VERSION,
+                                                           "rfc2307",  &nss_rfc2307_methods );         
+               if ( !NT_STATUS_IS_OK(status_nss_rfc2307) )
+                       return status_nss_rfc2307;
+       }
+
+       if ( !NT_STATUS_IS_OK( status_nss_sfu ) ) {
+               status_nss_sfu = smb_register_idmap_nss(SMB_NSS_INFO_INTERFACE_VERSION,
+                                                       "sfu",  &nss_sfu_methods );             
+               if ( !NT_STATUS_IS_OK(status_nss_sfu) )
+                       return status_nss_sfu;          
+       }
+
+       return NT_STATUS_OK;    
 }