r8654: merging cli_spoolss_XX() updates from trunk
[bbaumbach/samba-autobuild/.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, NULL);
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, (const char **)dosuserlist, NULL, 0)) {
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_m(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_m(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_m(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         return ret;
254 }
255
256 /****************************************************************************
257  Get_Pwnam wrapper without modification.
258   NOTE: This with NOT modify 'user'! 
259   This will return an allocated structure
260 ****************************************************************************/
261
262 struct passwd *Get_Pwnam_alloc(const char *user)
263 {
264         fstring user2;
265         struct passwd *ret;
266
267         if ( *user == '\0' ) {
268                 DEBUG(10,("Get_Pwnam: empty username!\n"));
269                 return NULL;
270         }
271
272         fstrcpy(user2, user);
273
274         DEBUG(5,("Finding user %s\n", user));
275
276         ret = Get_Pwnam_internals(user, user2);
277         
278         return ret;  
279 }
280
281 /****************************************************************************
282  Get_Pwnam wrapper without modification.
283   NOTE: This with NOT modify 'user'! 
284 ****************************************************************************/
285
286 struct passwd *Get_Pwnam(const char *user)
287 {
288         struct passwd *ret;
289
290         ret = Get_Pwnam_alloc(user);
291         
292         /* This call used to just return the 'passwd' static buffer.
293            This could then have accidental reuse implications, so 
294            we now malloc a copy, and free it in the next use.
295
296            This should cause the (ab)user to segfault if it 
297            uses an old struct. 
298            
299            This is better than useing the wrong data in security
300            critical operations.
301
302            The real fix is to make the callers free the returned 
303            malloc'ed data.
304         */
305
306         if (Get_Pwnam_ret) {
307                 passwd_free(&Get_Pwnam_ret);
308         }
309         
310         Get_Pwnam_ret = ret;
311
312         return ret;  
313 }
314
315 /****************************************************************************
316  Check if a user is in a netgroup user list. If at first we don't succeed,
317  try lower case.
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         fstring lowercase_user;
325
326         if (mydomain == NULL)
327                 yp_get_default_domain(&mydomain);
328
329         if(mydomain == NULL) {
330                 DEBUG(5,("Unable to get default yp domain\n"));
331                 return False;
332         }
333
334         DEBUG(5,("looking for user %s of domain %s in netgroup %s\n",
335                 user, mydomain, ngname));
336
337         if (innetgr(ngname, NULL, user, mydomain)) {
338                 DEBUG(5,("user_in_netgroup_list: Found\n"));
339                 return (True);
340         } else {
341
342                 /*
343                  * Ok, innetgr is case sensitive. Try once more with lowercase
344                  * just in case. Attempt to fix #703. JRA.
345                  */
346
347                 fstrcpy(lowercase_user, user);
348                 strlower_m(lowercase_user);
349         
350                 DEBUG(5,("looking for user %s of domain %s in netgroup %s\n",
351                         lowercase_user, mydomain, ngname));
352
353                 if (innetgr(ngname, NULL, lowercase_user, mydomain)) {
354                         DEBUG(5,("user_in_netgroup_list: Found\n"));
355                         return (True);
356                 }
357         }
358 #endif /* HAVE_NETGROUP */
359         return False;
360 }
361
362 /****************************************************************************
363  Check if a user is in a winbind group.
364 ****************************************************************************/
365   
366 static BOOL user_in_winbind_group_list(const char *user, const char *gname, BOOL *winbind_answered)
367 {
368         int i;
369         gid_t gid, gid_low, gid_high;
370         BOOL ret = False;
371         static gid_t *groups = NULL;
372         static int num_groups = 0;
373         static fstring last_user = "";
374  
375         *winbind_answered = False;
376  
377         if ((gid = nametogid(gname)) == (gid_t)-1) {
378                 DEBUG(0,("user_in_winbind_group_list: nametogid for group %s failed.\n",
379                         gname ));
380                 goto err;
381         }
382
383         if (!lp_idmap_gid(&gid_low, &gid_high)) {
384                 DEBUG(4, ("winbind gid range not configured, therefore %s cannot be a winbind group\n", gname));
385                 goto err;
386         }
387
388         if (gid < gid_low || gid > gid_high) {
389                 DEBUG(4, ("group %s is not a winbind group\n", gname));
390                 goto err;
391         }
392  
393         /* try to user the last user we looked up */
394         /* otherwise fall back to lookups */
395         
396         if ( !strequal( last_user, user ) || !groups )
397         {
398                 /* clear any cached information */
399                 
400                 SAFE_FREE(groups);
401                 fstrcpy( last_user, "" );
402
403                 /*
404                  * Get the gid's that this user belongs to.
405                  */
406  
407                 if ((num_groups = winbind_getgroups(user, &groups)) == -1)
408                         return False;
409                         
410                 if ( num_groups == -1 )
411                         return False;
412  
413                 if ( num_groups == 0 ) {
414                         *winbind_answered = True;
415                         return False;
416                 }
417                 
418                 /* save the last username */
419                 
420                 fstrcpy( last_user, user );
421                 
422         }
423         else 
424                 DEBUG(10,("user_in_winbind_group_list: using cached user groups for [%s]\n", user));
425  
426         if ( DEBUGLEVEL >= 10 ) {
427                 DEBUG(10,("user_in_winbind_group_list: using groups -- "));
428                 for ( i=0; i<num_groups; i++ )
429                         DEBUGADD(10,("%lu ", (unsigned long)groups[i]));
430                 DEBUGADD(10,("\n"));    
431         }
432  
433         /*
434          * Now we have the gid list for this user - convert the gname
435          * to a gid_t via either winbind or the local UNIX lookup and do the comparison.
436          */
437  
438         for (i = 0; i < num_groups; i++) {
439                 if (gid == groups[i]) {
440                         ret = True;
441                         break;
442                 }
443         }
444  
445         *winbind_answered = True;
446         SAFE_FREE(groups);
447         return ret;
448  
449    err:
450  
451         *winbind_answered = False;
452         SAFE_FREE(groups);
453         return False;
454 }             
455  
456 /****************************************************************************
457  Check if a user is in a UNIX group.
458 ****************************************************************************/
459
460 BOOL user_in_unix_group_list(const char *user,const char *gname)
461 {
462         struct passwd *pass = Get_Pwnam(user);
463         struct sys_userlist *user_list;
464         struct sys_userlist *member;
465
466         DEBUG(10,("user_in_unix_group_list: checking user %s in group %s\n", user, gname));
467
468         /*
469          * We need to check the users primary group as this
470          * group is implicit and often not listed in the group database.
471          */
472  
473         if (pass) {
474                 if (strequal(gname,gidtoname(pass->pw_gid))) {
475                         DEBUG(10,("user_in_unix_group_list: group %s is primary group.\n", gname ));
476                         return True;
477                 }
478         }
479  
480         user_list = get_users_in_group(gname);
481         if (user_list == NULL) {
482                 DEBUG(10,("user_in_unix_group_list: no such group %s\n", gname ));
483                 return False;
484         }
485
486         for (member = user_list; member; member = member->next) {
487                 DEBUG(10,("user_in_unix_group_list: checking user %s against member %s\n",
488                         user, member->unix_name ));
489                 if (strequal(member->unix_name,user)) {
490                         free_userlist(user_list);
491                         return(True);
492                 }
493         }
494
495         free_userlist(user_list);
496         return False;
497 }             
498
499 /****************************************************************************
500  Check if a user is in a group list. Ask winbind first, then use UNIX.
501 ****************************************************************************/
502
503 BOOL user_in_group_list(const char *user, const char *gname, gid_t *groups, size_t n_groups)
504 {
505         BOOL winbind_answered = False;
506         BOOL ret;
507         gid_t gid;
508         unsigned i;
509
510         gid = nametogid(gname);
511         if (gid == (gid_t)-1) 
512                 return False;
513
514         if (groups && n_groups > 0) {
515                 for (i=0; i < n_groups; i++) {
516                         if (groups[i] == gid) {
517                                 return True;
518                         }
519                 }
520                 return False;
521         }
522
523         /* fallback if we don't yet have the group list */
524
525         ret = user_in_winbind_group_list(user, gname, &winbind_answered);
526         if (!winbind_answered)
527                 ret = user_in_unix_group_list(user, gname);
528
529         if (ret)
530                 DEBUG(10,("user_in_group_list: user |%s| is in group |%s|\n", user, gname));
531         return ret;
532 }
533
534 /****************************************************************************
535  Check if a user is in a user list - can check combinations of UNIX
536  and netgroup lists.
537 ****************************************************************************/
538
539 BOOL user_in_list(const char *user,const char **list, gid_t *groups, size_t n_groups)
540 {
541         if (!list || !*list)
542                 return False;
543
544         DEBUG(10,("user_in_list: checking user %s in list\n", user));
545
546         while (*list) {
547
548                 DEBUG(10,("user_in_list: checking user |%s| against |%s|\n", user, *list));
549
550                 /*
551                  * Check raw username.
552                  */
553                 if (strequal(user, *list))
554                         return(True);
555
556                 /*
557                  * Now check to see if any combination
558                  * of UNIX and netgroups has been specified.
559                  */
560
561                 if(**list == '@') {
562                         /*
563                          * Old behaviour. Check netgroup list
564                          * followed by UNIX list.
565                          */
566                         if(user_in_netgroup_list(user, *list +1))
567                                 return True;
568                         if(user_in_group_list(user, *list +1, groups, n_groups))
569                                 return True;
570                 } else if (**list == '+') {
571
572                         if((*(*list +1)) == '&') {
573                                 /*
574                                  * Search UNIX list followed by netgroup.
575                                  */
576                                 if(user_in_group_list(user, *list +2, groups, n_groups))
577                                         return True;
578                                 if(user_in_netgroup_list(user, *list +2))
579                                         return True;
580
581                         } else {
582
583                                 /*
584                                  * Just search UNIX list.
585                                  */
586
587                                 if(user_in_group_list(user, *list +1, groups, n_groups))
588                                         return True;
589                         }
590
591                 } else if (**list == '&') {
592
593                         if(*(*list +1) == '+') {
594                                 /*
595                                  * Search netgroup list followed by UNIX list.
596                                  */
597                                 if(user_in_netgroup_list(user, *list +2))
598                                         return True;
599                                 if(user_in_group_list(user, *list +2, groups, n_groups))
600                                         return True;
601                         } else {
602                                 /*
603                                  * Just search netgroup list.
604                                  */
605                                 if(user_in_netgroup_list(user, *list +1))
606                                         return True;
607                         }
608                 } else if (!name_is_local(*list)) {
609                         /*
610                          * If user name did not match and token is not
611                          * a unix group and the token has a winbind separator in the
612                          * name then see if it is a Windows group.
613                          */
614
615                         DOM_SID g_sid;
616                         enum SID_NAME_USE name_type;
617                         BOOL winbind_answered = False;
618                         BOOL ret;
619                         fstring groupname, domain;
620                         
621                         /* Parse a string of the form DOMAIN/user into a domain and a user */
622
623                         char *p = strchr(*list,*lp_winbind_separator());
624                         
625                         DEBUG(10,("user_in_list: checking if user |%s| is in winbind group |%s|\n", user, *list));
626
627                         if (p) {
628                                 fstrcpy(groupname, p+1);
629                                 fstrcpy(domain, *list);
630                                 domain[PTR_DIFF(p, *list)] = 0;
631
632                                 /* Check to see if name is a Windows group;  Win2k native mode DCs
633                                    will return domain local groups; while NT4 or mixed mode 2k DCs
634                                    will not */
635                         
636                                 if ( winbind_lookup_name(domain, groupname, &g_sid, &name_type) 
637                                         && ( name_type==SID_NAME_DOM_GRP || 
638                                            (strequal(lp_workgroup(), domain) && name_type==SID_NAME_ALIAS) ) )
639                                 {
640                                         
641                                         /* Check if user name is in the Windows group */
642                                         ret = user_in_winbind_group_list(user, *list, &winbind_answered);
643                                         
644                                         if (winbind_answered && ret == True) {
645                                                 DEBUG(10,("user_in_list: user |%s| is in winbind group |%s|\n", user, *list));
646                                                 return ret;
647                                         }
648                                 }
649                         }
650                 }
651     
652                 list++;
653         }
654         return(False);
655 }
656
657 /* The functions below have been taken from password.c and slightly modified */
658 /****************************************************************************
659  Apply a function to upper/lower case combinations
660  of a string and return true if one of them returns true.
661  Try all combinations with N uppercase letters.
662  offset is the first char to try and change (start with 0)
663  it assumes the string starts lowercased
664 ****************************************************************************/
665
666 static struct passwd *uname_string_combinations2(char *s,int offset,struct passwd *(*fn)(const char *),int N)
667 {
668         ssize_t len = (ssize_t)strlen(s);
669         int i;
670         struct passwd *ret;
671
672         if (N <= 0 || offset >= len)
673                 return(fn(s));
674
675         for (i=offset;i<(len-(N-1));i++) {
676                 char c = s[i];
677                 if (!islower((int)c))
678                         continue;
679                 s[i] = toupper(c);
680                 ret = uname_string_combinations2(s,i+1,fn,N-1);
681                 if(ret)
682                         return(ret);
683                 s[i] = c;
684         }
685         return(NULL);
686 }
687
688 /****************************************************************************
689  Apply a function to upper/lower case combinations
690  of a string and return true if one of them returns true.
691  Try all combinations with up to N uppercase letters.
692  offset is the first char to try and change (start with 0)
693  it assumes the string starts lowercased
694 ****************************************************************************/
695
696 static struct passwd * uname_string_combinations(char *s,struct passwd * (*fn)(const char *),int N)
697 {
698         int n;
699         struct passwd *ret;
700
701         for (n=1;n<=N;n++) {
702                 ret = uname_string_combinations2(s,0,fn,n);
703                 if(ret)
704                         return(ret);
705         }  
706         return(NULL);
707 }
708