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