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())
237 int name_i=name_hash_function(name);
238 int hash_index=pht->names[name_i];
239 while(hash_index!=-1) {
240 struct passwd *pass=&pht->passwds[pht->entries[hash_index].entry];
241 if (strcmp(name,pass->pw_name)==0) {
242 DEBUG(5,("Found: %s:%s:%d:%d:%s:%s:%s\n",
250 return copy_passwd_struct(pass);
252 hash_index=pht->entries[hash_index].next;
256 DEBUG(5,("%s not found\n",name));
260 /* Fall back to real getpwnam() */
261 return sys_getpwnam(name);
264 /*******************************************************************
265 turn a uid into a user name
266 ********************************************************************/
267 char *uidtoname(uid_t uid)
269 static char name[40];
270 struct passwd_hash_table_s *pht=&passwd_hash_table;
271 struct passwd *pass=NULL;
273 DEBUG(5,("uidtoname(%d)\n",uid));
274 if (have_passwd_hash()) {
275 int hash_index=pht->uids[uid_hash_function(uid)];
276 while(hash_index!=-1) {
277 pass=&pht->passwds[pht->entries[hash_index].entry];
278 if (pass->pw_uid==uid) {
279 DEBUG(5,("Found: %s:%s:%d:%d:%s:%s:%s\n",
287 return pass->pw_name;
289 hash_index=pht->entries[hash_index].next;
291 DEBUG(5,("Hash miss"));
294 /* No hash table, fall back to getpwuid */
295 pass = getpwuid(uid);
297 if (pass) return(pass->pw_name);
298 slprintf(name, sizeof(name) - 1, "%d",(int)uid);
302 /****************************************************************************
303 get a users home directory.
304 ****************************************************************************/
305 char *get_home_dir(char *user)
307 const struct passwd *pass;
308 static pstring home_dir;
310 pass = Get_Pwnam(user, False);
312 if (pass == NULL || pass->pw_dir == NULL) return(NULL);
314 pstrcpy(home_dir, pass->pw_dir);
315 DEBUG(10,("get_home_dir: returning %s for user %s\n", home_dir, user));
320 /*******************************************************************
321 map a username from a dos name to a unix name by looking in the username
322 map. Note that this modifies the name in place.
323 This is the main function that should be called *once* on
324 any incoming or new username - in order to canonicalize the name.
325 This is being done to de-couple the case conversions from the user mapping
326 function. Previously, the map_username was being called
327 every time Get_Pwnam was called.
328 Returns True if username was changed, false otherwise.
329 ********************************************************************/
330 BOOL map_username(char *user)
332 static BOOL initialised=False;
333 static fstring last_from,last_to;
335 char *mapfile = lp_username_map();
338 BOOL mapped_user = False;
347 *last_from = *last_to = 0;
351 if (strequal(user,last_to))
354 if (strequal(user,last_from)) {
355 DEBUG(3,("Mapped user %s to %s\n",user,last_to));
356 fstrcpy(user,last_to);
360 f = sys_fopen(mapfile,"r");
362 DEBUG(0,("can't open username map %s\n",mapfile));
366 DEBUG(4,("Scanning username map %s\n",mapfile));
368 while((s=fgets_slash(buf,sizeof(buf),f))!=NULL) {
370 char *dosname = strchr(unixname,'=');
371 BOOL return_if_mapped = False;
378 while (isspace(*unixname))
380 if ('!' == *unixname) {
381 return_if_mapped = True;
383 while (*unixname && isspace(*unixname))
387 if (!*unixname || strchr("#;",*unixname))
391 int l = strlen(unixname);
392 while (l && isspace(unixname[l-1])) {
398 if (strchr(dosname,'*') || user_in_list(user,dosname)) {
399 DEBUG(3,("Mapped user %s to %s\n",user,unixname));
401 fstrcpy(last_from,user);
402 sscanf(unixname,"%s",user);
403 fstrcpy(last_to,user);
404 if(return_if_mapped) {
414 * Setup the last_from and last_to as an optimization so
415 * that we don't scan the file again for the same user.
417 fstrcpy(last_from,user);
418 fstrcpy(last_to,user);
424 /****************************************************************************
426 ****************************************************************************/
427 static struct passwd *_Get_Pwnam(char *s)
431 ret = hashed_getpwnam(s);
432 #ifdef HAVE_GETPWANAM
435 struct passwd_adjunct *pwret;
436 pwret = getpwanam(s);
437 if (pwret != NULL && pwret->pwa_passwd != NULL)
439 pstrcpy(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! Function returns const to emphasise
451 the fact that most of the members of the struct passwd * returned are
452 dynamically allocated.
453 ****************************************************************************/
454 const struct passwd *Get_Pwnam(char *user,BOOL allow_change)
458 int usernamelevel = lp_usernamelevel();
462 if (!user || !(*user))
465 StrnCpy(user2,user,sizeof(user2)-1);
471 ret = _Get_Pwnam(user);
472 if (ret) return(ret);
475 ret = _Get_Pwnam(user);
476 if (ret) return(ret);
479 ret = _Get_Pwnam(user);
480 if (ret) return(ret);
482 /* try with first letter capitalised */
483 if (strlen(user) > 1)
485 ret = _Get_Pwnam(user);
486 if (ret) return(ret);
488 /* try with last letter capitalised */
490 last_char = strlen(user)-1;
491 user[last_char] = toupper(user[last_char]);
492 ret = _Get_Pwnam(user);
493 if (ret) return(ret);
495 /* try all combinations up to usernamelevel */
497 ret = uname_string_combinations(user, _Get_Pwnam, usernamelevel);
498 if (ret) return(ret);
506 /****************************************************************************
507 check if a user is in a netgroup user list
508 ****************************************************************************/
509 static BOOL user_in_netgroup_list(char *user,char *ngname)
512 static char *mydomain = NULL;
513 if (mydomain == NULL)
514 yp_get_default_domain(&mydomain);
518 DEBUG(5,("Unable to get default yp domain\n"));
522 DEBUG(5,("looking for user %s of domain %s in netgroup %s\n",
523 user, mydomain, ngname));
524 DEBUG(5,("innetgr is %s\n",
525 innetgr(ngname, NULL, user, mydomain)
526 ? "TRUE" : "FALSE"));
528 if (innetgr(ngname, NULL, user, mydomain))
531 #endif /* HAVE_NETGROUP */
535 /****************************************************************************
536 check if a user is in a UNIX user list
537 ****************************************************************************/
538 static BOOL user_in_group_list(char *user,char *gname)
543 const struct passwd *pass = Get_Pwnam(user,False);
547 gptr = getgrgid(pass->pw_gid);
548 if (gptr && strequal(gptr->gr_name,gname))
552 gptr = (struct group *)getgrnam(gname);
556 member = gptr->gr_mem;
557 while (member && *member)
559 if (strequal(*member,user))
564 #endif /* HAVE_GETGRNAM */
568 /****************************************************************************
569 check if a user is in a user list - can check combinations of UNIX
571 ****************************************************************************/
572 BOOL user_in_list(char *user,char *list)
577 while (next_token(&p,tok,LIST_SEP, sizeof(tok)))
580 * Check raw username.
582 if (strequal(user,tok))
586 * Now check to see if any combination
587 * of UNIX and netgroups has been specified.
593 * Old behaviour. Check netgroup list
594 * followed by UNIX list.
596 if(user_in_netgroup_list(user,&tok[1]))
598 if(user_in_group_list(user,&tok[1]))
601 else if (*tok == '+')
606 * Search UNIX list followed by netgroup.
608 if(user_in_group_list(user,&tok[2]))
610 if(user_in_netgroup_list(user,&tok[2]))
616 * Just search UNIX list.
618 if(user_in_group_list(user,&tok[1]))
622 else if (*tok == '&')
627 * Search netgroup list followed by UNIX list.
629 if(user_in_netgroup_list(user,&tok[2]))
631 if(user_in_group_list(user,&tok[2]))
637 * Just search netgroup list.
639 if(user_in_netgroup_list(user,&tok[1]))
647 /* The functions below have been taken from password.c and slightly modified */
648 /****************************************************************************
649 apply a function to upper/lower case combinations
650 of a string and return true if one of them returns true.
651 try all combinations with N uppercase letters.
652 offset is the first char to try and change (start with 0)
653 it assumes the string starts lowercased
654 ****************************************************************************/
655 static struct passwd *uname_string_combinations2(char *s,int offset,struct passwd *(*fn)(char *),int N)
661 #ifdef PASSWORD_LENGTH
662 len = MIN(len,PASSWORD_LENGTH);
665 if (N <= 0 || offset >= len)
669 for (i=offset;i<(len-(N-1));i++)
673 if (!islower(c)) continue;
675 ret = uname_string_combinations2(s,i+1,fn,N-1);
682 /****************************************************************************
683 apply a function to upper/lower case combinations
684 of a string and return true if one of them returns true.
685 try all combinations with up to N uppercase letters.
686 offset is the first char to try and change (start with 0)
687 it assumes the string starts lowercased
688 ****************************************************************************/
689 static struct passwd * uname_string_combinations(char *s,struct passwd * (*fn)(char *),int N)
696 ret = uname_string_combinations2(s,0,fn,n);