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 Splits passed user or group name to domain and user/group name parts
40 Returns True if name was splitted and False otherwise.
41 *****************************************************************/
43 BOOL split_domain_and_name(const char *name, char *domain, char* username)
45 char *p = strchr(name,*lp_winbind_separator());
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));
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());
62 DEBUG(10,("split_domain_and_name: all is fine, domain is |%s| and name is |%s|\n", domain, username));
66 /****************************************************************************
67 Get a users home directory.
68 ****************************************************************************/
70 char *get_user_home_dir(const char *user)
74 /* Ensure the user exists. */
76 pass = Get_Pwnam(user);
80 /* Return home directory from struct passwd. */
86 /****************************************************************************
87 * A wrapper for sys_getpwnam(). The following variations are tried:
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 ****************************************************************************/
94 static struct passwd *Get_Pwnam_ret = NULL;
96 static struct passwd *Get_Pwnam_internals(const char *user, char *user2)
98 struct passwd *ret = NULL;
100 if (!user2 || !(*user2))
103 if (!user || !(*user))
106 /* Try in all lower case first as this is the most
107 common case on UNIX systems */
109 DEBUG(5,("Trying _Get_Pwnam(), username as lowercase is %s\n",user2));
110 ret = getpwnam_alloc(user2);
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);
122 /* Try as uppercase, if username wasn't originally uppercase */
124 if(strcmp(user, user2) != 0) {
125 DEBUG(5,("Trying _Get_Pwnam(), username as uppercase is %s\n", user2));
126 ret = getpwnam_alloc(user2);
131 /* Try all combinations up to usernamelevel */
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());
137 DEBUG(5,("Get_Pwnam_internals %s find user [%s]!\n",ret ? "did":"didn't", user));
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.
143 This should cause the (ab)user to segfault if it
146 This is better than useing the wrong data in security
149 The real fix is to make the callers free the returned
154 passwd_free(&Get_Pwnam_ret);
162 /****************************************************************************
163 Get_Pwnam wrapper without modification.
164 NOTE: This with NOT modify 'user'!
165 ****************************************************************************/
167 struct passwd *Get_Pwnam(const char *user)
172 fstrcpy(user2, user);
174 DEBUG(5,("Finding user %s\n", user));
176 ret = Get_Pwnam_internals(user, user2);
181 /****************************************************************************
182 Check if a user is in a netgroup user list.
183 ****************************************************************************/
185 static BOOL user_in_netgroup_list(const char *user, const char *ngname)
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);
194 if(mydomain == NULL) {
195 DEBUG(5,("Unable to get default yp domain\n"));
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"));
204 if (innetgr(ngname, NULL, user, mydomain))
206 #endif /* HAVE_NETGROUP */
210 /****************************************************************************
211 Check if a user is in a winbind group.
212 ****************************************************************************/
214 static BOOL user_in_winbind_group_list(const char *user, const char *gname, BOOL *winbind_answered)
218 gid_t *groups = NULL;
219 gid_t gid, gid_low, gid_high;
222 *winbind_answered = False;
224 if ((gid = nametogid(gname)) == (gid_t)-1) {
225 DEBUG(0,("user_in_winbind_group_list: nametogid for group %s failed.\n",
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));
235 if (gid < gid_low || gid > gid_high) {
236 DEBUG(4, ("group %s is not a winbind group\n", gname));
241 * Get the gid's that this user belongs to.
244 if ((num_groups = winbind_getgroups(user, 0, NULL)) == -1)
247 if (num_groups == 0) {
248 *winbind_answered = True;
252 if ((groups = (gid_t *)malloc(sizeof(gid_t) * num_groups )) == NULL) {
253 DEBUG(0,("user_in_winbind_group_list: malloc fail.\n"));
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) ));
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.
268 for (i = 0; i < num_groups; i++) {
269 if (gid == groups[i]) {
275 *winbind_answered = True;
281 *winbind_answered = False;
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)
291 struct passwd *pass = Get_Pwnam(user);
292 struct sys_userlist *user_list;
293 struct sys_userlist *member;
296 DEBUG(10,("user_in_unix_group_list: checking user %s in group %s\n", user, gname));
299 * We need to check the users primary group as this
300 * group is implicit and often not listed in the group database.
303 mem_ctx = talloc_init("smbgroupedit talloc");
304 if (!mem_ctx) return -1;
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 ));
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 ));
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);
327 free_userlist(user_list);
328 talloc_destroy(mem_ctx);
331 talloc_destroy(mem_ctx);
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)
340 BOOL winbind_answered = False;
345 gid = nametogid(gname);
346 if (gid == (gid_t)-1)
349 if (groups && n_groups > 0) {
350 for (i=0; i < n_groups; i++) {
351 if (groups[i] == gid) {
358 /* fallback if we don't yet have the group list */
360 ret = user_in_winbind_group_list(user, gname, &winbind_answered);
361 if (!winbind_answered)
362 ret = user_in_unix_group_list(user, gname);
365 DEBUG(10,("user_in_group_list: user |%s| is in group |%s|\n", user, gname));
369 /****************************************************************************
370 Check if a user is in a user list - can check combinations of UNIX
372 ****************************************************************************/
374 BOOL user_in_list(const char *user,const char **list, gid_t *groups, size_t n_groups)
379 DEBUG(10,("user_in_list: checking user %s in list\n", user));
383 DEBUG(10,("user_in_list: checking user |%s| against |%s|\n", user, *list));
386 * Check raw username.
388 if (strequal(user, *list))
392 * Now check to see if any combination
393 * of UNIX and netgroups has been specified.
398 * Old behaviour. Check netgroup list
399 * followed by UNIX list.
401 if(user_in_netgroup_list(user, *list +1))
403 if(user_in_group_list(user, *list +1, groups, n_groups))
405 } else if (**list == '+') {
407 if((*(*list +1)) == '&') {
409 * Search UNIX list followed by netgroup.
411 if(user_in_group_list(user, *list +2, groups, n_groups))
413 if(user_in_netgroup_list(user, *list +2))
419 * Just search UNIX list.
422 if(user_in_group_list(user, *list +1, groups, n_groups))
426 } else if (**list == '&') {
428 if(*(*list +1) == '+') {
430 * Search netgroup list followed by UNIX list.
432 if(user_in_netgroup_list(user, *list +2))
434 if(user_in_group_list(user, *list +2, groups, n_groups))
438 * Just search netgroup list.
440 if(user_in_netgroup_list(user, *list +1))
443 } else if (!name_is_local(*list)) {
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.
451 enum SID_NAME_USE name_type;
452 BOOL winbind_answered = False;
454 fstring groupname, domain;
456 /* Parse a string of the form DOMAIN/user into a domain and a user */
458 char *p = strchr(*list,*lp_winbind_separator());
460 DEBUG(10,("user_in_list: checking if user |%s| is in winbind group |%s|\n", user, *list));
463 fstrcpy(groupname, p+1);
464 fstrcpy(domain, *list);
465 domain[PTR_DIFF(p, *list)] = 0;
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) {
470 /* Check if user name is in the Windows group */
471 ret = user_in_winbind_group_list(user, *list, &winbind_answered);
473 if (winbind_answered && ret == True) {
474 DEBUG(10,("user_in_list: user |%s| is in winbind group |%s|\n", user, *list));
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 ****************************************************************************/
495 static struct passwd *uname_string_combinations2(char *s,int offset,struct passwd *(*fn)(const char *),int N)
497 ssize_t len = (ssize_t)strlen(s);
501 if (N <= 0 || offset >= len)
504 for (i=offset;i<(len-(N-1));i++) {
506 if (!islower((int)c))
509 ret = uname_string_combinations2(s,i+1,fn,N-1);
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 ****************************************************************************/
525 static struct passwd * uname_string_combinations(char *s,struct passwd * (*fn)(const char *),int N)
531 ret = uname_string_combinations2(s,0,fn,n);