r13316: Let the carnage begin....
[Samba.git] / source / lib / username.c
blobc04dfd05da10ba05fca94ff264f190b6be1d3234
1 /*
2 Unix SMB/CIFS implementation.
3 Username handling
4 Copyright (C) Andrew Tridgell 1992-1998
5 Copyright (C) Jeremy Allison 1997-2001.
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.
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.
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.
22 #include "includes.h"
24 /* internal functions */
25 static struct passwd *uname_string_combinations(char *s, TALLOC_CTX *mem_ctx,
26 struct passwd * (*fn) (TALLOC_CTX *mem_ctx, const char *),
27 int N);
28 static struct passwd *uname_string_combinations2(char *s, TALLOC_CTX *mem_ctx, int offset,
29 struct passwd * (*fn) (TALLOC_CTX *mem_ctx, const char *),
30 int N);
32 /*****************************************************************
33 Check if a user or group name is local (this is a *local* name for
34 *local* people, there's nothing for you here...).
35 *****************************************************************/
37 static BOOL name_is_local(const char *name)
39 return !(strchr_m(name, *lp_winbind_separator()));
42 /****************************************************************************
43 Get a users home directory.
44 ****************************************************************************/
46 char *get_user_home_dir(const char *user)
48 static struct passwd *pass;
50 /* Ensure the user exists. */
52 pass = Get_Pwnam(user);
54 if (!pass)
55 return(NULL);
56 /* Return home directory from struct passwd. */
58 return(pass->pw_dir);
61 /*******************************************************************
62 Map a username from a dos name to a unix name by looking in the username
63 map. Note that this modifies the name in place.
64 This is the main function that should be called *once* on
65 any incoming or new username - in order to canonicalize the name.
66 This is being done to de-couple the case conversions from the user mapping
67 function. Previously, the map_username was being called
68 every time Get_Pwnam was called.
69 Returns True if username was changed, false otherwise.
70 ********************************************************************/
72 BOOL map_username(fstring user)
74 static BOOL initialised=False;
75 static fstring last_from,last_to;
76 XFILE *f;
77 char *mapfile = lp_username_map();
78 char *s;
79 pstring buf;
80 BOOL mapped_user = False;
81 char *cmd = lp_username_map_script();
83 if (!*user)
84 return False;
86 if (strequal(user,last_to))
87 return False;
89 if (strequal(user,last_from)) {
90 DEBUG(3,("Mapped user %s to %s\n",user,last_to));
91 fstrcpy(user,last_to);
92 return True;
95 /* first try the username map script */
97 if ( *cmd ) {
98 char **qlines;
99 pstring command;
100 int numlines, ret, fd;
102 pstr_sprintf( command, "%s \"%s\"", cmd, user );
104 DEBUG(10,("Running [%s]\n", command));
105 ret = smbrun(command, &fd);
106 DEBUGADD(10,("returned [%d]\n", ret));
108 if ( ret != 0 ) {
109 if (fd != -1)
110 close(fd);
111 return False;
114 numlines = 0;
115 qlines = fd_lines_load(fd, &numlines,0);
116 DEBUGADD(10,("Lines returned = [%d]\n", numlines));
117 close(fd);
119 /* should be either no lines or a single line with the mapped username */
121 if (numlines) {
122 DEBUG(3,("Mapped user %s to %s\n", user, qlines[0] ));
123 fstrcpy( user, qlines[0] );
126 file_lines_free(qlines);
128 return numlines != 0;
131 /* ok. let's try the mapfile */
133 if (!*mapfile)
134 return False;
136 if (!initialised) {
137 *last_from = *last_to = 0;
138 initialised = True;
141 f = x_fopen(mapfile,O_RDONLY, 0);
142 if (!f) {
143 DEBUG(0,("can't open username map %s. Error %s\n",mapfile, strerror(errno) ));
144 return False;
147 DEBUG(4,("Scanning username map %s\n",mapfile));
149 while((s=fgets_slash(buf,sizeof(buf),f))!=NULL) {
150 char *unixname = s;
151 char *dosname = strchr_m(unixname,'=');
152 char **dosuserlist;
153 BOOL return_if_mapped = False;
155 if (!dosname)
156 continue;
158 *dosname++ = 0;
160 while (isspace((int)*unixname))
161 unixname++;
163 if ('!' == *unixname) {
164 return_if_mapped = True;
165 unixname++;
166 while (*unixname && isspace((int)*unixname))
167 unixname++;
170 if (!*unixname || strchr_m("#;",*unixname))
171 continue;
174 int l = strlen(unixname);
175 while (l && isspace((int)unixname[l-1])) {
176 unixname[l-1] = 0;
177 l--;
181 dosuserlist = str_list_make(dosname, NULL);
182 if (!dosuserlist) {
183 DEBUG(0,("Unable to build user list\n"));
184 return False;
187 if (strchr_m(dosname,'*') ||
188 user_in_list(user, (const char **)dosuserlist)) {
189 DEBUG(3,("Mapped user %s to %s\n",user,unixname));
190 mapped_user = True;
191 fstrcpy( last_from,user );
192 fstrcpy( user, unixname );
193 fstrcpy( last_to,user );
194 if ( return_if_mapped ) {
195 str_list_free (&dosuserlist);
196 x_fclose(f);
197 return True;
201 str_list_free (&dosuserlist);
204 x_fclose(f);
207 * Setup the last_from and last_to as an optimization so
208 * that we don't scan the file again for the same user.
210 fstrcpy(last_from,user);
211 fstrcpy(last_to,user);
213 return mapped_user;
216 /****************************************************************************
217 * A wrapper for sys_getpwnam(). The following variations are tried:
218 * - as transmitted
219 * - in all lower case if this differs from transmitted
220 * - in all upper case if this differs from transmitted
221 * - using lp_usernamelevel() for permutations.
222 ****************************************************************************/
224 static struct passwd *Get_Pwnam_ret = NULL;
226 static struct passwd *Get_Pwnam_internals(TALLOC_CTX *mem_ctx,
227 const char *user, char *user2)
229 struct passwd *ret = NULL;
231 if (!user2 || !(*user2))
232 return(NULL);
234 if (!user || !(*user))
235 return(NULL);
237 /* Try in all lower case first as this is the most
238 common case on UNIX systems */
239 strlower_m(user2);
240 DEBUG(5,("Trying _Get_Pwnam(), username as lowercase is %s\n",user2));
241 ret = getpwnam_alloc(mem_ctx, user2);
242 if(ret)
243 goto done;
245 /* Try as given, if username wasn't originally lowercase */
246 if(strcmp(user, user2) != 0) {
247 DEBUG(5,("Trying _Get_Pwnam(), username as given is %s\n",
248 user));
249 ret = getpwnam_alloc(mem_ctx, user);
250 if(ret)
251 goto done;
254 /* Try as uppercase, if username wasn't originally uppercase */
255 strupper_m(user2);
256 if(strcmp(user, user2) != 0) {
257 DEBUG(5,("Trying _Get_Pwnam(), username as uppercase is %s\n",
258 user2));
259 ret = getpwnam_alloc(mem_ctx, user2);
260 if(ret)
261 goto done;
264 /* Try all combinations up to usernamelevel */
265 strlower_m(user2);
266 DEBUG(5,("Checking combinations of %d uppercase letters in %s\n",
267 lp_usernamelevel(), user2));
268 ret = uname_string_combinations(user2, mem_ctx, getpwnam_alloc,
269 lp_usernamelevel());
271 done:
272 DEBUG(5,("Get_Pwnam_internals %s find user [%s]!\n",ret ?
273 "did":"didn't", user));
275 return ret;
278 /****************************************************************************
279 Get_Pwnam wrapper without modification.
280 NOTE: This with NOT modify 'user'!
281 This will return an allocated structure
282 ****************************************************************************/
284 struct passwd *Get_Pwnam_alloc(TALLOC_CTX *mem_ctx, const char *user)
286 fstring user2;
287 struct passwd *ret;
289 if ( *user == '\0' ) {
290 DEBUG(10,("Get_Pwnam: empty username!\n"));
291 return NULL;
294 fstrcpy(user2, user);
296 DEBUG(5,("Finding user %s\n", user));
298 ret = Get_Pwnam_internals(mem_ctx, user, user2);
300 return ret;
303 /****************************************************************************
304 Get_Pwnam wrapper without modification.
305 NOTE: This with NOT modify 'user'!
306 ****************************************************************************/
308 struct passwd *Get_Pwnam(const char *user)
310 struct passwd *ret;
312 ret = Get_Pwnam_alloc(NULL, user);
314 /* This call used to just return the 'passwd' static buffer.
315 This could then have accidental reuse implications, so
316 we now malloc a copy, and free it in the next use.
318 This should cause the (ab)user to segfault if it
319 uses an old struct.
321 This is better than useing the wrong data in security
322 critical operations.
324 The real fix is to make the callers free the returned
325 malloc'ed data.
328 if (Get_Pwnam_ret) {
329 talloc_free(Get_Pwnam_ret);
332 Get_Pwnam_ret = ret;
334 return ret;
337 /****************************************************************************
338 Check if a user is in a netgroup user list. If at first we don't succeed,
339 try lower case.
340 ****************************************************************************/
342 BOOL user_in_netgroup(const char *user, const char *ngname)
344 #ifdef HAVE_NETGROUP
345 static char *mydomain = NULL;
346 fstring lowercase_user;
348 if (mydomain == NULL)
349 yp_get_default_domain(&mydomain);
351 if(mydomain == NULL) {
352 DEBUG(5,("Unable to get default yp domain\n"));
353 return False;
356 DEBUG(5,("looking for user %s of domain %s in netgroup %s\n",
357 user, mydomain, ngname));
359 if (innetgr(ngname, NULL, user, mydomain)) {
360 DEBUG(5,("user_in_netgroup: Found\n"));
361 return (True);
362 } else {
365 * Ok, innetgr is case sensitive. Try once more with lowercase
366 * just in case. Attempt to fix #703. JRA.
369 fstrcpy(lowercase_user, user);
370 strlower_m(lowercase_user);
372 DEBUG(5,("looking for user %s of domain %s in netgroup %s\n",
373 lowercase_user, mydomain, ngname));
375 if (innetgr(ngname, NULL, lowercase_user, mydomain)) {
376 DEBUG(5,("user_in_netgroup: Found\n"));
377 return (True);
380 #endif /* HAVE_NETGROUP */
381 return False;
384 /****************************************************************************
385 Check if a user is in a winbind group.
386 ****************************************************************************/
388 static BOOL user_in_winbind_group(const char *user, const char *gname,
389 BOOL *winbind_answered)
391 int i;
392 gid_t gid, gid_low, gid_high;
393 BOOL ret = False;
394 static gid_t *groups = NULL;
395 static int num_groups = 0;
396 static fstring last_user = "";
398 *winbind_answered = False;
400 if ((gid = nametogid(gname)) == (gid_t)-1) {
401 DEBUG(0,("user_in_winbind_group: nametogid for group %s "
402 "failed.\n", gname ));
403 goto err;
406 if (!lp_idmap_gid(&gid_low, &gid_high)) {
407 DEBUG(4, ("winbind gid range not configured, therefore %s "
408 "cannot be a winbind group\n", gname));
409 goto err;
412 if (gid < gid_low || gid > gid_high) {
413 DEBUG(4, ("group %s is not a winbind group\n", gname));
414 goto err;
417 /* try to user the last user we looked up */
418 /* otherwise fall back to lookups */
420 if ( !strequal( last_user, user ) || !groups )
422 /* clear any cached information */
424 SAFE_FREE(groups);
425 fstrcpy( last_user, "" );
428 * Get the gid's that this user belongs to.
431 if ((num_groups = winbind_getgroups(user, &groups)) == -1)
432 return False;
434 if ( num_groups == -1 )
435 return False;
437 if ( num_groups == 0 ) {
438 *winbind_answered = True;
439 return False;
442 /* save the last username */
444 fstrcpy( last_user, user );
447 else
448 DEBUG(10,("user_in_winbind_group: using cached user "
449 "groups for [%s]\n", user));
451 if ( DEBUGLEVEL >= 10 ) {
452 DEBUG(10,("user_in_winbind_group: using groups -- "));
453 for ( i=0; i<num_groups; i++ )
454 DEBUGADD(10,("%lu ", (unsigned long)groups[i]));
455 DEBUGADD(10,("\n"));
459 * Now we have the gid list for this user - convert the gname to a
460 * gid_t via either winbind or the local UNIX lookup and do the
461 * comparison.
464 for (i = 0; i < num_groups; i++) {
465 if (gid == groups[i]) {
466 ret = True;
467 break;
471 *winbind_answered = True;
472 SAFE_FREE(groups);
473 return ret;
475 err:
477 *winbind_answered = False;
478 SAFE_FREE(groups);
479 return False;
482 /****************************************************************************
483 Check if a user is in a UNIX group.
484 ****************************************************************************/
486 BOOL user_in_unix_group(const char *user,const char *gname)
488 struct passwd *pass = Get_Pwnam(user);
489 struct sys_userlist *user_list;
490 struct sys_userlist *member;
492 DEBUG(10,("user_in_unix_group: checking user %s in group %s\n",
493 user, gname));
496 * We need to check the users primary group as this
497 * group is implicit and often not listed in the group database.
500 if (pass) {
501 if (strequal(gname,gidtoname(pass->pw_gid))) {
502 DEBUG(10,("user_in_unix_group: group %s is "
503 "primary group.\n", gname ));
504 return True;
508 user_list = get_users_in_group(gname);
509 if (user_list == NULL) {
510 DEBUG(10,("user_in_unix_group: no such group %s\n",
511 gname ));
512 return False;
515 for (member = user_list; member; member = member->next) {
516 DEBUG(10,("user_in_unix_group: checking user %s against "
517 "member %s\n", user, member->unix_name ));
518 if (strequal(member->unix_name,user)) {
519 free_userlist(user_list);
520 return(True);
524 free_userlist(user_list);
525 return False;
528 /****************************************************************************
529 Check if a user is in a group list. Ask winbind first, then use UNIX.
530 ****************************************************************************/
532 BOOL user_in_group(const char *user, const char *gname)
534 BOOL winbind_answered = False;
535 BOOL ret;
537 ret = user_in_winbind_group(user, gname, &winbind_answered);
538 if (!winbind_answered)
539 ret = user_in_unix_group(user, gname);
541 if (ret)
542 DEBUG(10,("user_in_group: user |%s| is in group |%s|\n",
543 user, gname));
544 return ret;
547 /****************************************************************************
548 Check if a user is in a user list - can check combinations of UNIX
549 and netgroup lists.
550 ****************************************************************************/
552 BOOL user_in_list(const char *user,const char **list)
554 if (!list || !*list)
555 return False;
557 DEBUG(10,("user_in_list: checking user %s in list\n", user));
559 while (*list) {
561 DEBUG(10,("user_in_list: checking user |%s| against |%s|\n",
562 user, *list));
565 * Check raw username.
567 if (strequal(user, *list))
568 return(True);
571 * Now check to see if any combination
572 * of UNIX and netgroups has been specified.
575 if(**list == '@') {
577 * Old behaviour. Check netgroup list
578 * followed by UNIX list.
580 if(user_in_netgroup(user, *list +1))
581 return True;
582 if(user_in_group(user, *list +1))
583 return True;
584 } else if (**list == '+') {
586 if((*(*list +1)) == '&') {
588 * Search UNIX list followed by netgroup.
590 if(user_in_group(user, *list +2))
591 return True;
592 if(user_in_netgroup(user, *list +2))
593 return True;
595 } else {
598 * Just search UNIX list.
601 if(user_in_group(user, *list +1))
602 return True;
605 } else if (**list == '&') {
607 if(*(*list +1) == '+') {
609 * Search netgroup list followed by UNIX list.
611 if(user_in_netgroup(user, *list +2))
612 return True;
613 if(user_in_group(user, *list +2))
614 return True;
615 } else {
617 * Just search netgroup list.
619 if(user_in_netgroup(user, *list +1))
620 return True;
622 } else if (!name_is_local(*list)) {
624 * If user name did not match and token is not a unix
625 * group and the token has a winbind separator in the
626 * name then see if it is a Windows group.
629 DOM_SID g_sid;
630 enum SID_NAME_USE name_type;
631 BOOL winbind_answered = False;
632 BOOL ret;
633 fstring groupname, domain;
635 /* Parse a string of the form DOMAIN/user into a
636 * domain and a user */
638 char *p = strchr(*list,*lp_winbind_separator());
640 DEBUG(10,("user_in_list: checking if user |%s| is in "
641 "winbind group |%s|\n", user, *list));
643 if (p) {
644 fstrcpy(groupname, p+1);
645 fstrcpy(domain, *list);
646 domain[PTR_DIFF(p, *list)] = 0;
648 /* Check to see if name is a Windows group;
649 Win2k native mode DCs will return domain
650 local groups; while NT4 or mixed mode 2k
651 DCs will not */
653 if ( winbind_lookup_name(domain, groupname,
654 &g_sid, &name_type)
655 && ( name_type==SID_NAME_DOM_GRP ||
656 (strequal(lp_workgroup(), domain) &&
657 name_type==SID_NAME_ALIAS) ) )
660 /* Check if user name is in the
661 * Windows group */
662 ret = user_in_winbind_group(
663 user, *list,
664 &winbind_answered);
666 if (winbind_answered && ret == True) {
667 DEBUG(10,("user_in_list: user "
668 "|%s| is in winbind "
669 "group |%s|\n",
670 user, *list));
671 return ret;
677 list++;
679 return(False);
682 /* The functions below have been taken from password.c and slightly modified */
683 /****************************************************************************
684 Apply a function to upper/lower case combinations
685 of a string and return true if one of them returns true.
686 Try all combinations with N uppercase letters.
687 offset is the first char to try and change (start with 0)
688 it assumes the string starts lowercased
689 ****************************************************************************/
691 static struct passwd *uname_string_combinations2(char *s, TALLOC_CTX *mem_ctx,
692 int offset,
693 struct passwd *(*fn)(TALLOC_CTX *mem_ctx, const char *),
694 int N)
696 ssize_t len = (ssize_t)strlen(s);
697 int i;
698 struct passwd *ret;
700 if (N <= 0 || offset >= len)
701 return(fn(mem_ctx, s));
703 for (i=offset;i<(len-(N-1));i++) {
704 char c = s[i];
705 if (!islower_ascii((int)c))
706 continue;
707 s[i] = toupper_ascii(c);
708 ret = uname_string_combinations2(s, mem_ctx, i+1, fn, N-1);
709 if(ret)
710 return(ret);
711 s[i] = c;
713 return(NULL);
716 /****************************************************************************
717 Apply a function to upper/lower case combinations
718 of a string and return true if one of them returns true.
719 Try all combinations with up to N uppercase letters.
720 offset is the first char to try and change (start with 0)
721 it assumes the string starts lowercased
722 ****************************************************************************/
724 static struct passwd * uname_string_combinations(char *s, TALLOC_CTX *mem_ctx,
725 struct passwd * (*fn)(TALLOC_CTX *mem_ctx, const char *),
726 int N)
728 int n;
729 struct passwd *ret;
731 for (n=1;n<=N;n++) {
732 ret = uname_string_combinations2(s,mem_ctx,0,fn,n);
733 if(ret)
734 return(ret);
736 return(NULL);