third_party/heimdal: import lorikeet-heimdal-202311290849 (commit 84fb4579594a5fd8f84...
[samba.git] / source3 / auth / user_util.c
blobcd97d62af4bc2db76ebb56ef757b2d03e635959e
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) Volker Lendecke 2006
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.
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.
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/>.
22 #include "includes.h"
23 #include "system/filesys.h"
24 #include "auth.h"
25 #include "lib/gencache.h"
27 /*******************************************************************
28 Map a username from a dos name to a unix name by looking in the username
29 map. Note that this modifies the name in place.
30 This is the main function that should be called *once* on
31 any incoming or new username - in order to canonicalize the name.
32 This is being done to de-couple the case conversions from the user mapping
33 function. Previously, the map_username was being called
34 every time Get_Pwnam_alloc was called.
35 Returns True if username was changed, false otherwise.
36 ********************************************************************/
38 static char *last_from = NULL;
39 static char *last_to = NULL;
41 static const char *get_last_from(void)
43 if (!last_from) {
44 return "";
46 return last_from;
49 static const char *get_last_to(void)
51 if (!last_to) {
52 return "";
54 return last_to;
57 static bool set_last_from_to(const char *from, const char *to)
59 char *orig_from = last_from;
60 char *orig_to = last_to;
62 last_from = SMB_STRDUP(from);
63 last_to = SMB_STRDUP(to);
65 SAFE_FREE(orig_from);
66 SAFE_FREE(orig_to);
68 if (!last_from || !last_to) {
69 SAFE_FREE(last_from);
70 SAFE_FREE(last_to);
71 return false;
73 return true;
76 static char *skip_space(char *s)
78 while (isspace((int)(*s))) {
79 s += 1;
81 return s;
84 static bool fetch_map_from_gencache(TALLOC_CTX *ctx,
85 const char *user_in,
86 char **p_user_out)
88 char *key, *value;
89 bool found;
91 if (lp_username_map_cache_time() == 0) {
92 return false;
95 key = talloc_asprintf_strupper_m(ctx, "USERNAME_MAP/%s",
96 user_in);
97 if (key == NULL) {
98 return false;
100 found = gencache_get(key, ctx, &value, NULL);
101 TALLOC_FREE(key);
102 if (!found) {
103 return false;
105 TALLOC_FREE(*p_user_out);
106 *p_user_out = value;
107 if (!*p_user_out) {
108 return false;
110 return true;
113 static void store_map_in_gencache(TALLOC_CTX *ctx, const char *from, const char *to)
115 char *key;
116 int cache_time = lp_username_map_cache_time();
118 if (cache_time == 0) {
119 return;
122 key = talloc_asprintf_strupper_m(ctx, "USERNAME_MAP/%s",
123 from);
124 if (key == NULL) {
125 return;
127 gencache_set(key, to, cache_time + time(NULL));
128 TALLOC_FREE(key);
131 /****************************************************************************
132 Check if a user is in a netgroup user list. If at first we don't succeed,
133 try lower case.
134 ****************************************************************************/
136 bool user_in_netgroup(TALLOC_CTX *ctx, const char *user, const char *ngname)
138 #if defined(HAVE_NETGROUP) && defined(HAVE_INNETGR)
139 char nis_domain_buf[256];
140 const char *nis_domain = NULL;
141 char *lowercase_user = NULL;
143 if (getdomainname(nis_domain_buf, sizeof(nis_domain_buf)) == 0) {
144 nis_domain = &nis_domain_buf[0];
145 } else {
146 DEBUG(5,("Unable to get default yp domain, "
147 "let's try without specifying it\n"));
148 nis_domain = NULL;
151 DEBUG(5,("looking for user %s of domain %s in netgroup %s\n",
152 user, nis_domain ? nis_domain : "(ANY)", ngname));
154 if (innetgr(ngname, NULL, user, nis_domain)) {
155 DEBUG(5,("user_in_netgroup: Found\n"));
156 return true;
160 * Ok, innetgr is case sensitive. Try once more with lowercase
161 * just in case. Attempt to fix #703. JRA.
163 lowercase_user = talloc_strdup(ctx, user);
164 if (!lowercase_user) {
165 return false;
167 if (!strlower_m(lowercase_user)) {
168 TALLOC_FREE(lowercase_user);
169 return false;
172 if (strcmp(user,lowercase_user) == 0) {
173 /* user name was already lower case! */
174 TALLOC_FREE(lowercase_user);
175 return false;
178 DEBUG(5,("looking for user %s of domain %s in netgroup %s\n",
179 lowercase_user, nis_domain ? nis_domain : "(ANY)", ngname));
181 if (innetgr(ngname, NULL, lowercase_user, nis_domain)) {
182 DEBUG(5,("user_in_netgroup: Found\n"));
183 TALLOC_FREE(lowercase_user);
184 return true;
186 #endif /* HAVE_NETGROUP and HAVE_INNETGR */
187 return false;
190 /****************************************************************************
191 Check if a user is in a user list - can check combinations of UNIX
192 and netgroup lists.
193 ****************************************************************************/
195 bool user_in_list(TALLOC_CTX *ctx, const char *user, const char * const *list)
197 if (!list || !*list)
198 return False;
200 DEBUG(10,("user_in_list: checking user %s in list\n", user));
202 while (*list) {
204 DEBUG(10,("user_in_list: checking user |%s| against |%s|\n",
205 user, *list));
208 * Check raw username.
210 if (strequal(user, *list))
211 return(True);
214 * Now check to see if any combination
215 * of UNIX and netgroups has been specified.
218 if(**list == '@') {
220 * Old behaviour. Check netgroup list
221 * followed by UNIX list.
223 if(user_in_netgroup(ctx, user, *list +1))
224 return True;
225 if(user_in_group(user, *list +1))
226 return True;
227 } else if (**list == '+') {
229 if((*(*list +1)) == '&') {
231 * Search UNIX list followed by netgroup.
233 if(user_in_group(user, *list +2))
234 return True;
235 if(user_in_netgroup(ctx, user, *list +2))
236 return True;
238 } else {
241 * Just search UNIX list.
244 if(user_in_group(user, *list +1))
245 return True;
248 } else if (**list == '&') {
250 if(*(*list +1) == '+') {
252 * Search netgroup list followed by UNIX list.
254 if(user_in_netgroup(ctx, user, *list +2))
255 return True;
256 if(user_in_group(user, *list +2))
257 return True;
258 } else {
260 * Just search netgroup list.
262 if(user_in_netgroup(ctx, user, *list +1))
263 return True;
267 list++;
269 return(False);
272 bool map_username(TALLOC_CTX *ctx, const char *user_in, char **p_user_out)
274 const struct loadparm_substitution *lp_sub =
275 loadparm_s3_global_substitution();
276 FILE *f;
277 char *mapfile = lp_username_map(talloc_tos(), lp_sub);
278 char *s;
279 char buf[512];
280 bool mapped_user = False;
281 char *cmd = lp_username_map_script(talloc_tos(), lp_sub);
283 *p_user_out = NULL;
285 if (!user_in)
286 return false;
288 /* Initially make a copy of the incoming name. */
289 *p_user_out = talloc_strdup(ctx, user_in);
290 if (!*p_user_out) {
291 return false;
294 if (strequal(user_in,get_last_to()))
295 return false;
297 if (strequal(user_in,get_last_from())) {
298 DEBUG(3,("Mapped user %s to %s\n",user_in,get_last_to()));
299 TALLOC_FREE(*p_user_out);
300 *p_user_out = talloc_strdup(ctx, get_last_to());
301 return true;
304 if (fetch_map_from_gencache(ctx, user_in, p_user_out)) {
305 return true;
308 /* first try the username map script */
310 if ( *cmd ) {
311 char **qlines;
312 char *command = NULL;
313 int numlines, ret, fd;
315 command = talloc_asprintf(ctx,
316 "%s \"%s\"",
317 cmd,
318 user_in);
319 if (!command) {
320 return false;
323 DEBUG(10,("Running [%s]\n", command));
324 ret = smbrun(command, &fd, NULL);
325 DEBUGADD(10,("returned [%d]\n", ret));
327 TALLOC_FREE(command);
329 if ( ret != 0 ) {
330 if (fd != -1)
331 close(fd);
332 return False;
335 numlines = 0;
336 qlines = fd_lines_load(fd, &numlines, 0, ctx);
337 DEBUGADD(10,("Lines returned = [%d]\n", numlines));
338 close(fd);
340 /* should be either no lines or a single line with the mapped username */
342 if (numlines && qlines) {
343 DEBUG(3,("Mapped user %s to %s\n", user_in, qlines[0] ));
344 set_last_from_to(user_in, qlines[0]);
345 store_map_in_gencache(ctx, user_in, qlines[0]);
346 TALLOC_FREE(*p_user_out);
347 *p_user_out = talloc_strdup(ctx, qlines[0]);
348 if (!*p_user_out) {
349 return false;
353 TALLOC_FREE(qlines);
355 return numlines != 0;
358 /* ok. let's try the mapfile */
359 if (!*mapfile)
360 return False;
362 f = fopen(mapfile, "r");
363 if (!f) {
364 DEBUG(0,("can't open username map %s. Error %s\n",mapfile, strerror(errno) ));
365 return False;
368 DEBUG(4,("Scanning username map %s\n",mapfile));
370 while((s=fgets_slash(NULL,buf,sizeof(buf),f))!=NULL) {
371 char *unixname = s;
372 char *dosname = strchr_m(unixname,'=');
373 char **dosuserlist;
374 bool return_if_mapped = False;
376 if (!dosname)
377 continue;
379 *dosname++ = 0;
381 unixname = skip_space(unixname);
383 if ('!' == *unixname) {
384 return_if_mapped = True;
385 unixname = skip_space(unixname+1);
388 if (!*unixname || strchr_m("#;",*unixname))
389 continue;
392 int l = strlen(unixname);
393 while (l && isspace((int)unixname[l-1])) {
394 unixname[l-1] = 0;
395 l--;
399 /* skip lines like 'user = ' */
401 dosuserlist = str_list_make_v3(ctx, dosname, NULL);
402 if (!dosuserlist) {
403 DEBUG(0,("Bad username map entry. Unable to build user list. Ignoring.\n"));
404 continue;
407 if (strchr_m(dosname,'*') ||
408 user_in_list(ctx, user_in, (const char * const *)dosuserlist)) {
409 DEBUG(3,("Mapped user %s to %s\n",user_in,unixname));
410 mapped_user = True;
412 set_last_from_to(user_in, unixname);
413 store_map_in_gencache(ctx, user_in, unixname);
414 TALLOC_FREE(*p_user_out);
415 *p_user_out = talloc_strdup(ctx, unixname);
416 if (!*p_user_out) {
417 TALLOC_FREE(dosuserlist);
418 fclose(f);
419 return false;
422 if ( return_if_mapped ) {
423 TALLOC_FREE(dosuserlist);
424 fclose(f);
425 return True;
429 TALLOC_FREE(dosuserlist);
432 fclose(f);
435 * If we didn't successfully map a user in the loop above,
436 * setup the last_from and last_to as an optimization so
437 * that we don't scan the file again for the same user.
439 if (!mapped_user) {
440 DEBUG(8, ("The user '%s' has no mapping. "
441 "Skip it next time.\n", user_in));
442 set_last_from_to(user_in, user_in);
443 store_map_in_gencache(ctx, user_in, user_in);
446 return mapped_user;