23bdb54faf29a4479068caf9e678723d0e95e1bc
[tprouty/samba.git] / source3 / lib / username.c
1 /* 
2    Unix SMB/Netbios implementation.
3    Version 1.9.
4    Username handling
5    Copyright (C) Andrew Tridgell 1992-1998
6    
7    This program is free software; you can redistribute it and/or modify
8    it under the terms of the GNU General Public License as published by
9    the Free Software Foundation; either version 2 of the License, or
10    (at your option) any later version.
11    
12    This program is distributed in the hope that it will be useful,
13    but WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15    GNU General Public License for more details.
16    
17    You should have received a copy of the GNU General Public License
18    along with this program; if not, write to the Free Software
19    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20 */
21
22 #include "includes.h"
23 extern int DEBUGLEVEL;
24
25 /* internal functions */
26 static struct passwd *uname_string_combinations(char *s, struct passwd * (*fn) (char *), int N);
27 static struct passwd *uname_string_combinations2(char *s, int offset, struct passwd * (*fn) (char *), int N);
28
29 /****************************************************************************
30   Since getpwnam() makes samba really slow with the NT-domain code
31   (reading /etc/passwd again and again and again), here is an implementation
32   of very simple passwd cache
33 ****************************************************************************/
34 #define PASSWD_HASH_SIZE 1009
35 /* The hashtable is rebuild every 15 seconds */
36 #define PASSWD_HASH_AGE 15
37 struct passwd_hash_entry {
38   int entry;
39   int next;
40 };
41
42 struct passwd_hash_table_s {
43   struct passwd *passwds;
44   int passwds_size;  
45   int *names;
46   int *uids;
47   struct passwd_hash_entry *entries;
48   int entries_size;
49   struct timeval build_time;
50 } passwd_hash_table = {
51   NULL,0,NULL,NULL,NULL,0,{0,0}
52 };
53
54 static int name_hash_function(const char *name) 
55 {
56   /* I guess that there must be better hash functions. This one was the
57    * first to come into mind :) */
58   unsigned int value=0;
59   while (*name) {
60     value=(value<<8)|(unsigned char)(*name);
61     if (value>1048576) value=value%PASSWD_HASH_SIZE;
62     name++;
63   }
64   value=value%PASSWD_HASH_SIZE;
65   return value;
66 }
67     
68 static int uid_hash_function(uid_t uid) 
69 {
70   return uid%PASSWD_HASH_SIZE;
71 }
72
73
74 static BOOL build_passwd_hash_table(void) 
75 {
76   struct passwd_hash_table_s *pht=&passwd_hash_table; /* Convenience */
77   int num_passwds=0;
78   int num_entries=0;
79   struct passwd *pass;
80   int i;
81   int name_i,uid_i;
82
83   DEBUG(3,("Building passwd hash table\n"));
84   /* Free the allocated strings in old hash table */
85   for (i=0;i<pht->passwds_size;i++) {
86     free(pht->passwds[i].pw_name); 
87     free(pht->passwds[i].pw_passwd);
88     free(pht->passwds[i].pw_gecos);
89     free(pht->passwds[i].pw_dir);
90     free(pht->passwds[i].pw_shell);    
91   }
92
93   /* Initialize hash table if first table build */
94   if (pht->passwds_size==0) {
95     DEBUG(3,("Building passwd hash table for the first time\n"));
96     pht->passwds=malloc(sizeof(struct passwd)*64); /* A reasonable default */
97     pht->passwds_size=64;
98   }
99   if (pht->names==NULL) {
100     pht->names=malloc(sizeof(struct passwd_hash_entry *)*PASSWD_HASH_SIZE);
101   }
102   if (pht->uids==NULL) {
103     pht->uids=malloc(sizeof(struct passwd_hash_entry *)*PASSWD_HASH_SIZE);
104   }
105   if (pht->entries==NULL) {
106     pht->entries=malloc(sizeof(struct passwd_hash_entry)*128);
107     pht->entries_size=128;
108   }
109   if (pht->passwds==NULL || pht->names==NULL || 
110       pht->uids==NULL || pht->entries==NULL) {
111     goto fail;
112   }
113   
114   /* Clear out the hash table */
115   for(i=0;i<PASSWD_HASH_SIZE;i++) pht->uids[i]=-1;
116   for(i=0;i<PASSWD_HASH_SIZE;i++) pht->names[i]=-1;
117
118   /* Now do the build */
119   setpwent();
120
121   while((pass=getpwent())) {
122
123     /* Check that we have enough space */
124     if (num_passwds==pht->passwds_size) {
125       struct passwd *new_passwds=NULL;
126       pht->passwds_size+=pht->passwds_size/2;
127       new_passwds=realloc(pht->passwds,
128                            sizeof(struct passwd)*pht->passwds_size);
129       if (new_passwds==NULL) goto fail;
130       pht->passwds=new_passwds;
131     }
132     if (num_entries+1>=pht->entries_size) {
133       pht->entries_size+=pht->entries_size/2;
134       pht->entries=realloc(pht->entries,
135                            sizeof(struct passwd_hash_entry)*pht->entries_size);
136       if (pht->entries==NULL) goto fail;
137     }
138
139     /* Copy the passwd struct */
140     memset(&pht->passwds[num_passwds],0,sizeof(struct passwd));
141     pht->passwds[num_passwds].pw_uid=pass->pw_uid;
142     pht->passwds[num_passwds].pw_gid=pass->pw_gid;  
143     if (
144         (pht->passwds[num_passwds].pw_name=strdup(pass->pw_name))==NULL ||
145         (pht->passwds[num_passwds].pw_passwd=strdup(pass->pw_passwd))==NULL ||
146         (pht->passwds[num_passwds].pw_gecos=strdup(pass->pw_gecos))==NULL ||
147         (pht->passwds[num_passwds].pw_dir=strdup(pass->pw_dir))==NULL ||
148         (pht->passwds[num_passwds].pw_shell=strdup(pass->pw_shell))==NULL ) {
149       num_passwds++;
150       goto fail;
151     }
152     
153     /* Add to the hash table */
154     /* Add the name */
155     pht->entries[num_entries].entry=num_passwds;
156     name_i=name_hash_function(pass->pw_name);
157     pht->entries[num_entries].next=pht->names[name_i];
158     pht->names[name_i]=num_entries;
159     num_entries++;
160     /* Add the uid */
161     pht->entries[num_entries].entry=num_passwds;
162     uid_i=uid_hash_function(pass->pw_uid);
163     pht->entries[num_entries].next=pht->uids[uid_i];
164     pht->uids[uid_i]=num_entries;
165     num_entries++;
166
167     /* This entry has been done */
168     num_passwds++;
169   }    
170   endpwent();
171   
172   if (pht->passwds_size>num_passwds) {
173     struct passwd *passwds;
174     passwds=realloc(pht->passwds,sizeof(pht->passwds[0])*num_passwds);
175     if (passwds==NULL) goto fail;
176     pht->passwds=passwds;
177     pht->passwds_size=num_passwds;
178   }
179   if (pht->entries_size>num_entries) {
180     struct passwd_hash_entry *entries;
181     entries=realloc(pht->entries,sizeof(pht->entries[0])*num_entries);
182     if (entries==NULL) goto fail;
183     pht->entries=entries;
184     pht->entries_size=num_entries;
185   }
186
187   /* Mark the creation time */
188   GetTimeOfDay(&pht->build_time);
189   /* Everything went smoothly. */
190   return True;
191
192  fail:
193   DEBUG(0,("Failed to create passwd hash table: %s",strerror(errno)));
194   /* OK: now the untested part. Normally this should never happen:
195    * Only running out of memory could cause this and even then
196    * we have enough trouble already. */
197   while (num_passwds>0) {
198     num_passwds--;
199     free(pht->passwds[num_passwds].pw_name);
200     free(pht->passwds[num_passwds].pw_passwd);
201     free(pht->passwds[num_passwds].pw_gecos);
202     free(pht->passwds[num_passwds].pw_dir);
203     free(pht->passwds[num_passwds].pw_shell);    
204   }
205   free(pht->entries);
206   free(pht->uids);
207   free(pht->names);
208   free(pht->passwds);
209   pht->passwds_size=0;
210   pht->entries_size=0;    
211   /* Also mark fail time, so that retry will happen after PASSWD_HASH_AGE */
212   GetTimeOfDay(&pht->build_time);
213   return False;
214 }
215
216 static BOOL have_passwd_hash(void)
217 {
218   struct passwd_hash_table_s *pht=&passwd_hash_table;
219   struct timeval tv;
220   GetTimeOfDay(&tv);
221   /* I'm ignoring microseconds. If you think they matter, go ahead
222    * and implement them */
223   if (tv.tv_sec - pht->build_time.tv_sec > PASSWD_HASH_AGE) {
224     return build_passwd_hash_table();
225   }
226   return pht->passwds_size>0;
227 }
228
229 struct passwd *hashed_getpwnam(const char *name)
230 {
231   struct passwd_hash_table_s *pht=&passwd_hash_table;
232
233   DEBUG(5,("getpwnam(%s)\n", name));
234
235   if (have_passwd_hash())
236   {
237     int name_i=name_hash_function(name);
238     int hash_index=pht->names[name_i];
239     while(hash_index!=-1) {
240       struct passwd *pass=&pht->passwds[pht->entries[hash_index].entry];
241       if (strcmp(name,pass->pw_name)==0) {
242         DEBUG(5,("Found: %s:%s:%d:%d:%s:%s:%s\n",
243                  pass->pw_name,
244                  pass->pw_passwd,
245                  pass->pw_uid,
246                  pass->pw_gid,
247                  pass->pw_gecos,
248                  pass->pw_dir,
249                  pass->pw_shell));
250         return copy_passwd_struct(pass);      
251       }
252       hash_index=pht->entries[hash_index].next;
253     }
254
255     /* Not found */
256     DEBUG(5,("%s not found\n",name));
257     return NULL;
258   } 
259
260   /* Fall back to real getpwnam() */
261   return sys_getpwnam(name);
262 }
263
264 /*******************************************************************
265 turn a uid into a user name
266 ********************************************************************/
267 char *uidtoname(uid_t uid)
268 {
269   static char name[40];
270   struct passwd_hash_table_s *pht=&passwd_hash_table;
271   struct passwd *pass=NULL;
272
273   DEBUG(5,("uidtoname(%d)\n",uid));
274   if (have_passwd_hash()) {
275     int hash_index=pht->uids[uid_hash_function(uid)];
276     while(hash_index!=-1) {
277       pass=&pht->passwds[pht->entries[hash_index].entry];
278       if (pass->pw_uid==uid) {
279         DEBUG(5,("Found: %s:%s:%d:%d:%s:%s:%s\n",
280                  pass->pw_name,
281                  pass->pw_passwd,
282                  pass->pw_uid,
283                  pass->pw_gid,
284                  pass->pw_gecos,
285                  pass->pw_dir,
286                  pass->pw_shell));
287         return pass->pw_name;      
288       }
289       hash_index=pht->entries[hash_index].next;
290     }
291     DEBUG(5,("Hash miss"));
292     pass=NULL;
293   } else {
294     /* No hash table, fall back to getpwuid */
295     pass = getpwuid(uid);
296   }
297   if (pass) return(pass->pw_name);
298   slprintf(name, sizeof(name) - 1, "%d",(int)uid);
299   return(name);
300 }
301
302 /****************************************************************************
303 get a users home directory.
304 ****************************************************************************/
305 char *get_unixhome_dir(char *user)
306 {
307         const struct passwd *pass;
308         static pstring home_dir;
309
310         pass = Get_Pwnam(user, False);
311
312         if (pass == NULL || pass->pw_dir == NULL) return(NULL);
313
314         pstrcpy(home_dir, pass->pw_dir);
315         DEBUG(10,("get_smbhome_dir: returning %s for user %s\n", home_dir, user));
316         return home_dir;
317 }
318
319
320 /*******************************************************************
321 map a username from a dos name to a unix name by looking in the username
322 map. Note that this modifies the name in place.
323 This is the main function that should be called *once* on
324 any incoming or new username - in order to canonicalize the name.
325 This is being done to de-couple the case conversions from the user mapping
326 function. Previously, the map_username was being called
327 every time Get_Pwnam was called.
328 Returns True if username was changed, false otherwise.
329 ********************************************************************/
330 BOOL map_username(char *user)
331 {
332   static BOOL initialised=False;
333   static fstring last_from,last_to;
334   FILE *f;
335   char *mapfile = lp_username_map();
336   char *s;
337   pstring buf;
338   BOOL mapped_user = False;
339
340   if (!*user)
341     return False;
342
343   if (!*mapfile)
344     return False;
345
346   if (!initialised) {
347     *last_from = *last_to = 0;
348     initialised = True;
349   }
350
351   if (strequal(user,last_to))
352     return False;
353
354   if (strequal(user,last_from)) {
355     DEBUG(3,("Mapped user %s to %s\n",user,last_to));
356     fstrcpy(user,last_to);
357     return True;
358   }
359   
360   f = sys_fopen(mapfile,"r");
361   if (!f) {
362     DEBUG(0,("can't open username map %s\n",mapfile));
363     return False;
364   }
365
366   DEBUG(4,("Scanning username map %s\n",mapfile));
367
368   while((s=fgets_slash(buf,sizeof(buf),f))!=NULL) {
369     char *unixname = s;
370     char *dosname = strchr(unixname,'=');
371     BOOL return_if_mapped = False;
372
373     if (!dosname)
374       continue;
375
376     *dosname++ = 0;
377
378     while (isspace(*unixname))
379       unixname++;
380     if ('!' == *unixname) {
381       return_if_mapped = True;
382       unixname++;
383       while (*unixname && isspace(*unixname))
384         unixname++;
385     }
386     
387     if (!*unixname || strchr("#;",*unixname))
388       continue;
389
390     {
391       int l = strlen(unixname);
392       while (l && isspace(unixname[l-1])) {
393         unixname[l-1] = 0;
394         l--;
395       }
396     }
397
398     if (strchr(dosname,'*') || user_in_list(user,dosname)) {
399       DEBUG(3,("Mapped user %s to %s\n",user,unixname));
400       mapped_user = True;
401       fstrcpy(last_from,user);
402       sscanf(unixname,"%s",user);
403       fstrcpy(last_to,user);
404       if(return_if_mapped) { 
405         fclose(f);
406         return True;
407       }
408     }
409   }
410
411   fclose(f);
412
413   /*
414    * Setup the last_from and last_to as an optimization so 
415    * that we don't scan the file again for the same user.
416    */
417   fstrcpy(last_from,user);
418   fstrcpy(last_to,user);
419
420   return mapped_user;
421 }
422
423
424 /****************************************************************************
425  Get_Pwnam wrapper
426 ****************************************************************************/
427 static struct passwd *_Get_Pwnam(char *s)
428 {
429         struct passwd *ret;
430
431         ret = hashed_getpwnam(s);
432 #ifdef HAVE_GETPWANAM
433         if (ret)
434         {
435                 struct passwd_adjunct *pwret;
436                 pwret = getpwanam(s);
437                 if (pwret != NULL && pwret->pwa_passwd != NULL)
438                 {
439                         pstrcpy(ret->pw_passwd, pwret->pwa_passwd);
440                 }
441         }
442 #endif
443
444         return ret;
445 }
446
447 /****************************************************************************
448 a wrapper for getpwnam() that tries with all lower and all upper case 
449 if the initial name fails. Also tried with first letter capitalised
450 Note that this can change user!  Function returns const to emphasise
451 the fact that most of the members of the struct passwd * returned are
452 dynamically allocated.
453 ****************************************************************************/
454 const struct passwd *Get_Pwnam(char *user,BOOL allow_change)
455 {
456   fstring user2;
457   int last_char;
458   int usernamelevel = lp_usernamelevel();
459
460   struct passwd *ret;  
461
462   if (!user || !(*user))
463     return(NULL);
464
465   StrnCpy(user2,user,sizeof(user2)-1);
466
467   if (!allow_change) {
468     user = &user2[0];
469   }
470
471   ret = _Get_Pwnam(user);
472   if (ret) return(ret);
473
474   strlower(user);
475   ret = _Get_Pwnam(user);
476   if (ret)  return(ret);
477
478   strupper(user);
479   ret = _Get_Pwnam(user);
480   if (ret) return(ret);
481
482   /* try with first letter capitalised */
483   if (strlen(user) > 1)
484     strlower(user+1);  
485   ret = _Get_Pwnam(user);
486   if (ret) return(ret);
487
488   /* try with last letter capitalised */
489   strlower(user);
490   last_char = strlen(user)-1;
491   user[last_char] = toupper(user[last_char]);
492   ret = _Get_Pwnam(user);
493   if (ret) return(ret);
494
495   /* try all combinations up to usernamelevel */
496   strlower(user);
497   ret = uname_string_combinations(user, _Get_Pwnam, usernamelevel);
498   if (ret) return(ret);
499
500   if (allow_change)
501     fstrcpy(user,user2);
502
503   return(NULL);
504 }
505
506 /****************************************************************************
507 check if a user is in a netgroup user list
508 ****************************************************************************/
509 static BOOL user_in_netgroup_list(char *user,char *ngname)
510 {
511 #ifdef HAVE_NETGROUP
512   static char *mydomain = NULL;
513   if (mydomain == NULL)
514     yp_get_default_domain(&mydomain);
515
516   if(mydomain == NULL)
517   {
518     DEBUG(5,("Unable to get default yp domain\n"));
519   }
520   else
521   {
522     DEBUG(5,("looking for user %s of domain %s in netgroup %s\n",
523           user, mydomain, ngname));
524     DEBUG(5,("innetgr is %s\n",
525           innetgr(ngname, NULL, user, mydomain)
526           ? "TRUE" : "FALSE"));
527
528     if (innetgr(ngname, NULL, user, mydomain))
529       return (True);
530   }
531 #endif /* HAVE_NETGROUP */
532   return False;
533 }
534
535 /****************************************************************************
536 check if a user is in a UNIX user list
537 ****************************************************************************/
538 static BOOL user_in_group_list(char *user,char *gname)
539 {
540 #ifdef HAVE_GETGRNAM 
541   struct group *gptr;
542   char **member;  
543   const struct passwd *pass = Get_Pwnam(user,False);
544
545   if (pass)
546   { 
547     gptr = getgrgid(pass->pw_gid);
548     if (gptr && strequal(gptr->gr_name,gname))
549       return(True); 
550   } 
551
552   gptr = (struct group *)getgrnam(gname);
553
554   if (gptr)
555   {
556     member = gptr->gr_mem;
557     while (member && *member)
558     {
559       if (strequal(*member,user))
560         return(True);
561       member++;
562     }
563   }
564 #endif /* HAVE_GETGRNAM */
565   return False;
566 }             
567
568 /****************************************************************************
569 check if a username is valid
570 ****************************************************************************/
571 BOOL user_ok(char *user,int snum)
572 {
573         pstring valid, invalid;
574         BOOL ret;
575
576         StrnCpy(valid, lp_valid_users(snum), sizeof(pstring));
577         StrnCpy(invalid, lp_invalid_users(snum), sizeof(pstring));
578
579         string_sub(valid,"%S",lp_servicename(snum));
580         string_sub(invalid,"%S",lp_servicename(snum));
581         
582         ret = !user_in_list(user,invalid);
583         
584         if (ret && valid && *valid) {
585                 ret = user_in_list(user,valid);
586         }
587
588         if (ret && lp_onlyuser(snum)) {
589                 char *user_list = lp_username(snum);
590                 string_sub(user_list,"%S",lp_servicename(snum));
591                 ret = user_in_list(user,user_list);
592         }
593
594         return(ret);
595 }
596
597 /****************************************************************************
598 check if a user is in a user list - can check combinations of UNIX
599 and netgroup lists.
600 ****************************************************************************/
601 BOOL user_in_list(char *user,char *list)
602 {
603   pstring tok;
604   char *p=list;
605
606   while (next_token(&p,tok,LIST_SEP, sizeof(tok)))
607   {
608     /*
609      * Check raw username.
610      */
611     if (strequal(user,tok))
612       return(True);
613
614     /*
615      * Now check to see if any combination
616      * of UNIX and netgroups has been specified.
617      */
618
619     if(*tok == '@')
620     {
621       /*
622        * Old behaviour. Check netgroup list
623        * followed by UNIX list.
624        */
625       if(user_in_netgroup_list(user,&tok[1]))
626         return True;
627       if(user_in_group_list(user,&tok[1]))
628         return True;
629     }
630     else if (*tok == '+')
631     {
632       if(tok[1] == '&')
633       {
634         /*
635          * Search UNIX list followed by netgroup.
636          */
637         if(user_in_group_list(user,&tok[2]))
638           return True;
639         if(user_in_netgroup_list(user,&tok[2]))
640           return True;
641       }
642       else
643       {
644         /*
645          * Just search UNIX list.
646          */
647         if(user_in_group_list(user,&tok[1]))
648           return True;
649       }
650     }
651     else if (*tok == '&')
652     {
653       if(tok[1] == '&')
654       {
655         /*
656          * Search netgroup list followed by UNIX list.
657          */
658         if(user_in_netgroup_list(user,&tok[2]))
659           return True;
660         if(user_in_group_list(user,&tok[2]))
661           return True;
662       }
663       else
664       {
665         /*
666          * Just search netgroup list.
667          */
668         if(user_in_netgroup_list(user,&tok[1]))
669           return True;
670       }
671     }
672   }
673   return(False);
674 }
675
676 /* The functions below have been taken from password.c and slightly modified */
677 /****************************************************************************
678 apply a function to upper/lower case combinations
679 of a string and return true if one of them returns true.
680 try all combinations with N uppercase letters.
681 offset is the first char to try and change (start with 0)
682 it assumes the string starts lowercased
683 ****************************************************************************/
684 static struct passwd *uname_string_combinations2(char *s,int offset,struct passwd *(*fn)(char *),int N)
685 {
686   int len = strlen(s);
687   int i;
688   struct passwd *ret;
689
690 #ifdef PASSWORD_LENGTH
691   len = MIN(len,PASSWORD_LENGTH);
692 #endif
693
694   if (N <= 0 || offset >= len)
695     return(fn(s));
696
697
698   for (i=offset;i<(len-(N-1));i++)
699
700     {
701       char c = s[i];
702       if (!islower(c)) continue;
703       s[i] = toupper(c);
704       ret = uname_string_combinations2(s,i+1,fn,N-1);
705       if(ret) return(ret);
706       s[i] = c;
707     }
708   return(NULL);
709 }
710
711 /****************************************************************************
712 apply a function to upper/lower case combinations
713 of a string and return true if one of them returns true.
714 try all combinations with up to N uppercase letters.
715 offset is the first char to try and change (start with 0)
716 it assumes the string starts lowercased
717 ****************************************************************************/
718 static struct passwd * uname_string_combinations(char *s,struct passwd * (*fn)(char *),int N)
719 {
720   int n;
721   struct passwd *ret;
722
723   for (n=1;n<=N;n++)
724   {
725     ret = uname_string_combinations2(s,0,fn,n);
726     if(ret) return(ret);
727   }
728   return(NULL);
729 }