87fe39466f4dbecaf4be7e96cf32c340f044f8aa
[kai/samba-autobuild/.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_home_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_home_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 user is in a user list - can check combinations of UNIX
570 and netgroup lists.
571 ****************************************************************************/
572 BOOL user_in_list(char *user,char *list)
573 {
574   pstring tok;
575   char *p=list;
576
577   while (next_token(&p,tok,LIST_SEP, sizeof(tok)))
578   {
579     /*
580      * Check raw username.
581      */
582     if (strequal(user,tok))
583       return(True);
584
585     /*
586      * Now check to see if any combination
587      * of UNIX and netgroups has been specified.
588      */
589
590     if(*tok == '@')
591     {
592       /*
593        * Old behaviour. Check netgroup list
594        * followed by UNIX list.
595        */
596       if(user_in_netgroup_list(user,&tok[1]))
597         return True;
598       if(user_in_group_list(user,&tok[1]))
599         return True;
600     }
601     else if (*tok == '+')
602     {
603       if(tok[1] == '&')
604       {
605         /*
606          * Search UNIX list followed by netgroup.
607          */
608         if(user_in_group_list(user,&tok[2]))
609           return True;
610         if(user_in_netgroup_list(user,&tok[2]))
611           return True;
612       }
613       else
614       {
615         /*
616          * Just search UNIX list.
617          */
618         if(user_in_group_list(user,&tok[1]))
619           return True;
620       }
621     }
622     else if (*tok == '&')
623     {
624       if(tok[1] == '&')
625       {
626         /*
627          * Search netgroup list followed by UNIX list.
628          */
629         if(user_in_netgroup_list(user,&tok[2]))
630           return True;
631         if(user_in_group_list(user,&tok[2]))
632           return True;
633       }
634       else
635       {
636         /*
637          * Just search netgroup list.
638          */
639         if(user_in_netgroup_list(user,&tok[1]))
640           return True;
641       }
642     }
643   }
644   return(False);
645 }
646
647 /* The functions below have been taken from password.c and slightly modified */
648 /****************************************************************************
649 apply a function to upper/lower case combinations
650 of a string and return true if one of them returns true.
651 try all combinations with N uppercase letters.
652 offset is the first char to try and change (start with 0)
653 it assumes the string starts lowercased
654 ****************************************************************************/
655 static struct passwd *uname_string_combinations2(char *s,int offset,struct passwd *(*fn)(char *),int N)
656 {
657   int len = strlen(s);
658   int i;
659   struct passwd *ret;
660
661 #ifdef PASSWORD_LENGTH
662   len = MIN(len,PASSWORD_LENGTH);
663 #endif
664
665   if (N <= 0 || offset >= len)
666     return(fn(s));
667
668
669   for (i=offset;i<(len-(N-1));i++)
670
671     {
672       char c = s[i];
673       if (!islower(c)) continue;
674       s[i] = toupper(c);
675       ret = uname_string_combinations2(s,i+1,fn,N-1);
676       if(ret) return(ret);
677       s[i] = c;
678     }
679   return(NULL);
680 }
681
682 /****************************************************************************
683 apply a function to upper/lower case combinations
684 of a string and return true if one of them returns true.
685 try all combinations with up to N uppercase letters.
686 offset is the first char to try and change (start with 0)
687 it assumes the string starts lowercased
688 ****************************************************************************/
689 static struct passwd * uname_string_combinations(char *s,struct passwd * (*fn)(char *),int N)
690 {
691   int n;
692   struct passwd *ret;
693
694   for (n=1;n<=N;n++)
695   {
696     ret = uname_string_combinations2(s,0,fn,n);
697     if(ret) return(ret);
698   }
699   return(NULL);
700 }