lib/util/util_pw: share sys_get{pw,gr} group of calls.
[kai/samba.git] / source3 / lib / username.c
1 /* 
2    Unix SMB/CIFS implementation.
3    Username handling
4    Copyright (C) Andrew Tridgell 1992-1998
5    Copyright (C) Jeremy Allison 1997-2001.
6    Copyright (C) Andrew Bartlett 2002
7
8    This program is free software; you can redistribute it and/or modify
9    it under the terms of the GNU General Public License as published by
10    the Free Software Foundation; either version 3 of the License, or
11    (at your option) any later version.
12
13    This program is distributed in the hope that it will be useful,
14    but WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16    GNU General Public License for more details.
17
18    You should have received a copy of the GNU General Public License
19    along with this program.  If not, see <http://www.gnu.org/licenses/>.
20 */
21
22 #include "includes.h"
23 #include "memcache.h"
24 #include "../lib/util/util_pw.h"
25
26 /* internal functions */
27 static struct passwd *uname_string_combinations(char *s, TALLOC_CTX *mem_ctx,
28                                                 struct passwd * (*fn) (TALLOC_CTX *mem_ctx, const char *),
29                                                 int N);
30 static struct passwd *uname_string_combinations2(char *s, TALLOC_CTX *mem_ctx, int offset,
31                                                  struct passwd * (*fn) (TALLOC_CTX *mem_ctx, const char *),
32                                                  int N);
33
34 static struct passwd *getpwnam_alloc_cached(TALLOC_CTX *mem_ctx, const char *name)
35 {
36         struct passwd *pw, *for_cache;
37
38         pw = (struct passwd *)memcache_lookup_talloc(
39                 NULL, GETPWNAM_CACHE, data_blob_string_const_null(name));
40         if (pw != NULL) {
41                 return tcopy_passwd(mem_ctx, pw);
42         }
43
44         pw = sys_getpwnam(name);
45         if (pw == NULL) {
46                 return NULL;
47         }
48
49         for_cache = tcopy_passwd(talloc_tos(), pw);
50         if (for_cache == NULL) {
51                 return NULL;
52         }
53
54         memcache_add_talloc(NULL, GETPWNAM_CACHE,
55                         data_blob_string_const_null(name), &for_cache);
56
57         return tcopy_passwd(mem_ctx, pw);
58 }
59
60 /****************************************************************************
61  talloc copy a struct passwd.
62 ****************************************************************************/
63
64 struct passwd *tcopy_passwd(TALLOC_CTX *mem_ctx, const struct passwd *from)
65 {
66         struct passwd *ret = TALLOC_P(mem_ctx, struct passwd);
67         if (!ret) {
68                 return NULL;
69         }
70         ret->pw_name = talloc_strdup(ret, from->pw_name);
71         ret->pw_passwd = talloc_strdup(ret, from->pw_passwd);
72         ret->pw_uid = from->pw_uid;
73         ret->pw_gid = from->pw_gid;
74         ret->pw_gecos = talloc_strdup(ret, from->pw_gecos);
75         ret->pw_dir = talloc_strdup(ret, from->pw_dir);
76         ret->pw_shell = talloc_strdup(ret, from->pw_shell);
77         return ret;
78 }
79
80 /****************************************************************************
81  Flush all cached passwd structs.
82 ****************************************************************************/
83
84 void flush_pwnam_cache(void)
85 {
86         memcache_flush(NULL, GETPWNAM_CACHE);
87 }
88
89 /****************************************************************************
90  talloc'ed version of getpwuid.
91 ****************************************************************************/
92
93 struct passwd *getpwuid_alloc(TALLOC_CTX *mem_ctx, uid_t uid)
94 {
95         struct passwd *temp = sys_getpwuid(uid);
96
97         if (!temp) {
98                 return NULL;
99         }
100
101         return tcopy_passwd(mem_ctx, temp);
102 }
103
104 /****************************************************************************
105  Get a users home directory.
106 ****************************************************************************/
107
108 char *get_user_home_dir(TALLOC_CTX *mem_ctx, const char *user)
109 {
110         struct passwd *pass;
111         char *result;
112
113         /* Ensure the user exists. */
114
115         pass = Get_Pwnam_alloc(mem_ctx, user);
116
117         if (!pass)
118                 return(NULL);
119
120         /* Return home directory from struct passwd. */
121
122         result = talloc_move(mem_ctx, &pass->pw_dir);
123
124         TALLOC_FREE(pass);
125         return result;
126 }
127
128 /****************************************************************************
129  * A wrapper for sys_getpwnam().  The following variations are tried:
130  *   - as transmitted
131  *   - in all lower case if this differs from transmitted
132  *   - in all upper case if this differs from transmitted
133  *   - using lp_usernamelevel() for permutations.
134 ****************************************************************************/
135
136 static struct passwd *Get_Pwnam_internals(TALLOC_CTX *mem_ctx,
137                                           const char *user, char *user2)
138 {
139         struct passwd *ret = NULL;
140
141         if (!user2 || !(*user2))
142                 return(NULL);
143
144         if (!user || !(*user))
145                 return(NULL);
146
147         /* Try in all lower case first as this is the most 
148            common case on UNIX systems */
149         strlower_m(user2);
150         DEBUG(5,("Trying _Get_Pwnam(), username as lowercase is %s\n",user2));
151         ret = getpwnam_alloc_cached(mem_ctx, user2);
152         if(ret)
153                 goto done;
154
155         /* Try as given, if username wasn't originally lowercase */
156         if(strcmp(user, user2) != 0) {
157                 DEBUG(5,("Trying _Get_Pwnam(), username as given is %s\n",
158                          user));
159                 ret = getpwnam_alloc_cached(mem_ctx, user);
160                 if(ret)
161                         goto done;
162         }
163
164         /* Try as uppercase, if username wasn't originally uppercase */
165         strupper_m(user2);
166         if(strcmp(user, user2) != 0) {
167                 DEBUG(5,("Trying _Get_Pwnam(), username as uppercase is %s\n",
168                          user2));
169                 ret = getpwnam_alloc_cached(mem_ctx, user2);
170                 if(ret)
171                         goto done;
172         }
173
174         /* Try all combinations up to usernamelevel */
175         strlower_m(user2);
176         DEBUG(5,("Checking combinations of %d uppercase letters in %s\n",
177                  lp_usernamelevel(), user2));
178         ret = uname_string_combinations(user2, mem_ctx, getpwnam_alloc_cached,
179                                         lp_usernamelevel());
180
181 done:
182         DEBUG(5,("Get_Pwnam_internals %s find user [%s]!\n",ret ?
183                  "did":"didn't", user));
184
185         return ret;
186 }
187
188 /****************************************************************************
189  Get_Pwnam wrapper without modification.
190   NOTE: This with NOT modify 'user'! 
191   This will return an allocated structure
192 ****************************************************************************/
193
194 struct passwd *Get_Pwnam_alloc(TALLOC_CTX *mem_ctx, const char *user)
195 {
196         fstring user2;
197
198         if ( *user == '\0' ) {
199                 DEBUG(10,("Get_Pwnam: empty username!\n"));
200                 return NULL;
201         }
202
203         fstrcpy(user2, user);
204
205         DEBUG(5,("Finding user %s\n", user));
206
207         return Get_Pwnam_internals(mem_ctx, user, user2);
208 }
209
210 /* The functions below have been taken from password.c and slightly modified */
211 /****************************************************************************
212  Apply a function to upper/lower case combinations
213  of a string and return true if one of them returns true.
214  Try all combinations with N uppercase letters.
215  offset is the first char to try and change (start with 0)
216  it assumes the string starts lowercased
217 ****************************************************************************/
218
219 static struct passwd *uname_string_combinations2(char *s, TALLOC_CTX *mem_ctx,
220                                                  int offset,
221                                                  struct passwd *(*fn)(TALLOC_CTX *mem_ctx, const char *),
222                                                  int N)
223 {
224         ssize_t len = (ssize_t)strlen(s);
225         int i;
226         struct passwd *ret;
227
228         if (N <= 0 || offset >= len)
229                 return(fn(mem_ctx, s));
230
231         for (i=offset;i<(len-(N-1));i++) {
232                 char c = s[i];
233                 if (!islower_ascii((int)c))
234                         continue;
235                 s[i] = toupper_ascii(c);
236                 ret = uname_string_combinations2(s, mem_ctx, i+1, fn, N-1);
237                 if(ret)
238                         return(ret);
239                 s[i] = c;
240         }
241         return(NULL);
242 }
243
244 /****************************************************************************
245  Apply a function to upper/lower case combinations
246  of a string and return true if one of them returns true.
247  Try all combinations with up to N uppercase letters.
248  offset is the first char to try and change (start with 0)
249  it assumes the string starts lowercased
250 ****************************************************************************/
251
252 static struct passwd * uname_string_combinations(char *s, TALLOC_CTX *mem_ctx,
253                                                  struct passwd * (*fn)(TALLOC_CTX *mem_ctx, const char *),
254                                                  int N)
255 {
256         int n;
257         struct passwd *ret;
258
259         for (n=1;n<=N;n++) {
260                 ret = uname_string_combinations2(s,mem_ctx,0,fn,n);
261                 if(ret)
262                         return(ret);
263         }  
264         return(NULL);
265 }
266