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