first pass at updating head branch to be to be the same as the SAMBA_2_0 branch
[ira/wip.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
33 char *get_user_home_dir(char *user)
34 {
35   static struct passwd *pass;
36
37   pass = Get_Pwnam(user, False);
38
39   if (!pass) return(NULL);
40   return(pass->pw_dir);      
41 }
42
43
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 ********************************************************************/
54
55 BOOL map_username(char *user)
56 {
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;
64
65   if (!*user)
66     return False;
67
68   if (!*mapfile)
69     return False;
70
71   if (!initialised) {
72     *last_from = *last_to = 0;
73     initialised = True;
74   }
75
76   if (strequal(user,last_to))
77     return False;
78
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;
83   }
84   
85   f = sys_fopen(mapfile,"r");
86   if (!f) {
87     DEBUG(0,("can't open username map %s\n",mapfile));
88     return False;
89   }
90
91   DEBUG(4,("Scanning username map %s\n",mapfile));
92
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;
97
98     if (!dosname)
99       continue;
100
101     *dosname++ = 0;
102
103     while (isspace(*unixname))
104       unixname++;
105     if ('!' == *unixname) {
106       return_if_mapped = True;
107       unixname++;
108       while (*unixname && isspace(*unixname))
109         unixname++;
110     }
111     
112     if (!*unixname || strchr("#;",*unixname))
113       continue;
114
115     {
116       int l = strlen(unixname);
117       while (l && isspace(unixname[l-1])) {
118         unixname[l-1] = 0;
119         l--;
120       }
121     }
122
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;
132       }
133     }
134   }
135
136   fclose(f);
137
138   /*
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.
141    */
142   fstrcpy(last_from,user);
143   fstrcpy(last_to,user);
144
145   return mapped_user;
146 }
147
148 /****************************************************************************
149  Get_Pwnam wrapper
150 ****************************************************************************/
151
152 static struct passwd *_Get_Pwnam(char *s)
153 {
154   struct passwd *ret;
155
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);
163     }
164 #endif
165   }
166
167   return(ret);
168 }
169
170
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 ****************************************************************************/
176
177 struct passwd *Get_Pwnam(char *user,BOOL allow_change)
178 {
179   fstring user2;
180   int last_char;
181   int usernamelevel = lp_usernamelevel();
182
183   struct passwd *ret;  
184
185   if (!user || !(*user))
186     return(NULL);
187
188   StrnCpy(user2,user,sizeof(user2)-1);
189
190   if (!allow_change) {
191     user = &user2[0];
192   }
193
194   ret = _Get_Pwnam(user);
195   if (ret)
196     return(ret);
197
198   strlower(user);
199   ret = _Get_Pwnam(user);
200   if (ret)
201     return(ret);
202
203   strupper(user);
204   ret = _Get_Pwnam(user);
205   if (ret)
206     return(ret);
207
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);
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)
221     return(ret);
222
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);
228
229   if (allow_change)
230     fstrcpy(user,user2);
231
232   return(NULL);
233 }
234
235 /****************************************************************************
236  Check if a user is in a netgroup user list.
237 ****************************************************************************/
238
239 static BOOL user_in_netgroup_list(char *user,char *ngname)
240 {
241 #ifdef HAVE_NETGROUP
242   static char *mydomain = NULL;
243   if (mydomain == NULL)
244     yp_get_default_domain(&mydomain);
245
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"));
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
266 static BOOL user_in_group_list(char *user,char *gname)
267 {
268 #ifdef HAVE_GETGRENT
269         struct group *gptr;
270         char **member;  
271         struct passwd *pass = Get_Pwnam(user,False);
272
273         if (pass) { 
274                 gptr = getgrgid(pass->pw_gid);
275                 if (gptr && strequal(gptr->gr_name,gname))
276                         return(True); 
277         } 
278
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);
287                         }
288                         member++;
289                 }
290         }
291
292         endgrent();
293 #endif /* HAVE_GETGRNAM */
294         return False;
295 }             
296
297 /****************************************************************************
298  Check if a user is in a user list - can check combinations of UNIX
299  and netgroup lists.
300 ****************************************************************************/
301
302 BOOL user_in_list(char *user,char *list)
303 {
304   pstring tok;
305   char *p=list;
306
307   while (next_token(&p,tok,LIST_SEP, sizeof(tok))) {
308     /*
309      * Check raw username.
310      */
311     if (strequal(user,tok))
312       return(True);
313
314     /*
315      * Now check to see if any combination
316      * of UNIX and netgroups has been specified.
317      */
318
319     if(*tok == '@') {
320       /*
321        * Old behaviour. Check netgroup list
322        * followed by UNIX list.
323        */
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 == '+') {
329
330       if(tok[1] == '&') {
331         /*
332          * Search UNIX list followed by netgroup.
333          */
334         if(user_in_group_list(user,&tok[2]))
335           return True;
336         if(user_in_netgroup_list(user,&tok[2]))
337           return True;
338
339       } else {
340
341         /*
342          * Just search UNIX list.
343          */
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          * Search netgroup list followed by UNIX list.
354          */
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 {
360         /*
361          * Just search netgroup list.
362          */
363         if(user_in_netgroup_list(user,&tok[1]))
364           return True;
365       }
366     }
367   }
368   return(False);
369 }
370
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 ****************************************************************************/
379
380 static struct passwd *uname_string_combinations2(char *s,int offset,struct passwd *(*fn)(char *),int N)
381 {
382   ssize_t len = (ssize_t)strlen(s);
383   int i;
384   struct passwd *ret;
385
386 #ifdef PASSWORD_LENGTH
387   len = MIN(len,PASSWORD_LENGTH);
388 #endif
389
390   if (N <= 0 || offset >= len)
391     return(fn(s));
392
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;
402   }
403   return(NULL);
404 }
405
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 ****************************************************************************/
413
414 static struct passwd * uname_string_combinations(char *s,struct passwd * (*fn)(char *),int N)
415 {
416   int n;
417   struct passwd *ret;
418
419   for (n=1;n<=N;n++) {
420     ret = uname_string_combinations2(s,0,fn,n);
421     if(ret)
422       return(ret);
423   }
424   return(NULL);
425 }