2 Unix SMB/CIFS implementation.
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.
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);
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 *****************************************************************/
33 static BOOL name_is_local(const char *name)
35 return !(strchr_m(name, *lp_winbind_separator()));
38 /****************************************************************************
39 Get a users home directory.
40 ****************************************************************************/
42 char *get_user_home_dir(const char *user)
44 static struct passwd *pass;
46 /* Ensure the user exists. */
48 pass = Get_Pwnam(user);
52 /* Return home directory from struct passwd. */
57 /*******************************************************************
58 Map a username from a dos name to a unix name by looking in the username
59 map. Note that this modifies the name in place.
60 This is the main function that should be called *once* on
61 any incoming or new username - in order to canonicalize the name.
62 This is being done to de-couple the case conversions from the user mapping
63 function. Previously, the map_username was being called
64 every time Get_Pwnam was called.
65 Returns True if username was changed, false otherwise.
66 ********************************************************************/
68 BOOL map_username(fstring user)
70 static BOOL initialised=False;
71 static fstring last_from,last_to;
73 char *mapfile = lp_username_map();
76 BOOL mapped_user = False;
77 char *cmd = lp_username_map_script();
82 if (strequal(user,last_to))
85 if (strequal(user,last_from)) {
86 DEBUG(3,("Mapped user %s to %s\n",user,last_to));
87 fstrcpy(user,last_to);
91 /* first try the username map script */
96 int numlines, ret, fd;
98 pstr_sprintf( command, "%s \"%s\"", cmd, user );
100 DEBUG(10,("Running [%s]\n", command));
101 ret = smbrun(command, &fd);
102 DEBUGADD(10,("returned [%d]\n", ret));
111 qlines = fd_lines_load(fd, &numlines);
112 DEBUGADD(10,("Lines returned = [%d]\n", numlines));
115 /* should be either no lines or a single line with the mapped username */
118 DEBUG(3,("Mapped user %s to %s\n", user, qlines[0] ));
119 fstrcpy( user, qlines[0] );
122 file_lines_free(qlines);
124 return numlines != 0;
127 /* ok. let's try the mapfile */
133 *last_from = *last_to = 0;
137 f = x_fopen(mapfile,O_RDONLY, 0);
139 DEBUG(0,("can't open username map %s. Error %s\n",mapfile, strerror(errno) ));
143 DEBUG(4,("Scanning username map %s\n",mapfile));
145 while((s=fgets_slash(buf,sizeof(buf),f))!=NULL) {
147 char *dosname = strchr_m(unixname,'=');
149 BOOL return_if_mapped = False;
156 while (isspace((int)*unixname))
159 if ('!' == *unixname) {
160 return_if_mapped = True;
162 while (*unixname && isspace((int)*unixname))
166 if (!*unixname || strchr_m("#;",*unixname))
170 int l = strlen(unixname);
171 while (l && isspace((int)unixname[l-1])) {
177 dosuserlist = str_list_make(dosname, NULL);
179 DEBUG(0,("Unable to build user list\n"));
183 if (strchr_m(dosname,'*') || user_in_list(user, (const char **)dosuserlist, NULL, 0)) {
184 DEBUG(3,("Mapped user %s to %s\n",user,unixname));
186 fstrcpy( last_from,user );
187 fstrcpy( user, unixname );
188 fstrcpy( last_to,user );
189 if ( return_if_mapped ) {
190 str_list_free (&dosuserlist);
196 str_list_free (&dosuserlist);
202 * Setup the last_from and last_to as an optimization so
203 * that we don't scan the file again for the same user.
205 fstrcpy(last_from,user);
206 fstrcpy(last_to,user);
211 /****************************************************************************
212 * A wrapper for sys_getpwnam(). The following variations are tried:
214 * - in all lower case if this differs from transmitted
215 * - in all upper case if this differs from transmitted
216 * - using lp_usernamelevel() for permutations.
217 ****************************************************************************/
219 static struct passwd *Get_Pwnam_ret = NULL;
221 static struct passwd *Get_Pwnam_internals(const char *user, char *user2)
223 struct passwd *ret = NULL;
225 if (!user2 || !(*user2))
228 if (!user || !(*user))
231 /* Try in all lower case first as this is the most
232 common case on UNIX systems */
234 DEBUG(5,("Trying _Get_Pwnam(), username as lowercase is %s\n",user2));
235 ret = getpwnam_alloc(user2);
239 /* Try as given, if username wasn't originally lowercase */
240 if(strcmp(user, user2) != 0) {
241 DEBUG(5,("Trying _Get_Pwnam(), username as given is %s\n",
243 ret = getpwnam_alloc(user);
248 /* Try as uppercase, if username wasn't originally uppercase */
250 if(strcmp(user, user2) != 0) {
251 DEBUG(5,("Trying _Get_Pwnam(), username as uppercase is %s\n",
253 ret = getpwnam_alloc(user2);
258 /* Try all combinations up to usernamelevel */
260 DEBUG(5,("Checking combinations of %d uppercase letters in %s\n",
261 lp_usernamelevel(), user2));
262 ret = uname_string_combinations(user2, getpwnam_alloc,
266 DEBUG(5,("Get_Pwnam_internals %s find user [%s]!\n",ret ?
267 "did":"didn't", user));
272 /****************************************************************************
273 Get_Pwnam wrapper without modification.
274 NOTE: This with NOT modify 'user'!
275 This will return an allocated structure
276 ****************************************************************************/
278 struct passwd *Get_Pwnam_alloc(const char *user)
283 if ( *user == '\0' ) {
284 DEBUG(10,("Get_Pwnam: empty username!\n"));
288 fstrcpy(user2, user);
290 DEBUG(5,("Finding user %s\n", user));
292 ret = Get_Pwnam_internals(user, user2);
297 /****************************************************************************
298 Get_Pwnam wrapper without modification.
299 NOTE: This with NOT modify 'user'!
300 ****************************************************************************/
302 struct passwd *Get_Pwnam(const char *user)
306 ret = Get_Pwnam_alloc(user);
308 /* This call used to just return the 'passwd' static buffer.
309 This could then have accidental reuse implications, so
310 we now malloc a copy, and free it in the next use.
312 This should cause the (ab)user to segfault if it
315 This is better than useing the wrong data in security
318 The real fix is to make the callers free the returned
323 passwd_free(&Get_Pwnam_ret);
331 /****************************************************************************
332 Check if a user is in a netgroup user list. If at first we don't succeed,
334 ****************************************************************************/
336 static BOOL user_in_netgroup_list(const char *user, const char *ngname)
339 static char *mydomain = NULL;
340 fstring lowercase_user;
342 if (mydomain == NULL)
343 yp_get_default_domain(&mydomain);
345 if(mydomain == NULL) {
346 DEBUG(5,("Unable to get default yp domain\n"));
350 DEBUG(5,("looking for user %s of domain %s in netgroup %s\n",
351 user, mydomain, ngname));
353 if (innetgr(ngname, NULL, user, mydomain)) {
354 DEBUG(5,("user_in_netgroup_list: Found\n"));
359 * Ok, innetgr is case sensitive. Try once more with lowercase
360 * just in case. Attempt to fix #703. JRA.
363 fstrcpy(lowercase_user, user);
364 strlower_m(lowercase_user);
366 DEBUG(5,("looking for user %s of domain %s in netgroup %s\n",
367 lowercase_user, mydomain, ngname));
369 if (innetgr(ngname, NULL, lowercase_user, mydomain)) {
370 DEBUG(5,("user_in_netgroup_list: Found\n"));
374 #endif /* HAVE_NETGROUP */
378 /****************************************************************************
379 Check if a user is in a winbind group.
380 ****************************************************************************/
382 static BOOL user_in_winbind_group_list(const char *user, const char *gname,
383 BOOL *winbind_answered)
386 gid_t gid, gid_low, gid_high;
388 static gid_t *groups = NULL;
389 static int num_groups = 0;
390 static fstring last_user = "";
392 *winbind_answered = False;
394 if ((gid = nametogid(gname)) == (gid_t)-1) {
395 DEBUG(0,("user_in_winbind_group_list: nametogid for group %s "
396 "failed.\n", gname ));
400 if (!lp_idmap_gid(&gid_low, &gid_high)) {
401 DEBUG(4, ("winbind gid range not configured, therefore %s "
402 "cannot be a winbind group\n", gname));
406 if (gid < gid_low || gid > gid_high) {
407 DEBUG(4, ("group %s is not a winbind group\n", gname));
411 /* try to user the last user we looked up */
412 /* otherwise fall back to lookups */
414 if ( !strequal( last_user, user ) || !groups )
416 /* clear any cached information */
419 fstrcpy( last_user, "" );
422 * Get the gid's that this user belongs to.
425 if ((num_groups = winbind_getgroups(user, &groups)) == -1)
428 if ( num_groups == -1 )
431 if ( num_groups == 0 ) {
432 *winbind_answered = True;
436 /* save the last username */
438 fstrcpy( last_user, user );
442 DEBUG(10,("user_in_winbind_group_list: using cached user "
443 "groups for [%s]\n", user));
445 if ( DEBUGLEVEL >= 10 ) {
446 DEBUG(10,("user_in_winbind_group_list: using groups -- "));
447 for ( i=0; i<num_groups; i++ )
448 DEBUGADD(10,("%lu ", (unsigned long)groups[i]));
453 * Now we have the gid list for this user - convert the gname to a
454 * gid_t via either winbind or the local UNIX lookup and do the
458 for (i = 0; i < num_groups; i++) {
459 if (gid == groups[i]) {
465 *winbind_answered = True;
471 *winbind_answered = False;
476 /****************************************************************************
477 Check if a user is in a UNIX group.
478 ****************************************************************************/
480 BOOL user_in_unix_group_list(const char *user,const char *gname)
482 struct passwd *pass = Get_Pwnam(user);
483 struct sys_userlist *user_list;
484 struct sys_userlist *member;
486 DEBUG(10,("user_in_unix_group_list: checking user %s in group %s\n",
490 * We need to check the users primary group as this
491 * group is implicit and often not listed in the group database.
495 if (strequal(gname,gidtoname(pass->pw_gid))) {
496 DEBUG(10,("user_in_unix_group_list: group %s is "
497 "primary group.\n", gname ));
502 user_list = get_users_in_group(gname);
503 if (user_list == NULL) {
504 DEBUG(10,("user_in_unix_group_list: no such group %s\n",
509 for (member = user_list; member; member = member->next) {
510 DEBUG(10,("user_in_unix_group_list: checking user %s against "
511 "member %s\n", user, member->unix_name ));
512 if (strequal(member->unix_name,user)) {
513 free_userlist(user_list);
518 free_userlist(user_list);
522 /****************************************************************************
523 Check if a user is in a group list. Ask winbind first, then use UNIX.
524 ****************************************************************************/
526 BOOL user_in_group_list(const char *user, const char *gname, gid_t *groups,
529 BOOL winbind_answered = False;
534 gid = nametogid(gname);
535 if (gid == (gid_t)-1)
538 if (groups && n_groups > 0) {
539 for (i=0; i < n_groups; i++) {
540 if (groups[i] == gid) {
547 /* fallback if we don't yet have the group list */
549 ret = user_in_winbind_group_list(user, gname, &winbind_answered);
550 if (!winbind_answered)
551 ret = user_in_unix_group_list(user, gname);
554 DEBUG(10,("user_in_group_list: user |%s| is in group |%s|\n",
559 /****************************************************************************
560 Check if a user is in a user list - can check combinations of UNIX
562 ****************************************************************************/
564 BOOL user_in_list(const char *user,const char **list, gid_t *groups,
570 DEBUG(10,("user_in_list: checking user %s in list\n", user));
574 DEBUG(10,("user_in_list: checking user |%s| against |%s|\n",
578 * Check raw username.
580 if (strequal(user, *list))
584 * Now check to see if any combination
585 * of UNIX and netgroups has been specified.
590 * Old behaviour. Check netgroup list
591 * followed by UNIX list.
593 if(user_in_netgroup_list(user, *list +1))
595 if(user_in_group_list(user, *list +1, groups,
598 } else if (**list == '+') {
600 if((*(*list +1)) == '&') {
602 * Search UNIX list followed by netgroup.
604 if(user_in_group_list(user, *list +2, groups,
607 if(user_in_netgroup_list(user, *list +2))
613 * Just search UNIX list.
616 if(user_in_group_list(user, *list +1, groups,
621 } else if (**list == '&') {
623 if(*(*list +1) == '+') {
625 * Search netgroup list followed by UNIX list.
627 if(user_in_netgroup_list(user, *list +2))
629 if(user_in_group_list(user, *list +2, groups,
634 * Just search netgroup list.
636 if(user_in_netgroup_list(user, *list +1))
639 } else if (!name_is_local(*list)) {
641 * If user name did not match and token is not a unix
642 * group and the token has a winbind separator in the
643 * name then see if it is a Windows group.
647 enum SID_NAME_USE name_type;
648 BOOL winbind_answered = False;
650 fstring groupname, domain;
652 /* Parse a string of the form DOMAIN/user into a
653 * domain and a user */
655 char *p = strchr(*list,*lp_winbind_separator());
657 DEBUG(10,("user_in_list: checking if user |%s| is in "
658 "winbind group |%s|\n", user, *list));
661 fstrcpy(groupname, p+1);
662 fstrcpy(domain, *list);
663 domain[PTR_DIFF(p, *list)] = 0;
665 /* Check to see if name is a Windows group;
666 Win2k native mode DCs will return domain
667 local groups; while NT4 or mixed mode 2k
670 if ( winbind_lookup_name(domain, groupname,
672 && ( name_type==SID_NAME_DOM_GRP ||
673 (strequal(lp_workgroup(), domain) &&
674 name_type==SID_NAME_ALIAS) ) )
677 /* Check if user name is in the
679 ret = user_in_winbind_group_list(
683 if (winbind_answered && ret == True) {
684 DEBUG(10,("user_in_list: user "
685 "|%s| is in winbind "
699 /* The functions below have been taken from password.c and slightly modified */
700 /****************************************************************************
701 Apply a function to upper/lower case combinations
702 of a string and return true if one of them returns true.
703 Try all combinations with N uppercase letters.
704 offset is the first char to try and change (start with 0)
705 it assumes the string starts lowercased
706 ****************************************************************************/
708 static struct passwd *uname_string_combinations2(char *s,int offset,struct passwd *(*fn)(const char *),int N)
710 ssize_t len = (ssize_t)strlen(s);
714 if (N <= 0 || offset >= len)
717 for (i=offset;i<(len-(N-1));i++) {
719 if (!islower_ascii((int)c))
721 s[i] = toupper_ascii(c);
722 ret = uname_string_combinations2(s,i+1,fn,N-1);
730 /****************************************************************************
731 Apply a function to upper/lower case combinations
732 of a string and return true if one of them returns true.
733 Try all combinations with up to N uppercase letters.
734 offset is the first char to try and change (start with 0)
735 it assumes the string starts lowercased
736 ****************************************************************************/
738 static struct passwd * uname_string_combinations(char *s,struct passwd * (*fn)(const char *),int N)
744 ret = uname_string_combinations2(s,0,fn,n);