084d7e0bdb2c80607037009cd1dd2a455eb6b546
[samba.git] / source4 / 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         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 /****************************************************************************
87  * A wrapper for sys_getpwnam().  The following variations are tried:
88  *   - as transmitted
89  *   - in all lower case if this differs from transmitted
90  *   - in all upper case if this differs from transmitted
91  *   - using lp_usernamelevel() for permutations.
92 ****************************************************************************/
93
94 static struct passwd *Get_Pwnam_ret = NULL;
95
96 static struct passwd *Get_Pwnam_internals(const char *user, char *user2)
97 {
98         struct passwd *ret = NULL;
99
100         if (!user2 || !(*user2))
101                 return(NULL);
102
103         if (!user || !(*user))
104                 return(NULL);
105
106         /* Try in all lower case first as this is the most 
107            common case on UNIX systems */
108         strlower(user2);
109         DEBUG(5,("Trying _Get_Pwnam(), username as lowercase is %s\n",user2));
110         ret = getpwnam_alloc(user2);
111         if(ret)
112                 goto done;
113
114         /* Try as given, if username wasn't originally lowercase */
115         if(strcmp(user, user2) != 0) {
116                 DEBUG(5,("Trying _Get_Pwnam(), username as given is %s\n", user));
117                 ret = getpwnam_alloc(user);
118                 if(ret)
119                         goto done;
120         }
121
122         /* Try as uppercase, if username wasn't originally uppercase */
123         strupper(user2);
124         if(strcmp(user, user2) != 0) {
125                 DEBUG(5,("Trying _Get_Pwnam(), username as uppercase is %s\n", user2));
126                 ret = getpwnam_alloc(user2);
127                 if(ret)
128                         goto done;
129         }
130
131         /* Try all combinations up to usernamelevel */
132         strlower(user2);
133         DEBUG(5,("Checking combinations of %d uppercase letters in %s\n", lp_usernamelevel(), user2));
134         ret = uname_string_combinations(user2, getpwnam_alloc, lp_usernamelevel());
135
136 done:
137         DEBUG(5,("Get_Pwnam_internals %s find user [%s]!\n",ret ? "did":"didn't", user));
138
139         /* This call used to just return the 'passwd' static buffer.
140            This could then have accidental reuse implications, so 
141            we now malloc a copy, and free it in the next use.
142
143            This should cause the (ab)user to segfault if it 
144            uses an old struct. 
145            
146            This is better than useing the wrong data in security
147            critical operations.
148
149            The real fix is to make the callers free the returned 
150            malloc'ed data.
151         */
152
153         if (Get_Pwnam_ret) {
154                 passwd_free(&Get_Pwnam_ret);
155         }
156         
157         Get_Pwnam_ret = ret;
158
159         return ret;
160 }
161
162 /****************************************************************************
163  Get_Pwnam wrapper without modification.
164   NOTE: This with NOT modify 'user'! 
165 ****************************************************************************/
166
167 struct passwd *Get_Pwnam(const char *user)
168 {
169         fstring user2;
170         struct passwd *ret;
171
172         fstrcpy(user2, user);
173
174         DEBUG(5,("Finding user %s\n", user));
175
176         ret = Get_Pwnam_internals(user, user2);
177         
178         return ret;  
179 }
180
181 /****************************************************************************
182  Check if a user is in a netgroup user list.
183 ****************************************************************************/
184
185 static BOOL user_in_netgroup_list(const char *user, const char *ngname)
186 {
187 #ifdef HAVE_NETGROUP
188         /*      static char *mydomain = NULL; */
189         /* REWRITE: make thread safe if caching */
190         char *mydomain = NULL;
191         /*if (mydomain == NULL) */
192                 yp_get_default_domain(&mydomain);
193
194         if(mydomain == NULL) {
195                 DEBUG(5,("Unable to get default yp domain\n"));
196                 return False;
197         }
198
199         DEBUG(5,("looking for user %s of domain %s in netgroup %s\n",
200                 user, mydomain, ngname));
201         DEBUG(5,("innetgr is %s\n", innetgr(ngname, NULL, user, mydomain)
202                 ? "TRUE" : "FALSE"));
203
204         if (innetgr(ngname, NULL, user, mydomain))
205                 return (True);
206 #endif /* HAVE_NETGROUP */
207         return False;
208 }
209
210 /****************************************************************************
211  Check if a user is in a winbind group.
212 ****************************************************************************/
213   
214 static BOOL user_in_winbind_group_list(const char *user, const char *gname, BOOL *winbind_answered)
215 {
216         int num_groups;
217         int i;
218         gid_t *groups = NULL;
219         gid_t gid, gid_low, gid_high;
220         BOOL ret = False;
221  
222         *winbind_answered = False;
223  
224         if ((gid = nametogid(gname)) == (gid_t)-1) {
225                 DEBUG(0,("user_in_winbind_group_list: nametogid for group %s failed.\n",
226                         gname ));
227                 goto err;
228         }
229
230         if (!lp_winbind_gid(&gid_low, &gid_high)) {
231                 DEBUG(4, ("winbind gid range not configured, therefore %s cannot be a winbind group\n", gname));
232                 goto err;
233         }
234
235         if (gid < gid_low || gid > gid_high) {
236                 DEBUG(4, ("group %s is not a winbind group\n", gname));
237                 goto err;
238         }
239  
240         /*
241          * Get the gid's that this user belongs to.
242          */
243  
244         if ((num_groups = winbind_getgroups(user, 0, NULL)) == -1)
245                 return False;
246  
247         if (num_groups == 0) {
248                 *winbind_answered = True;
249                 return False;
250         }
251  
252         if ((groups = (gid_t *)malloc(sizeof(gid_t) * num_groups )) == NULL) {
253                 DEBUG(0,("user_in_winbind_group_list: malloc fail.\n"));
254                 goto err;
255         }
256  
257         if ((num_groups = winbind_getgroups(user, num_groups, groups)) == -1) {
258                 DEBUG(0,("user_in_winbind_group_list: second winbind_getgroups call \
259 failed with error %s\n", strerror(errno) ));
260                 goto err;
261         }
262  
263         /*
264          * Now we have the gid list for this user - convert the gname
265          * to a gid_t via either winbind or the local UNIX lookup and do the comparison.
266          */
267  
268         for (i = 0; i < num_groups; i++) {
269                 if (gid == groups[i]) {
270                         ret = True;
271                         break;
272                 }
273         }
274  
275         *winbind_answered = True;
276         SAFE_FREE(groups);
277         return ret;
278  
279    err:
280  
281         *winbind_answered = False;
282         SAFE_FREE(groups);
283         return False;
284 }             
285  
286 /****************************************************************************
287  Check if a user is in a UNIX group.
288 ****************************************************************************/
289 static BOOL user_in_unix_group_list(const char *user,const char *gname)
290 {
291         struct passwd *pass = Get_Pwnam(user);
292         struct sys_userlist *user_list;
293         struct sys_userlist *member;
294         TALLOC_CTX *mem_ctx;
295
296         DEBUG(10,("user_in_unix_group_list: checking user %s in group %s\n", user, gname));
297
298         /*
299          * We need to check the users primary group as this
300          * group is implicit and often not listed in the group database.
301          */
302  
303         mem_ctx = talloc_init("smbgroupedit talloc");
304         if (!mem_ctx) return -1;
305         if (pass) {
306                 if (strequal(gname,gidtoname(mem_ctx, pass->pw_gid))) {
307                         DEBUG(10,("user_in_unix_group_list: group %s is primary group.\n", gname ));
308                         goto exit;
309                 }
310         }
311  
312         user_list = get_users_in_group(gname);
313         if (user_list == NULL) {
314                 DEBUG(10,("user_in_unix_group_list: no such group %s\n", gname ));
315                 return False;
316         }
317
318         for (member = user_list; member; member = member->next) {
319                 DEBUG(10,("user_in_unix_group_list: checking user %s against member %s\n",
320                         user, member->unix_name ));
321                 if (strequal(member->unix_name,user)) {
322                         free_userlist(user_list);
323                         goto exit;
324                 }
325         }
326
327         free_userlist(user_list);
328         talloc_destroy(mem_ctx);
329         return False;
330 exit:
331         talloc_destroy(mem_ctx);
332         return True;
333 }             
334
335 /****************************************************************************
336  Check if a user is in a group list. Ask winbind first, then use UNIX.
337 ****************************************************************************/
338 static BOOL user_in_group_list(const char *user, const char *gname, gid_t *groups, size_t n_groups)
339 {
340         BOOL winbind_answered = False;
341         BOOL ret;
342         gid_t gid;
343         unsigned i;
344
345         gid = nametogid(gname);
346         if (gid == (gid_t)-1) 
347                 return False;
348
349         if (groups && n_groups > 0) {
350                 for (i=0; i < n_groups; i++) {
351                         if (groups[i] == gid) {
352                                 return True;
353                         }
354                 }
355                 return False;
356         }
357
358         /* fallback if we don't yet have the group list */
359
360         ret = user_in_winbind_group_list(user, gname, &winbind_answered);
361         if (!winbind_answered)
362                 ret = user_in_unix_group_list(user, gname);
363
364         if (ret)
365                 DEBUG(10,("user_in_group_list: user |%s| is in group |%s|\n", user, gname));
366         return ret;
367 }
368
369 /****************************************************************************
370  Check if a user is in a user list - can check combinations of UNIX
371  and netgroup lists.
372 ****************************************************************************/
373
374 BOOL user_in_list(const char *user,const char **list, gid_t *groups, size_t n_groups)
375 {
376         if (!list || !*list)
377                 return False;
378
379         DEBUG(10,("user_in_list: checking user %s in list\n", user));
380
381         while (*list) {
382
383                 DEBUG(10,("user_in_list: checking user |%s| against |%s|\n", user, *list));
384
385                 /*
386                  * Check raw username.
387                  */
388                 if (strequal(user, *list))
389                         return(True);
390
391                 /*
392                  * Now check to see if any combination
393                  * of UNIX and netgroups has been specified.
394                  */
395
396                 if(**list == '@') {
397                         /*
398                          * Old behaviour. Check netgroup list
399                          * followed by UNIX list.
400                          */
401                         if(user_in_netgroup_list(user, *list +1))
402                                 return True;
403                         if(user_in_group_list(user, *list +1, groups, n_groups))
404                                 return True;
405                 } else if (**list == '+') {
406
407                         if((*(*list +1)) == '&') {
408                                 /*
409                                  * Search UNIX list followed by netgroup.
410                                  */
411                                 if(user_in_group_list(user, *list +2, groups, n_groups))
412                                         return True;
413                                 if(user_in_netgroup_list(user, *list +2))
414                                         return True;
415
416                         } else {
417
418                                 /*
419                                  * Just search UNIX list.
420                                  */
421
422                                 if(user_in_group_list(user, *list +1, groups, n_groups))
423                                         return True;
424                         }
425
426                 } else if (**list == '&') {
427
428                         if(*(*list +1) == '+') {
429                                 /*
430                                  * Search netgroup list followed by UNIX list.
431                                  */
432                                 if(user_in_netgroup_list(user, *list +2))
433                                         return True;
434                                 if(user_in_group_list(user, *list +2, groups, n_groups))
435                                         return True;
436                         } else {
437                                 /*
438                                  * Just search netgroup list.
439                                  */
440                                 if(user_in_netgroup_list(user, *list +1))
441                                         return True;
442                         }
443                 } else if (!name_is_local(*list)) {
444                         /*
445                          * If user name did not match and token is not
446                          * a unix group and the token has a winbind separator in the
447                          * name then see if it is a Windows group.
448                          */
449
450                         DOM_SID g_sid;
451                         enum SID_NAME_USE name_type;
452                         BOOL winbind_answered = False;
453                         BOOL ret;
454                         fstring groupname, domain;
455                         
456                         /* Parse a string of the form DOMAIN/user into a domain and a user */
457
458                         char *p = strchr(*list,*lp_winbind_separator());
459                         
460                         DEBUG(10,("user_in_list: checking if user |%s| is in winbind group |%s|\n", user, *list));
461
462                         if (p) {
463                                 fstrcpy(groupname, p+1);
464                                 fstrcpy(domain, *list);
465                                 domain[PTR_DIFF(p, *list)] = 0;
466
467                                 /* Check to see if name is a Windows group */
468                                 if (winbind_lookup_name(domain, groupname, &g_sid, &name_type) && name_type == SID_NAME_DOM_GRP) {
469                                         
470                                 /* Check if user name is in the Windows group */
471                                         ret = user_in_winbind_group_list(user, *list, &winbind_answered);
472                                         
473                                         if (winbind_answered && ret == True) {
474                                                 DEBUG(10,("user_in_list: user |%s| is in winbind group |%s|\n", user, *list));
475                                                 return ret;
476                                         }
477                                 }
478                         }
479                 }
480     
481                 list++;
482         }
483         return(False);
484 }
485
486 /* The functions below have been taken from password.c and slightly modified */
487 /****************************************************************************
488  Apply a function to upper/lower case combinations
489  of a string and return true if one of them returns true.
490  Try all combinations with N uppercase letters.
491  offset is the first char to try and change (start with 0)
492  it assumes the string starts lowercased
493 ****************************************************************************/
494
495 static struct passwd *uname_string_combinations2(char *s,int offset,struct passwd *(*fn)(const char *),int N)
496 {
497         ssize_t len = (ssize_t)strlen(s);
498         int i;
499         struct passwd *ret;
500
501         if (N <= 0 || offset >= len)
502                 return(fn(s));
503
504         for (i=offset;i<(len-(N-1));i++) {
505                 char c = s[i];
506                 if (!islower((int)c))
507                         continue;
508                 s[i] = toupper(c);
509                 ret = uname_string_combinations2(s,i+1,fn,N-1);
510                 if(ret)
511                         return(ret);
512                 s[i] = c;
513         }
514         return(NULL);
515 }
516
517 /****************************************************************************
518  Apply a function to upper/lower case combinations
519  of a string and return true if one of them returns true.
520  Try all combinations with up to N uppercase letters.
521  offset is the first char to try and change (start with 0)
522  it assumes the string starts lowercased
523 ****************************************************************************/
524
525 static struct passwd * uname_string_combinations(char *s,struct passwd * (*fn)(const char *),int N)
526 {
527         int n;
528         struct passwd *ret;
529
530         for (n=1;n<=N;n++) {
531                 ret = uname_string_combinations2(s,0,fn,n);
532                 if(ret)
533                         return(ret);
534         }  
535         return(NULL);
536 }
537