7d66b320adf8d61abd2aefdddd2b056bc1ba8ffe
[kai/samba.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  Get a users home directory.
40 ****************************************************************************/
41
42 char *get_user_home_dir(const char *user)
43 {
44         static struct passwd *pass;
45
46         /* Ensure the user exists. */
47
48         pass = Get_Pwnam(user);
49
50         if (!pass)
51                 return(NULL);
52         /* Return home directory from struct passwd. */
53
54         return(pass->pw_dir);      
55 }
56
57 /*******************************************************************
58  Map a username from a dos name to a unix name by looking in the username
59  map. Note that this modifies the name in place.
60  This is the main function that should be called *once* on
61  any incoming or new username - in order to canonicalize the name.
62  This is being done to de-couple the case conversions from the user mapping
63  function. Previously, the map_username was being called
64  every time Get_Pwnam was called.
65  Returns True if username was changed, false otherwise.
66 ********************************************************************/
67
68 BOOL map_username(fstring user)
69 {
70         static BOOL initialised=False;
71         static fstring last_from,last_to;
72         XFILE *f;
73         char *mapfile = lp_username_map();
74         char *s;
75         pstring buf;
76         BOOL mapped_user = False;
77         char *cmd = lp_username_map_script();
78         
79         if (!*user)
80                 return False;
81                 
82         if (strequal(user,last_to))
83                 return False;
84
85         if (strequal(user,last_from)) {
86                 DEBUG(3,("Mapped user %s to %s\n",user,last_to));
87                 fstrcpy(user,last_to);
88                 return True;
89         }
90         
91         /* first try the username map script */
92         
93         if ( *cmd ) {
94                 char **qlines;
95                 pstring command;
96                 int numlines, ret, fd;
97
98                 pstr_sprintf( command, "%s \"%s\"", cmd, user );
99
100                 DEBUG(10,("Running [%s]\n", command));
101                 ret = smbrun(command, &fd);
102                 DEBUGADD(10,("returned [%d]\n", ret));
103
104                 if ( ret != 0 ) {
105                         if (fd != -1)
106                                 close(fd);
107                         return False;
108                 }
109
110                 numlines = 0;
111                 qlines = fd_lines_load(fd, &numlines);
112                 DEBUGADD(10,("Lines returned = [%d]\n", numlines));
113                 close(fd);
114
115                 /* should be either no lines or a single line with the mapped username */
116
117                 if (numlines) {
118                         DEBUG(3,("Mapped user %s to %s\n", user, qlines[0] ));
119                         fstrcpy( user, qlines[0] );
120                 }
121
122                 file_lines_free(qlines);
123                 
124                 return numlines != 0;
125         }
126
127         /* ok.  let's try the mapfile */
128         
129         if (!*mapfile)
130                 return False;
131
132         if (!initialised) {
133                 *last_from = *last_to = 0;
134                 initialised = True;
135         }
136   
137         f = x_fopen(mapfile,O_RDONLY, 0);
138         if (!f) {
139                 DEBUG(0,("can't open username map %s. Error %s\n",mapfile, strerror(errno) ));
140                 return False;
141         }
142
143         DEBUG(4,("Scanning username map %s\n",mapfile));
144
145         while((s=fgets_slash(buf,sizeof(buf),f))!=NULL) {
146                 char *unixname = s;
147                 char *dosname = strchr_m(unixname,'=');
148                 char **dosuserlist;
149                 BOOL return_if_mapped = False;
150
151                 if (!dosname)
152                         continue;
153
154                 *dosname++ = 0;
155
156                 while (isspace((int)*unixname))
157                         unixname++;
158
159                 if ('!' == *unixname) {
160                         return_if_mapped = True;
161                         unixname++;
162                         while (*unixname && isspace((int)*unixname))
163                                 unixname++;
164                 }
165     
166                 if (!*unixname || strchr_m("#;",*unixname))
167                         continue;
168
169                 {
170                         int l = strlen(unixname);
171                         while (l && isspace((int)unixname[l-1])) {
172                                 unixname[l-1] = 0;
173                                 l--;
174                         }
175                 }
176
177                 dosuserlist = str_list_make(dosname, NULL);
178                 if (!dosuserlist) {
179                         DEBUG(0,("Unable to build user list\n"));
180                         return False;
181                 }
182
183                 if (strchr_m(dosname,'*') || user_in_list(user, (const char **)dosuserlist, NULL, 0)) {
184                         DEBUG(3,("Mapped user %s to %s\n",user,unixname));
185                         mapped_user = True;
186                         fstrcpy( last_from,user );
187                         fstrcpy( user, unixname );
188                         fstrcpy( last_to,user );
189                         if ( return_if_mapped ) {
190                                 str_list_free (&dosuserlist);
191                                 x_fclose(f);
192                                 return True;
193                         }
194                 }
195     
196                 str_list_free (&dosuserlist);
197         }
198
199         x_fclose(f);
200
201         /*
202          * Setup the last_from and last_to as an optimization so 
203          * that we don't scan the file again for the same user.
204          */
205         fstrcpy(last_from,user);
206         fstrcpy(last_to,user);
207
208         return mapped_user;
209 }
210
211 /****************************************************************************
212  * A wrapper for sys_getpwnam().  The following variations are tried:
213  *   - as transmitted
214  *   - in all lower case if this differs from transmitted
215  *   - in all upper case if this differs from transmitted
216  *   - using lp_usernamelevel() for permutations.
217 ****************************************************************************/
218
219 static struct passwd *Get_Pwnam_ret = NULL;
220
221 static struct passwd *Get_Pwnam_internals(const char *user, char *user2)
222 {
223         struct passwd *ret = NULL;
224
225         if (!user2 || !(*user2))
226                 return(NULL);
227
228         if (!user || !(*user))
229                 return(NULL);
230
231         /* Try in all lower case first as this is the most 
232            common case on UNIX systems */
233         strlower_m(user2);
234         DEBUG(5,("Trying _Get_Pwnam(), username as lowercase is %s\n",user2));
235         ret = getpwnam_alloc(user2);
236         if(ret)
237                 goto done;
238
239         /* Try as given, if username wasn't originally lowercase */
240         if(strcmp(user, user2) != 0) {
241                 DEBUG(5,("Trying _Get_Pwnam(), username as given is %s\n",
242                          user));
243                 ret = getpwnam_alloc(user);
244                 if(ret)
245                         goto done;
246         }
247
248         /* Try as uppercase, if username wasn't originally uppercase */
249         strupper_m(user2);
250         if(strcmp(user, user2) != 0) {
251                 DEBUG(5,("Trying _Get_Pwnam(), username as uppercase is %s\n",
252                          user2));
253                 ret = getpwnam_alloc(user2);
254                 if(ret)
255                         goto done;
256         }
257
258         /* Try all combinations up to usernamelevel */
259         strlower_m(user2);
260         DEBUG(5,("Checking combinations of %d uppercase letters in %s\n",
261                  lp_usernamelevel(), user2));
262         ret = uname_string_combinations(user2, getpwnam_alloc,
263                                         lp_usernamelevel());
264
265 done:
266         DEBUG(5,("Get_Pwnam_internals %s find user [%s]!\n",ret ?
267                  "did":"didn't", user));
268
269         return ret;
270 }
271
272 /****************************************************************************
273  Get_Pwnam wrapper without modification.
274   NOTE: This with NOT modify 'user'! 
275   This will return an allocated structure
276 ****************************************************************************/
277
278 struct passwd *Get_Pwnam_alloc(const char *user)
279 {
280         fstring user2;
281         struct passwd *ret;
282
283         if ( *user == '\0' ) {
284                 DEBUG(10,("Get_Pwnam: empty username!\n"));
285                 return NULL;
286         }
287
288         fstrcpy(user2, user);
289
290         DEBUG(5,("Finding user %s\n", user));
291
292         ret = Get_Pwnam_internals(user, user2);
293         
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         struct passwd *ret;
305
306         ret = Get_Pwnam_alloc(user);
307         
308         /* This call used to just return the 'passwd' static buffer.
309            This could then have accidental reuse implications, so 
310            we now malloc a copy, and free it in the next use.
311
312            This should cause the (ab)user to segfault if it 
313            uses an old struct. 
314            
315            This is better than useing the wrong data in security
316            critical operations.
317
318            The real fix is to make the callers free the returned 
319            malloc'ed data.
320         */
321
322         if (Get_Pwnam_ret) {
323                 passwd_free(&Get_Pwnam_ret);
324         }
325         
326         Get_Pwnam_ret = ret;
327
328         return ret;  
329 }
330
331 /****************************************************************************
332  Check if a user is in a netgroup user list. If at first we don't succeed,
333  try lower case.
334 ****************************************************************************/
335
336 static BOOL user_in_netgroup_list(const char *user, const char *ngname)
337 {
338 #ifdef HAVE_NETGROUP
339         static char *mydomain = NULL;
340         fstring lowercase_user;
341
342         if (mydomain == NULL)
343                 yp_get_default_domain(&mydomain);
344
345         if(mydomain == NULL) {
346                 DEBUG(5,("Unable to get default yp domain\n"));
347                 return False;
348         }
349
350         DEBUG(5,("looking for user %s of domain %s in netgroup %s\n",
351                 user, mydomain, ngname));
352
353         if (innetgr(ngname, NULL, user, mydomain)) {
354                 DEBUG(5,("user_in_netgroup_list: Found\n"));
355                 return (True);
356         } else {
357
358                 /*
359                  * Ok, innetgr is case sensitive. Try once more with lowercase
360                  * just in case. Attempt to fix #703. JRA.
361                  */
362
363                 fstrcpy(lowercase_user, user);
364                 strlower_m(lowercase_user);
365         
366                 DEBUG(5,("looking for user %s of domain %s in netgroup %s\n",
367                         lowercase_user, mydomain, ngname));
368
369                 if (innetgr(ngname, NULL, lowercase_user, mydomain)) {
370                         DEBUG(5,("user_in_netgroup_list: Found\n"));
371                         return (True);
372                 }
373         }
374 #endif /* HAVE_NETGROUP */
375         return False;
376 }
377
378 /****************************************************************************
379  Check if a user is in a winbind group.
380 ****************************************************************************/
381   
382 static BOOL user_in_winbind_group_list(const char *user, const char *gname,
383                                        BOOL *winbind_answered)
384 {
385         int i;
386         gid_t gid, gid_low, gid_high;
387         BOOL ret = False;
388         static gid_t *groups = NULL;
389         static int num_groups = 0;
390         static fstring last_user = "";
391  
392         *winbind_answered = False;
393  
394         if ((gid = nametogid(gname)) == (gid_t)-1) {
395                 DEBUG(0,("user_in_winbind_group_list: nametogid for group %s "
396                          "failed.\n", gname ));
397                 goto err;
398         }
399
400         if (!lp_idmap_gid(&gid_low, &gid_high)) {
401                 DEBUG(4, ("winbind gid range not configured, therefore %s "
402                           "cannot be a winbind group\n", gname));
403                 goto err;
404         }
405
406         if (gid < gid_low || gid > gid_high) {
407                 DEBUG(4, ("group %s is not a winbind group\n", gname));
408                 goto err;
409         }
410  
411         /* try to user the last user we looked up */
412         /* otherwise fall back to lookups */
413         
414         if ( !strequal( last_user, user ) || !groups )
415         {
416                 /* clear any cached information */
417                 
418                 SAFE_FREE(groups);
419                 fstrcpy( last_user, "" );
420
421                 /*
422                  * Get the gid's that this user belongs to.
423                  */
424  
425                 if ((num_groups = winbind_getgroups(user, &groups)) == -1)
426                         return False;
427                         
428                 if ( num_groups == -1 )
429                         return False;
430  
431                 if ( num_groups == 0 ) {
432                         *winbind_answered = True;
433                         return False;
434                 }
435                 
436                 /* save the last username */
437                 
438                 fstrcpy( last_user, user );
439                 
440         }
441         else 
442                 DEBUG(10,("user_in_winbind_group_list: using cached user "
443                           "groups for [%s]\n", user));
444  
445         if ( DEBUGLEVEL >= 10 ) {
446                 DEBUG(10,("user_in_winbind_group_list: using groups -- "));
447                 for ( i=0; i<num_groups; i++ )
448                         DEBUGADD(10,("%lu ", (unsigned long)groups[i]));
449                 DEBUGADD(10,("\n"));    
450         }
451  
452         /*
453          * Now we have the gid list for this user - convert the gname to a
454          * gid_t via either winbind or the local UNIX lookup and do the
455          * comparison.
456          */
457  
458         for (i = 0; i < num_groups; i++) {
459                 if (gid == groups[i]) {
460                         ret = True;
461                         break;
462                 }
463         }
464  
465         *winbind_answered = True;
466         SAFE_FREE(groups);
467         return ret;
468  
469    err:
470  
471         *winbind_answered = False;
472         SAFE_FREE(groups);
473         return False;
474 }             
475  
476 /****************************************************************************
477  Check if a user is in a UNIX group.
478 ****************************************************************************/
479
480 BOOL user_in_unix_group_list(const char *user,const char *gname)
481 {
482         struct passwd *pass = Get_Pwnam(user);
483         struct sys_userlist *user_list;
484         struct sys_userlist *member;
485
486         DEBUG(10,("user_in_unix_group_list: checking user %s in group %s\n",
487                   user, gname));
488
489         /*
490          * We need to check the users primary group as this
491          * group is implicit and often not listed in the group database.
492          */
493  
494         if (pass) {
495                 if (strequal(gname,gidtoname(pass->pw_gid))) {
496                         DEBUG(10,("user_in_unix_group_list: group %s is "
497                                   "primary group.\n", gname ));
498                         return True;
499                 }
500         }
501  
502         user_list = get_users_in_group(gname);
503         if (user_list == NULL) {
504                 DEBUG(10,("user_in_unix_group_list: no such group %s\n",
505                           gname ));
506                 return False;
507         }
508
509         for (member = user_list; member; member = member->next) {
510                 DEBUG(10,("user_in_unix_group_list: checking user %s against "
511                           "member %s\n", user, member->unix_name ));
512                 if (strequal(member->unix_name,user)) {
513                         free_userlist(user_list);
514                         return(True);
515                 }
516         }
517
518         free_userlist(user_list);
519         return False;
520 }             
521
522 /****************************************************************************
523  Check if a user is in a group list. Ask winbind first, then use UNIX.
524 ****************************************************************************/
525
526 BOOL user_in_group_list(const char *user, const char *gname, gid_t *groups,
527                         size_t n_groups)
528 {
529         BOOL winbind_answered = False;
530         BOOL ret;
531         gid_t gid;
532         unsigned i;
533
534         gid = nametogid(gname);
535         if (gid == (gid_t)-1) 
536                 return False;
537
538         if (groups && n_groups > 0) {
539                 for (i=0; i < n_groups; i++) {
540                         if (groups[i] == gid) {
541                                 return True;
542                         }
543                 }
544                 return False;
545         }
546
547         /* fallback if we don't yet have the group list */
548
549         ret = user_in_winbind_group_list(user, gname, &winbind_answered);
550         if (!winbind_answered)
551                 ret = user_in_unix_group_list(user, gname);
552
553         if (ret)
554                 DEBUG(10,("user_in_group_list: user |%s| is in group |%s|\n",
555                           user, gname));
556         return ret;
557 }
558
559 /****************************************************************************
560  Check if a user is in a user list - can check combinations of UNIX
561  and netgroup lists.
562 ****************************************************************************/
563
564 BOOL user_in_list(const char *user,const char **list, gid_t *groups,
565                   size_t n_groups)
566 {
567         if (!list || !*list)
568                 return False;
569
570         DEBUG(10,("user_in_list: checking user %s in list\n", user));
571
572         while (*list) {
573
574                 DEBUG(10,("user_in_list: checking user |%s| against |%s|\n",
575                           user, *list));
576
577                 /*
578                  * Check raw username.
579                  */
580                 if (strequal(user, *list))
581                         return(True);
582
583                 /*
584                  * Now check to see if any combination
585                  * of UNIX and netgroups has been specified.
586                  */
587
588                 if(**list == '@') {
589                         /*
590                          * Old behaviour. Check netgroup list
591                          * followed by UNIX list.
592                          */
593                         if(user_in_netgroup_list(user, *list +1))
594                                 return True;
595                         if(user_in_group_list(user, *list +1, groups,
596                                               n_groups))
597                                 return True;
598                 } else if (**list == '+') {
599
600                         if((*(*list +1)) == '&') {
601                                 /*
602                                  * Search UNIX list followed by netgroup.
603                                  */
604                                 if(user_in_group_list(user, *list +2, groups,
605                                                       n_groups))
606                                         return True;
607                                 if(user_in_netgroup_list(user, *list +2))
608                                         return True;
609
610                         } else {
611
612                                 /*
613                                  * Just search UNIX list.
614                                  */
615
616                                 if(user_in_group_list(user, *list +1, groups,
617                                                       n_groups))
618                                         return True;
619                         }
620
621                 } else if (**list == '&') {
622
623                         if(*(*list +1) == '+') {
624                                 /*
625                                  * Search netgroup list followed by UNIX list.
626                                  */
627                                 if(user_in_netgroup_list(user, *list +2))
628                                         return True;
629                                 if(user_in_group_list(user, *list +2, groups,
630                                                       n_groups))
631                                         return True;
632                         } else {
633                                 /*
634                                  * Just search netgroup list.
635                                  */
636                                 if(user_in_netgroup_list(user, *list +1))
637                                         return True;
638                         }
639                 } else if (!name_is_local(*list)) {
640                         /*
641                          * If user name did not match and token is not a unix
642                          * group and the token has a winbind separator in the
643                          * name then see if it is a Windows group.
644                          */
645
646                         DOM_SID g_sid;
647                         enum SID_NAME_USE name_type;
648                         BOOL winbind_answered = False;
649                         BOOL ret;
650                         fstring groupname, domain;
651                         
652                         /* Parse a string of the form DOMAIN/user into a
653                          * domain and a user */
654
655                         char *p = strchr(*list,*lp_winbind_separator());
656                         
657                         DEBUG(10,("user_in_list: checking if user |%s| is in "
658                                   "winbind group |%s|\n", user, *list));
659
660                         if (p) {
661                                 fstrcpy(groupname, p+1);
662                                 fstrcpy(domain, *list);
663                                 domain[PTR_DIFF(p, *list)] = 0;
664
665                                 /* Check to see if name is a Windows group;
666                                    Win2k native mode DCs will return domain
667                                    local groups; while NT4 or mixed mode 2k
668                                    DCs will not */
669                         
670                                 if ( winbind_lookup_name(domain, groupname,
671                                                          &g_sid, &name_type) 
672                                      && ( name_type==SID_NAME_DOM_GRP || 
673                                           (strequal(lp_workgroup(), domain) &&
674                                            name_type==SID_NAME_ALIAS) ) )
675                                 {
676                                         
677                                         /* Check if user name is in the
678                                          * Windows group */
679                                         ret = user_in_winbind_group_list(
680                                                 user, *list,
681                                                 &winbind_answered);
682                                         
683                                         if (winbind_answered && ret == True) {
684                                                 DEBUG(10,("user_in_list: user "
685                                                           "|%s| is in winbind "
686                                                           "group |%s|\n",
687                                                           user, *list));
688                                                 return ret;
689                                         }
690                                 }
691                         }
692                 }
693     
694                 list++;
695         }
696         return(False);
697 }
698
699 /* The functions below have been taken from password.c and slightly modified */
700 /****************************************************************************
701  Apply a function to upper/lower case combinations
702  of a string and return true if one of them returns true.
703  Try all combinations with N uppercase letters.
704  offset is the first char to try and change (start with 0)
705  it assumes the string starts lowercased
706 ****************************************************************************/
707
708 static struct passwd *uname_string_combinations2(char *s,int offset,struct passwd *(*fn)(const char *),int N)
709 {
710         ssize_t len = (ssize_t)strlen(s);
711         int i;
712         struct passwd *ret;
713
714         if (N <= 0 || offset >= len)
715                 return(fn(s));
716
717         for (i=offset;i<(len-(N-1));i++) {
718                 char c = s[i];
719                 if (!islower_ascii((int)c))
720                         continue;
721                 s[i] = toupper_ascii(c);
722                 ret = uname_string_combinations2(s,i+1,fn,N-1);
723                 if(ret)
724                         return(ret);
725                 s[i] = c;
726         }
727         return(NULL);
728 }
729
730 /****************************************************************************
731  Apply a function to upper/lower case combinations
732  of a string and return true if one of them returns true.
733  Try all combinations with up to N uppercase letters.
734  offset is the first char to try and change (start with 0)
735  it assumes the string starts lowercased
736 ****************************************************************************/
737
738 static struct passwd * uname_string_combinations(char *s,struct passwd * (*fn)(const char *),int N)
739 {
740         int n;
741         struct passwd *ret;
742
743         for (n=1;n<=N;n++) {
744                 ret = uname_string_combinations2(s,0,fn,n);
745                 if(ret)
746                         return(ret);
747         }  
748         return(NULL);
749 }
750