Same infinite loop bug as I fixed in SAMBA_2_0, but I just spent half
[samba.git] / source3 / lib / username.c
1 /* 
2    Unix SMB/Netbios implementation.
3    Version 1.9.
4    Username handling
5    Copyright (C) Andrew Tridgell 1992-1998
6    
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.
11    
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.
16    
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.
20 */
21
22 #include "includes.h"
23 extern int DEBUGLEVEL;
24
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);
28
29 /****************************************************************************
30 get a users home directory.
31 ****************************************************************************/
32 char *get_home_dir(char *user)
33 {
34         struct passwd *pass;
35         static pstring home_dir;
36
37         pass = Get_Pwnam(user, False);
38
39         if (pass == NULL || pass->pw_dir == NULL) return(NULL);
40
41         pstrcpy(home_dir, pass->pw_dir);
42         DEBUG(10,("get_home_dir: returning %s for user %s\n", home_dir, user));
43         return home_dir;
44 }
45
46
47 /*******************************************************************
48 map a username from a dos name to a unix name by looking in the username
49 map. Note that this modifies the name in place.
50 This is the main function that should be called *once* on
51 any incoming or new username - in order to canonicalize the name.
52 This is being done to de-couple the case conversions from the user mapping
53 function. Previously, the map_username was being called
54 every time Get_Pwnam was called.
55 Returns True if username was changed, false otherwise.
56 ********************************************************************/
57 BOOL map_username(char *user)
58 {
59   static BOOL initialised=False;
60   static fstring last_from,last_to;
61   FILE *f;
62   char *mapfile = lp_username_map();
63   char *s;
64   pstring buf;
65   BOOL mapped_user = False;
66
67   if (!*user)
68     return False;
69
70   if (!*mapfile)
71     return False;
72
73   if (!initialised) {
74     *last_from = *last_to = 0;
75     initialised = True;
76   }
77
78   if (strequal(user,last_to))
79     return False;
80
81   if (strequal(user,last_from)) {
82     DEBUG(3,("Mapped user %s to %s\n",user,last_to));
83     fstrcpy(user,last_to);
84     return True;
85   }
86   
87   f = sys_fopen(mapfile,"r");
88   if (!f) {
89     DEBUG(0,("can't open username map %s\n",mapfile));
90     return False;
91   }
92
93   DEBUG(4,("Scanning username map %s\n",mapfile));
94
95   while((s=fgets_slash(buf,sizeof(buf),f))!=NULL) {
96     char *unixname = s;
97     char *dosname = strchr(unixname,'=');
98     BOOL return_if_mapped = False;
99
100     if (!dosname)
101       continue;
102
103     *dosname++ = 0;
104
105     while (isspace(*unixname))
106       unixname++;
107     if ('!' == *unixname) {
108       return_if_mapped = True;
109       unixname++;
110       while (*unixname && isspace(*unixname))
111         unixname++;
112     }
113     
114     if (!*unixname || strchr("#;",*unixname))
115       continue;
116
117     {
118       int l = strlen(unixname);
119       while (l && isspace(unixname[l-1])) {
120         unixname[l-1] = 0;
121         l--;
122       }
123     }
124
125     if (strchr(dosname,'*') || user_in_list(user,dosname)) {
126       DEBUG(3,("Mapped user %s to %s\n",user,unixname));
127       mapped_user = True;
128       fstrcpy(last_from,user);
129       sscanf(unixname,"%s",user);
130       fstrcpy(last_to,user);
131       if(return_if_mapped) { 
132         fclose(f);
133         return True;
134       }
135     }
136   }
137
138   fclose(f);
139
140   /*
141    * Setup the last_from and last_to as an optimization so 
142    * that we don't scan the file again for the same user.
143    */
144   fstrcpy(last_from,user);
145   fstrcpy(last_to,user);
146
147   return mapped_user;
148 }
149
150 /****************************************************************************
151 Get_Pwnam wrapper
152 ****************************************************************************/
153 static struct passwd *_Get_Pwnam(char *s)
154 {
155   struct passwd *ret;
156
157   ret = getpwnam(s);
158   if (ret)
159     {
160 #ifdef HAVE_GETPWANAM
161       struct passwd_adjunct *pwret;
162       pwret = getpwanam(s);
163       if (pwret)
164         {
165           free(ret->pw_passwd);
166           ret->pw_passwd = pwret->pwa_passwd;
167         }
168 #endif
169
170     }
171
172   return(ret);
173 }
174
175
176 /****************************************************************************
177 a wrapper for getpwnam() that tries with all lower and all upper case 
178 if the initial name fails. Also tried with first letter capitalised
179 Note that this can change user!
180 ****************************************************************************/
181 struct passwd *Get_Pwnam(char *user,BOOL allow_change)
182 {
183   fstring user2;
184   int last_char;
185   int usernamelevel = lp_usernamelevel();
186
187   struct passwd *ret;  
188
189   if (!user || !(*user))
190     return(NULL);
191
192   StrnCpy(user2,user,sizeof(user2)-1);
193
194   if (!allow_change) {
195     user = &user2[0];
196   }
197
198   ret = _Get_Pwnam(user);
199   if (ret) return(ret);
200
201   strlower(user);
202   ret = _Get_Pwnam(user);
203   if (ret)  return(ret);
204
205   strupper(user);
206   ret = _Get_Pwnam(user);
207   if (ret) return(ret);
208
209   /* try with first letter capitalised */
210   if (strlen(user) > 1)
211     strlower(user+1);  
212   ret = _Get_Pwnam(user);
213   if (ret) return(ret);
214
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) return(ret);
221
222   /* try all combinations up to usernamelevel */
223   strlower(user);
224   ret = uname_string_combinations(user, _Get_Pwnam, usernamelevel);
225   if (ret) return(ret);
226
227   if (allow_change)
228     fstrcpy(user,user2);
229
230   return(NULL);
231 }
232
233 /****************************************************************************
234 check if a user is in a netgroup user list
235 ****************************************************************************/
236 static BOOL user_in_netgroup_list(char *user,char *ngname)
237 {
238 #ifdef HAVE_NETGROUP
239   static char *mydomain = NULL;
240   if (mydomain == NULL)
241     yp_get_default_domain(&mydomain);
242
243   if(mydomain == NULL)
244   {
245     DEBUG(5,("Unable to get default yp domain\n"));
246   }
247   else
248   {
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"));
254
255     if (innetgr(ngname, NULL, user, mydomain))
256       return (True);
257   }
258 #endif /* HAVE_NETGROUP */
259   return False;
260 }
261
262 /****************************************************************************
263 check if a user is in a UNIX user list
264 ****************************************************************************/
265 static BOOL user_in_group_list(char *user,char *gname)
266 {
267 #ifdef HAVE_GETGRNAM 
268   struct group *gptr;
269   char **member;  
270   struct passwd *pass = Get_Pwnam(user,False);
271
272   if (pass)
273   { 
274     gptr = getgrgid(pass->pw_gid);
275     if (gptr && strequal(gptr->gr_name,gname))
276       return(True); 
277   } 
278
279   gptr = (struct group *)getgrnam(gname);
280
281   if (gptr)
282   {
283     member = gptr->gr_mem;
284     while (member && *member)
285     {
286       if (strequal(*member,user))
287         return(True);
288       member++;
289     }
290   }
291 #endif /* HAVE_GETGRNAM */
292   return False;
293 }             
294
295 /****************************************************************************
296 check if a user is in a user list - can check combinations of UNIX
297 and netgroup lists.
298 ****************************************************************************/
299 BOOL user_in_list(char *user,char *list)
300 {
301   pstring tok;
302   char *p=list;
303
304   while (next_token(&p,tok,LIST_SEP, sizeof(tok)))
305   {
306     /*
307      * Check raw username.
308      */
309     if (strequal(user,tok))
310       return(True);
311
312     /*
313      * Now check to see if any combination
314      * of UNIX and netgroups has been specified.
315      */
316
317     if(*tok == '@')
318     {
319       /*
320        * Old behaviour. Check netgroup list
321        * followed by UNIX list.
322        */
323       if(user_in_netgroup_list(user,&tok[1]))
324         return True;
325       if(user_in_group_list(user,&tok[1]))
326         return True;
327     }
328     else if (*tok == '+')
329     {
330       if(tok[1] == '&')
331       {
332         /*
333          * Search UNIX list followed by netgroup.
334          */
335         if(user_in_group_list(user,&tok[2]))
336           return True;
337         if(user_in_netgroup_list(user,&tok[2]))
338           return True;
339       }
340       else
341       {
342         /*
343          * Just search UNIX list.
344          */
345         if(user_in_group_list(user,&tok[1]))
346           return True;
347       }
348     }
349     else if (*tok == '&')
350     {
351       if(tok[1] == '&')
352       {
353         /*
354          * Search netgroup list followed by UNIX list.
355          */
356         if(user_in_netgroup_list(user,&tok[2]))
357           return True;
358         if(user_in_group_list(user,&tok[2]))
359           return True;
360       }
361       else
362       {
363         /*
364          * Just search netgroup list.
365          */
366         if(user_in_netgroup_list(user,&tok[1]))
367           return True;
368       }
369     }
370   }
371   return(False);
372 }
373
374 /* The functions below have been taken from password.c and slightly modified */
375 /****************************************************************************
376 apply a function to upper/lower case combinations
377 of a string and return true if one of them returns true.
378 try all combinations with N uppercase letters.
379 offset is the first char to try and change (start with 0)
380 it assumes the string starts lowercased
381 ****************************************************************************/
382 static struct passwd *uname_string_combinations2(char *s,int offset,struct passwd *(*fn)(char *),int N)
383 {
384   int len = strlen(s);
385   int i;
386   struct passwd *ret;
387
388 #ifdef PASSWORD_LENGTH
389   len = MIN(len,PASSWORD_LENGTH);
390 #endif
391
392   if (N <= 0 || offset >= len)
393     return(fn(s));
394
395
396   for (i=offset;i<(len-(N-1));i++)
397
398     {
399       char c = s[i];
400       if (!islower(c)) continue;
401       s[i] = toupper(c);
402       ret = uname_string_combinations2(s,i+1,fn,N-1);
403       if(ret) return(ret);
404       s[i] = c;
405     }
406   return(NULL);
407 }
408
409 /****************************************************************************
410 apply a function to upper/lower case combinations
411 of a string and return true if one of them returns true.
412 try all combinations with up to N uppercase letters.
413 offset is the first char to try and change (start with 0)
414 it assumes the string starts lowercased
415 ****************************************************************************/
416 static struct passwd * uname_string_combinations(char *s,struct passwd * (*fn)(char *),int N)
417 {
418   int n;
419   struct passwd *ret;
420
421   for (n=1;n<=N;n++)
422   {
423     ret = uname_string_combinations2(s,0,fn,n);
424     if(ret) return(ret);
425   }
426   return(NULL);
427 }