Inlined several vfs_XXX calls to smb_macros for speed.
[Samba.git] / source / lib / username.c
blob99bee8a2172f37bb824bb28c01107bdc647c8daf
1 /*
2 Unix SMB/Netbios implementation.
3 Version 1.9.
4 Username handling
5 Copyright (C) Andrew Tridgell 1992-1998
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"
23 extern int DEBUGLEVEL;
25 /* internal functions */
26 static struct passwd *uname_string_combinations(char *s, struct passwd * (*fn) (char *), int N);
27 static struct passwd *uname_string_combinations2(char *s, int offset, struct passwd * (*fn) (char *), int N);
29 /****************************************************************************
30 Get a users home directory.
31 ****************************************************************************/
33 char *get_user_home_dir(char *user)
35 static struct passwd *pass;
37 pass = Get_Pwnam(user, False);
39 if (!pass) return(NULL);
40 return(pass->pw_dir);
44 /*******************************************************************
45 Map a username from a dos name to a unix name by looking in the username
46 map. Note that this modifies the name in place.
47 This is the main function that should be called *once* on
48 any incoming or new username - in order to canonicalize the name.
49 This is being done to de-couple the case conversions from the user mapping
50 function. Previously, the map_username was being called
51 every time Get_Pwnam was called.
52 Returns True if username was changed, false otherwise.
53 ********************************************************************/
55 BOOL map_username(char *user)
57 static BOOL initialised=False;
58 static fstring last_from,last_to;
59 FILE *f;
60 char *mapfile = lp_username_map();
61 char *s;
62 pstring buf;
63 BOOL mapped_user = False;
65 if (!*user)
66 return False;
68 if (!*mapfile)
69 return False;
71 if (!initialised) {
72 *last_from = *last_to = 0;
73 initialised = True;
76 if (strequal(user,last_to))
77 return False;
79 if (strequal(user,last_from)) {
80 DEBUG(3,("Mapped user %s to %s\n",user,last_to));
81 fstrcpy(user,last_to);
82 return True;
85 f = sys_fopen(mapfile,"r");
86 if (!f) {
87 DEBUG(0,("can't open username map %s\n",mapfile));
88 return False;
91 DEBUG(4,("Scanning username map %s\n",mapfile));
93 while((s=fgets_slash(buf,sizeof(buf),f))!=NULL) {
94 char *unixname = s;
95 char *dosname = strchr(unixname,'=');
96 BOOL return_if_mapped = False;
98 if (!dosname)
99 continue;
101 *dosname++ = 0;
103 while (isspace(*unixname))
104 unixname++;
105 if ('!' == *unixname) {
106 return_if_mapped = True;
107 unixname++;
108 while (*unixname && isspace(*unixname))
109 unixname++;
112 if (!*unixname || strchr("#;",*unixname))
113 continue;
116 int l = strlen(unixname);
117 while (l && isspace(unixname[l-1])) {
118 unixname[l-1] = 0;
119 l--;
123 if (strchr(dosname,'*') || user_in_list(user,dosname)) {
124 DEBUG(3,("Mapped user %s to %s\n",user,unixname));
125 mapped_user = True;
126 fstrcpy(last_from,user);
127 sscanf(unixname,"%s",user);
128 fstrcpy(last_to,user);
129 if(return_if_mapped) {
130 fclose(f);
131 return True;
136 fclose(f);
139 * Setup the last_from and last_to as an optimization so
140 * that we don't scan the file again for the same user.
142 fstrcpy(last_from,user);
143 fstrcpy(last_to,user);
145 return mapped_user;
148 /****************************************************************************
149 Get_Pwnam wrapper
150 ****************************************************************************/
152 static struct passwd *_Get_Pwnam(char *s)
154 struct passwd *ret;
156 ret = sys_getpwnam(s);
157 if (ret) {
158 #ifdef HAVE_GETPWANAM
159 struct passwd_adjunct *pwret;
160 pwret = getpwanam(s);
161 if (pwret && pwret->pwa_passwd) {
162 pstrcpy(ret->pw_passwd,pwret->pwa_passwd);
164 #endif
167 return(ret);
171 /****************************************************************************
172 A wrapper for getpwnam() that tries with all lower and all upper case
173 if the initial name fails. Also tried with first letter capitalised
174 Note that this can change user!
175 ****************************************************************************/
177 struct passwd *Get_Pwnam(char *user,BOOL allow_change)
179 fstring user2;
180 int last_char;
181 int usernamelevel = lp_usernamelevel();
183 struct passwd *ret;
185 if (!user || !(*user))
186 return(NULL);
188 StrnCpy(user2,user,sizeof(user2)-1);
190 if (!allow_change) {
191 user = &user2[0];
194 ret = _Get_Pwnam(user);
195 if (ret)
196 return(ret);
198 strlower(user);
199 ret = _Get_Pwnam(user);
200 if (ret)
201 return(ret);
203 strupper(user);
204 ret = _Get_Pwnam(user);
205 if (ret)
206 return(ret);
208 /* Try with first letter capitalised. */
209 if (strlen(user) > 1)
210 strlower(user+1);
211 ret = _Get_Pwnam(user);
212 if (ret)
213 return(ret);
215 /* try with last letter capitalised */
216 strlower(user);
217 last_char = strlen(user)-1;
218 user[last_char] = toupper(user[last_char]);
219 ret = _Get_Pwnam(user);
220 if (ret)
221 return(ret);
223 /* Try all combinations up to usernamelevel. */
224 strlower(user);
225 ret = uname_string_combinations(user, _Get_Pwnam, usernamelevel);
226 if (ret)
227 return(ret);
229 if (allow_change)
230 fstrcpy(user,user2);
232 return(NULL);
235 /****************************************************************************
236 Check if a user is in a netgroup user list.
237 ****************************************************************************/
239 static BOOL user_in_netgroup_list(char *user,char *ngname)
241 #ifdef HAVE_NETGROUP
242 static char *mydomain = NULL;
243 if (mydomain == NULL)
244 yp_get_default_domain(&mydomain);
246 if(mydomain == NULL) {
247 DEBUG(5,("Unable to get default yp domain\n"));
248 } else {
249 DEBUG(5,("looking for user %s of domain %s in netgroup %s\n",
250 user, mydomain, ngname));
251 DEBUG(5,("innetgr is %s\n",
252 innetgr(ngname, NULL, user, mydomain)
253 ? "TRUE" : "FALSE"));
255 if (innetgr(ngname, NULL, user, mydomain))
256 return (True);
258 #endif /* HAVE_NETGROUP */
259 return False;
262 /****************************************************************************
263 Check if a user is in a UNIX user list.
264 ****************************************************************************/
266 static BOOL user_in_group_list(char *user,char *gname)
268 #ifdef HAVE_GETGRENT
269 struct group *gptr;
270 char **member;
271 struct passwd *pass = Get_Pwnam(user,False);
273 if (pass) {
274 gptr = getgrgid(pass->pw_gid);
275 if (gptr && strequal(gptr->gr_name,gname))
276 return(True);
279 while ((gptr = (struct group *)getgrent())) {
280 if (!strequal(gptr->gr_name,gname))
281 continue;
282 member = gptr->gr_mem;
283 while (member && *member) {
284 if (strequal(*member,user)) {
285 endgrent();
286 return(True);
288 member++;
292 endgrent();
293 #endif /* HAVE_GETGRNAM */
294 return False;
297 /****************************************************************************
298 Check if a user is in a user list - can check combinations of UNIX
299 and netgroup lists.
300 ****************************************************************************/
302 BOOL user_in_list(char *user,char *list)
304 pstring tok;
305 char *p=list;
307 while (next_token(&p,tok,LIST_SEP, sizeof(tok))) {
309 * Check raw username.
311 if (strequal(user,tok))
312 return(True);
315 * Now check to see if any combination
316 * of UNIX and netgroups has been specified.
319 if(*tok == '@') {
321 * Old behaviour. Check netgroup list
322 * followed by UNIX list.
324 if(user_in_netgroup_list(user,&tok[1]))
325 return True;
326 if(user_in_group_list(user,&tok[1]))
327 return True;
328 } else if (*tok == '+') {
330 if(tok[1] == '&') {
332 * Search UNIX list followed by netgroup.
334 if(user_in_group_list(user,&tok[2]))
335 return True;
336 if(user_in_netgroup_list(user,&tok[2]))
337 return True;
339 } else {
342 * Just search UNIX list.
345 if(user_in_group_list(user,&tok[1]))
346 return True;
349 } else if (*tok == '&') {
351 if(tok[1] == '+') {
353 * Search netgroup list followed by UNIX list.
355 if(user_in_netgroup_list(user,&tok[2]))
356 return True;
357 if(user_in_group_list(user,&tok[2]))
358 return True;
359 } else {
361 * Just search netgroup list.
363 if(user_in_netgroup_list(user,&tok[1]))
364 return True;
368 return(False);
371 /* The functions below have been taken from password.c and slightly modified */
372 /****************************************************************************
373 Apply a function to upper/lower case combinations
374 of a string and return true if one of them returns true.
375 Try all combinations with N uppercase letters.
376 offset is the first char to try and change (start with 0)
377 it assumes the string starts lowercased
378 ****************************************************************************/
380 static struct passwd *uname_string_combinations2(char *s,int offset,struct passwd *(*fn)(char *),int N)
382 ssize_t len = (ssize_t)strlen(s);
383 int i;
384 struct passwd *ret;
386 #ifdef PASSWORD_LENGTH
387 len = MIN(len,PASSWORD_LENGTH);
388 #endif
390 if (N <= 0 || offset >= len)
391 return(fn(s));
393 for (i=offset;i<(len-(N-1));i++) {
394 char c = s[i];
395 if (!islower(c))
396 continue;
397 s[i] = toupper(c);
398 ret = uname_string_combinations2(s,i+1,fn,N-1);
399 if(ret)
400 return(ret);
401 s[i] = c;
403 return(NULL);
406 /****************************************************************************
407 Apply a function to upper/lower case combinations
408 of a string and return true if one of them returns true.
409 Try all combinations with up to N uppercase letters.
410 offset is the first char to try and change (start with 0)
411 it assumes the string starts lowercased
412 ****************************************************************************/
414 static struct passwd * uname_string_combinations(char *s,struct passwd * (*fn)(char *),int N)
416 int n;
417 struct passwd *ret;
419 for (n=1;n<=N;n++) {
420 ret = uname_string_combinations2(s,0,fn,n);
421 if(ret)
422 return(ret);
424 return(NULL);
428 /****************************************************************************
429 these wrappers allow appliance mode to work. In appliance mode the username
430 takes the form DOMAIN/user
431 ****************************************************************************/
432 struct passwd *smb_getpwnam(char *user, BOOL allow_change)
434 struct passwd *pw;
435 char *p;
436 char *sep;
437 extern pstring global_myname;
439 pw = Get_Pwnam(user, allow_change);
440 if (pw) return pw;
442 /* if it is a domain qualified name and it isn't in our password
443 database but the domain portion matches our local machine name then
444 lookup just the username portion locally */
445 sep = lp_winbind_separator();
446 if (!sep || !*sep) sep = "\\";
447 p = strchr(user,*sep);
448 if (p &&
449 strncasecmp(global_myname, user, strlen(global_myname))==0) {
450 return Get_Pwnam(p+1, allow_change);
453 return NULL;