ctdb: Print locks latency in machinereadable stats
[Samba.git] / source3 / auth / user_util.c
bloba76b5d47ba2c18ff849ec401980439b4d8a7c12d
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 FILE *f;
285 char *mapfile = lp_username_map(talloc_tos());
286 char *s;
287 char buf[512];
288 bool mapped_user = False;
289 char *cmd = lp_username_map_script(talloc_tos());
291 *p_user_out = NULL;
293 if (!user_in)
294 return false;
296 /* Initially make a copy of the incoming name. */
297 *p_user_out = talloc_strdup(ctx, user_in);
298 if (!*p_user_out) {
299 return false;
302 if (strequal(user_in,get_last_to()))
303 return false;
305 if (strequal(user_in,get_last_from())) {
306 DEBUG(3,("Mapped user %s to %s\n",user_in,get_last_to()));
307 TALLOC_FREE(*p_user_out);
308 *p_user_out = talloc_strdup(ctx, get_last_to());
309 return true;
312 if (fetch_map_from_gencache(ctx, user_in, p_user_out)) {
313 return true;
316 /* first try the username map script */
318 if ( *cmd ) {
319 char **qlines;
320 char *command = NULL;
321 int numlines, ret, fd;
323 command = talloc_asprintf(ctx,
324 "%s \"%s\"",
325 cmd,
326 user_in);
327 if (!command) {
328 return false;
331 DEBUG(10,("Running [%s]\n", command));
332 ret = smbrun(command, &fd, NULL);
333 DEBUGADD(10,("returned [%d]\n", ret));
335 TALLOC_FREE(command);
337 if ( ret != 0 ) {
338 if (fd != -1)
339 close(fd);
340 return False;
343 numlines = 0;
344 qlines = fd_lines_load(fd, &numlines, 0, ctx);
345 DEBUGADD(10,("Lines returned = [%d]\n", numlines));
346 close(fd);
348 /* should be either no lines or a single line with the mapped username */
350 if (numlines && qlines) {
351 DEBUG(3,("Mapped user %s to %s\n", user_in, qlines[0] ));
352 set_last_from_to(user_in, qlines[0]);
353 store_map_in_gencache(ctx, user_in, qlines[0]);
354 TALLOC_FREE(*p_user_out);
355 *p_user_out = talloc_strdup(ctx, qlines[0]);
356 if (!*p_user_out) {
357 return false;
361 TALLOC_FREE(qlines);
363 return numlines != 0;
366 /* ok. let's try the mapfile */
367 if (!*mapfile)
368 return False;
370 f = fopen(mapfile, "r");
371 if (!f) {
372 DEBUG(0,("can't open username map %s. Error %s\n",mapfile, strerror(errno) ));
373 return False;
376 DEBUG(4,("Scanning username map %s\n",mapfile));
378 while((s=fgets_slash(NULL,buf,sizeof(buf),f))!=NULL) {
379 char *unixname = s;
380 char *dosname = strchr_m(unixname,'=');
381 char **dosuserlist;
382 bool return_if_mapped = False;
384 if (!dosname)
385 continue;
387 *dosname++ = 0;
389 unixname = skip_space(unixname);
391 if ('!' == *unixname) {
392 return_if_mapped = True;
393 unixname = skip_space(unixname+1);
396 if (!*unixname || strchr_m("#;",*unixname))
397 continue;
400 int l = strlen(unixname);
401 while (l && isspace((int)unixname[l-1])) {
402 unixname[l-1] = 0;
403 l--;
407 /* skip lines like 'user = ' */
409 dosuserlist = str_list_make_v3(ctx, dosname, NULL);
410 if (!dosuserlist) {
411 DEBUG(0,("Bad username map entry. Unable to build user list. Ignoring.\n"));
412 continue;
415 if (strchr_m(dosname,'*') ||
416 user_in_list(ctx, user_in, (const char * const *)dosuserlist)) {
417 DEBUG(3,("Mapped user %s to %s\n",user_in,unixname));
418 mapped_user = True;
420 set_last_from_to(user_in, unixname);
421 store_map_in_gencache(ctx, user_in, unixname);
422 TALLOC_FREE(*p_user_out);
423 *p_user_out = talloc_strdup(ctx, unixname);
424 if (!*p_user_out) {
425 TALLOC_FREE(dosuserlist);
426 fclose(f);
427 return false;
430 if ( return_if_mapped ) {
431 TALLOC_FREE(dosuserlist);
432 fclose(f);
433 return True;
437 TALLOC_FREE(dosuserlist);
440 fclose(f);
443 * If we didn't successfully map a user in the loop above,
444 * setup the last_from and last_to as an optimization so
445 * that we don't scan the file again for the same user.
447 if (!mapped_user) {
448 DEBUG(8, ("The user '%s' has no mapping. "
449 "Skip it next time.\n", user_in));
450 set_last_from_to(user_in, user_in);
451 store_map_in_gencache(ctx, user_in, user_in);
454 return mapped_user;