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
, TALLOC_CTX
*mem_ctx
,
26 struct passwd
* (*fn
) (TALLOC_CTX
*mem_ctx
, const char *),
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 *),
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
);
56 /* Return home directory from struct passwd. */
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
;
77 char *mapfile
= lp_username_map();
80 BOOL mapped_user
= False
;
81 char *cmd
= lp_username_map_script();
86 if (strequal(user
,last_to
))
89 if (strequal(user
,last_from
)) {
90 DEBUG(3,("Mapped user %s to %s\n",user
,last_to
));
91 fstrcpy(user
,last_to
);
95 /* first try the username map script */
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
));
115 qlines
= fd_lines_load(fd
, &numlines
,0);
116 DEBUGADD(10,("Lines returned = [%d]\n", numlines
));
119 /* should be either no lines or a single line with the mapped username */
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 */
137 *last_from
= *last_to
= 0;
141 f
= x_fopen(mapfile
,O_RDONLY
, 0);
143 DEBUG(0,("can't open username map %s. Error %s\n",mapfile
, strerror(errno
) ));
147 DEBUG(4,("Scanning username map %s\n",mapfile
));
149 while((s
=fgets_slash(buf
,sizeof(buf
),f
))!=NULL
) {
151 char *dosname
= strchr_m(unixname
,'=');
153 BOOL return_if_mapped
= False
;
160 while (isspace((int)*unixname
))
163 if ('!' == *unixname
) {
164 return_if_mapped
= True
;
166 while (*unixname
&& isspace((int)*unixname
))
170 if (!*unixname
|| strchr_m("#;",*unixname
))
174 int l
= strlen(unixname
);
175 while (l
&& isspace((int)unixname
[l
-1])) {
181 dosuserlist
= str_list_make(dosname
, NULL
);
183 DEBUG(0,("Unable to build user list\n"));
187 if (strchr_m(dosname
,'*') ||
188 user_in_list(user
, (const char **)dosuserlist
)) {
189 DEBUG(3,("Mapped user %s to %s\n",user
,unixname
));
191 fstrcpy( last_from
,user
);
192 fstrcpy( user
, unixname
);
193 fstrcpy( last_to
,user
);
194 if ( return_if_mapped
) {
195 str_list_free (&dosuserlist
);
201 str_list_free (&dosuserlist
);
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
);
216 /****************************************************************************
217 * A wrapper for sys_getpwnam(). The following variations are tried:
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
))
234 if (!user
|| !(*user
))
237 /* Try in all lower case first as this is the most
238 common case on UNIX systems */
240 DEBUG(5,("Trying _Get_Pwnam(), username as lowercase is %s\n",user2
));
241 ret
= getpwnam_alloc(mem_ctx
, user2
);
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",
249 ret
= getpwnam_alloc(mem_ctx
, user
);
254 /* Try as uppercase, if username wasn't originally uppercase */
256 if(strcmp(user
, user2
) != 0) {
257 DEBUG(5,("Trying _Get_Pwnam(), username as uppercase is %s\n",
259 ret
= getpwnam_alloc(mem_ctx
, user2
);
264 /* Try all combinations up to usernamelevel */
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
,
272 DEBUG(5,("Get_Pwnam_internals %s find user [%s]!\n",ret
?
273 "did":"didn't", user
));
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
)
289 if ( *user
== '\0' ) {
290 DEBUG(10,("Get_Pwnam: empty username!\n"));
294 fstrcpy(user2
, user
);
296 DEBUG(5,("Finding user %s\n", user
));
298 ret
= Get_Pwnam_internals(mem_ctx
, user
, user2
);
303 /****************************************************************************
304 Get_Pwnam wrapper without modification.
305 NOTE: This with NOT modify 'user'!
306 ****************************************************************************/
308 struct passwd
*Get_Pwnam(const char *user
)
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
321 This is better than useing the wrong data in security
324 The real fix is to make the callers free the returned
329 talloc_free(Get_Pwnam_ret
);
337 /****************************************************************************
338 Check if a user is in a netgroup user list. If at first we don't succeed,
340 ****************************************************************************/
342 BOOL
user_in_netgroup(const char *user
, const char *ngname
)
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"));
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"));
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"));
380 #endif /* HAVE_NETGROUP */
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
)
392 gid_t gid
, gid_low
, gid_high
;
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
));
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
));
412 if (gid
< gid_low
|| gid
> gid_high
) {
413 DEBUG(4, ("group %s is not a winbind group\n", gname
));
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 */
425 fstrcpy( last_user
, "" );
428 * Get the gid's that this user belongs to.
431 if ((num_groups
= winbind_getgroups(user
, &groups
)) == -1)
434 if ( num_groups
== -1 )
437 if ( num_groups
== 0 ) {
438 *winbind_answered
= True
;
442 /* save the last username */
444 fstrcpy( last_user
, user
);
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
]));
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
464 for (i
= 0; i
< num_groups
; i
++) {
465 if (gid
== groups
[i
]) {
471 *winbind_answered
= True
;
477 *winbind_answered
= 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",
496 * We need to check the users primary group as this
497 * group is implicit and often not listed in the group database.
501 if (strequal(gname
,gidtoname(pass
->pw_gid
))) {
502 DEBUG(10,("user_in_unix_group: group %s is "
503 "primary group.\n", gname
));
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",
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
);
524 free_userlist(user_list
);
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
;
537 ret
= user_in_winbind_group(user
, gname
, &winbind_answered
);
538 if (!winbind_answered
)
539 ret
= user_in_unix_group(user
, gname
);
542 DEBUG(10,("user_in_group: user |%s| is in group |%s|\n",
547 /****************************************************************************
548 Check if a user is in a user list - can check combinations of UNIX
550 ****************************************************************************/
552 BOOL
user_in_list(const char *user
,const char **list
)
557 DEBUG(10,("user_in_list: checking user %s in list\n", user
));
561 DEBUG(10,("user_in_list: checking user |%s| against |%s|\n",
565 * Check raw username.
567 if (strequal(user
, *list
))
571 * Now check to see if any combination
572 * of UNIX and netgroups has been specified.
577 * Old behaviour. Check netgroup list
578 * followed by UNIX list.
580 if(user_in_netgroup(user
, *list
+1))
582 if(user_in_group(user
, *list
+1))
584 } else if (**list
== '+') {
586 if((*(*list
+1)) == '&') {
588 * Search UNIX list followed by netgroup.
590 if(user_in_group(user
, *list
+2))
592 if(user_in_netgroup(user
, *list
+2))
598 * Just search UNIX list.
601 if(user_in_group(user
, *list
+1))
605 } else if (**list
== '&') {
607 if(*(*list
+1) == '+') {
609 * Search netgroup list followed by UNIX list.
611 if(user_in_netgroup(user
, *list
+2))
613 if(user_in_group(user
, *list
+2))
617 * Just search netgroup list.
619 if(user_in_netgroup(user
, *list
+1))
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.
630 enum SID_NAME_USE name_type
;
631 BOOL winbind_answered
= False
;
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
));
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
653 if ( winbind_lookup_name(domain
, groupname
,
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
662 ret
= user_in_winbind_group(
666 if (winbind_answered
&& ret
== True
) {
667 DEBUG(10,("user_in_list: user "
668 "|%s| is in winbind "
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
,
693 struct passwd
*(*fn
)(TALLOC_CTX
*mem_ctx
, const char *),
696 ssize_t len
= (ssize_t
)strlen(s
);
700 if (N
<= 0 || offset
>= len
)
701 return(fn(mem_ctx
, s
));
703 for (i
=offset
;i
<(len
-(N
-1));i
++) {
705 if (!islower_ascii((int)c
))
707 s
[i
] = toupper_ascii(c
);
708 ret
= uname_string_combinations2(s
, mem_ctx
, i
+1, fn
, N
-1);
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 *),
732 ret
= uname_string_combinations2(s
,mem_ctx
,0,fn
,n
);