This commit was manufactured by cvs2svn to create branch 'SAMBA_3_0'.(This used to...
[ira/wip.git] / source3 / lib / username.c
1 /* 
2    Unix SMB/CIFS implementation.
3    Username handling
4    Copyright (C) Andrew Tridgell 1992-1998
5    Copyright (C) Jeremy Allison 1997-2001.
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
24 /* internal functions */
25 static struct passwd *uname_string_combinations(char *s, struct passwd * (*fn) (const char *), int N);
26 static struct passwd *uname_string_combinations2(char *s, int offset, struct passwd * (*fn) (const char *), int N);
27
28 /*****************************************************************
29  Check if a user or group name is local (this is a *local* name for
30  *local* people, there's nothing for you here...).
31 *****************************************************************/
32
33 static BOOL name_is_local(const char *name)
34 {
35         return !(strchr_m(name, *lp_winbind_separator()));
36 }
37
38 /*****************************************************************
39  Splits passed user or group name to domain and user/group name parts
40  Returns True if name was splitted and False otherwise.
41 *****************************************************************/
42
43 BOOL split_domain_and_name(const char *name, char *domain, char* username)
44 {
45         char *p = strchr(name,*lp_winbind_separator());
46         
47         
48         /* Parse a string of the form DOMAIN/user into a domain and a user */
49         DEBUG(10,("split_domain_and_name: checking whether name |%s| local or not\n", name));
50         
51         if (p) {
52                 fstrcpy(username, p+1);
53                 fstrcpy(domain, name);
54                 domain[PTR_DIFF(p, name)] = 0;
55         } else if (lp_winbind_use_default_domain()) {
56                 fstrcpy(username, name);
57                 fstrcpy(domain, lp_workgroup());
58         } else {
59                 return False;
60         }
61
62         DEBUG(10,("split_domain_and_name: all is fine, domain is |%s| and name is |%s|\n", domain, username));
63         return True;
64 }
65
66 /****************************************************************************
67  Get a users home directory.
68 ****************************************************************************/
69
70 char *get_user_home_dir(const char *user)
71 {
72         static struct passwd *pass;
73
74         /* Ensure the user exists. */
75
76         pass = Get_Pwnam(user);
77
78         if (!pass)
79                 return(NULL);
80         /* Return home directory from struct passwd. */
81
82         return(pass->pw_dir);      
83 }
84
85 /*******************************************************************
86  Map a username from a dos name to a unix name by looking in the username
87  map. Note that this modifies the name in place.
88  This is the main function that should be called *once* on
89  any incoming or new username - in order to canonicalize the name.
90  This is being done to de-couple the case conversions from the user mapping
91  function. Previously, the map_username was being called
92  every time Get_Pwnam was called.
93  Returns True if username was changed, false otherwise.
94 ********************************************************************/
95
96 BOOL map_username(char *user)
97 {
98         static BOOL initialised=False;
99         static fstring last_from,last_to;
100         XFILE *f;
101         char *mapfile = lp_username_map();
102         char *s;
103         pstring buf;
104         BOOL mapped_user = False;
105
106         if (!*user)
107                 return False;
108
109         if (!*mapfile)
110                 return False;
111
112         if (!initialised) {
113                 *last_from = *last_to = 0;
114                 initialised = True;
115         }
116
117         if (strequal(user,last_to))
118                 return False;
119
120         if (strequal(user,last_from)) {
121                 DEBUG(3,("Mapped user %s to %s\n",user,last_to));
122                 fstrcpy(user,last_to);
123                 return True;
124         }
125   
126         f = x_fopen(mapfile,O_RDONLY, 0);
127         if (!f) {
128                 DEBUG(0,("can't open username map %s. Error %s\n",mapfile, strerror(errno) ));
129                 return False;
130         }
131
132         DEBUG(4,("Scanning username map %s\n",mapfile));
133
134         while((s=fgets_slash(buf,sizeof(buf),f))!=NULL) {
135                 char *unixname = s;
136                 char *dosname = strchr_m(unixname,'=');
137                 char **dosuserlist;
138                 BOOL return_if_mapped = False;
139
140                 if (!dosname)
141                         continue;
142
143                 *dosname++ = 0;
144
145                 while (isspace((int)*unixname))
146                         unixname++;
147
148                 if ('!' == *unixname) {
149                         return_if_mapped = True;
150                         unixname++;
151                         while (*unixname && isspace((int)*unixname))
152                                 unixname++;
153                 }
154     
155                 if (!*unixname || strchr_m("#;",*unixname))
156                         continue;
157
158                 {
159                         int l = strlen(unixname);
160                         while (l && isspace((int)unixname[l-1])) {
161                                 unixname[l-1] = 0;
162                                 l--;
163                         }
164                 }
165
166                 dosuserlist = str_list_make(dosname);
167                 if (!dosuserlist) {
168                         DEBUG(0,("Unable to build user list\n"));
169                         return False;
170                 }
171
172                 if (strchr_m(dosname,'*') || user_in_list(user, dosuserlist)) {
173                         DEBUG(3,("Mapped user %s to %s\n",user,unixname));
174                         mapped_user = True;
175                         fstrcpy(last_from,user);
176                         sscanf(unixname,"%s",user);
177                         fstrcpy(last_to,user);
178                         if(return_if_mapped) {
179                                 str_list_free (&dosuserlist);
180                                 x_fclose(f);
181                                 return True;
182                         }
183                 }
184     
185                 str_list_free (&dosuserlist);
186         }
187
188         x_fclose(f);
189
190         /*
191          * Setup the last_from and last_to as an optimization so 
192          * that we don't scan the file again for the same user.
193          */
194         fstrcpy(last_from,user);
195         fstrcpy(last_to,user);
196
197         return mapped_user;
198 }
199
200 /****************************************************************************
201  * A wrapper for sys_getpwnam().  The following variations are tried:
202  *   - as transmitted
203  *   - in all lower case if this differs from transmitted
204  *   - in all upper case if this differs from transmitted
205  *   - using lp_usernamelevel() for permutations.
206 ****************************************************************************/
207
208 static struct passwd *Get_Pwnam_ret = NULL;
209
210 static struct passwd *Get_Pwnam_internals(const char *user, char *user2)
211 {
212         struct passwd *ret = NULL;
213
214         if (!user2 || !(*user2))
215                 return(NULL);
216
217         if (!user || !(*user))
218                 return(NULL);
219
220         /* Try in all lower case first as this is the most 
221            common case on UNIX systems */
222         strlower(user2);
223         DEBUG(5,("Trying _Get_Pwnam(), username as lowercase is %s\n",user2));
224         ret = getpwnam_alloc(user2);
225         if(ret)
226                 goto done;
227
228         /* Try as given, if username wasn't originally lowercase */
229         if(strcmp(user, user2) != 0) {
230                 DEBUG(5,("Trying _Get_Pwnam(), username as given is %s\n", user));
231                 ret = getpwnam_alloc(user);
232                 if(ret)
233                         goto done;
234         }
235
236         /* Try as uppercase, if username wasn't originally uppercase */
237         strupper(user2);
238         if(strcmp(user, user2) != 0) {
239                 DEBUG(5,("Trying _Get_Pwnam(), username as uppercase is %s\n", user2));
240                 ret = getpwnam_alloc(user2);
241                 if(ret)
242                         goto done;
243         }
244
245         /* Try all combinations up to usernamelevel */
246         strlower(user2);
247         DEBUG(5,("Checking combinations of %d uppercase letters in %s\n", lp_usernamelevel(), user2));
248         ret = uname_string_combinations(user2, getpwnam_alloc, lp_usernamelevel());
249
250 done:
251         DEBUG(5,("Get_Pwnam_internals %s find user [%s]!\n",ret ? "did":"didn't", user));
252
253         /* This call used to just return the 'passwd' static buffer.
254            This could then have accidental reuse implications, so 
255            we now malloc a copy, and free it in the next use.
256
257            This should cause the (ab)user to segfault if it 
258            uses an old struct. 
259            
260            This is better than useing the wrong data in security
261            critical operations.
262
263            The real fix is to make the callers free the returned 
264            malloc'ed data.
265         */
266
267         if (Get_Pwnam_ret) {
268                 passwd_free(&Get_Pwnam_ret);
269         }
270         
271         Get_Pwnam_ret = ret;
272
273         return ret;
274 }
275
276 /****************************************************************************
277  Get_Pwnam wrapper for modification.
278   NOTE: This can potentially modify 'user'! 
279 ****************************************************************************/
280
281 struct passwd *Get_Pwnam_Modify(fstring user)
282 {
283         fstring user2;
284         struct passwd *ret;
285
286         fstrcpy(user2, user);
287
288         ret = Get_Pwnam_internals(user, user2);
289         
290         /* If caller wants the modified username, ensure they get it  */
291         fstrcpy(user,user2);
292
293         /* We can safely assume ret is NULL if none of the above succeed */
294         return(ret);  
295 }
296
297 /****************************************************************************
298  Get_Pwnam wrapper without modification.
299   NOTE: This with NOT modify 'user'! 
300 ****************************************************************************/
301
302 struct passwd *Get_Pwnam(const char *user)
303 {
304         fstring user2;
305         struct passwd *ret;
306
307         fstrcpy(user2, user);
308
309         DEBUG(5,("Finding user %s\n", user));
310
311         ret = Get_Pwnam_internals(user, user2);
312         
313         return ret;  
314 }
315
316 /****************************************************************************
317  Check if a user is in a netgroup user list.
318 ****************************************************************************/
319
320 static BOOL user_in_netgroup_list(const char *user, const char *ngname)
321 {
322 #ifdef HAVE_NETGROUP
323         static char *mydomain = NULL;
324         if (mydomain == NULL)
325                 yp_get_default_domain(&mydomain);
326
327         if(mydomain == NULL) {
328                 DEBUG(5,("Unable to get default yp domain\n"));
329                 return False;
330         }
331
332         DEBUG(5,("looking for user %s of domain %s in netgroup %s\n",
333                 user, mydomain, ngname));
334         DEBUG(5,("innetgr is %s\n", innetgr(ngname, NULL, user, mydomain)
335                 ? "TRUE" : "FALSE"));
336
337         if (innetgr(ngname, NULL, user, mydomain))
338                 return (True);
339 #endif /* HAVE_NETGROUP */
340         return False;
341 }
342
343 /****************************************************************************
344  Check if a user is in a winbind group.
345 ****************************************************************************/
346   
347 static BOOL user_in_winbind_group_list(const char *user, const char *gname, BOOL *winbind_answered)
348 {
349         int num_groups;
350         int i;
351         gid_t *groups = NULL;
352         gid_t gid;
353         BOOL ret = False;
354  
355         *winbind_answered = False;
356  
357         /*
358          * Get the gid's that this user belongs to.
359          */
360  
361         if ((num_groups = winbind_getgroups(user, 0, NULL)) == -1)
362                 return False;
363  
364         if (num_groups == 0) {
365                 *winbind_answered = True;
366                 return False;
367         }
368  
369         if ((groups = (gid_t *)malloc(sizeof(gid_t) * num_groups )) == NULL) {
370                 DEBUG(0,("user_in_winbind_group_list: malloc fail.\n"));
371                 goto err;
372         }
373  
374         if ((num_groups = winbind_getgroups(user, num_groups, groups)) == -1) {
375                 DEBUG(0,("user_in_winbind_group_list: second winbind_getgroups call \
376 failed with error %s\n", strerror(errno) ));
377                 goto err;
378         }
379  
380         /*
381          * Now we have the gid list for this user - convert the gname
382          * to a gid_t via either winbind or the local UNIX lookup and do the comparison.
383          */
384  
385         if ((gid = nametogid(gname)) == (gid_t)-1) {
386                 DEBUG(0,("user_in_winbind_group_list: winbind_lookup_name for group %s failed.\n",
387                         gname ));
388                 goto err;
389         }
390  
391         for (i = 0; i < num_groups; i++) {
392                 if (gid == groups[i]) {
393                         ret = True;
394                         break;
395                 }
396         }
397  
398         *winbind_answered = True;
399         SAFE_FREE(groups);
400         return ret;
401  
402    err:
403  
404         *winbind_answered = False;
405         SAFE_FREE(groups);
406         return False;
407 }             
408  
409 /****************************************************************************
410  Check if a user is in a UNIX group.
411 ****************************************************************************/
412
413 static BOOL user_in_unix_group_list(const char *user,const char *gname)
414 {
415         struct passwd *pass = Get_Pwnam(user);
416         struct sys_userlist *user_list;
417         struct sys_userlist *member;
418
419         DEBUG(10,("user_in_unix_group_list: checking user %s in group %s\n", user, gname));
420
421         /*
422          * We need to check the users primary group as this
423          * group is implicit and often not listed in the group database.
424          */
425  
426         if (pass) {
427                 if (strequal(gname,gidtoname(pass->pw_gid))) {
428                         DEBUG(10,("user_in_unix_group_list: group %s is primary group.\n", gname ));
429                         return True;
430                 }
431         }
432  
433         user_list = get_users_in_group(gname);
434         if (user_list == NULL) {
435                 DEBUG(10,("user_in_unix_group_list: no such group %s\n", gname ));
436                 return False;
437         }
438
439         for (member = user_list; member; member = member->next) {
440                 DEBUG(10,("user_in_unix_group_list: checking user %s against member %s\n",
441                         user, member->unix_name ));
442                 if (strequal(member->unix_name,user)) {
443                         free_userlist(user_list);
444                         return(True);
445                 }
446         }
447
448         free_userlist(user_list);
449         return False;
450 }             
451
452 /****************************************************************************
453  Check if a user is in a group list. Ask winbind first, then use UNIX.
454 ****************************************************************************/
455
456 BOOL user_in_group_list(const char *user, const char *gname)
457 {
458         BOOL winbind_answered = False;
459         BOOL ret;
460
461         ret = user_in_winbind_group_list(user, gname, &winbind_answered);
462         if (!winbind_answered)
463                 ret = user_in_unix_group_list(user, gname);
464
465         if (ret)
466                 DEBUG(10,("user_in_group_list: user |%s| is in group |%s|\n", user, gname));
467         return ret;
468 }
469
470 /****************************************************************************
471  Check if a user is in a user list - can check combinations of UNIX
472  and netgroup lists.
473 ****************************************************************************/
474
475 BOOL user_in_list(const char *user,char **list)
476 {
477         if (!list || !*list)
478                 return False;
479
480         DEBUG(10,("user_in_list: checking user %s in list\n", user));
481
482         while (*list) {
483
484                 DEBUG(10,("user_in_list: checking user |%s| against |%s|\n", user, *list));
485
486                 /*
487                  * Check raw username.
488                  */
489                 if (strequal(user, *list))
490                         return(True);
491
492                 /*
493                  * Now check to see if any combination
494                  * of UNIX and netgroups has been specified.
495                  */
496
497                 if(**list == '@') {
498                         /*
499                          * Old behaviour. Check netgroup list
500                          * followed by UNIX list.
501                          */
502                         if(user_in_netgroup_list(user, *list +1))
503                                 return True;
504                         if(user_in_group_list(user, *list +1))
505                                 return True;
506                 } else if (**list == '+') {
507
508                         if((*(*list +1)) == '&') {
509                                 /*
510                                  * Search UNIX list followed by netgroup.
511                                  */
512                                 if(user_in_group_list(user, *list +2))
513                                         return True;
514                                 if(user_in_netgroup_list(user, *list +2))
515                                         return True;
516
517                         } else {
518
519                                 /*
520                                  * Just search UNIX list.
521                                  */
522
523                                 if(user_in_group_list(user, *list +1))
524                                         return True;
525                         }
526
527                 } else if (**list == '&') {
528
529                         if(*(*list +1) == '+') {
530                                 /*
531                                  * Search netgroup list followed by UNIX list.
532                                  */
533                                 if(user_in_netgroup_list(user, *list +2))
534                                         return True;
535                                 if(user_in_group_list(user, *list +2))
536                                         return True;
537                         } else {
538                                 /*
539                                  * Just search netgroup list.
540                                  */
541                                 if(user_in_netgroup_list(user, *list +1))
542                                         return True;
543                         }
544                 } else if (!name_is_local(*list)) {
545                         /*
546                          * If user name did not match and token is not
547                          * a unix group and the token has a winbind separator in the
548                          * name then see if it is a Windows group.
549                          */
550
551                         DOM_SID g_sid;
552                         enum SID_NAME_USE name_type;
553                         BOOL winbind_answered = False;
554                         BOOL ret;
555                         fstring groupname, domain;
556                         
557                         /* Parse a string of the form DOMAIN/user into a domain and a user */
558
559                         char *p = strchr(*list,*lp_winbind_separator());
560                         
561                         DEBUG(10,("user_in_list: checking if user |%s| is in winbind group |%s|\n", user, *list));
562
563                         if (p) {
564                                 fstrcpy(groupname, p+1);
565                                 fstrcpy(domain, *list);
566                                 domain[PTR_DIFF(p, *list)] = 0;
567
568                                 /* Check to see if name is a Windows group */
569                                 if (winbind_lookup_name(domain, groupname, &g_sid, &name_type) && name_type == SID_NAME_DOM_GRP) {
570                                         
571                                 /* Check if user name is in the Windows group */
572                                         ret = user_in_winbind_group_list(user, *list, &winbind_answered);
573                                         
574                                         if (winbind_answered && ret == True) {
575                                                 DEBUG(10,("user_in_list: user |%s| is in winbind group |%s|\n", user, *list));
576                                                 return ret;
577                                         }
578                                 }
579                         }
580                 }
581     
582                 list++;
583         }
584         return(False);
585 }
586
587 /* The functions below have been taken from password.c and slightly modified */
588 /****************************************************************************
589  Apply a function to upper/lower case combinations
590  of a string and return true if one of them returns true.
591  Try all combinations with N uppercase letters.
592  offset is the first char to try and change (start with 0)
593  it assumes the string starts lowercased
594 ****************************************************************************/
595
596 static struct passwd *uname_string_combinations2(char *s,int offset,struct passwd *(*fn)(const char *),int N)
597 {
598         ssize_t len = (ssize_t)strlen(s);
599         int i;
600         struct passwd *ret;
601
602         if (N <= 0 || offset >= len)
603                 return(fn(s));
604
605         for (i=offset;i<(len-(N-1));i++) {
606                 char c = s[i];
607                 if (!islower((int)c))
608                         continue;
609                 s[i] = toupper(c);
610                 ret = uname_string_combinations2(s,i+1,fn,N-1);
611                 if(ret)
612                         return(ret);
613                 s[i] = c;
614         }
615         return(NULL);
616 }
617
618 /****************************************************************************
619  Apply a function to upper/lower case combinations
620  of a string and return true if one of them returns true.
621  Try all combinations with up to N uppercase letters.
622  offset is the first char to try and change (start with 0)
623  it assumes the string starts lowercased
624 ****************************************************************************/
625
626 static struct passwd * uname_string_combinations(char *s,struct passwd * (*fn)(const char *),int N)
627 {
628         int n;
629         struct passwd *ret;
630
631         for (n=1;n<=N;n++) {
632                 ret = uname_string_combinations2(s,0,fn,n);
633                 if(ret)
634                         return(ret);
635         }  
636         return(NULL);
637 }
638
639 /****************************************************************************
640  These wrappers allow appliance mode to work. In appliance mode the username
641  takes the form DOMAIN/user.
642 ****************************************************************************/
643
644 struct passwd *smb_getpwnam(char *user, BOOL allow_change)
645 {
646         struct passwd *pw;
647         char *p;
648         char *sep;
649         extern pstring global_myname;
650
651         if (allow_change)
652                 pw = Get_Pwnam_Modify(user);
653         else
654                 pw = Get_Pwnam(user);
655
656         if (pw)
657                 return pw;
658
659         /*
660          * If it is a domain qualified name and it isn't in our password
661          * database but the domain portion matches our local machine name then
662          * lookup just the username portion locally.
663          */
664
665         sep = lp_winbind_separator();
666         p = strchr_m(user,*sep);
667         if (p && strncasecmp(global_myname, user, strlen(global_myname))==0) {
668                 if (allow_change)
669                         pw = Get_Pwnam_Modify(p+1);
670                 else
671                         pw = Get_Pwnam(p+1);
672         }
673         return NULL;
674 }