param: remove FN_LOCAL_STRING
[Samba.git] / source3 / auth / user_util.c
blobf4bdd2d323e34ac10644d21b0a0be110b3a3c29d
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 #ifdef HAVE_NETGROUP
28 /* rpc/xdr.h uses TRUE and FALSE */
29 #ifdef TRUE
30 #undef TRUE
31 #endif
33 #ifdef FALSE
34 #undef FALSE
35 #endif
37 #include "system/nis.h"
38 #endif
40 /*******************************************************************
41 Map a username from a dos name to a unix name by looking in the username
42 map. Note that this modifies the name in place.
43 This is the main function that should be called *once* on
44 any incoming or new username - in order to canonicalize the name.
45 This is being done to de-couple the case conversions from the user mapping
46 function. Previously, the map_username was being called
47 every time Get_Pwnam_alloc was called.
48 Returns True if username was changed, false otherwise.
49 ********************************************************************/
51 static char *last_from = NULL;
52 static char *last_to = NULL;
54 static const char *get_last_from(void)
56 if (!last_from) {
57 return "";
59 return last_from;
62 static const char *get_last_to(void)
64 if (!last_to) {
65 return "";
67 return last_to;
70 static bool set_last_from_to(const char *from, const char *to)
72 char *orig_from = last_from;
73 char *orig_to = last_to;
75 last_from = SMB_STRDUP(from);
76 last_to = SMB_STRDUP(to);
78 SAFE_FREE(orig_from);
79 SAFE_FREE(orig_to);
81 if (!last_from || !last_to) {
82 SAFE_FREE(last_from);
83 SAFE_FREE(last_to);
84 return false;
86 return true;
89 static char *skip_space(char *s)
91 while (isspace((int)(*s))) {
92 s += 1;
94 return s;
97 static bool fetch_map_from_gencache(TALLOC_CTX *ctx,
98 const char *user_in,
99 char **p_user_out)
101 char *key, *value;
102 bool found;
104 if (lp_username_map_cache_time() == 0) {
105 return false;
108 key = talloc_asprintf_strupper_m(ctx, "USERNAME_MAP/%s",
109 user_in);
110 if (key == NULL) {
111 return false;
113 found = gencache_get(key, ctx, &value, NULL);
114 TALLOC_FREE(key);
115 if (!found) {
116 return false;
118 TALLOC_FREE(*p_user_out);
119 *p_user_out = value;
120 if (!*p_user_out) {
121 return false;
123 return true;
126 static void store_map_in_gencache(TALLOC_CTX *ctx, const char *from, const char *to)
128 char *key;
129 int cache_time = lp_username_map_cache_time();
131 if (cache_time == 0) {
132 return;
135 key = talloc_asprintf_strupper_m(ctx, "USERNAME_MAP/%s",
136 from);
137 if (key == NULL) {
138 return;
140 gencache_set(key, to, cache_time + time(NULL));
141 TALLOC_FREE(key);
144 /****************************************************************************
145 Check if a user is in a netgroup user list. If at first we don't succeed,
146 try lower case.
147 ****************************************************************************/
149 bool user_in_netgroup(TALLOC_CTX *ctx, const char *user, const char *ngname)
151 #ifdef HAVE_NETGROUP
152 static char *my_yp_domain = NULL;
153 char *lowercase_user = NULL;
155 if (my_yp_domain == NULL) {
156 yp_get_default_domain(&my_yp_domain);
159 if (my_yp_domain == NULL) {
160 DEBUG(5,("Unable to get default yp domain, "
161 "let's try without specifying it\n"));
164 DEBUG(5,("looking for user %s of domain %s in netgroup %s\n",
165 user, my_yp_domain?my_yp_domain:"(ANY)", ngname));
167 if (innetgr(ngname, NULL, user, my_yp_domain)) {
168 DEBUG(5,("user_in_netgroup: Found\n"));
169 return true;
173 * Ok, innetgr is case sensitive. Try once more with lowercase
174 * just in case. Attempt to fix #703. JRA.
176 lowercase_user = talloc_strdup(ctx, user);
177 if (!lowercase_user) {
178 return false;
180 if (!strlower_m(lowercase_user)) {
181 return false;
184 if (strcmp(user,lowercase_user) == 0) {
185 /* user name was already lower case! */
186 return false;
189 DEBUG(5,("looking for user %s of domain %s in netgroup %s\n",
190 lowercase_user, my_yp_domain?my_yp_domain:"(ANY)", ngname));
192 if (innetgr(ngname, NULL, lowercase_user, my_yp_domain)) {
193 DEBUG(5,("user_in_netgroup: Found\n"));
194 return true;
196 #endif /* HAVE_NETGROUP */
197 return false;
200 /****************************************************************************
201 Check if a user is in a user list - can check combinations of UNIX
202 and netgroup lists.
203 ****************************************************************************/
205 bool user_in_list(TALLOC_CTX *ctx, const char *user, const char * const *list)
207 if (!list || !*list)
208 return False;
210 DEBUG(10,("user_in_list: checking user %s in list\n", user));
212 while (*list) {
214 DEBUG(10,("user_in_list: checking user |%s| against |%s|\n",
215 user, *list));
218 * Check raw username.
220 if (strequal(user, *list))
221 return(True);
224 * Now check to see if any combination
225 * of UNIX and netgroups has been specified.
228 if(**list == '@') {
230 * Old behaviour. Check netgroup list
231 * followed by UNIX list.
233 if(user_in_netgroup(ctx, user, *list +1))
234 return True;
235 if(user_in_group(user, *list +1))
236 return True;
237 } else if (**list == '+') {
239 if((*(*list +1)) == '&') {
241 * Search UNIX list followed by netgroup.
243 if(user_in_group(user, *list +2))
244 return True;
245 if(user_in_netgroup(ctx, user, *list +2))
246 return True;
248 } else {
251 * Just search UNIX list.
254 if(user_in_group(user, *list +1))
255 return True;
258 } else if (**list == '&') {
260 if(*(*list +1) == '+') {
262 * Search netgroup list followed by UNIX list.
264 if(user_in_netgroup(ctx, user, *list +2))
265 return True;
266 if(user_in_group(user, *list +2))
267 return True;
268 } else {
270 * Just search netgroup list.
272 if(user_in_netgroup(ctx, user, *list +1))
273 return True;
277 list++;
279 return(False);
282 bool map_username(TALLOC_CTX *ctx, const char *user_in, char **p_user_out)
284 const struct loadparm_substitution *lp_sub =
285 loadparm_s3_global_substitution();
286 FILE *f;
287 char *mapfile = lp_username_map(talloc_tos(), lp_sub);
288 char *s;
289 char buf[512];
290 bool mapped_user = False;
291 char *cmd = lp_username_map_script(talloc_tos(), lp_sub);
293 *p_user_out = NULL;
295 if (!user_in)
296 return false;
298 /* Initially make a copy of the incoming name. */
299 *p_user_out = talloc_strdup(ctx, user_in);
300 if (!*p_user_out) {
301 return false;
304 if (strequal(user_in,get_last_to()))
305 return false;
307 if (strequal(user_in,get_last_from())) {
308 DEBUG(3,("Mapped user %s to %s\n",user_in,get_last_to()));
309 TALLOC_FREE(*p_user_out);
310 *p_user_out = talloc_strdup(ctx, get_last_to());
311 return true;
314 if (fetch_map_from_gencache(ctx, user_in, p_user_out)) {
315 return true;
318 /* first try the username map script */
320 if ( *cmd ) {
321 char **qlines;
322 char *command = NULL;
323 int numlines, ret, fd;
325 command = talloc_asprintf(ctx,
326 "%s \"%s\"",
327 cmd,
328 user_in);
329 if (!command) {
330 return false;
333 DEBUG(10,("Running [%s]\n", command));
334 ret = smbrun(command, &fd, NULL);
335 DEBUGADD(10,("returned [%d]\n", ret));
337 TALLOC_FREE(command);
339 if ( ret != 0 ) {
340 if (fd != -1)
341 close(fd);
342 return False;
345 numlines = 0;
346 qlines = fd_lines_load(fd, &numlines, 0, ctx);
347 DEBUGADD(10,("Lines returned = [%d]\n", numlines));
348 close(fd);
350 /* should be either no lines or a single line with the mapped username */
352 if (numlines && qlines) {
353 DEBUG(3,("Mapped user %s to %s\n", user_in, qlines[0] ));
354 set_last_from_to(user_in, qlines[0]);
355 store_map_in_gencache(ctx, user_in, qlines[0]);
356 TALLOC_FREE(*p_user_out);
357 *p_user_out = talloc_strdup(ctx, qlines[0]);
358 if (!*p_user_out) {
359 return false;
363 TALLOC_FREE(qlines);
365 return numlines != 0;
368 /* ok. let's try the mapfile */
369 if (!*mapfile)
370 return False;
372 f = fopen(mapfile, "r");
373 if (!f) {
374 DEBUG(0,("can't open username map %s. Error %s\n",mapfile, strerror(errno) ));
375 return False;
378 DEBUG(4,("Scanning username map %s\n",mapfile));
380 while((s=fgets_slash(NULL,buf,sizeof(buf),f))!=NULL) {
381 char *unixname = s;
382 char *dosname = strchr_m(unixname,'=');
383 char **dosuserlist;
384 bool return_if_mapped = False;
386 if (!dosname)
387 continue;
389 *dosname++ = 0;
391 unixname = skip_space(unixname);
393 if ('!' == *unixname) {
394 return_if_mapped = True;
395 unixname = skip_space(unixname+1);
398 if (!*unixname || strchr_m("#;",*unixname))
399 continue;
402 int l = strlen(unixname);
403 while (l && isspace((int)unixname[l-1])) {
404 unixname[l-1] = 0;
405 l--;
409 /* skip lines like 'user = ' */
411 dosuserlist = str_list_make_v3(ctx, dosname, NULL);
412 if (!dosuserlist) {
413 DEBUG(0,("Bad username map entry. Unable to build user list. Ignoring.\n"));
414 continue;
417 if (strchr_m(dosname,'*') ||
418 user_in_list(ctx, user_in, (const char * const *)dosuserlist)) {
419 DEBUG(3,("Mapped user %s to %s\n",user_in,unixname));
420 mapped_user = True;
422 set_last_from_to(user_in, unixname);
423 store_map_in_gencache(ctx, user_in, unixname);
424 TALLOC_FREE(*p_user_out);
425 *p_user_out = talloc_strdup(ctx, unixname);
426 if (!*p_user_out) {
427 TALLOC_FREE(dosuserlist);
428 fclose(f);
429 return false;
432 if ( return_if_mapped ) {
433 TALLOC_FREE(dosuserlist);
434 fclose(f);
435 return True;
439 TALLOC_FREE(dosuserlist);
442 fclose(f);
445 * If we didn't successfully map a user in the loop above,
446 * setup the last_from and last_to as an optimization so
447 * that we don't scan the file again for the same user.
449 if (!mapped_user) {
450 DEBUG(8, ("The user '%s' has no mapping. "
451 "Skip it next time.\n", user_in));
452 set_last_from_to(user_in, user_in);
453 store_map_in_gencache(ctx, user_in, user_in);
456 return mapped_user;