python:tests: Store keys as bytes rather than as lists of ints
[samba.git] / source3 / auth / user_util.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) Volker Lendecke 2006
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 "lib/util/util_file.h"
24 #include "system/filesys.h"
25 #include "auth.h"
26 #include "lib/gencache.h"
27
28 /*******************************************************************
29  Map a username from a dos name to a unix name by looking in the username
30  map. Note that this modifies the name in place.
31  This is the main function that should be called *once* on
32  any incoming or new username - in order to canonicalize the name.
33  This is being done to de-couple the case conversions from the user mapping
34  function. Previously, the map_username was being called
35  every time Get_Pwnam_alloc was called.
36  Returns True if username was changed, false otherwise.
37 ********************************************************************/
38
39 static char *last_from = NULL;
40 static char *last_to = NULL;
41
42 static const char *get_last_from(void)
43 {
44         if (!last_from) {
45                 return "";
46         }
47         return last_from;
48 }
49
50 static const char *get_last_to(void)
51 {
52         if (!last_to) {
53                 return "";
54         }
55         return last_to;
56 }
57
58 static bool set_last_from_to(const char *from, const char *to)
59 {
60         char *orig_from = last_from;
61         char *orig_to = last_to;
62
63         last_from = SMB_STRDUP(from);
64         last_to = SMB_STRDUP(to);
65
66         SAFE_FREE(orig_from);
67         SAFE_FREE(orig_to);
68
69         if (!last_from || !last_to) {
70                 SAFE_FREE(last_from);
71                 SAFE_FREE(last_to);
72                 return false;
73         }
74         return true;
75 }
76
77 static char *skip_space(char *s)
78 {
79         while (isspace((int)(*s))) {
80                 s += 1;
81         }
82         return s;
83 }
84
85 static bool fetch_map_from_gencache(TALLOC_CTX *ctx,
86                         const char *user_in,
87                         char **p_user_out)
88 {
89         char *key, *value;
90         bool found;
91
92         if (lp_username_map_cache_time() == 0) {
93                 return false;
94         }
95
96         key = talloc_asprintf_strupper_m(ctx, "USERNAME_MAP/%s",
97                                          user_in);
98         if (key == NULL) {
99                 return false;
100         }
101         found = gencache_get(key, ctx, &value, NULL);
102         TALLOC_FREE(key);
103         if (!found) {
104                 return false;
105         }
106         TALLOC_FREE(*p_user_out);
107         *p_user_out = value;
108         if (!*p_user_out) {
109                 return false;
110         }
111         return true;
112 }
113
114 static void store_map_in_gencache(TALLOC_CTX *ctx, const char *from, const char *to)
115 {
116         char *key;
117         int cache_time = lp_username_map_cache_time();
118
119         if (cache_time == 0) {
120                 return;
121         }
122
123         key = talloc_asprintf_strupper_m(ctx, "USERNAME_MAP/%s",
124                                          from);
125         if (key == NULL) {
126                 return;
127         }
128         gencache_set(key, to, cache_time + time(NULL));
129         TALLOC_FREE(key);
130 }
131
132 /****************************************************************************
133  Check if a user is in a netgroup user list. If at first we don't succeed,
134  try lower case.
135 ****************************************************************************/
136
137 bool user_in_netgroup(TALLOC_CTX *ctx, const char *user, const char *ngname)
138 {
139 #if defined(HAVE_NETGROUP) && defined(HAVE_INNETGR)
140         char nis_domain_buf[256];
141         const char *nis_domain = NULL;
142         char *lowercase_user = NULL;
143
144         if (getdomainname(nis_domain_buf, sizeof(nis_domain_buf)) == 0) {
145                 nis_domain = &nis_domain_buf[0];
146         } else {
147                 DEBUG(5,("Unable to get default yp domain, "
148                         "let's try without specifying it\n"));
149                 nis_domain = NULL;
150         }
151
152         DEBUG(5,("looking for user %s of domain %s in netgroup %s\n",
153                 user, nis_domain ? nis_domain : "(ANY)", ngname));
154
155         if (innetgr(ngname, NULL, user, nis_domain)) {
156                 DEBUG(5,("user_in_netgroup: Found\n"));
157                 return true;
158         }
159
160         /*
161          * Ok, innetgr is case sensitive. Try once more with lowercase
162          * just in case. Attempt to fix #703. JRA.
163          */
164         lowercase_user = talloc_strdup(ctx, user);
165         if (!lowercase_user) {
166                 return false;
167         }
168         if (!strlower_m(lowercase_user)) {
169                 TALLOC_FREE(lowercase_user);
170                 return false;
171         }
172
173         if (strcmp(user,lowercase_user) == 0) {
174                 /* user name was already lower case! */
175                 TALLOC_FREE(lowercase_user);
176                 return false;
177         }
178
179         DEBUG(5,("looking for user %s of domain %s in netgroup %s\n",
180                 lowercase_user, nis_domain ? nis_domain : "(ANY)", ngname));
181
182         if (innetgr(ngname, NULL, lowercase_user, nis_domain)) {
183                 DEBUG(5,("user_in_netgroup: Found\n"));
184                 TALLOC_FREE(lowercase_user);
185                 return true;
186         }
187 #endif /* HAVE_NETGROUP and HAVE_INNETGR */
188         return false;
189 }
190
191 /****************************************************************************
192  Check if a user is in a user list - can check combinations of UNIX
193  and netgroup lists.
194 ****************************************************************************/
195
196 bool user_in_list(TALLOC_CTX *ctx, const char *user, const char * const *list)
197 {
198         if (!list || !*list)
199                 return False;
200
201         DEBUG(10,("user_in_list: checking user %s in list\n", user));
202
203         while (*list) {
204
205                 DEBUG(10,("user_in_list: checking user |%s| against |%s|\n",
206                           user, *list));
207
208                 /*
209                  * Check raw username.
210                  */
211                 if (strequal(user, *list))
212                         return(True);
213
214                 /*
215                  * Now check to see if any combination
216                  * of UNIX and netgroups has been specified.
217                  */
218
219                 if(**list == '@') {
220                         /*
221                          * Old behaviour. Check netgroup list
222                          * followed by UNIX list.
223                          */
224                         if(user_in_netgroup(ctx, user, *list +1))
225                                 return True;
226                         if(user_in_group(user, *list +1))
227                                 return True;
228                 } else if (**list == '+') {
229
230                         if((*(*list +1)) == '&') {
231                                 /*
232                                  * Search UNIX list followed by netgroup.
233                                  */
234                                 if(user_in_group(user, *list +2))
235                                         return True;
236                                 if(user_in_netgroup(ctx, user, *list +2))
237                                         return True;
238
239                         } else {
240
241                                 /*
242                                  * Just search UNIX list.
243                                  */
244
245                                 if(user_in_group(user, *list +1))
246                                         return True;
247                         }
248
249                 } else if (**list == '&') {
250
251                         if(*(*list +1) == '+') {
252                                 /*
253                                  * Search netgroup list followed by UNIX list.
254                                  */
255                                 if(user_in_netgroup(ctx, user, *list +2))
256                                         return True;
257                                 if(user_in_group(user, *list +2))
258                                         return True;
259                         } else {
260                                 /*
261                                  * Just search netgroup list.
262                                  */
263                                 if(user_in_netgroup(ctx, user, *list +1))
264                                         return True;
265                         }
266                 }
267
268                 list++;
269         }
270         return(False);
271 }
272
273 bool map_username(TALLOC_CTX *ctx, const char *user_in, char **p_user_out)
274 {
275         const struct loadparm_substitution *lp_sub =
276                 loadparm_s3_global_substitution();
277         FILE *f;
278         char *mapfile = lp_username_map(talloc_tos(), lp_sub);
279         char *s;
280         char buf[512];
281         bool mapped_user = False;
282         char *cmd = lp_username_map_script(talloc_tos(), lp_sub);
283
284         *p_user_out = NULL;
285
286         if (!user_in)
287                 return false;
288
289         /* Initially make a copy of the incoming name. */
290         *p_user_out = talloc_strdup(ctx, user_in);
291         if (!*p_user_out) {
292                 return false;
293         }
294
295         if (strequal(user_in,get_last_to()))
296                 return false;
297
298         if (strequal(user_in,get_last_from())) {
299                 DEBUG(3,("Mapped user %s to %s\n",user_in,get_last_to()));
300                 TALLOC_FREE(*p_user_out);
301                 *p_user_out = talloc_strdup(ctx, get_last_to());
302                 return true;
303         }
304
305         if (fetch_map_from_gencache(ctx, user_in, p_user_out)) {
306                 return true;
307         }
308
309         /* first try the username map script */
310
311         if ( *cmd ) {
312                 char **qlines;
313                 char *command = NULL;
314                 int numlines, ret, fd;
315
316                 command = talloc_asprintf(ctx,
317                                         "%s \"%s\"",
318                                         cmd,
319                                         user_in);
320                 if (!command) {
321                         return false;
322                 }
323
324                 DEBUG(10,("Running [%s]\n", command));
325                 ret = smbrun(command, &fd, NULL);
326                 DEBUGADD(10,("returned [%d]\n", ret));
327
328                 TALLOC_FREE(command);
329
330                 if ( ret != 0 ) {
331                         if (fd != -1)
332                                 close(fd);
333                         return False;
334                 }
335
336                 numlines = 0;
337                 qlines = fd_lines_load(fd, &numlines, 0, ctx);
338                 DEBUGADD(10,("Lines returned = [%d]\n", numlines));
339                 close(fd);
340
341                 /* should be either no lines or a single line with the mapped username */
342
343                 if (numlines && qlines) {
344                         DEBUG(3,("Mapped user %s to %s\n", user_in, qlines[0] ));
345                         set_last_from_to(user_in, qlines[0]);
346                         store_map_in_gencache(ctx, user_in, qlines[0]);
347                         TALLOC_FREE(*p_user_out);
348                         *p_user_out = talloc_strdup(ctx, qlines[0]);
349                         if (!*p_user_out) {
350                                 return false;
351                         }
352                 }
353
354                 TALLOC_FREE(qlines);
355
356                 return numlines != 0;
357         }
358
359         /* ok.  let's try the mapfile */
360         if (!*mapfile)
361                 return False;
362
363         f = fopen(mapfile, "r");
364         if (!f) {
365                 DEBUG(0,("can't open username map %s. Error %s\n",mapfile, strerror(errno) ));
366                 return False;
367         }
368
369         DEBUG(4,("Scanning username map %s\n",mapfile));
370
371         while((s=fgets_slash(NULL,buf,sizeof(buf),f))!=NULL) {
372                 char *unixname = s;
373                 char *dosname = strchr_m(unixname,'=');
374                 char **dosuserlist;
375                 bool return_if_mapped = False;
376
377                 if (!dosname)
378                         continue;
379
380                 *dosname++ = 0;
381
382                 unixname = skip_space(unixname);
383
384                 if ('!' == *unixname) {
385                         return_if_mapped = True;
386                         unixname = skip_space(unixname+1);
387                 }
388
389                 if (!*unixname || strchr_m("#;",*unixname))
390                         continue;
391
392                 {
393                         int l = strlen(unixname);
394                         while (l && isspace((int)unixname[l-1])) {
395                                 unixname[l-1] = 0;
396                                 l--;
397                         }
398                 }
399
400                 /* skip lines like 'user = ' */
401
402                 dosuserlist = str_list_make_v3(ctx, dosname, NULL);
403                 if (!dosuserlist) {
404                         DEBUG(0,("Bad username map entry.  Unable to build user list.  Ignoring.\n"));
405                         continue;
406                 }
407
408                 if (strchr_m(dosname,'*') ||
409                     user_in_list(ctx, user_in, (const char * const *)dosuserlist)) {
410                         DEBUG(3,("Mapped user %s to %s\n",user_in,unixname));
411                         mapped_user = True;
412
413                         set_last_from_to(user_in, unixname);
414                         store_map_in_gencache(ctx, user_in, unixname);
415                         TALLOC_FREE(*p_user_out);
416                         *p_user_out = talloc_strdup(ctx, unixname);
417                         if (!*p_user_out) {
418                                 TALLOC_FREE(dosuserlist);
419                                 fclose(f);
420                                 return false;
421                         }
422
423                         if ( return_if_mapped ) {
424                                 TALLOC_FREE(dosuserlist);
425                                 fclose(f);
426                                 return True;
427                         }
428                 }
429
430                 TALLOC_FREE(dosuserlist);
431         }
432
433         fclose(f);
434
435         /*
436          * If we didn't successfully map a user in the loop above,
437          * setup the last_from and last_to as an optimization so
438          * that we don't scan the file again for the same user.
439          */
440         if (!mapped_user) {
441                 DEBUG(8, ("The user '%s' has no mapping. "
442                           "Skip it next time.\n", user_in));
443                 set_last_from_to(user_in, user_in);
444                 store_map_in_gencache(ctx, user_in, user_in);
445         }
446
447         return mapped_user;
448 }