ipc.c: map_username is now a BOOL function.
[tprouty/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 extern DOM_SID global_machine_sid;
25
26 /* internal functions */
27 static struct passwd *uname_string_combinations(char *s, struct passwd * (*fn) (char *), int N);
28 static struct passwd *uname_string_combinations2(char *s, int offset, struct passwd * (*fn) (char *), int N);
29
30 /****************************************************************************
31 get a users home directory.
32 ****************************************************************************/
33 char *get_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 BOOL map_username(char *user)
55 {
56   static BOOL initialised=False;
57   static fstring last_from,last_to;
58   FILE *f;
59   char *mapfile = lp_username_map();
60   char *s;
61   pstring buf;
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 = 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       fstrcpy(last_from,user);
124       sscanf(unixname,"%s",user);
125       fstrcpy(last_to,user);
126       if(return_if_mapped) { 
127         fclose(f);
128         return True;
129       }
130     }
131   }
132
133   fclose(f);
134
135   /*
136    * Username wasn't mapped. Setup the last_from and last_to
137    * as an optimization so that we don't scan the file again
138    * for the same user.
139    */
140   fstrcpy(last_from,user);
141   fstrcpy(last_to,user);
142
143   return False;
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 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   DEBUG(3, ("Trying username %s\n", user));
216   ret = _Get_Pwnam(user);
217   if (ret) return(ret);
218
219   /* try all combinations up to usernamelevel */
220   strlower(user);
221   ret = uname_string_combinations(user, _Get_Pwnam, usernamelevel);
222   if (ret) return(ret);
223
224   if (allow_change)
225     fstrcpy(user,user2);
226
227   return(NULL);
228 }
229
230 /****************************************************************************
231 check if a user is in a user list
232 ****************************************************************************/
233 BOOL user_in_list(char *user,char *list)
234 {
235   pstring tok;
236   char *p=list;
237
238   while (next_token(&p,tok,LIST_SEP))
239     {
240       if (strequal(user,tok))
241         return(True);
242
243 #ifdef NETGROUP
244       if (*tok == '@')
245         {
246           static char *mydomain = NULL;
247           if (mydomain == 0)
248             yp_get_default_domain(&mydomain);
249
250           if(mydomain == 0)
251             {
252               DEBUG(5,("Unable to get default yp domain\n"));
253             }
254           else
255             {
256           
257               DEBUG(5,("looking for user %s of domain %s in netgroup %s\n",
258                    user, mydomain, &tok[1]));
259               DEBUG(5,("innetgr is %s\n",
260                    innetgr(&tok[1], (char *) 0, user, mydomain)
261                    ? "TRUE" : "FALSE"));
262           
263               if (innetgr(&tok[1], (char *)0, user, mydomain))
264                 return (True);
265             }
266         }
267 #endif
268
269
270 #if HAVE_GETGRNAM 
271       if (*tok == '@')
272         {
273           struct group *gptr;
274           char **member;  
275           struct passwd *pass = Get_Pwnam(user,False);
276
277           if (pass) { 
278             gptr = getgrgid(pass->pw_gid);
279             if (gptr && strequal(gptr->gr_name,&tok[1]))
280               return(True); 
281           } 
282
283           gptr = (struct group *)getgrnam(&tok[1]);
284
285           if (gptr)
286             {
287               member = gptr->gr_mem;
288               while (member && *member)
289                 {
290                   if (strequal(*member,user))
291                     return(True);
292                   member++;
293                 }
294             }
295         }             
296 #endif
297     }
298   return(False);
299 }
300
301 /* The functions below have been taken from password.c and slightly modified */
302 /****************************************************************************
303 apply a function to upper/lower case combinations
304 of a string and return true if one of them returns true.
305 try all combinations with N uppercase letters.
306 offset is the first char to try and change (start with 0)
307 it assumes the string starts lowercased
308 ****************************************************************************/
309 static struct passwd *uname_string_combinations2(char *s,int offset,struct passwd *(*fn)(char *),int N)
310 {
311   int len = strlen(s);
312   int i;
313   struct passwd *ret;
314
315 #ifdef PASSWORD_LENGTH
316   len = MIN(len,PASSWORD_LENGTH);
317 #endif
318
319   if (N <= 0 || offset >= len)
320     return(fn(s));
321
322
323   for (i=offset;i<(len-(N-1));i++)
324
325     {
326       char c = s[i];
327       if (!islower(c)) continue;
328       s[i] = toupper(c);
329       ret = uname_string_combinations2(s,i+1,fn,N-1);
330       if(ret) return(ret);
331       s[i] = c;
332     }
333   return(NULL);
334 }
335
336 /****************************************************************************
337 apply a function to upper/lower case combinations
338 of a string and return true if one of them returns true.
339 try all combinations with up to N uppercase letters.
340 offset is the first char to try and change (start with 0)
341 it assumes the string starts lowercased
342 ****************************************************************************/
343 static struct passwd * uname_string_combinations(char *s,struct passwd * (*fn)(char *),int N)
344 {
345   int n;
346   struct passwd *ret;
347
348   for (n=1;n<=N;n++)
349   {
350     ret = uname_string_combinations2(s,0,fn,n);
351     if(ret) return(ret);
352   }
353   return(NULL);
354 }
355
356 #if 0 
357 /* JRATEST - under construction. */
358 /**************************************************************************
359  Groupname map functionality. The code loads a groupname map file and
360  (currently) loads it into a linked list. This is slow and memory
361  hungry, but can be changed into a more efficient storage format
362  if the demands on it become excessive.
363 ***************************************************************************/
364
365 typedef struct groupname_map {
366    ubi_slNode next;
367
368    char *windows_name;
369    DOM_SID windows_sid;
370    char *unix_name;
371    gid_t unix_gid;
372 } groupname_map_entry;
373
374 static ubi_slList groupname_map_list;
375
376 /**************************************************************************
377  Delete all the entries in the groupname map list.
378 ***************************************************************************/
379
380 static void delete_groupname_map_list(void)
381 {
382   groupname_map_entry *gmep;
383
384   while((gmep = (groupname_map_entry *)ubi_slRemHead( groupname_map_list )) != NULL) {
385     if(gmep->windows_name)
386       free(gmep->windows_name);
387     if(gmep->unix_name)
388       free(gmep->unix_name);
389     free((char *)gmep);
390   }
391 }
392
393 /**************************************************************************
394  Load a groupname map file. Sets last accessed timestamp.
395 ***************************************************************************/
396
397 void load_groupname_map(void)
398 {
399   static time_t groupmap_file_last_modified = (time_t)0;
400   static BOOL initialized = False;
401   char *groupname_map_file = lp_groupname_map();
402   struct stat st;
403   FILE *fp;
404   char *s;
405   pstring buf;
406
407   if(!initialized) {
408     ubi_slInsert( &groupname_map_list );
409     initialized = True;
410   }
411
412   if (!*groupname_map_file)
413     return;
414
415   if(stat(groupname_map_file, &st) != 0) {
416     DEBUG(0, ("load_groupname_map: Unable to stat file %s. Error was %s\n",
417                groupname_map_file, strerror(errno) ));
418     return;
419   }
420
421   /*
422    * Check if file has changed.
423    */
424   if( st.st_mtime <= groupmap_file_last_modified)
425     return;
426
427   groupmap_file_last_modified = st.st_mtime;
428
429   /*
430    * Load the file.
431    */
432
433   fp = fopen(groupname_map_file,"r");
434   if (!fp) {
435     DEBUG(0,("load_groupname_map: can't open groupname map %s. Error was %s\n",
436           mapfile, strerror(errno)));
437     return;
438   }
439
440   /*
441    * Throw away any previous list.
442    */
443   delete_groupname_map_list();
444
445   DEBUG(4,("load_groupname_map: Scanning groupname map %s\n",groupname_map_file));
446
447   while((s=fgets_slash(buf,sizeof(buf),fp))!=NULL) {
448     pstring unixname;
449     pstring windows_name;
450     struct group *gptr;
451     DOM_SID tmp_sid;
452
453     DEBUG(10,("load_groupname_map: Read line |%s|\n", s);
454
455     if (!*s || strchr("#;",*s))
456       continue;
457
458     if(!next_token(&s,unixname, "\t\n\r="))
459       continue;
460
461     if(!next_token(&s,windows_name, "\t\n\r="))
462       continue;
463
464     trim_string(unixname, " ", " ");
465     trim_string(windows_name, " ", " ");
466
467     if (!*dosname)
468       continue;
469
470     if(!*unixname)
471       continue;
472
473     /*
474      * Attempt to get the unix gid_t for this name.
475      */
476
477     DEBUG(5,("load_groupname_map: Attempting to find unix group %s.\n",
478           unixname ));
479
480     if((gptr = (struct group *)getgrnam(unixname)) == NULL) {
481       DEBUG(0,("load_groupname_map: getgrnam for group %s failed.\
482 Error was %s.\n", unixname, strerror(errno) ));
483       continue;
484     }
485
486     /*
487      * Now map to an NT SID.
488      */
489
490     if(!lookup_wellknown_sid_from_name(windows_name, &tmp_sid)) {
491       /*
492        * It's not a well known name, convert the UNIX gid_t
493        * to a rid within this domain SID.
494        */
495       tmp_sid = global_machine_sid;
496       tmp_sid.sub_auths[tmp_sid.num_auths++] = 
497                     pdb_gid_to_group_rid((gid_t)gptr->gr_gid);
498     }
499
500     /*
501      * Create the list entry and add it onto the list.
502      */
503
504   }
505
506   fclose(fp);
507 }
508 #endif /* JRATEST */