Initial import
[samba] / source / passdb / lookup_sid.c
diff --git a/source/passdb/lookup_sid.c b/source/passdb/lookup_sid.c
new file mode 100644 (file)
index 0000000..b397e08
--- /dev/null
@@ -0,0 +1,529 @@
+/* 
+   Unix SMB/CIFS implementation.
+   uid/user handling
+   Copyright (C) Andrew Tridgell         1992-1998
+   Copyright (C) Gerald (Jerry) Carter   2003
+   
+   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 2 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, write to the Free Software
+   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+
+/*****************************************************************
+ *THE CANONICAL* convert name to SID function.
+ Tries local lookup first - for local domains - then uses winbind.
+*****************************************************************/  
+
+BOOL lookup_name(const char *domain, const char *name, DOM_SID *psid, enum SID_NAME_USE *name_type)
+{
+       fstring sid;
+       BOOL local_lookup = False;
+       
+       *name_type = SID_NAME_UNKNOWN;
+
+       /* If we are looking up a domain user, make sure it is
+          for the local machine only */
+       
+       if (strequal(domain, get_global_sam_name())) {
+               if (local_lookup_name(name, psid, name_type)) {
+                       DEBUG(10,
+                             ("lookup_name: (local) [%s]\\[%s] -> SID %s (type %s: %u)\n",
+                              domain, name, sid_to_string(sid,psid),
+                              sid_type_lookup(*name_type), (unsigned int)*name_type));
+                       return True;
+               }
+       } else {
+               /* Remote */
+               if (winbind_lookup_name(domain, name, psid, name_type)) {
+                       
+                       DEBUG(10,("lookup_name (winbindd): [%s]\\[%s] -> SID %s (type %u)\n",
+                                 domain, name, sid_to_string(sid, psid), 
+                                 (unsigned int)*name_type));
+                       return True;
+               }
+       }
+       
+       DEBUG(10, ("lookup_name: %s lookup for [%s]\\[%s] failed\n", 
+                  local_lookup ? "local" : "winbind", domain, name));
+
+       return False;
+}
+
+/*****************************************************************
+ *THE CANONICAL* convert SID to name function.
+ Tries local lookup first - for local sids, then tries winbind.
+*****************************************************************/  
+
+BOOL lookup_sid(const DOM_SID *sid, fstring dom_name, fstring name,
+               enum SID_NAME_USE *name_type)
+{
+       if (!name_type)
+               return False;
+
+       *name_type = SID_NAME_UNKNOWN;
+
+       /* Check if this is our own sid.  This should perhaps be done by
+          winbind?  For the moment handle it here. */
+
+       if (sid_check_is_domain(sid)) {
+               fstrcpy(dom_name, get_global_sam_name());
+               fstrcpy(name, "");
+               *name_type = SID_NAME_DOMAIN;
+               return True;
+       }
+
+       if (sid_check_is_in_our_domain(sid)) {
+               uint32 rid;
+               SMB_ASSERT(sid_peek_rid(sid, &rid));
+
+               /* For our own domain passdb is responsible */
+               fstrcpy(dom_name, get_global_sam_name());
+               return lookup_global_sam_rid(rid, name, name_type);
+       }
+
+       if (sid_check_is_builtin(sid)) {
+
+               /* Got through map_domain_sid_to_name here so that the mapping
+                * of S-1-5-32 to the name "BUILTIN" in as few places as
+                * possible. We might add i18n... */
+               SMB_ASSERT(map_domain_sid_to_name(sid, dom_name));
+
+               /* Yes, W2k3 returns "BUILTIN" both as domain and name here */
+               fstrcpy(name, dom_name); 
+
+               *name_type = SID_NAME_DOMAIN;
+               return True;
+       }
+
+       if (sid_check_is_in_builtin(sid)) {
+               uint32 rid;
+
+               SMB_ASSERT(sid_peek_rid(sid, &rid));
+
+               /* Got through map_domain_sid_to_name here so that the mapping
+                * of S-1-5-32 to the name "BUILTIN" in as few places as
+                * possible. We might add i18n... */
+               SMB_ASSERT(map_domain_sid_to_name(&global_sid_Builtin,
+                                                 dom_name));
+
+               /* There's only aliases in S-1-5-32 */
+               *name_type = SID_NAME_ALIAS;
+
+               return lookup_builtin_rid(rid, name);
+       }
+
+       if (winbind_lookup_sid(sid, dom_name, name, name_type)) {
+               return True;
+       }
+
+       DEBUG(10,("lookup_sid: winbind lookup for SID %s failed - trying "
+                 "special SIDs.\n", sid_string_static(sid)));
+
+       {
+               const char *dom, *obj_name;
+               
+               if (lookup_special_sid(sid, &dom, &obj_name, name_type)) {
+                       DEBUG(10, ("found %s\\%s\n", dom, obj_name));
+                       fstrcpy(dom_name, dom);
+                       fstrcpy(name, obj_name);
+                       return True;
+               }
+       }
+
+       DEBUG(10, ("lookup_sid failed\n"));
+
+       return False;
+}
+
+/*****************************************************************
+ Id mapping cache.  This is to avoid Winbind mappings already
+ seen by smbd to be queried too frequently, keeping winbindd
+ busy, and blocking smbd while winbindd is busy with other
+ stuff. Written by Michael Steffens <michael.steffens@hp.com>,
+ modified to use linked lists by jra.
+*****************************************************************/  
+
+#define MAX_UID_SID_CACHE_SIZE 100
+#define TURNOVER_UID_SID_CACHE_SIZE 10
+#define MAX_GID_SID_CACHE_SIZE 100
+#define TURNOVER_GID_SID_CACHE_SIZE 10
+
+static size_t n_uid_sid_cache = 0;
+static size_t n_gid_sid_cache = 0;
+
+static struct uid_sid_cache {
+       struct uid_sid_cache *next, *prev;
+       uid_t uid;
+       DOM_SID sid;
+       enum SID_NAME_USE sidtype;
+} *uid_sid_cache_head;
+
+static struct gid_sid_cache {
+       struct gid_sid_cache *next, *prev;
+       gid_t gid;
+       DOM_SID sid;
+       enum SID_NAME_USE sidtype;
+} *gid_sid_cache_head;
+
+/*****************************************************************
+  Find a SID given a uid.
+*****************************************************************/  
+
+static BOOL fetch_sid_from_uid_cache(DOM_SID *psid, uid_t uid)
+{
+       struct uid_sid_cache *pc;
+
+       for (pc = uid_sid_cache_head; pc; pc = pc->next) {
+               if (pc->uid == uid) {
+                       fstring sid;
+                       *psid = pc->sid;
+                       DEBUG(3,("fetch sid from uid cache %u -> %s\n",
+                               (unsigned int)uid, sid_to_string(sid, psid)));
+                       DLIST_PROMOTE(uid_sid_cache_head, pc);
+                       return True;
+               }
+       }
+       return False;
+}
+
+/*****************************************************************
+  Find a uid given a SID.
+*****************************************************************/  
+
+static BOOL fetch_uid_from_cache( uid_t *puid, const DOM_SID *psid )
+{
+       struct uid_sid_cache *pc;
+
+       for (pc = uid_sid_cache_head; pc; pc = pc->next) {
+               if (sid_compare(&pc->sid, psid) == 0) {
+                       fstring sid;
+                       *puid = pc->uid;
+                       DEBUG(3,("fetch uid from cache %u -> %s\n",
+                               (unsigned int)*puid, sid_to_string(sid, psid)));
+                       DLIST_PROMOTE(uid_sid_cache_head, pc);
+                       return True;
+               }
+       }
+       return False;
+}
+
+/*****************************************************************
+ Store uid to SID mapping in cache.
+*****************************************************************/  
+
+static void store_uid_sid_cache(const DOM_SID *psid, uid_t uid)
+{
+       struct uid_sid_cache *pc;
+
+       if (n_uid_sid_cache >= MAX_UID_SID_CACHE_SIZE && n_uid_sid_cache > TURNOVER_UID_SID_CACHE_SIZE) {
+               /* Delete the last TURNOVER_UID_SID_CACHE_SIZE entries. */
+               struct uid_sid_cache *pc_next;
+               size_t i;
+
+               for (i = 0, pc = uid_sid_cache_head; i < (n_uid_sid_cache - TURNOVER_UID_SID_CACHE_SIZE); i++, pc = pc->next)
+                       ;
+               for(; pc; pc = pc_next) {
+                       pc_next = pc->next;
+                       DLIST_REMOVE(uid_sid_cache_head,pc);
+                       SAFE_FREE(pc);
+                       n_uid_sid_cache--;
+               }
+       }
+
+       pc = SMB_MALLOC_P(struct uid_sid_cache);
+       if (!pc)
+               return;
+       pc->uid = uid;
+       sid_copy(&pc->sid, psid);
+       DLIST_ADD(uid_sid_cache_head, pc);
+       n_uid_sid_cache++;
+}
+
+/*****************************************************************
+  Find a SID given a gid.
+*****************************************************************/  
+
+static BOOL fetch_sid_from_gid_cache(DOM_SID *psid, gid_t gid)
+{
+       struct gid_sid_cache *pc;
+
+       for (pc = gid_sid_cache_head; pc; pc = pc->next) {
+               if (pc->gid == gid) {
+                       fstring sid;
+                       *psid = pc->sid;
+                       DEBUG(3,("fetch sid from gid cache %u -> %s\n",
+                               (unsigned int)gid, sid_to_string(sid, psid)));
+                       DLIST_PROMOTE(gid_sid_cache_head, pc);
+                       return True;
+               }
+       }
+       return False;
+}
+
+/*****************************************************************
+  Find a gid given a SID.
+*****************************************************************/  
+
+static BOOL fetch_gid_from_cache(gid_t *pgid, const DOM_SID *psid)
+{
+       struct gid_sid_cache *pc;
+
+       for (pc = gid_sid_cache_head; pc; pc = pc->next) {
+               if (sid_compare(&pc->sid, psid) == 0) {
+                       fstring sid;
+                       *pgid = pc->gid;
+                       DEBUG(3,("fetch gid from cache %u -> %s\n",
+                                (unsigned int)*pgid, sid_to_string(sid, psid)));
+                       DLIST_PROMOTE(gid_sid_cache_head, pc);
+                       return True;
+               }
+       }
+       return False;
+}
+
+/*****************************************************************
+ Store gid to SID mapping in cache.
+*****************************************************************/  
+
+static void store_gid_sid_cache(const DOM_SID *psid, gid_t gid)
+{
+       struct gid_sid_cache *pc;
+
+       if (n_gid_sid_cache >= MAX_GID_SID_CACHE_SIZE && n_gid_sid_cache > TURNOVER_GID_SID_CACHE_SIZE) {
+               /* Delete the last TURNOVER_GID_SID_CACHE_SIZE entries. */
+               struct gid_sid_cache *pc_next;
+               size_t i;
+
+               for (i = 0, pc = gid_sid_cache_head; i < (n_gid_sid_cache - TURNOVER_GID_SID_CACHE_SIZE); i++, pc = pc->next)
+                       ;
+               for(; pc; pc = pc_next) {
+                       pc_next = pc->next;
+                       DLIST_REMOVE(gid_sid_cache_head,pc);
+                       SAFE_FREE(pc);
+                       n_gid_sid_cache--;
+               }
+       }
+
+       pc = SMB_MALLOC_P(struct gid_sid_cache);
+       if (!pc)
+               return;
+       pc->gid = gid;
+       sid_copy(&pc->sid, psid);
+       DLIST_ADD(gid_sid_cache_head, pc);
+       n_gid_sid_cache++;
+}
+
+/*****************************************************************
+ *THE CANONICAL* convert uid_t to SID function.
+*****************************************************************/  
+
+NTSTATUS uid_to_sid(DOM_SID *psid, uid_t uid)
+{
+       fstring sid;
+       uid_t low, high;
+
+       ZERO_STRUCTP(psid);
+
+       if (fetch_sid_from_uid_cache(psid, uid))
+               return ( psid ? NT_STATUS_OK : NT_STATUS_UNSUCCESSFUL );
+
+       /* DC's never use winbindd to resolve users outside the 
+          defined idmap range */
+
+       if ( lp_server_role()==ROLE_DOMAIN_MEMBER 
+               || (lp_idmap_uid(&low, &high) && uid >= low && uid <= high) ) 
+       {
+               if (winbind_uid_to_sid(psid, uid)) {
+
+                       DEBUG(10,("uid_to_sid: winbindd %u -> %s\n",
+                               (unsigned int)uid, sid_to_string(sid, psid)));
+
+                       if (psid)
+                               store_uid_sid_cache(psid, uid);
+                       return ( psid ? NT_STATUS_OK : NT_STATUS_UNSUCCESSFUL );
+               }
+       }
+
+       if (!local_uid_to_sid(psid, uid)) {
+               DEBUG(10,("uid_to_sid: local %u failed to map to sid\n", (unsigned int)uid ));
+               return NT_STATUS_UNSUCCESSFUL;
+       }
+        
+       DEBUG(10,("uid_to_sid: local %u -> %s\n", (unsigned int)uid, sid_to_string(sid, psid)));
+
+       store_uid_sid_cache(psid, uid);
+       return NT_STATUS_OK;
+}
+
+/*****************************************************************
+ *THE CANONICAL* convert gid_t to SID function.
+*****************************************************************/  
+
+NTSTATUS gid_to_sid(DOM_SID *psid, gid_t gid)
+{
+       fstring sid;
+       gid_t low, high;
+
+       ZERO_STRUCTP(psid);
+
+       if (fetch_sid_from_gid_cache(psid, gid))
+               return ( psid ? NT_STATUS_OK : NT_STATUS_UNSUCCESSFUL );
+
+       /* DC's never use winbindd to resolve groups outside the
+          defined idmap range */
+
+       if ( lp_server_role()==ROLE_DOMAIN_MEMBER
+               || (lp_idmap_gid(&low, &high) && gid >= low && gid <= high) )
+        {
+               if (winbind_gid_to_sid(psid, gid)) {
+
+                       DEBUG(10,("gid_to_sid: winbindd %u -> %s\n",
+                               (unsigned int)gid, sid_to_string(sid, psid)));
+                        
+                       if (psid)
+                               store_gid_sid_cache(psid, gid);
+                       return ( psid ? NT_STATUS_OK : NT_STATUS_UNSUCCESSFUL );
+               }
+       }
+
+       if (!local_gid_to_sid(psid, gid)) {
+               DEBUG(10,("gid_to_sid: local %u failed to map to sid\n", (unsigned int)gid ));
+               return NT_STATUS_UNSUCCESSFUL;
+       }
+        
+       DEBUG(10,("gid_to_sid: local %u -> %s\n", (unsigned int)gid, sid_to_string(sid, psid)));
+
+       store_gid_sid_cache(psid, gid);
+       return NT_STATUS_OK;
+}
+
+/*****************************************************************
+ *THE CANONICAL* convert SID to uid function.
+*****************************************************************/  
+
+NTSTATUS sid_to_uid(const DOM_SID *psid, uid_t *puid)
+{
+       fstring dom_name, name, sid_str;
+       enum SID_NAME_USE name_type;
+
+       if (fetch_uid_from_cache(puid, psid))
+               return NT_STATUS_OK;
+
+       /* if this is our SID then go straight to a local lookup */
+       
+       if ( sid_compare_domain(get_global_sam_sid(), psid) == 0 ) {
+               DEBUG(10,("sid_to_uid: my domain (%s) - trying local.\n",
+                       sid_string_static(psid) ));
+               
+               if ( local_sid_to_uid(puid, psid, &name_type) )
+                       goto success;
+                       
+               DEBUG(10,("sid_to_uid: local lookup failed\n"));
+               
+               return NT_STATUS_UNSUCCESSFUL;
+       }
+       
+       /* If it is not our local domain, only hope is winbindd */
+
+       if ( !winbind_lookup_sid(psid, dom_name, name, &name_type) ) {
+               DEBUG(10,("sid_to_uid: winbind lookup for non-local sid %s failed\n",
+                       sid_string_static(psid) ));
+                       
+               return NT_STATUS_UNSUCCESSFUL;
+       }
+
+       /* If winbindd does know the SID, ensure this is a user */
+
+       if (name_type != SID_NAME_USER) {
+               DEBUG(10,("sid_to_uid: winbind lookup succeeded but SID is not a user (%u)\n",
+                       (unsigned int)name_type ));
+               return NT_STATUS_INVALID_PARAMETER;
+       }
+
+       /* get the uid.  Has to work or else we are dead in the water */
+
+       if ( !winbind_sid_to_uid(puid, psid) ) {
+               DEBUG(10,("sid_to_uid: winbind failed to allocate a new uid for sid %s\n",
+                       sid_to_string(sid_str, psid) ));
+               return NT_STATUS_UNSUCCESSFUL;
+       }
+
+success:
+       DEBUG(10,("sid_to_uid: %s -> %u\n", sid_to_string(sid_str, psid),
+               (unsigned int)*puid ));
+
+       store_uid_sid_cache(psid, *puid);
+       
+       return NT_STATUS_OK;
+}
+/*****************************************************************
+ *THE CANONICAL* convert SID to gid function.
+ Group mapping is used for gids that maps to Wellknown SIDs
+*****************************************************************/  
+
+NTSTATUS sid_to_gid(const DOM_SID *psid, gid_t *pgid)
+{
+       fstring dom_name, name, sid_str;
+       enum SID_NAME_USE name_type;
+
+       if (fetch_gid_from_cache(pgid, psid))
+               return NT_STATUS_OK;
+
+       /*
+        * First we must look up the name and decide if this is a group sid.
+        * Group mapping can deal with foreign SIDs
+        */
+
+       if ( local_sid_to_gid(pgid, psid, &name_type) )
+               goto success;
+       
+       if (!winbind_lookup_sid(psid, dom_name, name, &name_type)) {
+               DEBUG(10,("sid_to_gid: no one knows the SID %s (tried local, then winbind)\n", sid_to_string(sid_str, psid)));
+               
+               return NT_STATUS_UNSUCCESSFUL;
+       }
+
+       /* winbindd knows it; Ensure this is a group sid */
+
+       if ((name_type != SID_NAME_DOM_GRP) && (name_type != SID_NAME_ALIAS) 
+               && (name_type != SID_NAME_WKN_GRP)) 
+       {
+               DEBUG(10,("sid_to_gid: winbind lookup succeeded but SID is not a known group (%u)\n",
+                       (unsigned int)name_type ));
+
+               /* winbindd is running and knows about this SID.  Just the wrong type.
+                  Don't fallback to a local lookup here */
+                  
+               return NT_STATUS_INVALID_PARAMETER;
+       }
+       
+       /* winbindd knows it and it is a type of group; sid_to_gid must succeed
+          or we are dead in the water */
+
+       if ( !winbind_sid_to_gid(pgid, psid) ) {
+               DEBUG(10,("sid_to_gid: winbind failed to allocate a new gid for sid %s\n",
+                       sid_to_string(sid_str, psid) ));
+               return NT_STATUS_UNSUCCESSFUL;
+       }
+
+success:
+       DEBUG(10,("sid_to_gid: %s -> %u\n", sid_to_string(sid_str, psid),
+               (unsigned int)*pgid ));
+
+       store_gid_sid_cache(psid, *pgid);
+       
+       return NT_STATUS_OK;
+}
+