first pass at updating head branch to be to be the same as the SAMBA_2_0 branch
[ira/wip.git] / source / lib / username.c
index 23bdb54faf29a4479068caf9e678723d0e95e1bc..9a189980d5d0f896454043859e13fa6c284c200e 100644 (file)
@@ -27,306 +27,31 @@ static struct passwd *uname_string_combinations(char *s, struct passwd * (*fn) (
 static struct passwd *uname_string_combinations2(char *s, int offset, struct passwd * (*fn) (char *), int N);
 
 /****************************************************************************
-  Since getpwnam() makes samba really slow with the NT-domain code
-  (reading /etc/passwd again and again and again), here is an implementation
-  of very simple passwd cache
+ Get a users home directory.
 ****************************************************************************/
-#define PASSWD_HASH_SIZE 1009
-/* The hashtable is rebuild every 15 seconds */
-#define PASSWD_HASH_AGE 15
-struct passwd_hash_entry {
-  int entry;
-  int next;
-};
-
-struct passwd_hash_table_s {
-  struct passwd *passwds;
-  int passwds_size;  
-  int *names;
-  int *uids;
-  struct passwd_hash_entry *entries;
-  int entries_size;
-  struct timeval build_time;
-} passwd_hash_table = {
-  NULL,0,NULL,NULL,NULL,0,{0,0}
-};
-
-static int name_hash_function(const char *name) 
-{
-  /* I guess that there must be better hash functions. This one was the
-   * first to come into mind :) */
-  unsigned int value=0;
-  while (*name) {
-    value=(value<<8)|(unsigned char)(*name);
-    if (value>1048576) value=value%PASSWD_HASH_SIZE;
-    name++;
-  }
-  value=value%PASSWD_HASH_SIZE;
-  return value;
-}
-    
-static int uid_hash_function(uid_t uid) 
-{
-  return uid%PASSWD_HASH_SIZE;
-}
-
 
-static BOOL build_passwd_hash_table(void) 
+char *get_user_home_dir(char *user)
 {
-  struct passwd_hash_table_s *pht=&passwd_hash_table; /* Convenience */
-  int num_passwds=0;
-  int num_entries=0;
-  struct passwd *pass;
-  int i;
-  int name_i,uid_i;
-
-  DEBUG(3,("Building passwd hash table\n"));
-  /* Free the allocated strings in old hash table */
-  for (i=0;i<pht->passwds_size;i++) {
-    free(pht->passwds[i].pw_name); 
-    free(pht->passwds[i].pw_passwd);
-    free(pht->passwds[i].pw_gecos);
-    free(pht->passwds[i].pw_dir);
-    free(pht->passwds[i].pw_shell);    
-  }
-
-  /* Initialize hash table if first table build */
-  if (pht->passwds_size==0) {
-    DEBUG(3,("Building passwd hash table for the first time\n"));
-    pht->passwds=malloc(sizeof(struct passwd)*64); /* A reasonable default */
-    pht->passwds_size=64;
-  }
-  if (pht->names==NULL) {
-    pht->names=malloc(sizeof(struct passwd_hash_entry *)*PASSWD_HASH_SIZE);
-  }
-  if (pht->uids==NULL) {
-    pht->uids=malloc(sizeof(struct passwd_hash_entry *)*PASSWD_HASH_SIZE);
-  }
-  if (pht->entries==NULL) {
-    pht->entries=malloc(sizeof(struct passwd_hash_entry)*128);
-    pht->entries_size=128;
-  }
-  if (pht->passwds==NULL || pht->names==NULL || 
-      pht->uids==NULL || pht->entries==NULL) {
-    goto fail;
-  }
-  
-  /* Clear out the hash table */
-  for(i=0;i<PASSWD_HASH_SIZE;i++) pht->uids[i]=-1;
-  for(i=0;i<PASSWD_HASH_SIZE;i++) pht->names[i]=-1;
-
-  /* Now do the build */
-  setpwent();
-
-  while((pass=getpwent())) {
-
-    /* Check that we have enough space */
-    if (num_passwds==pht->passwds_size) {
-      struct passwd *new_passwds=NULL;
-      pht->passwds_size+=pht->passwds_size/2;
-      new_passwds=realloc(pht->passwds,
-                          sizeof(struct passwd)*pht->passwds_size);
-      if (new_passwds==NULL) goto fail;
-      pht->passwds=new_passwds;
-    }
-    if (num_entries+1>=pht->entries_size) {
-      pht->entries_size+=pht->entries_size/2;
-      pht->entries=realloc(pht->entries,
-                          sizeof(struct passwd_hash_entry)*pht->entries_size);
-      if (pht->entries==NULL) goto fail;
-    }
+  static struct passwd *pass;
 
-    /* Copy the passwd struct */
-    memset(&pht->passwds[num_passwds],0,sizeof(struct passwd));
-    pht->passwds[num_passwds].pw_uid=pass->pw_uid;
-    pht->passwds[num_passwds].pw_gid=pass->pw_gid;  
-    if (
-       (pht->passwds[num_passwds].pw_name=strdup(pass->pw_name))==NULL ||
-       (pht->passwds[num_passwds].pw_passwd=strdup(pass->pw_passwd))==NULL ||
-       (pht->passwds[num_passwds].pw_gecos=strdup(pass->pw_gecos))==NULL ||
-       (pht->passwds[num_passwds].pw_dir=strdup(pass->pw_dir))==NULL ||
-       (pht->passwds[num_passwds].pw_shell=strdup(pass->pw_shell))==NULL ) {
-      num_passwds++;
-      goto fail;
-    }
-    
-    /* Add to the hash table */
-    /* Add the name */
-    pht->entries[num_entries].entry=num_passwds;
-    name_i=name_hash_function(pass->pw_name);
-    pht->entries[num_entries].next=pht->names[name_i];
-    pht->names[name_i]=num_entries;
-    num_entries++;
-    /* Add the uid */
-    pht->entries[num_entries].entry=num_passwds;
-    uid_i=uid_hash_function(pass->pw_uid);
-    pht->entries[num_entries].next=pht->uids[uid_i];
-    pht->uids[uid_i]=num_entries;
-    num_entries++;
-
-    /* This entry has been done */
-    num_passwds++;
-  }    
-  endpwent();
-  
-  if (pht->passwds_size>num_passwds) {
-    struct passwd *passwds;
-    passwds=realloc(pht->passwds,sizeof(pht->passwds[0])*num_passwds);
-    if (passwds==NULL) goto fail;
-    pht->passwds=passwds;
-    pht->passwds_size=num_passwds;
-  }
-  if (pht->entries_size>num_entries) {
-    struct passwd_hash_entry *entries;
-    entries=realloc(pht->entries,sizeof(pht->entries[0])*num_entries);
-    if (entries==NULL) goto fail;
-    pht->entries=entries;
-    pht->entries_size=num_entries;
-  }
+  pass = Get_Pwnam(user, False);
 
-  /* Mark the creation time */
-  GetTimeOfDay(&pht->build_time);
-  /* Everything went smoothly. */
-  return True;
-
- fail:
-  DEBUG(0,("Failed to create passwd hash table: %s",strerror(errno)));
-  /* OK: now the untested part. Normally this should never happen:
-   * Only running out of memory could cause this and even then
-   * we have enough trouble already. */
-  while (num_passwds>0) {
-    num_passwds--;
-    free(pht->passwds[num_passwds].pw_name);
-    free(pht->passwds[num_passwds].pw_passwd);
-    free(pht->passwds[num_passwds].pw_gecos);
-    free(pht->passwds[num_passwds].pw_dir);
-    free(pht->passwds[num_passwds].pw_shell);    
-  }
-  free(pht->entries);
-  free(pht->uids);
-  free(pht->names);
-  free(pht->passwds);
-  pht->passwds_size=0;
-  pht->entries_size=0;    
-  /* Also mark fail time, so that retry will happen after PASSWD_HASH_AGE */
-  GetTimeOfDay(&pht->build_time);
-  return False;
+  if (!pass) return(NULL);
+  return(pass->pw_dir);      
 }
 
-static BOOL have_passwd_hash(void)
-{
-  struct passwd_hash_table_s *pht=&passwd_hash_table;
-  struct timeval tv;
-  GetTimeOfDay(&tv);
-  /* I'm ignoring microseconds. If you think they matter, go ahead
-   * and implement them */
-  if (tv.tv_sec - pht->build_time.tv_sec > PASSWD_HASH_AGE) {
-    return build_passwd_hash_table();
-  }
-  return pht->passwds_size>0;
-}
-
-struct passwd *hashed_getpwnam(const char *name)
-{
-  struct passwd_hash_table_s *pht=&passwd_hash_table;
-
-  DEBUG(5,("getpwnam(%s)\n", name));
-
-  if (have_passwd_hash())
-  {
-    int name_i=name_hash_function(name);
-    int hash_index=pht->names[name_i];
-    while(hash_index!=-1) {
-      struct passwd *pass=&pht->passwds[pht->entries[hash_index].entry];
-      if (strcmp(name,pass->pw_name)==0) {
-       DEBUG(5,("Found: %s:%s:%d:%d:%s:%s:%s\n",
-                pass->pw_name,
-                pass->pw_passwd,
-                pass->pw_uid,
-                pass->pw_gid,
-                pass->pw_gecos,
-                pass->pw_dir,
-                pass->pw_shell));
-       return copy_passwd_struct(pass);      
-      }
-      hash_index=pht->entries[hash_index].next;
-    }
-
-    /* Not found */
-    DEBUG(5,("%s not found\n",name));
-    return NULL;
-  } 
-
-  /* Fall back to real getpwnam() */
-  return sys_getpwnam(name);
-}
 
 /*******************************************************************
-turn a uid into a user name
+ Map a username from a dos name to a unix name by looking in the username
+ map. Note that this modifies the name in place.
+ This is the main function that should be called *once* on
+ any incoming or new username - in order to canonicalize the name.
+ This is being done to de-couple the case conversions from the user mapping
+ function. Previously, the map_username was being called
+ every time Get_Pwnam was called.
+ Returns True if username was changed, false otherwise.
 ********************************************************************/
-char *uidtoname(uid_t uid)
-{
-  static char name[40];
-  struct passwd_hash_table_s *pht=&passwd_hash_table;
-  struct passwd *pass=NULL;
-
-  DEBUG(5,("uidtoname(%d)\n",uid));
-  if (have_passwd_hash()) {
-    int hash_index=pht->uids[uid_hash_function(uid)];
-    while(hash_index!=-1) {
-      pass=&pht->passwds[pht->entries[hash_index].entry];
-      if (pass->pw_uid==uid) {
-       DEBUG(5,("Found: %s:%s:%d:%d:%s:%s:%s\n",
-                pass->pw_name,
-                pass->pw_passwd,
-                pass->pw_uid,
-                pass->pw_gid,
-                pass->pw_gecos,
-                pass->pw_dir,
-                pass->pw_shell));
-       return pass->pw_name;      
-      }
-      hash_index=pht->entries[hash_index].next;
-    }
-    DEBUG(5,("Hash miss"));
-    pass=NULL;
-  } else {
-    /* No hash table, fall back to getpwuid */
-    pass = getpwuid(uid);
-  }
-  if (pass) return(pass->pw_name);
-  slprintf(name, sizeof(name) - 1, "%d",(int)uid);
-  return(name);
-}
-
-/****************************************************************************
-get a users home directory.
-****************************************************************************/
-char *get_unixhome_dir(char *user)
-{
-       const struct passwd *pass;
-       static pstring home_dir;
 
-       pass = Get_Pwnam(user, False);
-
-       if (pass == NULL || pass->pw_dir == NULL) return(NULL);
-
-       pstrcpy(home_dir, pass->pw_dir);
-       DEBUG(10,("get_smbhome_dir: returning %s for user %s\n", home_dir, user));
-       return home_dir;
-}
-
-
-/*******************************************************************
-map a username from a dos name to a unix name by looking in the username
-map. Note that this modifies the name in place.
-This is the main function that should be called *once* on
-any incoming or new username - in order to canonicalize the name.
-This is being done to de-couple the case conversions from the user mapping
-function. Previously, the map_username was being called
-every time Get_Pwnam was called.
-Returns True if username was changed, false otherwise.
-********************************************************************/
 BOOL map_username(char *user)
 {
   static BOOL initialised=False;
@@ -420,38 +145,36 @@ BOOL map_username(char *user)
   return mapped_user;
 }
 
-
 /****************************************************************************
  Get_Pwnam wrapper
 ****************************************************************************/
+
 static struct passwd *_Get_Pwnam(char *s)
 {
-       struct passwd *ret;
+  struct passwd *ret;
 
-       ret = hashed_getpwnam(s);
+  ret = sys_getpwnam(s);
+  if (ret) {
 #ifdef HAVE_GETPWANAM
-       if (ret)
-       {
-               struct passwd_adjunct *pwret;
-               pwret = getpwanam(s);
-               if (pwret != NULL && pwret->pwa_passwd != NULL)
-               {
-                       pstrcpy(ret->pw_passwd, pwret->pwa_passwd);
-               }
-       }
+    struct passwd_adjunct *pwret;
+    pwret = getpwanam(s);
+    if (pwret && pwret->pwa_passwd) {
+      pstrcpy(ret->pw_passwd,pwret->pwa_passwd);
+    }
 #endif
+  }
 
-       return ret;
+  return(ret);
 }
 
+
 /****************************************************************************
-a wrapper for getpwnam() that tries with all lower and all upper case 
-if the initial name fails. Also tried with first letter capitalised
-Note that this can change user!  Function returns const to emphasise
-the fact that most of the members of the struct passwd * returned are
-dynamically allocated.
+ A wrapper for getpwnam() that tries with all lower and all upper case 
+ if the initial name fails. Also tried with first letter capitalised
+ Note that this can change user!
 ****************************************************************************/
-const struct passwd *Get_Pwnam(char *user,BOOL allow_change)
+
+struct passwd *Get_Pwnam(char *user,BOOL allow_change)
 {
   fstring user2;
   int last_char;
@@ -469,33 +192,39 @@ const struct passwd *Get_Pwnam(char *user,BOOL allow_change)
   }
 
   ret = _Get_Pwnam(user);
-  if (ret) return(ret);
+  if (ret)
+    return(ret);
 
   strlower(user);
   ret = _Get_Pwnam(user);
-  if (ret)  return(ret);
+  if (ret)
+    return(ret);
 
   strupper(user);
   ret = _Get_Pwnam(user);
-  if (ret) return(ret);
+  if (ret)
+    return(ret);
 
-  /* try with first letter capitalised */
+  /* Try with first letter capitalised. */
   if (strlen(user) > 1)
     strlower(user+1);  
   ret = _Get_Pwnam(user);
-  if (ret) return(ret);
+  if (ret)
+    return(ret);
 
   /* try with last letter capitalised */
   strlower(user);
   last_char = strlen(user)-1;
   user[last_char] = toupper(user[last_char]);
   ret = _Get_Pwnam(user);
-  if (ret) return(ret);
+  if (ret)
+    return(ret);
 
-  /* try all combinations up to usernamelevel */
+  /* Try all combinations up to usernamelevel. */
   strlower(user);
   ret = uname_string_combinations(user, _Get_Pwnam, usernamelevel);
-  if (ret) return(ret);
+  if (ret)
+    return(ret);
 
   if (allow_change)
     fstrcpy(user,user2);
@@ -504,8 +233,9 @@ const struct passwd *Get_Pwnam(char *user,BOOL allow_change)
 }
 
 /****************************************************************************
-check if a user is in a netgroup user list
+ Check if a user is in a netgroup user list.
 ****************************************************************************/
+
 static BOOL user_in_netgroup_list(char *user,char *ngname)
 {
 #ifdef HAVE_NETGROUP
@@ -513,12 +243,9 @@ static BOOL user_in_netgroup_list(char *user,char *ngname)
   if (mydomain == NULL)
     yp_get_default_domain(&mydomain);
 
-  if(mydomain == NULL)
-  {
+  if(mydomain == NULL) {
     DEBUG(5,("Unable to get default yp domain\n"));
-  }
-  else
-  {
+  } else {
     DEBUG(5,("looking for user %s of domain %s in netgroup %s\n",
           user, mydomain, ngname));
     DEBUG(5,("innetgr is %s\n",
@@ -533,78 +260,51 @@ static BOOL user_in_netgroup_list(char *user,char *ngname)
 }
 
 /****************************************************************************
-check if a user is in a UNIX user list
+ Check if a user is in a UNIX user list.
 ****************************************************************************/
+
 static BOOL user_in_group_list(char *user,char *gname)
 {
-#ifdef HAVE_GETGRNAM 
-  struct group *gptr;
-  char **member;  
-  const struct passwd *pass = Get_Pwnam(user,False);
-
-  if (pass)
-  { 
-    gptr = getgrgid(pass->pw_gid);
-    if (gptr && strequal(gptr->gr_name,gname))
-      return(True); 
-  } 
-
-  gptr = (struct group *)getgrnam(gname);
-
-  if (gptr)
-  {
-    member = gptr->gr_mem;
-    while (member && *member)
-    {
-      if (strequal(*member,user))
-        return(True);
-      member++;
-    }
-  }
+#ifdef HAVE_GETGRENT
+       struct group *gptr;
+       char **member;  
+       struct passwd *pass = Get_Pwnam(user,False);
+
+       if (pass) { 
+               gptr = getgrgid(pass->pw_gid);
+               if (gptr && strequal(gptr->gr_name,gname))
+                       return(True); 
+       } 
+
+       while ((gptr = (struct group *)getgrent())) {
+               if (!strequal(gptr->gr_name,gname))
+                       continue;
+               member = gptr->gr_mem;
+               while (member && *member) {
+                       if (strequal(*member,user)) {
+                               endgrent();
+                               return(True);
+                       }
+                       member++;
+               }
+       }
+
+       endgrent();
 #endif /* HAVE_GETGRNAM */
-  return False;
+       return False;
 }            
 
 /****************************************************************************
-check if a username is valid
+ Check if a user is in a user list - can check combinations of UNIX
+ and netgroup lists.
 ****************************************************************************/
-BOOL user_ok(char *user,int snum)
-{
-       pstring valid, invalid;
-       BOOL ret;
-
-       StrnCpy(valid, lp_valid_users(snum), sizeof(pstring));
-       StrnCpy(invalid, lp_invalid_users(snum), sizeof(pstring));
-
-       string_sub(valid,"%S",lp_servicename(snum));
-       string_sub(invalid,"%S",lp_servicename(snum));
-       
-       ret = !user_in_list(user,invalid);
-       
-       if (ret && valid && *valid) {
-               ret = user_in_list(user,valid);
-       }
-
-       if (ret && lp_onlyuser(snum)) {
-               char *user_list = lp_username(snum);
-               string_sub(user_list,"%S",lp_servicename(snum));
-               ret = user_in_list(user,user_list);
-       }
 
-       return(ret);
-}
-
-/****************************************************************************
-check if a user is in a user list - can check combinations of UNIX
-and netgroup lists.
-****************************************************************************/
 BOOL user_in_list(char *user,char *list)
 {
   pstring tok;
   char *p=list;
 
-  while (next_token(&p,tok,LIST_SEP, sizeof(tok)))
-  {
+  while (next_token(&p,tok,LIST_SEP, sizeof(tok))) {
     /*
      * Check raw username.
      */
@@ -616,8 +316,7 @@ BOOL user_in_list(char *user,char *list)
      * of UNIX and netgroups has been specified.
      */
 
-    if(*tok == '@')
-    {
+    if(*tok == '@') {
       /*
        * Old behaviour. Check netgroup list
        * followed by UNIX list.
@@ -626,11 +325,9 @@ BOOL user_in_list(char *user,char *list)
         return True;
       if(user_in_group_list(user,&tok[1]))
         return True;
-    }
-    else if (*tok == '+')
-    {
-      if(tok[1] == '&')
-      {
+    } else if (*tok == '+') {
+
+      if(tok[1] == '&') {
         /*
          * Search UNIX list followed by netgroup.
          */
@@ -638,20 +335,20 @@ BOOL user_in_list(char *user,char *list)
           return True;
         if(user_in_netgroup_list(user,&tok[2]))
           return True;
-      }
-      else
-      {
+
+      } else {
+
         /*
          * Just search UNIX list.
          */
+
         if(user_in_group_list(user,&tok[1]))
           return True;
       }
-    }
-    else if (*tok == '&')
-    {
-      if(tok[1] == '&')
-      {
+
+    } else if (*tok == '&') {
+
+      if(tok[1] == '+') {
         /*
          * Search netgroup list followed by UNIX list.
          */
@@ -659,9 +356,7 @@ BOOL user_in_list(char *user,char *list)
           return True;
         if(user_in_group_list(user,&tok[2]))
           return True;
-      }
-      else
-      {
+      } else {
         /*
          * Just search netgroup list.
          */
@@ -675,15 +370,16 @@ BOOL user_in_list(char *user,char *list)
 
 /* The functions below have been taken from password.c and slightly modified */
 /****************************************************************************
-apply a function to upper/lower case combinations
-of a string and return true if one of them returns true.
-try all combinations with N uppercase letters.
-offset is the first char to try and change (start with 0)
-it assumes the string starts lowercased
+ Apply a function to upper/lower case combinations
+ of a string and return true if one of them returns true.
+ Try all combinations with N uppercase letters.
+ offset is the first char to try and change (start with 0)
+ it assumes the string starts lowercased
 ****************************************************************************/
+
 static struct passwd *uname_string_combinations2(char *s,int offset,struct passwd *(*fn)(char *),int N)
 {
-  int len = strlen(s);
+  ssize_t len = (ssize_t)strlen(s);
   int i;
   struct passwd *ret;
 
@@ -694,36 +390,36 @@ static struct passwd *uname_string_combinations2(char *s,int offset,struct passw
   if (N <= 0 || offset >= len)
     return(fn(s));
 
-
-  for (i=offset;i<(len-(N-1));i++)
-
-    {
-      char c = s[i];
-      if (!islower(c)) continue;
-      s[i] = toupper(c);
-      ret = uname_string_combinations2(s,i+1,fn,N-1);
-      if(ret) return(ret);
-      s[i] = c;
-    }
+  for (i=offset;i<(len-(N-1));i++) {
+    char c = s[i];
+    if (!islower(c))
+      continue;
+    s[i] = toupper(c);
+    ret = uname_string_combinations2(s,i+1,fn,N-1);
+    if(ret)
+      return(ret);
+    s[i] = c;
+  }
   return(NULL);
 }
 
 /****************************************************************************
-apply a function to upper/lower case combinations
-of a string and return true if one of them returns true.
-try all combinations with up to N uppercase letters.
-offset is the first char to try and change (start with 0)
-it assumes the string starts lowercased
+ Apply a function to upper/lower case combinations
+ of a string and return true if one of them returns true.
+ Try all combinations with up to N uppercase letters.
+ offset is the first char to try and change (start with 0)
+ it assumes the string starts lowercased
 ****************************************************************************/
+
 static struct passwd * uname_string_combinations(char *s,struct passwd * (*fn)(char *),int N)
 {
   int n;
   struct passwd *ret;
 
-  for (n=1;n<=N;n++)
-  {
+  for (n=1;n<=N;n++) {
     ret = uname_string_combinations2(s,0,fn,n);
-    if(ret) return(ret);
+    if(ret)
+      return(ret);
   }
   return(NULL);
 }