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