2 Unix SMB/Netbios implementation.
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.
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 Since getpwnam() makes samba really slow with the NT-domain code
31 (reading /etc/passwd again and again and again), here is an implementation
32 of very simple passwd cache
33 ****************************************************************************/
34 #define PASSWD_HASH_SIZE 1009
35 /* The hashtable is rebuild every 15 seconds */
36 #define PASSWD_HASH_AGE 15
37 struct passwd_hash_entry {
42 struct passwd_hash_table_s {
43 struct passwd *passwds;
47 struct passwd_hash_entry *entries;
49 struct timeval build_time;
50 } passwd_hash_table = {
51 NULL,0,NULL,NULL,NULL,0,{0,0}
54 static int name_hash_function(const char *name)
56 /* I guess that there must be better hash functions. This one was the
57 * first to come into mind :) */
60 value=(value<<8)|(unsigned char)(*name);
61 if (value>1048576) value=value%PASSWD_HASH_SIZE;
64 value=value%PASSWD_HASH_SIZE;
68 static int uid_hash_function(uid_t uid)
70 return uid%PASSWD_HASH_SIZE;
74 static BOOL build_passwd_hash_table(void)
76 struct passwd_hash_table_s *pht=&passwd_hash_table; /* Convenience */
83 DEBUG(3,("Building passwd hash table\n"));
84 /* Free the allocated strings in old hash table */
85 for (i=0;i<pht->passwds_size;i++) {
86 free(pht->passwds[i].pw_name);
87 free(pht->passwds[i].pw_passwd);
88 free(pht->passwds[i].pw_gecos);
89 free(pht->passwds[i].pw_dir);
90 free(pht->passwds[i].pw_shell);
93 /* Initialize hash table if first table build */
94 if (pht->passwds_size==0) {
95 DEBUG(3,("Building passwd hash table for the first time\n"));
96 pht->passwds=malloc(sizeof(struct passwd)*64); /* A reasonable default */
99 if (pht->names==NULL) {
100 pht->names=malloc(sizeof(struct passwd_hash_entry *)*PASSWD_HASH_SIZE);
102 if (pht->uids==NULL) {
103 pht->uids=malloc(sizeof(struct passwd_hash_entry *)*PASSWD_HASH_SIZE);
105 if (pht->entries==NULL) {
106 pht->entries=malloc(sizeof(struct passwd_hash_entry)*128);
107 pht->entries_size=128;
109 if (pht->passwds==NULL || pht->names==NULL ||
110 pht->uids==NULL || pht->entries==NULL) {
114 /* Clear out the hash table */
115 for(i=0;i<PASSWD_HASH_SIZE;i++) pht->uids[i]=-1;
116 for(i=0;i<PASSWD_HASH_SIZE;i++) pht->names[i]=-1;
118 /* Now do the build */
121 while((pass=getpwent())) {
123 /* Check that we have enough space */
124 if (num_passwds==pht->passwds_size) {
125 struct passwd *new_passwds=NULL;
126 pht->passwds_size+=pht->passwds_size/2;
127 new_passwds=realloc(pht->passwds,
128 sizeof(struct passwd)*pht->passwds_size);
129 if (new_passwds==NULL) goto fail;
130 pht->passwds=new_passwds;
132 if (num_entries+1>=pht->entries_size) {
133 pht->entries_size+=pht->entries_size/2;
134 pht->entries=realloc(pht->entries,
135 sizeof(struct passwd_hash_entry)*pht->entries_size);
136 if (pht->entries==NULL) goto fail;
139 /* Copy the passwd struct */
140 memset(&pht->passwds[num_passwds],0,sizeof(struct passwd));
141 pht->passwds[num_passwds].pw_uid=pass->pw_uid;
142 pht->passwds[num_passwds].pw_gid=pass->pw_gid;
144 (pht->passwds[num_passwds].pw_name=strdup(pass->pw_name))==NULL ||
145 (pht->passwds[num_passwds].pw_passwd=strdup(pass->pw_passwd))==NULL ||
146 (pht->passwds[num_passwds].pw_gecos=strdup(pass->pw_gecos))==NULL ||
147 (pht->passwds[num_passwds].pw_dir=strdup(pass->pw_dir))==NULL ||
148 (pht->passwds[num_passwds].pw_shell=strdup(pass->pw_shell))==NULL ) {
153 /* Add to the hash table */
155 pht->entries[num_entries].entry=num_passwds;
156 name_i=name_hash_function(pass->pw_name);
157 pht->entries[num_entries].next=pht->names[name_i];
158 pht->names[name_i]=num_entries;
161 pht->entries[num_entries].entry=num_passwds;
162 uid_i=uid_hash_function(pass->pw_uid);
163 pht->entries[num_entries].next=pht->uids[uid_i];
164 pht->uids[uid_i]=num_entries;
167 /* This entry has been done */
172 if (pht->passwds_size>num_passwds) {
173 struct passwd *passwds;
174 passwds=realloc(pht->passwds,sizeof(pht->passwds[0])*num_passwds);
175 if (passwds==NULL) goto fail;
176 pht->passwds=passwds;
177 pht->passwds_size=num_passwds;
179 if (pht->entries_size>num_entries) {
180 struct passwd_hash_entry *entries;
181 entries=realloc(pht->entries,sizeof(pht->entries[0])*num_entries);
182 if (entries==NULL) goto fail;
183 pht->entries=entries;
184 pht->entries_size=num_entries;
187 /* Mark the creation time */
188 GetTimeOfDay(&pht->build_time);
189 /* Everything went smoothly. */
193 DEBUG(0,("Failed to create passwd hash table: %s",strerror(errno)));
194 /* OK: now the untested part. Normally this should never happen:
195 * Only running out of memory could cause this and even then
196 * we have enough trouble already. */
197 while (num_passwds>0) {
199 free(pht->passwds[num_passwds].pw_name);
200 free(pht->passwds[num_passwds].pw_passwd);
201 free(pht->passwds[num_passwds].pw_gecos);
202 free(pht->passwds[num_passwds].pw_dir);
203 free(pht->passwds[num_passwds].pw_shell);
211 /* Also mark fail time, so that retry will happen after PASSWD_HASH_AGE */
212 GetTimeOfDay(&pht->build_time);
216 static BOOL have_passwd_hash(void)
218 struct passwd_hash_table_s *pht=&passwd_hash_table;
221 /* I'm ignoring microseconds. If you think they matter, go ahead
222 * and implement them */
223 if (tv.tv_sec - pht->build_time.tv_sec > PASSWD_HASH_AGE) {
224 return build_passwd_hash_table();
226 return pht->passwds_size>0;
229 struct passwd *hashed_getpwnam(const char *name)
231 struct passwd_hash_table_s *pht=&passwd_hash_table;
233 DEBUG(5,("getpwnam(%s)\n", name));
235 if (have_passwd_hash()) {
236 int name_i=name_hash_function(name);
237 int hash_index=pht->names[name_i];
238 while(hash_index!=-1) {
239 struct passwd *pass=&pht->passwds[pht->entries[hash_index].entry];
240 if (strcmp(name,pass->pw_name)==0) {
241 DEBUG(5,("Found: %s:%s:%d:%d:%s:%s:%s\n",
251 hash_index=pht->entries[hash_index].next;
255 DEBUG(5,("%s not found\n",name));
258 /* Fall back to real getpwnam() */
259 return getpwnam(name);
262 /*******************************************************************
263 turn a uid into a user name
264 ********************************************************************/
265 char *uidtoname(uid_t uid)
267 static char name[40];
268 struct passwd_hash_table_s *pht=&passwd_hash_table;
269 struct passwd *pass=NULL;
271 DEBUG(5,("uidtoname(%d)\n",uid));
272 if (have_passwd_hash()) {
273 int hash_index=pht->uids[uid_hash_function(uid)];
274 while(hash_index!=-1) {
275 pass=&pht->passwds[pht->entries[hash_index].entry];
276 if (pass->pw_uid==uid) {
277 DEBUG(5,("Found: %s:%s:%d:%d:%s:%s:%s\n",
285 return pass->pw_name;
287 hash_index=pht->entries[hash_index].next;
289 DEBUG(5,("Hash miss"));
292 /* No hash table, fall back to getpwuid */
293 pass = getpwuid(uid);
295 if (pass) return(pass->pw_name);
296 slprintf(name, sizeof(name) - 1, "%d",(int)uid);
300 /****************************************************************************
301 get a users home directory.
302 ****************************************************************************/
303 char *get_home_dir(char *user)
306 static pstring home_dir;
308 pass = Get_Pwnam(user, False);
310 if (pass == NULL || pass->pw_dir == NULL) return(NULL);
312 pstrcpy(home_dir, pass->pw_dir);
313 DEBUG(10,("get_home_dir: returning %s for user %s\n", home_dir, user));
318 /*******************************************************************
319 map a username from a dos name to a unix name by looking in the username
320 map. Note that this modifies the name in place.
321 This is the main function that should be called *once* on
322 any incoming or new username - in order to canonicalize the name.
323 This is being done to de-couple the case conversions from the user mapping
324 function. Previously, the map_username was being called
325 every time Get_Pwnam was called.
326 Returns True if username was changed, false otherwise.
327 ********************************************************************/
328 BOOL map_username(char *user)
330 static BOOL initialised=False;
331 static fstring last_from,last_to;
333 char *mapfile = lp_username_map();
336 BOOL mapped_user = False;
345 *last_from = *last_to = 0;
349 if (strequal(user,last_to))
352 if (strequal(user,last_from)) {
353 DEBUG(3,("Mapped user %s to %s\n",user,last_to));
354 fstrcpy(user,last_to);
358 f = sys_fopen(mapfile,"r");
360 DEBUG(0,("can't open username map %s\n",mapfile));
364 DEBUG(4,("Scanning username map %s\n",mapfile));
366 while((s=fgets_slash(buf,sizeof(buf),f))!=NULL) {
368 char *dosname = strchr(unixname,'=');
369 BOOL return_if_mapped = False;
376 while (isspace(*unixname))
378 if ('!' == *unixname) {
379 return_if_mapped = True;
381 while (*unixname && isspace(*unixname))
385 if (!*unixname || strchr("#;",*unixname))
389 int l = strlen(unixname);
390 while (l && isspace(unixname[l-1])) {
396 if (strchr(dosname,'*') || user_in_list(user,dosname)) {
397 DEBUG(3,("Mapped user %s to %s\n",user,unixname));
399 fstrcpy(last_from,user);
400 sscanf(unixname,"%s",user);
401 fstrcpy(last_to,user);
402 if(return_if_mapped) {
412 * Setup the last_from and last_to as an optimization so
413 * that we don't scan the file again for the same user.
415 fstrcpy(last_from,user);
416 fstrcpy(last_to,user);
421 /****************************************************************************
423 ****************************************************************************/
424 static struct passwd *_Get_Pwnam(char *s)
428 ret = hashed_getpwnam(s);
431 #ifdef HAVE_GETPWANAM
432 struct passwd_adjunct *pwret;
433 pwret = getpwanam(s);
436 free(ret->pw_passwd);
437 ret->pw_passwd = pwret->pwa_passwd;
447 /****************************************************************************
448 a wrapper for getpwnam() that tries with all lower and all upper case
449 if the initial name fails. Also tried with first letter capitalised
450 Note that this can change user!
451 ****************************************************************************/
452 struct passwd *Get_Pwnam(char *user,BOOL allow_change)
456 int usernamelevel = lp_usernamelevel();
460 if (!user || !(*user))
463 StrnCpy(user2,user,sizeof(user2)-1);
469 ret = _Get_Pwnam(user);
470 if (ret) return(ret);
473 ret = _Get_Pwnam(user);
474 if (ret) return(ret);
477 ret = _Get_Pwnam(user);
478 if (ret) return(ret);
480 /* try with first letter capitalised */
481 if (strlen(user) > 1)
483 ret = _Get_Pwnam(user);
484 if (ret) return(ret);
486 /* try with last letter capitalised */
488 last_char = strlen(user)-1;
489 user[last_char] = toupper(user[last_char]);
490 ret = _Get_Pwnam(user);
491 if (ret) return(ret);
493 /* try all combinations up to usernamelevel */
495 ret = uname_string_combinations(user, _Get_Pwnam, usernamelevel);
496 if (ret) return(ret);
504 /****************************************************************************
505 check if a user is in a netgroup user list
506 ****************************************************************************/
507 static BOOL user_in_netgroup_list(char *user,char *ngname)
510 static char *mydomain = NULL;
511 if (mydomain == NULL)
512 yp_get_default_domain(&mydomain);
516 DEBUG(5,("Unable to get default yp domain\n"));
520 DEBUG(5,("looking for user %s of domain %s in netgroup %s\n",
521 user, mydomain, ngname));
522 DEBUG(5,("innetgr is %s\n",
523 innetgr(ngname, NULL, user, mydomain)
524 ? "TRUE" : "FALSE"));
526 if (innetgr(ngname, NULL, user, mydomain))
529 #endif /* HAVE_NETGROUP */
533 /****************************************************************************
534 check if a user is in a UNIX user list
535 ****************************************************************************/
536 static BOOL user_in_group_list(char *user,char *gname)
541 struct passwd *pass = Get_Pwnam(user,False);
545 gptr = getgrgid(pass->pw_gid);
546 if (gptr && strequal(gptr->gr_name,gname))
550 gptr = (struct group *)getgrnam(gname);
554 member = gptr->gr_mem;
555 while (member && *member)
557 if (strequal(*member,user))
562 #endif /* HAVE_GETGRNAM */
566 /****************************************************************************
567 check if a user is in a user list - can check combinations of UNIX
569 ****************************************************************************/
570 BOOL user_in_list(char *user,char *list)
575 while (next_token(&p,tok,LIST_SEP, sizeof(tok)))
578 * Check raw username.
580 if (strequal(user,tok))
584 * Now check to see if any combination
585 * of UNIX and netgroups has been specified.
591 * Old behaviour. Check netgroup list
592 * followed by UNIX list.
594 if(user_in_netgroup_list(user,&tok[1]))
596 if(user_in_group_list(user,&tok[1]))
599 else if (*tok == '+')
604 * Search UNIX list followed by netgroup.
606 if(user_in_group_list(user,&tok[2]))
608 if(user_in_netgroup_list(user,&tok[2]))
614 * Just search UNIX list.
616 if(user_in_group_list(user,&tok[1]))
620 else if (*tok == '&')
625 * Search netgroup list followed by UNIX list.
627 if(user_in_netgroup_list(user,&tok[2]))
629 if(user_in_group_list(user,&tok[2]))
635 * Just search netgroup list.
637 if(user_in_netgroup_list(user,&tok[1]))
645 /* The functions below have been taken from password.c and slightly modified */
646 /****************************************************************************
647 apply a function to upper/lower case combinations
648 of a string and return true if one of them returns true.
649 try all combinations with N uppercase letters.
650 offset is the first char to try and change (start with 0)
651 it assumes the string starts lowercased
652 ****************************************************************************/
653 static struct passwd *uname_string_combinations2(char *s,int offset,struct passwd *(*fn)(char *),int N)
659 #ifdef PASSWORD_LENGTH
660 len = MIN(len,PASSWORD_LENGTH);
663 if (N <= 0 || offset >= len)
667 for (i=offset;i<(len-(N-1));i++)
671 if (!islower(c)) continue;
673 ret = uname_string_combinations2(s,i+1,fn,N-1);
680 /****************************************************************************
681 apply a function to upper/lower case combinations
682 of a string and return true if one of them returns true.
683 try all combinations with up to N uppercase letters.
684 offset is the first char to try and change (start with 0)
685 it assumes the string starts lowercased
686 ****************************************************************************/
687 static struct passwd * uname_string_combinations(char *s,struct passwd * (*fn)(char *),int N)
694 ret = uname_string_combinations2(s,0,fn,n);