s3:tldap: add tldap_extended*
[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 "system/passwd.h"
24 #include "../lib/util/memcache.h"
25 #include "../lib/util/util_pw.h"
26 #include "lib/util/string_wrappers.h"
27
28 /* internal functions */
29 static struct passwd *uname_string_combinations(char *s, TALLOC_CTX *mem_ctx,
30                                                 struct passwd * (*fn) (TALLOC_CTX *mem_ctx, const char *),
31                                                 int N);
32 static struct passwd *uname_string_combinations2(char *s, TALLOC_CTX *mem_ctx, int offset,
33                                                  struct passwd * (*fn) (TALLOC_CTX *mem_ctx, const char *),
34                                                  int N);
35
36 static struct passwd *getpwnam_alloc_cached(TALLOC_CTX *mem_ctx, const char *name)
37 {
38         struct passwd *pw, *for_cache;
39
40         pw = (struct passwd *)memcache_lookup_talloc(
41                 NULL, GETPWNAM_CACHE, data_blob_string_const_null(name));
42         if (pw != NULL) {
43                 return tcopy_passwd(mem_ctx, pw);
44         }
45
46         pw = getpwnam(name);
47         if (pw == NULL) {
48                 return NULL;
49         }
50
51         for_cache = tcopy_passwd(talloc_tos(), pw);
52         if (for_cache == NULL) {
53                 return NULL;
54         }
55
56         memcache_add_talloc(NULL, GETPWNAM_CACHE,
57                         data_blob_string_const_null(name), &for_cache);
58
59         return tcopy_passwd(mem_ctx, pw);
60 }
61
62 /****************************************************************************
63  Flush all cached passwd structs.
64 ****************************************************************************/
65
66 void flush_pwnam_cache(void)
67 {
68         memcache_flush(NULL, GETPWNAM_CACHE);
69 }
70
71 /****************************************************************************
72  Get a users home directory.
73 ****************************************************************************/
74
75 char *get_user_home_dir(TALLOC_CTX *mem_ctx, const char *user)
76 {
77         struct passwd *pass;
78         char *result;
79
80         /* Ensure the user exists. */
81
82         pass = Get_Pwnam_alloc(mem_ctx, user);
83
84         if (!pass)
85                 return(NULL);
86
87         /* Return home directory from struct passwd. */
88
89         result = talloc_move(mem_ctx, &pass->pw_dir);
90
91         TALLOC_FREE(pass);
92         return result;
93 }
94
95 /****************************************************************************
96  * A wrapper for getpwnam().  The following variations are tried:
97  *   - as transmitted
98  *   - in all lower case if this differs from transmitted
99  *   - in all upper case if this differs from transmitted
100  *   - using lp_username_level() for permutations.
101 ****************************************************************************/
102
103 static struct passwd *Get_Pwnam_internals(TALLOC_CTX *mem_ctx,
104                                           const char *user, char *user2)
105 {
106         struct passwd *ret = NULL;
107
108         if (!user2 || !(*user2))
109                 return(NULL);
110
111         if (!user || !(*user))
112                 return(NULL);
113
114         /* Try in all lower case first as this is the most 
115            common case on UNIX systems */
116         if (!strlower_m(user2)) {
117                 DEBUG(5,("strlower_m %s failed\n", user2));
118                 goto done;
119         }
120
121         DEBUG(5,("Trying _Get_Pwnam(), username as lowercase is %s\n",user2));
122         ret = getpwnam_alloc_cached(mem_ctx, user2);
123         if(ret)
124                 goto done;
125
126         /* Try as given, if username wasn't originally lowercase */
127         if(strcmp(user, user2) != 0) {
128                 DEBUG(5,("Trying _Get_Pwnam(), username as given is %s\n",
129                          user));
130                 ret = getpwnam_alloc_cached(mem_ctx, user);
131                 if(ret)
132                         goto done;
133         }
134
135         /* Try as uppercase, if username wasn't originally uppercase */
136         if (!strupper_m(user2)) {
137                 goto done;
138         }
139
140         if(strcmp(user, user2) != 0) {
141                 DEBUG(5,("Trying _Get_Pwnam(), username as uppercase is %s\n",
142                          user2));
143                 ret = getpwnam_alloc_cached(mem_ctx, user2);
144                 if(ret)
145                         goto done;
146         }
147
148         /* Try all combinations up to usernamelevel */
149         if (!strlower_m(user2)) {
150                 DEBUG(5,("strlower_m %s failed\n", user2));
151                 goto done;
152         }
153         DEBUG(5,("Checking combinations of %d uppercase letters in %s\n",
154                  lp_username_level(), user2));
155         ret = uname_string_combinations(user2, mem_ctx, getpwnam_alloc_cached,
156                                         lp_username_level());
157
158 done:
159         DEBUG(5,("Get_Pwnam_internals %s find user [%s]!\n",ret ?
160                  "did":"didn't", user));
161
162         return ret;
163 }
164
165 /****************************************************************************
166  Get_Pwnam wrapper without modification.
167   NOTE: This with NOT modify 'user'! 
168   This will return an allocated structure
169 ****************************************************************************/
170
171 struct passwd *Get_Pwnam_alloc(TALLOC_CTX *mem_ctx, const char *user)
172 {
173         fstring user2;
174
175         if ( *user == '\0' ) {
176                 DEBUG(10,("Get_Pwnam: empty username!\n"));
177                 return NULL;
178         }
179
180         fstrcpy(user2, user);
181
182         DEBUG(5,("Finding user %s\n", user));
183
184         return Get_Pwnam_internals(mem_ctx, user, user2);
185 }
186
187 /* The functions below have been taken from password.c and slightly modified */
188 /****************************************************************************
189  Apply a function to upper/lower case combinations
190  of a string and return true if one of them returns true.
191  Try all combinations with N uppercase letters.
192  offset is the first char to try and change (start with 0)
193  it assumes the string starts lowercased
194 ****************************************************************************/
195
196 static struct passwd *uname_string_combinations2(char *s, TALLOC_CTX *mem_ctx,
197                                                  int offset,
198                                                  struct passwd *(*fn)(TALLOC_CTX *mem_ctx, const char *),
199                                                  int N)
200 {
201         ssize_t len = (ssize_t)strlen(s);
202         int i;
203         struct passwd *ret;
204
205         if (N <= 0 || offset >= len)
206                 return(fn(mem_ctx, s));
207
208         for (i=offset;i<(len-(N-1));i++) {
209                 char c = s[i];
210                 if (!islower_m((int)c))
211                         continue;
212                 s[i] = toupper_m(c);
213                 ret = uname_string_combinations2(s, mem_ctx, i+1, fn, N-1);
214                 if(ret)
215                         return(ret);
216                 s[i] = c;
217         }
218         return(NULL);
219 }
220
221 /****************************************************************************
222  Apply a function to upper/lower case combinations
223  of a string and return true if one of them returns true.
224  Try all combinations with up to N uppercase letters.
225  offset is the first char to try and change (start with 0)
226  it assumes the string starts lowercased
227 ****************************************************************************/
228
229 static struct passwd * uname_string_combinations(char *s, TALLOC_CTX *mem_ctx,
230                                                  struct passwd * (*fn)(TALLOC_CTX *mem_ctx, const char *),
231                                                  int N)
232 {
233         int n;
234         struct passwd *ret;
235
236         for (n=1;n<=N;n++) {
237                 ret = uname_string_combinations2(s,mem_ctx,0,fn,n);
238                 if(ret)
239                         return(ret);
240         }  
241         return(NULL);
242 }
243