b8f33494ee4ad53c27a3b51be836cd284eb6767b
[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, 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(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 without modification.
278   NOTE: This with NOT modify 'user'! 
279 ****************************************************************************/
280
281 struct passwd *Get_Pwnam(const char *user)
282 {
283         fstring user2;
284         struct passwd *ret;
285
286         fstrcpy(user2, user);
287
288         DEBUG(5,("Finding user %s\n", user));
289
290         ret = Get_Pwnam_internals(user, user2);
291         
292         return ret;  
293 }
294
295 /****************************************************************************
296  Check if a user is in a netgroup user list.
297 ****************************************************************************/
298
299 static BOOL user_in_netgroup_list(const char *user, const char *ngname)
300 {
301 #ifdef HAVE_NETGROUP
302         static char *mydomain = NULL;
303         if (mydomain == NULL)
304                 yp_get_default_domain(&mydomain);
305
306         if(mydomain == NULL) {
307                 DEBUG(5,("Unable to get default yp domain\n"));
308                 return False;
309         }
310
311         DEBUG(5,("looking for user %s of domain %s in netgroup %s\n",
312                 user, mydomain, ngname));
313         DEBUG(5,("innetgr is %s\n", innetgr(ngname, NULL, user, mydomain)
314                 ? "TRUE" : "FALSE"));
315
316         if (innetgr(ngname, NULL, user, mydomain))
317                 return (True);
318 #endif /* HAVE_NETGROUP */
319         return False;
320 }
321
322 /****************************************************************************
323  Check if a user is in a winbind group.
324 ****************************************************************************/
325   
326 static BOOL user_in_winbind_group_list(const char *user, const char *gname, BOOL *winbind_answered)
327 {
328         int num_groups;
329         int i;
330         gid_t *groups = NULL;
331         gid_t gid, gid_low, gid_high;
332         BOOL ret = False;
333  
334         *winbind_answered = False;
335  
336         if ((gid = nametogid(gname)) == (gid_t)-1) {
337                 DEBUG(0,("user_in_winbind_group_list: nametogid for group %s failed.\n",
338                         gname ));
339                 goto err;
340         }
341
342         if (!lp_winbind_gid(&gid_low, &gid_high)) {
343                 DEBUG(4, ("winbind gid range not configured, therefore %s cannot be a winbind group\n", gname));
344                 goto err;
345         }
346
347         if (gid < gid_low || gid > gid_high) {
348                 DEBUG(4, ("group %s is not a winbind group\n", gname));
349                 goto err;
350         }
351  
352         /*
353          * Get the gid's that this user belongs to.
354          */
355  
356         if ((num_groups = winbind_getgroups(user, 0, NULL)) == -1)
357                 return False;
358  
359         if (num_groups == 0) {
360                 *winbind_answered = True;
361                 return False;
362         }
363  
364         if ((groups = (gid_t *)malloc(sizeof(gid_t) * num_groups )) == NULL) {
365                 DEBUG(0,("user_in_winbind_group_list: malloc fail.\n"));
366                 goto err;
367         }
368  
369         if ((num_groups = winbind_getgroups(user, num_groups, groups)) == -1) {
370                 DEBUG(0,("user_in_winbind_group_list: second winbind_getgroups call \
371 failed with error %s\n", strerror(errno) ));
372                 goto err;
373         }
374  
375         /*
376          * Now we have the gid list for this user - convert the gname
377          * to a gid_t via either winbind or the local UNIX lookup and do the comparison.
378          */
379  
380         for (i = 0; i < num_groups; i++) {
381                 if (gid == groups[i]) {
382                         ret = True;
383                         break;
384                 }
385         }
386  
387         *winbind_answered = True;
388         SAFE_FREE(groups);
389         return ret;
390  
391    err:
392  
393         *winbind_answered = False;
394         SAFE_FREE(groups);
395         return False;
396 }             
397  
398 /****************************************************************************
399  Check if a user is in a UNIX group.
400 ****************************************************************************/
401
402 BOOL user_in_unix_group_list(const char *user,const char *gname)
403 {
404         struct passwd *pass = Get_Pwnam(user);
405         struct sys_userlist *user_list;
406         struct sys_userlist *member;
407
408         DEBUG(10,("user_in_unix_group_list: checking user %s in group %s\n", user, gname));
409
410         /*
411          * We need to check the users primary group as this
412          * group is implicit and often not listed in the group database.
413          */
414  
415         if (pass) {
416                 if (strequal(gname,gidtoname(pass->pw_gid))) {
417                         DEBUG(10,("user_in_unix_group_list: group %s is primary group.\n", gname ));
418                         return True;
419                 }
420         }
421  
422         user_list = get_users_in_group(gname);
423         if (user_list == NULL) {
424                 DEBUG(10,("user_in_unix_group_list: no such group %s\n", gname ));
425                 return False;
426         }
427
428         for (member = user_list; member; member = member->next) {
429                 DEBUG(10,("user_in_unix_group_list: checking user %s against member %s\n",
430                         user, member->unix_name ));
431                 if (strequal(member->unix_name,user)) {
432                         free_userlist(user_list);
433                         return(True);
434                 }
435         }
436
437         free_userlist(user_list);
438         return False;
439 }             
440
441 /****************************************************************************
442  Check if a user is in a group list. Ask winbind first, then use UNIX.
443 ****************************************************************************/
444
445 BOOL user_in_group_list(const char *user, const char *gname, gid_t *groups, size_t n_groups)
446 {
447         BOOL winbind_answered = False;
448         BOOL ret;
449         gid_t gid;
450         unsigned i;
451
452         gid = nametogid(gname);
453         if (gid == (gid_t)-1) 
454                 return False;
455
456         if (groups && n_groups > 0) {
457                 for (i=0; i < n_groups; i++) {
458                         if (groups[i] == gid) {
459                                 return True;
460                         }
461                 }
462                 return False;
463         }
464
465         /* fallback if we don't yet have the group list */
466
467         ret = user_in_winbind_group_list(user, gname, &winbind_answered);
468         if (!winbind_answered)
469                 ret = user_in_unix_group_list(user, gname);
470
471         if (ret)
472                 DEBUG(10,("user_in_group_list: user |%s| is in group |%s|\n", user, gname));
473         return ret;
474 }
475
476 /****************************************************************************
477  Check if a user is in a user list - can check combinations of UNIX
478  and netgroup lists.
479 ****************************************************************************/
480
481 BOOL user_in_list(const char *user,const char **list, gid_t *groups, size_t n_groups)
482 {
483         if (!list || !*list)
484                 return False;
485
486         DEBUG(10,("user_in_list: checking user %s in list\n", user));
487
488         while (*list) {
489
490                 DEBUG(10,("user_in_list: checking user |%s| against |%s|\n", user, *list));
491
492                 /*
493                  * Check raw username.
494                  */
495                 if (strequal(user, *list))
496                         return(True);
497
498                 /*
499                  * Now check to see if any combination
500                  * of UNIX and netgroups has been specified.
501                  */
502
503                 if(**list == '@') {
504                         /*
505                          * Old behaviour. Check netgroup list
506                          * followed by UNIX list.
507                          */
508                         if(user_in_netgroup_list(user, *list +1))
509                                 return True;
510                         if(user_in_group_list(user, *list +1, groups, n_groups))
511                                 return True;
512                 } else if (**list == '+') {
513
514                         if((*(*list +1)) == '&') {
515                                 /*
516                                  * Search UNIX list followed by netgroup.
517                                  */
518                                 if(user_in_group_list(user, *list +2, groups, n_groups))
519                                         return True;
520                                 if(user_in_netgroup_list(user, *list +2))
521                                         return True;
522
523                         } else {
524
525                                 /*
526                                  * Just search UNIX list.
527                                  */
528
529                                 if(user_in_group_list(user, *list +1, groups, n_groups))
530                                         return True;
531                         }
532
533                 } else if (**list == '&') {
534
535                         if(*(*list +1) == '+') {
536                                 /*
537                                  * Search netgroup list followed by UNIX list.
538                                  */
539                                 if(user_in_netgroup_list(user, *list +2))
540                                         return True;
541                                 if(user_in_group_list(user, *list +2, groups, n_groups))
542                                         return True;
543                         } else {
544                                 /*
545                                  * Just search netgroup list.
546                                  */
547                                 if(user_in_netgroup_list(user, *list +1))
548                                         return True;
549                         }
550                 } else if (!name_is_local(*list)) {
551                         /*
552                          * If user name did not match and token is not
553                          * a unix group and the token has a winbind separator in the
554                          * name then see if it is a Windows group.
555                          */
556
557                         DOM_SID g_sid;
558                         enum SID_NAME_USE name_type;
559                         BOOL winbind_answered = False;
560                         BOOL ret;
561                         fstring groupname, domain;
562                         
563                         /* Parse a string of the form DOMAIN/user into a domain and a user */
564
565                         char *p = strchr(*list,*lp_winbind_separator());
566                         
567                         DEBUG(10,("user_in_list: checking if user |%s| is in winbind group |%s|\n", user, *list));
568
569                         if (p) {
570                                 fstrcpy(groupname, p+1);
571                                 fstrcpy(domain, *list);
572                                 domain[PTR_DIFF(p, *list)] = 0;
573
574                                 /* Check to see if name is a Windows group */
575                                 if (winbind_lookup_name(domain, groupname, &g_sid, &name_type) && name_type == SID_NAME_DOM_GRP) {
576                                         
577                                 /* Check if user name is in the Windows group */
578                                         ret = user_in_winbind_group_list(user, *list, &winbind_answered);
579                                         
580                                         if (winbind_answered && ret == True) {
581                                                 DEBUG(10,("user_in_list: user |%s| is in winbind group |%s|\n", user, *list));
582                                                 return ret;
583                                         }
584                                 }
585                         }
586                 }
587     
588                 list++;
589         }
590         return(False);
591 }
592
593 /* The functions below have been taken from password.c and slightly modified */
594 /****************************************************************************
595  Apply a function to upper/lower case combinations
596  of a string and return true if one of them returns true.
597  Try all combinations with N uppercase letters.
598  offset is the first char to try and change (start with 0)
599  it assumes the string starts lowercased
600 ****************************************************************************/
601
602 static struct passwd *uname_string_combinations2(char *s,int offset,struct passwd *(*fn)(const char *),int N)
603 {
604         ssize_t len = (ssize_t)strlen(s);
605         int i;
606         struct passwd *ret;
607
608         if (N <= 0 || offset >= len)
609                 return(fn(s));
610
611         for (i=offset;i<(len-(N-1));i++) {
612                 char c = s[i];
613                 if (!islower((int)c))
614                         continue;
615                 s[i] = toupper(c);
616                 ret = uname_string_combinations2(s,i+1,fn,N-1);
617                 if(ret)
618                         return(ret);
619                 s[i] = c;
620         }
621         return(NULL);
622 }
623
624 /****************************************************************************
625  Apply a function to upper/lower case combinations
626  of a string and return true if one of them returns true.
627  Try all combinations with up to N uppercase letters.
628  offset is the first char to try and change (start with 0)
629  it assumes the string starts lowercased
630 ****************************************************************************/
631
632 static struct passwd * uname_string_combinations(char *s,struct passwd * (*fn)(const char *),int N)
633 {
634         int n;
635         struct passwd *ret;
636
637         for (n=1;n<=N;n++) {
638                 ret = uname_string_combinations2(s,0,fn,n);
639                 if(ret)
640                         return(ret);
641         }  
642         return(NULL);
643 }
644