Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * ProFTPD - FTP server daemon |
3 | | * Copyright (c) 1997, 1998 Public Flood Software |
4 | | * Copyright (c) 1999, 2000 MacGyver aka Habeeb J. Dihu <macgyver@tos.net> |
5 | | * Copyright (c) 2001-2022 The ProFTPD Project team |
6 | | * |
7 | | * This program is free software; you can redistribute it and/or modify |
8 | | * it under the terms of the GNU General Public License as published by |
9 | | * the Free Software Foundation; either version 2 of the License, or |
10 | | * (at your option) any later version. |
11 | | * |
12 | | * This program is distributed in the hope that it will be useful, |
13 | | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
14 | | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
15 | | * GNU General Public License for more details. |
16 | | * |
17 | | * You should have received a copy of the GNU General Public License |
18 | | * along with this program; if not, write to the Free Software |
19 | | * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA. |
20 | | * |
21 | | * As a special exemption, Public Flood Software/MacGyver aka Habeeb J. Dihu |
22 | | * and other respective copyright holders give permission to link this program |
23 | | * with OpenSSL, and distribute the resulting executable, without including |
24 | | * the source code for OpenSSL in the source distribution. |
25 | | */ |
26 | | |
27 | | /* Authentication front-end for ProFTPD. */ |
28 | | |
29 | | #include "conf.h" |
30 | | #include "privs.h" |
31 | | #include "error.h" |
32 | | #include "openbsd-blowfish.h" |
33 | | |
34 | | static pool *auth_pool = NULL; |
35 | | static size_t auth_max_passwd_len = PR_TUNABLE_PASSWORD_MAX; |
36 | | static pr_table_t *auth_tab = NULL, *uid_tab = NULL, *user_tab = NULL, |
37 | | *gid_tab = NULL, *group_tab = NULL; |
38 | | static xaset_t *auth_module_list = NULL; |
39 | | |
40 | | struct auth_module_elt { |
41 | | struct auth_module_elt *next, *prev; |
42 | | const char *name; |
43 | | }; |
44 | | |
45 | | static const char *trace_channel = "auth"; |
46 | | |
47 | | /* Caching of ID-to-name lookups, for both UIDs and GIDs, is enabled by |
48 | | * default. |
49 | | */ |
50 | | static unsigned int auth_caching = PR_AUTH_CACHE_FL_DEFAULT; |
51 | | |
52 | | /* Key comparison callback for the uidcache and gidcache. */ |
53 | | static int uid_keycmp_cb(const void *key1, size_t keysz1, |
54 | 0 | const void *key2, size_t keysz2) { |
55 | | |
56 | | /* Return zero to indicate a match, non-zero otherwise. */ |
57 | 0 | return (*((uid_t *) key1) == *((uid_t *) key2) ? 0 : 1); |
58 | 0 | } |
59 | | |
60 | | static int gid_keycmp_cb(const void *key1, size_t keysz1, |
61 | 0 | const void *key2, size_t keysz2) { |
62 | | |
63 | | /* Return zero to indicate a match, non-zero otherwise. */ |
64 | 0 | return (*((gid_t *) key1) == *((gid_t *) key2) ? 0 : 1); |
65 | 0 | } |
66 | | |
67 | | /* Key "hash" callback for the uidcache and gidcache. */ |
68 | 0 | static unsigned int uid_hash_cb(const void *key, size_t keysz) { |
69 | 0 | uid_t u; |
70 | 0 | unsigned int res; |
71 | |
|
72 | 0 | memcpy(&u, key, keysz); |
73 | 0 | res = (unsigned int) (u << 8); |
74 | |
|
75 | 0 | return res; |
76 | 0 | } |
77 | | |
78 | 0 | static unsigned int gid_hash_cb(const void *key, size_t keysz) { |
79 | 0 | gid_t g; |
80 | 0 | unsigned int res; |
81 | |
|
82 | 0 | memcpy(&g, key, keysz); |
83 | 0 | res = (unsigned int) (g << 8); |
84 | |
|
85 | 0 | return res; |
86 | 0 | } |
87 | | |
88 | 0 | static void uidcache_create(void) { |
89 | 0 | if (uid_tab == NULL && |
90 | 0 | auth_pool != NULL) { |
91 | 0 | uid_tab = pr_table_alloc(auth_pool, 0); |
92 | |
|
93 | 0 | (void) pr_table_ctl(uid_tab, PR_TABLE_CTL_SET_KEY_CMP, |
94 | 0 | (void *) uid_keycmp_cb); |
95 | 0 | (void) pr_table_ctl(uid_tab, PR_TABLE_CTL_SET_KEY_HASH, |
96 | 0 | (void *) uid_hash_cb); |
97 | 0 | } |
98 | 0 | } |
99 | | |
100 | 0 | static void uidcache_add(uid_t uid, const char *name) { |
101 | 0 | uidcache_create(); |
102 | |
|
103 | 0 | if (uid_tab != NULL) { |
104 | 0 | int count; |
105 | |
|
106 | 0 | (void) pr_table_rewind(uid_tab); |
107 | 0 | count = pr_table_kexists(uid_tab, (const void *) &uid, sizeof(uid_t)); |
108 | 0 | if (count <= 0) { |
109 | 0 | uid_t *cache_uid; |
110 | 0 | size_t namelen; |
111 | | |
112 | | /* Allocate memory for a UID out of the ID cache pool, so that this |
113 | | * UID can be used as a key. |
114 | | */ |
115 | 0 | cache_uid = palloc(auth_pool, sizeof(uid_t)); |
116 | 0 | *cache_uid = uid; |
117 | |
|
118 | 0 | namelen = strlen(name); |
119 | |
|
120 | 0 | if (pr_table_kadd(uid_tab, (const void *) cache_uid, sizeof(uid_t), |
121 | 0 | pstrndup(auth_pool, name, namelen), namelen + 1) < 0 && |
122 | 0 | errno != EEXIST) { |
123 | 0 | pr_trace_msg(trace_channel, 3, |
124 | 0 | "error adding name '%s' for UID %s to the uidcache: %s", name, |
125 | 0 | pr_uid2str(NULL, uid), strerror(errno)); |
126 | |
|
127 | 0 | } else { |
128 | 0 | pr_trace_msg(trace_channel, 5, |
129 | 0 | "stashed name '%s' for UID %s in the uidcache", name, |
130 | 0 | pr_uid2str(NULL, uid)); |
131 | 0 | } |
132 | 0 | } |
133 | 0 | } |
134 | 0 | } |
135 | | |
136 | 0 | static int uidcache_get(uid_t uid, char *name, size_t namesz) { |
137 | 0 | if (uid_tab != NULL) { |
138 | 0 | const void *v = NULL; |
139 | |
|
140 | 0 | v = pr_table_kget(uid_tab, (const void *) &uid, sizeof(uid_t), NULL); |
141 | 0 | if (v != NULL) { |
142 | 0 | memset(name, '\0', namesz); |
143 | 0 | sstrncpy(name, v, namesz); |
144 | |
|
145 | 0 | pr_trace_msg(trace_channel, 8, |
146 | 0 | "using name '%s' from uidcache for UID %s", name, |
147 | 0 | pr_uid2str(NULL, uid)); |
148 | 0 | return 0; |
149 | 0 | } |
150 | | |
151 | 0 | pr_trace_msg(trace_channel, 9, |
152 | 0 | "no value found in uidcache for UID %s: %s", pr_uid2str(NULL, uid), |
153 | 0 | strerror(errno)); |
154 | 0 | } |
155 | | |
156 | 0 | errno = ENOENT; |
157 | 0 | return -1; |
158 | 0 | } |
159 | | |
160 | 0 | static void gidcache_create(void) { |
161 | 0 | if (gid_tab == NULL&& |
162 | 0 | auth_pool != NULL) { |
163 | 0 | gid_tab = pr_table_alloc(auth_pool, 0); |
164 | |
|
165 | 0 | (void) pr_table_ctl(gid_tab, PR_TABLE_CTL_SET_KEY_CMP, |
166 | 0 | (void *) gid_keycmp_cb); |
167 | 0 | (void) pr_table_ctl(gid_tab, PR_TABLE_CTL_SET_KEY_HASH, |
168 | 0 | (void *) gid_hash_cb); |
169 | 0 | } |
170 | 0 | } |
171 | | |
172 | 0 | static void gidcache_add(gid_t gid, const char *name) { |
173 | 0 | gidcache_create(); |
174 | |
|
175 | 0 | if (gid_tab != NULL) { |
176 | 0 | int count; |
177 | |
|
178 | 0 | (void) pr_table_rewind(gid_tab); |
179 | 0 | count = pr_table_kexists(gid_tab, (const void *) &gid, sizeof(gid_t)); |
180 | 0 | if (count <= 0) { |
181 | 0 | gid_t *cache_gid; |
182 | 0 | size_t namelen; |
183 | | |
184 | | /* Allocate memory for a GID out of the ID cache pool, so that this |
185 | | * GID can be used as a key. |
186 | | */ |
187 | 0 | cache_gid = palloc(auth_pool, sizeof(gid_t)); |
188 | 0 | *cache_gid = gid; |
189 | |
|
190 | 0 | namelen = strlen(name); |
191 | |
|
192 | 0 | if (pr_table_kadd(gid_tab, (const void *) cache_gid, sizeof(gid_t), |
193 | 0 | pstrndup(auth_pool, name, namelen), namelen + 1) < 0 && |
194 | 0 | errno != EEXIST) { |
195 | 0 | pr_trace_msg(trace_channel, 3, |
196 | 0 | "error adding name '%s' for GID %s to the gidcache: %s", name, |
197 | 0 | pr_gid2str(NULL, gid), strerror(errno)); |
198 | |
|
199 | 0 | } else { |
200 | 0 | pr_trace_msg(trace_channel, 5, |
201 | 0 | "stashed name '%s' for GID %s in the gidcache", name, |
202 | 0 | pr_gid2str(NULL, gid)); |
203 | 0 | } |
204 | 0 | } |
205 | 0 | } |
206 | 0 | } |
207 | | |
208 | 0 | static int gidcache_get(gid_t gid, char *name, size_t namesz) { |
209 | 0 | if (gid_tab != NULL) { |
210 | 0 | const void *v = NULL; |
211 | |
|
212 | 0 | v = pr_table_kget(gid_tab, (const void *) &gid, sizeof(gid_t), NULL); |
213 | 0 | if (v != NULL) { |
214 | 0 | memset(name, '\0', namesz); |
215 | 0 | sstrncpy(name, v, namesz); |
216 | |
|
217 | 0 | pr_trace_msg(trace_channel, 8, |
218 | 0 | "using name '%s' from gidcache for GID %s", name, |
219 | 0 | pr_gid2str(NULL, gid)); |
220 | 0 | return 0; |
221 | 0 | } |
222 | | |
223 | 0 | pr_trace_msg(trace_channel, 9, |
224 | 0 | "no value found in gidcache for GID %s: %s", pr_gid2str(NULL, gid), |
225 | 0 | strerror(errno)); |
226 | 0 | } |
227 | | |
228 | 0 | errno = ENOENT; |
229 | 0 | return -1; |
230 | 0 | } |
231 | | |
232 | 0 | static void usercache_create(void) { |
233 | 0 | if (user_tab == NULL && |
234 | 0 | auth_pool != NULL) { |
235 | 0 | user_tab = pr_table_alloc(auth_pool, 0); |
236 | 0 | } |
237 | 0 | } |
238 | | |
239 | 0 | static void usercache_add(const char *name, uid_t uid) { |
240 | 0 | usercache_create(); |
241 | |
|
242 | 0 | if (user_tab != NULL) { |
243 | 0 | int count; |
244 | |
|
245 | 0 | (void) pr_table_rewind(user_tab); |
246 | 0 | count = pr_table_exists(user_tab, name); |
247 | 0 | if (count <= 0) { |
248 | 0 | const char *cache_name; |
249 | 0 | uid_t *cache_key; |
250 | | |
251 | | /* Allocate memory for a key out of the ID cache pool, so that this |
252 | | * name can be used as a key. |
253 | | */ |
254 | 0 | cache_name = pstrdup(auth_pool, name); |
255 | 0 | cache_key = palloc(auth_pool, sizeof(uid_t)); |
256 | 0 | *cache_key = uid; |
257 | |
|
258 | 0 | if (pr_table_add(user_tab, cache_name, cache_key, sizeof(uid_t)) < 0 && |
259 | 0 | errno != EEXIST) { |
260 | 0 | pr_trace_msg(trace_channel, 3, |
261 | 0 | "error adding UID %s for user '%s' to the usercache: %s", |
262 | 0 | pr_uid2str(NULL, uid), name, strerror(errno)); |
263 | |
|
264 | 0 | } else { |
265 | 0 | pr_trace_msg(trace_channel, 5, |
266 | 0 | "stashed UID %s for user '%s' in the usercache", |
267 | 0 | pr_uid2str(NULL, uid), name); |
268 | 0 | } |
269 | 0 | } |
270 | 0 | } |
271 | 0 | } |
272 | | |
273 | 0 | static int usercache_get(const char *name, uid_t *uid) { |
274 | 0 | if (user_tab != NULL) { |
275 | 0 | const void *v = NULL; |
276 | |
|
277 | 0 | v = pr_table_get(user_tab, name, NULL); |
278 | 0 | if (v != NULL) { |
279 | 0 | *uid = *((uid_t *) v); |
280 | |
|
281 | 0 | pr_trace_msg(trace_channel, 8, |
282 | 0 | "using UID %s for user '%s' from usercache", pr_uid2str(NULL, *uid), |
283 | 0 | name); |
284 | 0 | return 0; |
285 | 0 | } |
286 | | |
287 | 0 | pr_trace_msg(trace_channel, 9, |
288 | 0 | "no value found in usercache for user '%s': %s", name, strerror(errno)); |
289 | 0 | } |
290 | | |
291 | 0 | errno = ENOENT; |
292 | 0 | return -1; |
293 | 0 | } |
294 | | |
295 | 0 | static void groupcache_create(void) { |
296 | 0 | if (group_tab == NULL && |
297 | 0 | auth_pool != NULL) { |
298 | 0 | group_tab = pr_table_alloc(auth_pool, 0); |
299 | 0 | } |
300 | 0 | } |
301 | | |
302 | 0 | static void groupcache_add(const char *name, gid_t gid) { |
303 | 0 | groupcache_create(); |
304 | |
|
305 | 0 | if (group_tab != NULL) { |
306 | 0 | int count; |
307 | |
|
308 | 0 | (void) pr_table_rewind(group_tab); |
309 | 0 | count = pr_table_exists(group_tab, name); |
310 | 0 | if (count <= 0) { |
311 | 0 | const char *cache_name; |
312 | 0 | gid_t *cache_key; |
313 | | |
314 | | /* Allocate memory for a key out of the ID cache pool, so that this |
315 | | * name can be used as a key. |
316 | | */ |
317 | 0 | cache_name = pstrdup(auth_pool, name); |
318 | 0 | cache_key = palloc(auth_pool, sizeof(gid_t)); |
319 | 0 | *cache_key = gid; |
320 | |
|
321 | 0 | if (pr_table_add(group_tab, cache_name, cache_key, sizeof(gid_t)) < 0 && |
322 | 0 | errno != EEXIST) { |
323 | 0 | pr_trace_msg(trace_channel, 3, |
324 | 0 | "error adding GID %s for group '%s' to the groupcache: %s", |
325 | 0 | pr_gid2str(NULL, gid), name, strerror(errno)); |
326 | |
|
327 | 0 | } else { |
328 | 0 | pr_trace_msg(trace_channel, 5, |
329 | 0 | "stashed GID %s for group '%s' in the groupcache", |
330 | 0 | pr_gid2str(NULL, gid), name); |
331 | 0 | } |
332 | 0 | } |
333 | 0 | } |
334 | 0 | } |
335 | | |
336 | 0 | static int groupcache_get(const char *name, gid_t *gid) { |
337 | 0 | if (group_tab != NULL) { |
338 | 0 | const void *v = NULL; |
339 | |
|
340 | 0 | v = pr_table_get(group_tab, name, NULL); |
341 | 0 | if (v != NULL) { |
342 | 0 | *gid = *((gid_t *) v); |
343 | |
|
344 | 0 | pr_trace_msg(trace_channel, 8, |
345 | 0 | "using GID %s for group '%s' from groupcache", pr_gid2str(NULL, *gid), |
346 | 0 | name); |
347 | 0 | return 0; |
348 | 0 | } |
349 | | |
350 | 0 | pr_trace_msg(trace_channel, 9, |
351 | 0 | "no value found in groupcache for group '%s': %s", name, strerror(errno)); |
352 | 0 | } |
353 | | |
354 | 0 | errno = ENOENT; |
355 | 0 | return -1; |
356 | 0 | } |
357 | | |
358 | | /* The difference between this function, and pr_cmd_alloc(), is that this |
359 | | * allocates the cmd_rec directly from the given pool, whereas pr_cmd_alloc() |
360 | | * will allocate a subpool from the given pool, and allocate its cmd_rec |
361 | | * from the subpool. This means that pr_cmd_alloc()'s cmd_rec's can be |
362 | | * subsequently destroyed easily; this function's cmd_rec's will be destroyed |
363 | | * when the given pool is destroyed. |
364 | | */ |
365 | 0 | static cmd_rec *make_cmd(pool *cp, unsigned int argc, ...) { |
366 | 0 | va_list args; |
367 | 0 | cmd_rec *c; |
368 | 0 | pool *sub_pool, *tmp_pool; |
369 | |
|
370 | 0 | c = pcalloc(cp, sizeof(cmd_rec)); |
371 | 0 | c->argc = argc; |
372 | 0 | c->stash_index = -1; |
373 | 0 | c->stash_hash = 0; |
374 | |
|
375 | 0 | if (argc > 0) { |
376 | 0 | register unsigned int i; |
377 | |
|
378 | 0 | c->argv = pcalloc(cp, sizeof(void *) * (argc + 1)); |
379 | |
|
380 | 0 | va_start(args, argc); |
381 | |
|
382 | 0 | for (i = 0; i < argc; i++) { |
383 | 0 | c->argv[i] = (void *) va_arg(args, char *); |
384 | 0 | } |
385 | |
|
386 | 0 | va_end(args); |
387 | |
|
388 | 0 | c->argv[argc] = NULL; |
389 | 0 | } |
390 | | |
391 | | /* Make sure we provide pool and tmp_pool for the consumers. */ |
392 | 0 | sub_pool = make_sub_pool(cp); |
393 | 0 | pr_pool_tag(sub_pool, "auth cmd subpool"); |
394 | 0 | c->pool = sub_pool; |
395 | |
|
396 | 0 | tmp_pool = make_sub_pool(c->pool); |
397 | 0 | pr_pool_tag(tmp_pool, "auth cmd tmp pool"); |
398 | 0 | c->tmp_pool = tmp_pool; |
399 | |
|
400 | 0 | return c; |
401 | 0 | } |
402 | | |
403 | 0 | static modret_t *dispatch_auth(cmd_rec *cmd, char *match, module **m) { |
404 | 0 | authtable *start_tab = NULL, *iter_tab = NULL; |
405 | 0 | modret_t *mr = NULL; |
406 | |
|
407 | 0 | start_tab = pr_stash_get_symbol2(PR_SYM_AUTH, match, NULL, |
408 | 0 | &cmd->stash_index, &cmd->stash_hash); |
409 | 0 | if (start_tab == NULL) { |
410 | 0 | int xerrno = errno; |
411 | |
|
412 | 0 | pr_trace_msg(trace_channel, 1, "error finding start symbol for '%s': %s", |
413 | 0 | match, strerror(xerrno)); |
414 | 0 | return PR_ERROR_MSG(cmd, NULL, strerror(xerrno)); |
415 | 0 | } |
416 | | |
417 | 0 | iter_tab = start_tab; |
418 | |
|
419 | 0 | while (iter_tab) { |
420 | 0 | pr_signals_handle(); |
421 | |
|
422 | 0 | if (m && *m && *m != iter_tab->m) { |
423 | 0 | goto next; |
424 | 0 | } |
425 | | |
426 | 0 | pr_trace_msg(trace_channel, 6, |
427 | 0 | "dispatching auth request \"%s\" to module mod_%s", |
428 | 0 | match, iter_tab->m->name); |
429 | |
|
430 | 0 | mr = pr_module_call(iter_tab->m, iter_tab->handler, cmd); |
431 | | |
432 | | /* Return a pointer, if requested, to the module which answered the |
433 | | * auth request. This is used, for example, by auth_getpwnam() for |
434 | | * associating the answering auth module with the data looked up. |
435 | | */ |
436 | |
|
437 | 0 | if (iter_tab->auth_flags & PR_AUTH_FL_REQUIRED) { |
438 | 0 | pr_trace_msg(trace_channel, 6, |
439 | 0 | "\"%s\" response from module mod_%s is authoritative", match, |
440 | 0 | iter_tab->m->name); |
441 | |
|
442 | 0 | if (m) { |
443 | 0 | *m = iter_tab->m; |
444 | 0 | } |
445 | |
|
446 | 0 | break; |
447 | 0 | } |
448 | | |
449 | 0 | if (MODRET_ISHANDLED(mr) || |
450 | 0 | MODRET_ISERROR(mr)) { |
451 | |
|
452 | 0 | if (m) { |
453 | 0 | *m = iter_tab->m; |
454 | 0 | } |
455 | |
|
456 | 0 | break; |
457 | 0 | } |
458 | | |
459 | 0 | next: |
460 | 0 | iter_tab = pr_stash_get_symbol2(PR_SYM_AUTH, match, iter_tab, |
461 | 0 | &cmd->stash_index, &cmd->stash_hash); |
462 | |
|
463 | 0 | if (iter_tab == start_tab) { |
464 | | /* We have looped back to the start. Break out now and do not loop |
465 | | * around again (and again, and again...) |
466 | | */ |
467 | 0 | pr_trace_msg(trace_channel, 15, "reached end of symbols for '%s'", match); |
468 | 0 | mr = PR_DECLINED(cmd); |
469 | 0 | break; |
470 | 0 | } |
471 | 0 | } |
472 | | |
473 | 0 | return mr; |
474 | 0 | } |
475 | | |
476 | 0 | void pr_auth_setpwent(pool *p) { |
477 | 0 | cmd_rec *cmd = NULL; |
478 | |
|
479 | 0 | cmd = make_cmd(p, 0); |
480 | 0 | (void) dispatch_auth(cmd, "setpwent", NULL); |
481 | |
|
482 | 0 | if (cmd->tmp_pool != NULL) { |
483 | 0 | destroy_pool(cmd->tmp_pool); |
484 | 0 | cmd->tmp_pool = NULL; |
485 | 0 | } |
486 | 0 | } |
487 | | |
488 | 0 | void pr_auth_endpwent(pool *p) { |
489 | 0 | cmd_rec *cmd = NULL; |
490 | |
|
491 | 0 | cmd = make_cmd(p, 0); |
492 | 0 | (void) dispatch_auth(cmd, "endpwent", NULL); |
493 | |
|
494 | 0 | if (cmd->tmp_pool != NULL) { |
495 | 0 | destroy_pool(cmd->tmp_pool); |
496 | 0 | cmd->tmp_pool = NULL; |
497 | 0 | } |
498 | |
|
499 | 0 | if (auth_tab != NULL) { |
500 | 0 | int item_count; |
501 | |
|
502 | 0 | item_count = pr_table_count(auth_tab); |
503 | 0 | pr_trace_msg(trace_channel, 5, "emptying authcache (%d %s)", item_count, |
504 | 0 | item_count != 1 ? "items" : "item"); |
505 | |
|
506 | 0 | (void) pr_table_empty(auth_tab); |
507 | 0 | (void) pr_table_free(auth_tab); |
508 | 0 | auth_tab = NULL; |
509 | 0 | } |
510 | 0 | } |
511 | | |
512 | 0 | void pr_auth_setgrent(pool *p) { |
513 | 0 | cmd_rec *cmd = NULL; |
514 | |
|
515 | 0 | cmd = make_cmd(p, 0); |
516 | 0 | (void) dispatch_auth(cmd, "setgrent", NULL); |
517 | |
|
518 | 0 | if (cmd->tmp_pool != NULL) { |
519 | 0 | destroy_pool(cmd->tmp_pool); |
520 | 0 | cmd->tmp_pool = NULL; |
521 | 0 | } |
522 | 0 | } |
523 | | |
524 | 0 | void pr_auth_endgrent(pool *p) { |
525 | 0 | cmd_rec *cmd = NULL; |
526 | |
|
527 | 0 | cmd = make_cmd(p, 0); |
528 | 0 | (void) dispatch_auth(cmd, "endgrent", NULL); |
529 | |
|
530 | 0 | if (cmd->tmp_pool != NULL) { |
531 | 0 | destroy_pool(cmd->tmp_pool); |
532 | 0 | cmd->tmp_pool = NULL; |
533 | 0 | } |
534 | 0 | } |
535 | | |
536 | 0 | struct passwd *pr_auth_getpwent(pool *p) { |
537 | 0 | cmd_rec *cmd = NULL; |
538 | 0 | modret_t *mr = NULL; |
539 | 0 | struct passwd *res = NULL; |
540 | |
|
541 | 0 | if (p == NULL) { |
542 | 0 | errno = EINVAL; |
543 | 0 | return NULL; |
544 | 0 | } |
545 | | |
546 | 0 | cmd = make_cmd(p, 0); |
547 | 0 | mr = dispatch_auth(cmd, "getpwent", NULL); |
548 | |
|
549 | 0 | if (MODRET_ISHANDLED(mr) && |
550 | 0 | MODRET_HASDATA(mr)) { |
551 | 0 | res = mr->data; |
552 | 0 | } |
553 | |
|
554 | 0 | if (cmd->tmp_pool != NULL) { |
555 | 0 | destroy_pool(cmd->tmp_pool); |
556 | 0 | cmd->tmp_pool = NULL; |
557 | 0 | } |
558 | | |
559 | | /* Sanity check */ |
560 | 0 | if (res == NULL) { |
561 | 0 | return NULL; |
562 | 0 | } |
563 | | |
564 | | /* Make sure the UID and GID are not -1 */ |
565 | 0 | if (res->pw_uid == (uid_t) -1) { |
566 | 0 | pr_log_pri(PR_LOG_WARNING, "error: UID of -1 not allowed"); |
567 | 0 | errno = ENOENT; |
568 | 0 | return NULL; |
569 | 0 | } |
570 | | |
571 | 0 | if (res->pw_gid == (gid_t) -1) { |
572 | 0 | pr_log_pri(PR_LOG_WARNING, "error: GID of -1 not allowed"); |
573 | 0 | errno = ENOENT; |
574 | 0 | return NULL; |
575 | 0 | } |
576 | | |
577 | 0 | return res; |
578 | 0 | } |
579 | | |
580 | 0 | struct group *pr_auth_getgrent(pool *p) { |
581 | 0 | cmd_rec *cmd = NULL; |
582 | 0 | modret_t *mr = NULL; |
583 | 0 | struct group *res = NULL; |
584 | |
|
585 | 0 | if (p == NULL) { |
586 | 0 | errno = EINVAL; |
587 | 0 | return NULL; |
588 | 0 | } |
589 | | |
590 | 0 | cmd = make_cmd(p, 0); |
591 | 0 | mr = dispatch_auth(cmd, "getgrent", NULL); |
592 | |
|
593 | 0 | if (MODRET_ISHANDLED(mr) && |
594 | 0 | MODRET_HASDATA(mr)) { |
595 | 0 | res = mr->data; |
596 | 0 | } |
597 | |
|
598 | 0 | if (cmd->tmp_pool != NULL) { |
599 | 0 | destroy_pool(cmd->tmp_pool); |
600 | 0 | cmd->tmp_pool = NULL; |
601 | 0 | } |
602 | | |
603 | | /* Sanity check */ |
604 | 0 | if (res == NULL) { |
605 | 0 | return NULL; |
606 | 0 | } |
607 | | |
608 | | /* Make sure the GID is not -1 */ |
609 | 0 | if (res->gr_gid == (gid_t) -1) { |
610 | 0 | pr_log_pri(PR_LOG_WARNING, "error: GID of -1 not allowed"); |
611 | 0 | errno = ENOENT; |
612 | 0 | return NULL; |
613 | 0 | } |
614 | | |
615 | 0 | return res; |
616 | 0 | } |
617 | | |
618 | 0 | struct passwd *pr_auth_getpwnam(pool *p, const char *name) { |
619 | 0 | cmd_rec *cmd = NULL; |
620 | 0 | modret_t *mr = NULL; |
621 | 0 | struct passwd *res = NULL; |
622 | 0 | module *m = NULL; |
623 | |
|
624 | 0 | if (p == NULL || |
625 | 0 | name == NULL) { |
626 | 0 | errno = EINVAL; |
627 | 0 | return NULL; |
628 | 0 | } |
629 | | |
630 | 0 | cmd = make_cmd(p, 1, name); |
631 | 0 | mr = dispatch_auth(cmd, "getpwnam", &m); |
632 | |
|
633 | 0 | if (MODRET_ISHANDLED(mr) && |
634 | 0 | MODRET_HASDATA(mr)) { |
635 | 0 | res = mr->data; |
636 | 0 | } |
637 | |
|
638 | 0 | if (cmd->tmp_pool) { |
639 | 0 | destroy_pool(cmd->tmp_pool); |
640 | 0 | cmd->tmp_pool = NULL; |
641 | 0 | } |
642 | | |
643 | | /* Sanity check */ |
644 | 0 | if (res == NULL) { |
645 | 0 | errno = ENOENT; |
646 | 0 | return NULL; |
647 | 0 | } |
648 | | |
649 | | /* Make sure the UID and GID are not -1 */ |
650 | 0 | if (res->pw_uid == (uid_t) -1) { |
651 | 0 | pr_log_pri(PR_LOG_WARNING, "error: UID of -1 not allowed"); |
652 | 0 | errno = ENOENT; |
653 | 0 | return NULL; |
654 | 0 | } |
655 | | |
656 | 0 | if (res->pw_gid == (gid_t) -1) { |
657 | 0 | pr_log_pri(PR_LOG_WARNING, "error: GID of -1 not allowed"); |
658 | 0 | errno = ENOENT; |
659 | 0 | return NULL; |
660 | 0 | } |
661 | | |
662 | 0 | if ((auth_caching & PR_AUTH_CACHE_FL_AUTH_MODULE) && |
663 | 0 | !auth_tab && |
664 | 0 | auth_pool) { |
665 | 0 | auth_tab = pr_table_alloc(auth_pool, 0); |
666 | 0 | } |
667 | |
|
668 | 0 | if (m && |
669 | 0 | auth_tab) { |
670 | 0 | int count = 0; |
671 | 0 | void *value = NULL; |
672 | |
|
673 | 0 | value = palloc(auth_pool, sizeof(module *)); |
674 | 0 | *((module **) value) = m; |
675 | |
|
676 | 0 | count = pr_table_exists(auth_tab, name); |
677 | 0 | if (count <= 0) { |
678 | 0 | if (pr_table_add(auth_tab, pstrdup(auth_pool, name), value, |
679 | 0 | sizeof(module *)) < 0) { |
680 | 0 | pr_trace_msg(trace_channel, 3, |
681 | 0 | "error adding module 'mod_%s.c' for user '%s' to the authcache: %s", |
682 | 0 | m->name, name, strerror(errno)); |
683 | |
|
684 | 0 | } else { |
685 | 0 | pr_trace_msg(trace_channel, 5, |
686 | 0 | "stashed module 'mod_%s.c' for user '%s' in the authcache", |
687 | 0 | m->name, name); |
688 | 0 | } |
689 | |
|
690 | 0 | } else { |
691 | 0 | if (pr_table_set(auth_tab, pstrdup(auth_pool, name), value, |
692 | 0 | sizeof(module *)) < 0) { |
693 | 0 | pr_trace_msg(trace_channel, 3, |
694 | 0 | "error setting module 'mod_%s.c' for user '%s' in the authcache: %s", |
695 | 0 | m->name, name, strerror(errno)); |
696 | |
|
697 | 0 | } else { |
698 | 0 | pr_trace_msg(trace_channel, 5, |
699 | 0 | "stashed module 'mod_%s.c' for user '%s' in the authcache", |
700 | 0 | m->name, name); |
701 | 0 | } |
702 | 0 | } |
703 | 0 | } |
704 | |
|
705 | 0 | if (auth_caching & PR_AUTH_CACHE_FL_UID2NAME) { |
706 | 0 | uidcache_add(res->pw_uid, res->pw_name); |
707 | 0 | } |
708 | |
|
709 | 0 | if (auth_caching & PR_AUTH_CACHE_FL_NAME2UID) { |
710 | 0 | usercache_add(res->pw_name, res->pw_uid); |
711 | 0 | } |
712 | | |
713 | | /* Get the (possibly rewritten) home directory. */ |
714 | 0 | res->pw_dir = (char *) pr_auth_get_home(p, res->pw_dir); |
715 | |
|
716 | 0 | pr_log_debug(DEBUG10, "retrieved UID %s for user '%s'", |
717 | 0 | pr_uid2str(NULL, res->pw_uid), name); |
718 | 0 | return res; |
719 | 0 | } |
720 | | |
721 | 0 | struct passwd *pr_auth_getpwuid(pool *p, uid_t uid) { |
722 | 0 | cmd_rec *cmd = NULL; |
723 | 0 | modret_t *mr = NULL; |
724 | 0 | struct passwd *res = NULL; |
725 | |
|
726 | 0 | if (p == NULL) { |
727 | 0 | errno = EINVAL; |
728 | 0 | return NULL; |
729 | 0 | } |
730 | | |
731 | 0 | cmd = make_cmd(p, 1, (void *) &uid); |
732 | 0 | mr = dispatch_auth(cmd, "getpwuid", NULL); |
733 | |
|
734 | 0 | if (MODRET_ISHANDLED(mr) && |
735 | 0 | MODRET_HASDATA(mr)) { |
736 | 0 | res = mr->data; |
737 | 0 | } |
738 | |
|
739 | 0 | if (cmd->tmp_pool) { |
740 | 0 | destroy_pool(cmd->tmp_pool); |
741 | 0 | cmd->tmp_pool = NULL; |
742 | 0 | } |
743 | | |
744 | | /* Sanity check */ |
745 | 0 | if (res == NULL) { |
746 | 0 | errno = ENOENT; |
747 | 0 | return NULL; |
748 | 0 | } |
749 | | |
750 | | /* Make sure the UID and GID are not -1 */ |
751 | 0 | if (res->pw_uid == (uid_t) -1) { |
752 | 0 | pr_log_pri(PR_LOG_WARNING, "error: UID of -1 not allowed"); |
753 | 0 | errno = ENOENT; |
754 | 0 | return NULL; |
755 | 0 | } |
756 | | |
757 | 0 | if (res->pw_gid == (gid_t) -1) { |
758 | 0 | pr_log_pri(PR_LOG_WARNING, "error: GID of -1 not allowed"); |
759 | 0 | errno = ENOENT; |
760 | 0 | return NULL; |
761 | 0 | } |
762 | | |
763 | 0 | pr_log_debug(DEBUG10, "retrieved user '%s' for UID %s", |
764 | 0 | res->pw_name, pr_uid2str(NULL, uid)); |
765 | 0 | return res; |
766 | 0 | } |
767 | | |
768 | 0 | struct group *pr_auth_getgrnam(pool *p, const char *name) { |
769 | 0 | cmd_rec *cmd = NULL; |
770 | 0 | modret_t *mr = NULL; |
771 | 0 | struct group *res = NULL; |
772 | |
|
773 | 0 | if (p == NULL || |
774 | 0 | name == NULL) { |
775 | 0 | errno = EINVAL; |
776 | 0 | return NULL; |
777 | 0 | } |
778 | | |
779 | 0 | cmd = make_cmd(p, 1, name); |
780 | 0 | mr = dispatch_auth(cmd, "getgrnam", NULL); |
781 | |
|
782 | 0 | if (MODRET_ISHANDLED(mr) && |
783 | 0 | MODRET_HASDATA(mr)) { |
784 | 0 | res = mr->data; |
785 | 0 | } |
786 | |
|
787 | 0 | if (cmd->tmp_pool) { |
788 | 0 | destroy_pool(cmd->tmp_pool); |
789 | 0 | cmd->tmp_pool = NULL; |
790 | 0 | } |
791 | | |
792 | | /* Sanity check */ |
793 | 0 | if (res == NULL) { |
794 | 0 | errno = ENOENT; |
795 | 0 | return NULL; |
796 | 0 | } |
797 | | |
798 | | /* Make sure the GID is not -1 */ |
799 | 0 | if (res->gr_gid == (gid_t) -1) { |
800 | 0 | pr_log_pri(PR_LOG_WARNING, "error: GID of -1 not allowed"); |
801 | 0 | errno = ENOENT; |
802 | 0 | return NULL; |
803 | 0 | } |
804 | | |
805 | 0 | if (auth_caching & PR_AUTH_CACHE_FL_GID2NAME) { |
806 | 0 | gidcache_add(res->gr_gid, name); |
807 | 0 | } |
808 | |
|
809 | 0 | if (auth_caching & PR_AUTH_CACHE_FL_NAME2GID) { |
810 | 0 | groupcache_add(name, res->gr_gid); |
811 | 0 | } |
812 | |
|
813 | 0 | pr_log_debug(DEBUG10, "retrieved GID %s for group '%s'", |
814 | 0 | pr_gid2str(NULL, res->gr_gid), name); |
815 | 0 | return res; |
816 | 0 | } |
817 | | |
818 | 0 | struct group *pr_auth_getgrgid(pool *p, gid_t gid) { |
819 | 0 | cmd_rec *cmd = NULL; |
820 | 0 | modret_t *mr = NULL; |
821 | 0 | struct group *res = NULL; |
822 | |
|
823 | 0 | if (p == NULL) { |
824 | 0 | errno = EINVAL; |
825 | 0 | return NULL; |
826 | 0 | } |
827 | | |
828 | 0 | cmd = make_cmd(p, 1, (void *) &gid); |
829 | 0 | mr = dispatch_auth(cmd, "getgrgid", NULL); |
830 | |
|
831 | 0 | if (MODRET_ISHANDLED(mr) && |
832 | 0 | MODRET_HASDATA(mr)) { |
833 | 0 | res = mr->data; |
834 | 0 | } |
835 | |
|
836 | 0 | if (cmd->tmp_pool) { |
837 | 0 | destroy_pool(cmd->tmp_pool); |
838 | 0 | cmd->tmp_pool = NULL; |
839 | 0 | } |
840 | | |
841 | | /* Sanity check */ |
842 | 0 | if (res == NULL) { |
843 | 0 | errno = ENOENT; |
844 | 0 | return NULL; |
845 | 0 | } |
846 | | |
847 | | /* Make sure the GID is not -1 */ |
848 | 0 | if (res->gr_gid == (gid_t) -1) { |
849 | 0 | pr_log_pri(PR_LOG_WARNING, "error: GID of -1 not allowed"); |
850 | 0 | errno = ENOENT; |
851 | 0 | return NULL; |
852 | 0 | } |
853 | | |
854 | 0 | pr_log_debug(DEBUG10, "retrieved group '%s' for GID %lu", |
855 | 0 | res->gr_name, (unsigned long) gid); |
856 | 0 | return res; |
857 | 0 | } |
858 | | |
859 | 0 | static const char *get_authcode_str(int auth_code) { |
860 | 0 | const char *name = "(unknown)"; |
861 | |
|
862 | 0 | switch (auth_code) { |
863 | 0 | case PR_AUTH_OK_NO_PASS: |
864 | 0 | name = "OK_NO_PASS"; |
865 | 0 | break; |
866 | | |
867 | 0 | case PR_AUTH_RFC2228_OK: |
868 | 0 | name = "RFC2228_OK"; |
869 | 0 | break; |
870 | | |
871 | 0 | case PR_AUTH_OK: |
872 | 0 | name = "OK"; |
873 | 0 | break; |
874 | | |
875 | 0 | case PR_AUTH_ERROR: |
876 | 0 | name = "ERROR"; |
877 | 0 | break; |
878 | | |
879 | 0 | case PR_AUTH_NOPWD: |
880 | 0 | name = "NOPWD"; |
881 | 0 | break; |
882 | | |
883 | 0 | case PR_AUTH_BADPWD: |
884 | 0 | name = "BADPWD"; |
885 | 0 | break; |
886 | | |
887 | 0 | case PR_AUTH_AGEPWD: |
888 | 0 | name = "AGEPWD"; |
889 | 0 | break; |
890 | | |
891 | 0 | case PR_AUTH_DISABLEDPWD: |
892 | 0 | name = "DISABLEDPWD"; |
893 | 0 | break; |
894 | | |
895 | 0 | case PR_AUTH_CRED_INSUFFICIENT: |
896 | 0 | name = "CRED_INSUFFICIENT"; |
897 | 0 | break; |
898 | | |
899 | 0 | case PR_AUTH_CRED_UNAVAIL: |
900 | 0 | name = "CRED_UNAVAIL"; |
901 | 0 | break; |
902 | | |
903 | 0 | case PR_AUTH_CRED_ERROR: |
904 | 0 | name = "CRED_ERROR"; |
905 | 0 | break; |
906 | | |
907 | 0 | case PR_AUTH_INFO_UNAVAIL: |
908 | 0 | name = "INFO_UNAVAIL"; |
909 | 0 | break; |
910 | | |
911 | 0 | case PR_AUTH_MAX_ATTEMPTS_EXCEEDED: |
912 | 0 | name = "MAX_ATTEMPTS_EXCEEDED"; |
913 | 0 | break; |
914 | | |
915 | 0 | case PR_AUTH_INIT_ERROR: |
916 | 0 | name = "INIT_ERROR"; |
917 | 0 | break; |
918 | | |
919 | 0 | case PR_AUTH_NEW_TOKEN_REQUIRED: |
920 | 0 | name = "NEW_TOKEN_REQUIRED"; |
921 | 0 | break; |
922 | | |
923 | 0 | default: |
924 | 0 | break; |
925 | 0 | } |
926 | | |
927 | 0 | return name; |
928 | 0 | } |
929 | | |
930 | 0 | int pr_auth_authenticate(pool *p, const char *name, const char *pw) { |
931 | 0 | cmd_rec *cmd = NULL; |
932 | 0 | modret_t *mr = NULL; |
933 | 0 | module *m = NULL; |
934 | 0 | int res = PR_AUTH_NOPWD; |
935 | |
|
936 | 0 | if (p == NULL || |
937 | 0 | name == NULL || |
938 | 0 | pw == NULL) { |
939 | 0 | errno = EINVAL; |
940 | 0 | return -1; |
941 | 0 | } |
942 | | |
943 | 0 | cmd = make_cmd(p, 2, name, pw); |
944 | | |
945 | | /* First, check for any of the modules in the "authenticating only" list |
946 | | * of modules. This is usually only mod_auth_pam, but other modules |
947 | | * might also add themselves (e.g. mod_radius under certain conditions). |
948 | | */ |
949 | 0 | if (auth_module_list != NULL) { |
950 | 0 | struct auth_module_elt *elt; |
951 | |
|
952 | 0 | for (elt = (struct auth_module_elt *) auth_module_list->xas_list; elt; |
953 | 0 | elt = elt->next) { |
954 | 0 | pr_signals_handle(); |
955 | |
|
956 | 0 | pr_trace_msg(trace_channel, 7, "checking with auth-only module '%s'", |
957 | 0 | elt->name); |
958 | |
|
959 | 0 | m = pr_module_get(elt->name); |
960 | 0 | if (m != NULL) { |
961 | 0 | mr = dispatch_auth(cmd, "auth", &m); |
962 | |
|
963 | 0 | if (MODRET_ISHANDLED(mr)) { |
964 | 0 | pr_trace_msg(trace_channel, 4, |
965 | 0 | "module '%s' used for authenticating user '%s'", elt->name, name); |
966 | |
|
967 | 0 | res = MODRET_HASDATA(mr) ? PR_AUTH_RFC2228_OK : PR_AUTH_OK; |
968 | |
|
969 | 0 | if (cmd->tmp_pool) { |
970 | 0 | destroy_pool(cmd->tmp_pool); |
971 | 0 | cmd->tmp_pool = NULL; |
972 | 0 | } |
973 | |
|
974 | 0 | pr_trace_msg(trace_channel, 9, |
975 | 0 | "module '%s' returned HANDLED (%s) for authenticating user '%s'", |
976 | 0 | elt->name, get_authcode_str(res), name); |
977 | 0 | return res; |
978 | 0 | } |
979 | | |
980 | 0 | if (MODRET_ISERROR(mr)) { |
981 | 0 | pr_trace_msg(trace_channel, 4, |
982 | 0 | "module '%s' used for authenticating user '%s'", elt->name, name); |
983 | |
|
984 | 0 | res = MODRET_ERROR(mr); |
985 | |
|
986 | 0 | if (cmd->tmp_pool) { |
987 | 0 | destroy_pool(cmd->tmp_pool); |
988 | 0 | cmd->tmp_pool = NULL; |
989 | 0 | } |
990 | |
|
991 | 0 | pr_trace_msg(trace_channel, 9, |
992 | 0 | "module '%s' returned ERROR (%s) for authenticating user '%s'", |
993 | 0 | elt->name, get_authcode_str(res), name); |
994 | 0 | return res; |
995 | 0 | } |
996 | | |
997 | 0 | m = NULL; |
998 | 0 | } |
999 | 0 | } |
1000 | 0 | } |
1001 | | |
1002 | 0 | if (auth_tab != NULL) { |
1003 | 0 | const void *v; |
1004 | | |
1005 | | /* Fetch the specific module to be used for authenticating this user. */ |
1006 | 0 | v = pr_table_get(auth_tab, name, NULL); |
1007 | 0 | if (v != NULL) { |
1008 | 0 | m = *((module **) v); |
1009 | |
|
1010 | 0 | pr_trace_msg(trace_channel, 4, |
1011 | 0 | "using module 'mod_%s.c' from authcache to authenticate user '%s'", |
1012 | 0 | m->name, name); |
1013 | 0 | } |
1014 | 0 | } |
1015 | |
|
1016 | 0 | mr = dispatch_auth(cmd, "auth", m ? &m : NULL); |
1017 | |
|
1018 | 0 | if (MODRET_ISHANDLED(mr)) { |
1019 | 0 | res = MODRET_HASDATA(mr) ? PR_AUTH_RFC2228_OK : PR_AUTH_OK; |
1020 | 0 | pr_trace_msg(trace_channel, 9, |
1021 | 0 | "obtained HANDLED (%s) for authenticating user '%s'", |
1022 | 0 | get_authcode_str(res), name); |
1023 | |
|
1024 | 0 | } else if (MODRET_ISERROR(mr)) { |
1025 | 0 | res = MODRET_ERROR(mr); |
1026 | 0 | pr_trace_msg(trace_channel, 9, |
1027 | 0 | "obtained ERROR (%s) for authenticating user '%s'", get_authcode_str(res), |
1028 | 0 | name); |
1029 | 0 | } |
1030 | |
|
1031 | 0 | if (cmd->tmp_pool) { |
1032 | 0 | destroy_pool(cmd->tmp_pool); |
1033 | 0 | cmd->tmp_pool = NULL; |
1034 | 0 | } |
1035 | |
|
1036 | 0 | return res; |
1037 | 0 | } |
1038 | | |
1039 | 0 | int pr_auth_authorize(pool *p, const char *name) { |
1040 | 0 | cmd_rec *cmd = NULL; |
1041 | 0 | modret_t *mr = NULL; |
1042 | 0 | module *m = NULL; |
1043 | 0 | int res = PR_AUTH_OK; |
1044 | |
|
1045 | 0 | if (p == NULL || |
1046 | 0 | name == NULL) { |
1047 | 0 | errno = EINVAL; |
1048 | 0 | return -1; |
1049 | 0 | } |
1050 | | |
1051 | 0 | cmd = make_cmd(p, 1, name); |
1052 | |
|
1053 | 0 | if (auth_tab != NULL) { |
1054 | 0 | const void *v; |
1055 | | |
1056 | | /* Fetch the specific module to be used for authenticating this user. */ |
1057 | 0 | v = pr_table_get(auth_tab, name, NULL); |
1058 | 0 | if (v != NULL) { |
1059 | 0 | m = *((module **) v); |
1060 | |
|
1061 | 0 | pr_trace_msg(trace_channel, 4, |
1062 | 0 | "using module 'mod_%s.c' from authcache to authorize user '%s'", |
1063 | 0 | m->name, name); |
1064 | 0 | } |
1065 | 0 | } |
1066 | |
|
1067 | 0 | mr = dispatch_auth(cmd, "authorize", m ? &m : NULL); |
1068 | | |
1069 | | /* Unlike the other auth calls, we assume here that unless the handlers |
1070 | | * explicitly return ERROR, the user is authorized. Thus HANDLED and |
1071 | | * DECLINED are both treated as "yes, this user is authorized". This |
1072 | | * handles the case where the authenticating module (e.g. mod_sql) |
1073 | | * does NOT provide an 'authorize' handler. |
1074 | | */ |
1075 | |
|
1076 | 0 | if (MODRET_ISERROR(mr)) { |
1077 | 0 | res = MODRET_ERROR(mr); |
1078 | 0 | pr_trace_msg(trace_channel, 9, |
1079 | 0 | "obtained ERROR (%s) for authorizing user '%s'", get_authcode_str(res), |
1080 | 0 | name); |
1081 | 0 | } |
1082 | |
|
1083 | 0 | if (cmd->tmp_pool) { |
1084 | 0 | destroy_pool(cmd->tmp_pool); |
1085 | 0 | cmd->tmp_pool = NULL; |
1086 | 0 | } |
1087 | |
|
1088 | 0 | return res; |
1089 | 0 | } |
1090 | | |
1091 | | int pr_auth_check(pool *p, const char *ciphertext_passwd, const char *name, |
1092 | 0 | const char *cleartext_passwd) { |
1093 | 0 | cmd_rec *cmd = NULL; |
1094 | 0 | modret_t *mr = NULL; |
1095 | 0 | module *m = NULL; |
1096 | 0 | int res = PR_AUTH_BADPWD; |
1097 | 0 | size_t cleartext_passwd_len = 0; |
1098 | | |
1099 | | /* Note: it's possible for ciphertext_passwd to be NULL (mod_ldap might do |
1100 | | * this, for example), so we cannot enforce that it be non-NULL. |
1101 | | */ |
1102 | |
|
1103 | 0 | if (p == NULL || |
1104 | 0 | name == NULL || |
1105 | 0 | cleartext_passwd == NULL) { |
1106 | 0 | errno = EINVAL; |
1107 | 0 | return -1; |
1108 | 0 | } |
1109 | | |
1110 | 0 | cleartext_passwd_len = strlen(cleartext_passwd); |
1111 | 0 | if (cleartext_passwd_len > auth_max_passwd_len) { |
1112 | 0 | pr_log_auth(PR_LOG_INFO, |
1113 | 0 | "client-provided password size exceeds MaxPasswordSize (%lu), " |
1114 | 0 | "rejecting", (unsigned long) auth_max_passwd_len); |
1115 | 0 | errno = EPERM; |
1116 | 0 | return -1; |
1117 | 0 | } |
1118 | | |
1119 | 0 | cmd = make_cmd(p, 3, ciphertext_passwd, name, cleartext_passwd); |
1120 | | |
1121 | | /* First, check for any of the modules in the "authenticating only" list |
1122 | | * of modules. This is usually only mod_auth_pam, but other modules |
1123 | | * might also add themselves (e.g. mod_radius under certain conditions). |
1124 | | */ |
1125 | 0 | if (auth_module_list != NULL) { |
1126 | 0 | struct auth_module_elt *elt; |
1127 | |
|
1128 | 0 | for (elt = (struct auth_module_elt *) auth_module_list->xas_list; elt; |
1129 | 0 | elt = elt->next) { |
1130 | 0 | pr_signals_handle(); |
1131 | |
|
1132 | 0 | m = pr_module_get(elt->name); |
1133 | 0 | if (m != NULL) { |
1134 | 0 | mr = dispatch_auth(cmd, "check", &m); |
1135 | 0 | if (MODRET_ISHANDLED(mr)) { |
1136 | 0 | pr_trace_msg(trace_channel, 4, |
1137 | 0 | "module '%s' used for authenticating user '%s'", elt->name, name); |
1138 | |
|
1139 | 0 | res = MODRET_HASDATA(mr) ? PR_AUTH_RFC2228_OK : PR_AUTH_OK; |
1140 | |
|
1141 | 0 | if (cmd->tmp_pool != NULL) { |
1142 | 0 | destroy_pool(cmd->tmp_pool); |
1143 | 0 | cmd->tmp_pool = NULL; |
1144 | 0 | } |
1145 | |
|
1146 | 0 | pr_trace_msg(trace_channel, 9, |
1147 | 0 | "module '%s' returned HANDLED (%s) for checking user '%s'", |
1148 | 0 | elt->name, get_authcode_str(res), name); |
1149 | 0 | return res; |
1150 | 0 | } |
1151 | | |
1152 | 0 | if (MODRET_ISERROR(mr)) { |
1153 | 0 | res = MODRET_ERROR(mr); |
1154 | |
|
1155 | 0 | if (cmd->tmp_pool != NULL) { |
1156 | 0 | destroy_pool(cmd->tmp_pool); |
1157 | 0 | cmd->tmp_pool = NULL; |
1158 | 0 | } |
1159 | |
|
1160 | 0 | pr_trace_msg(trace_channel, 9, |
1161 | 0 | "module '%s' returned ERROR (%d %s) for checking user '%s'", |
1162 | 0 | elt->name, res, get_authcode_str(res), name); |
1163 | 0 | return res; |
1164 | 0 | } |
1165 | | |
1166 | 0 | m = NULL; |
1167 | 0 | } |
1168 | 0 | } |
1169 | 0 | } |
1170 | | |
1171 | 0 | if (auth_tab != NULL) { |
1172 | 0 | const void *v; |
1173 | | |
1174 | | /* Fetch the specific module to be used for authenticating this user. */ |
1175 | 0 | v = pr_table_get(auth_tab, name, NULL); |
1176 | 0 | if (v != NULL) { |
1177 | 0 | m = *((module **) v); |
1178 | |
|
1179 | 0 | pr_trace_msg(trace_channel, 4, |
1180 | 0 | "using module 'mod_%s.c' from authcache to authenticate user '%s'", |
1181 | 0 | m->name, name); |
1182 | 0 | } |
1183 | 0 | } |
1184 | |
|
1185 | 0 | mr = dispatch_auth(cmd, "check", m ? &m : NULL); |
1186 | |
|
1187 | 0 | if (MODRET_ISHANDLED(mr)) { |
1188 | 0 | res = MODRET_HASDATA(mr) ? PR_AUTH_RFC2228_OK : PR_AUTH_OK; |
1189 | 0 | pr_trace_msg(trace_channel, 9, |
1190 | 0 | "obtained HANDLED (%s) for checking user '%s'", get_authcode_str(res), |
1191 | 0 | name); |
1192 | 0 | } |
1193 | |
|
1194 | 0 | if (cmd->tmp_pool != NULL) { |
1195 | 0 | destroy_pool(cmd->tmp_pool); |
1196 | 0 | cmd->tmp_pool = NULL; |
1197 | 0 | } |
1198 | |
|
1199 | 0 | return res; |
1200 | 0 | } |
1201 | | |
1202 | 0 | int pr_auth_requires_pass(pool *p, const char *name) { |
1203 | 0 | cmd_rec *cmd; |
1204 | 0 | modret_t *mr; |
1205 | 0 | int res = TRUE; |
1206 | |
|
1207 | 0 | if (p == NULL || |
1208 | 0 | name == NULL) { |
1209 | 0 | errno = EINVAL; |
1210 | 0 | return -1; |
1211 | 0 | } |
1212 | | |
1213 | 0 | cmd = make_cmd(p, 1, name); |
1214 | 0 | mr = dispatch_auth(cmd, "requires_pass", NULL); |
1215 | |
|
1216 | 0 | if (MODRET_ISHANDLED(mr)) { |
1217 | 0 | res = FALSE; |
1218 | |
|
1219 | 0 | } else if (MODRET_ISERROR(mr)) { |
1220 | 0 | res = MODRET_ERROR(mr); |
1221 | 0 | } |
1222 | |
|
1223 | 0 | if (cmd->tmp_pool) { |
1224 | 0 | destroy_pool(cmd->tmp_pool); |
1225 | 0 | cmd->tmp_pool = NULL; |
1226 | 0 | } |
1227 | |
|
1228 | 0 | return res; |
1229 | 0 | } |
1230 | | |
1231 | 0 | const char *pr_auth_uid2name(pool *p, uid_t uid) { |
1232 | 0 | static char namebuf[PR_TUNABLE_LOGIN_MAX+1]; |
1233 | 0 | cmd_rec *cmd = NULL; |
1234 | 0 | modret_t *mr = NULL; |
1235 | 0 | char *res = NULL; |
1236 | 0 | unsigned int cache_lookup_flags = (PR_AUTH_CACHE_FL_UID2NAME|PR_AUTH_CACHE_FL_BAD_UID2NAME); |
1237 | 0 | int have_name = FALSE; |
1238 | |
|
1239 | 0 | if (p == NULL) { |
1240 | 0 | errno = EINVAL; |
1241 | 0 | return NULL; |
1242 | 0 | } |
1243 | | |
1244 | 0 | uidcache_create(); |
1245 | |
|
1246 | 0 | if (auth_caching & cache_lookup_flags) { |
1247 | 0 | if (uidcache_get(uid, namebuf, sizeof(namebuf)) == 0) { |
1248 | 0 | res = namebuf; |
1249 | 0 | return res; |
1250 | 0 | } |
1251 | 0 | } |
1252 | | |
1253 | 0 | cmd = make_cmd(p, 1, (void *) &uid); |
1254 | 0 | mr = dispatch_auth(cmd, "uid2name", NULL); |
1255 | |
|
1256 | 0 | if (MODRET_ISHANDLED(mr) && |
1257 | 0 | MODRET_HASDATA(mr)) { |
1258 | 0 | res = mr->data; |
1259 | 0 | sstrncpy(namebuf, res, sizeof(namebuf)); |
1260 | 0 | res = namebuf; |
1261 | |
|
1262 | 0 | if (auth_caching & PR_AUTH_CACHE_FL_UID2NAME) { |
1263 | 0 | uidcache_add(uid, res); |
1264 | 0 | } |
1265 | |
|
1266 | 0 | have_name = TRUE; |
1267 | 0 | } |
1268 | |
|
1269 | 0 | if (cmd->tmp_pool) { |
1270 | 0 | destroy_pool(cmd->tmp_pool); |
1271 | 0 | cmd->tmp_pool = NULL; |
1272 | 0 | } |
1273 | |
|
1274 | 0 | if (!have_name) { |
1275 | | /* TODO: This conversion is data type sensitive, per Bug#4164. */ |
1276 | 0 | pr_snprintf(namebuf, sizeof(namebuf)-1, "%lu", (unsigned long) uid); |
1277 | 0 | res = namebuf; |
1278 | |
|
1279 | 0 | if (auth_caching & PR_AUTH_CACHE_FL_BAD_UID2NAME) { |
1280 | 0 | uidcache_add(uid, res); |
1281 | 0 | } |
1282 | 0 | } |
1283 | |
|
1284 | 0 | return res; |
1285 | 0 | } |
1286 | | |
1287 | 0 | const char *pr_auth_gid2name(pool *p, gid_t gid) { |
1288 | 0 | static char namebuf[PR_TUNABLE_LOGIN_MAX+1]; |
1289 | 0 | cmd_rec *cmd = NULL; |
1290 | 0 | modret_t *mr = NULL; |
1291 | 0 | char *res = NULL; |
1292 | 0 | unsigned int cache_lookup_flags = (PR_AUTH_CACHE_FL_GID2NAME|PR_AUTH_CACHE_FL_BAD_GID2NAME); |
1293 | 0 | int have_name = FALSE; |
1294 | |
|
1295 | 0 | if (p == NULL) { |
1296 | 0 | errno = EINVAL; |
1297 | 0 | return NULL; |
1298 | 0 | } |
1299 | | |
1300 | 0 | gidcache_create(); |
1301 | |
|
1302 | 0 | if (auth_caching & cache_lookup_flags) { |
1303 | 0 | if (gidcache_get(gid, namebuf, sizeof(namebuf)) == 0) { |
1304 | 0 | res = namebuf; |
1305 | 0 | return res; |
1306 | 0 | } |
1307 | 0 | } |
1308 | | |
1309 | 0 | cmd = make_cmd(p, 1, (void *) &gid); |
1310 | 0 | mr = dispatch_auth(cmd, "gid2name", NULL); |
1311 | |
|
1312 | 0 | if (MODRET_ISHANDLED(mr) && |
1313 | 0 | MODRET_HASDATA(mr)) { |
1314 | 0 | res = mr->data; |
1315 | 0 | sstrncpy(namebuf, res, sizeof(namebuf)); |
1316 | 0 | res = namebuf; |
1317 | |
|
1318 | 0 | if (auth_caching & PR_AUTH_CACHE_FL_GID2NAME) { |
1319 | 0 | gidcache_add(gid, res); |
1320 | 0 | } |
1321 | |
|
1322 | 0 | have_name = TRUE; |
1323 | 0 | } |
1324 | |
|
1325 | 0 | if (cmd->tmp_pool) { |
1326 | 0 | destroy_pool(cmd->tmp_pool); |
1327 | 0 | cmd->tmp_pool = NULL; |
1328 | 0 | } |
1329 | |
|
1330 | 0 | if (!have_name) { |
1331 | | /* TODO: This conversion is data type sensitive, per Bug#4164. */ |
1332 | 0 | pr_snprintf(namebuf, sizeof(namebuf)-1, "%lu", (unsigned long) gid); |
1333 | 0 | res = namebuf; |
1334 | |
|
1335 | 0 | if (auth_caching & PR_AUTH_CACHE_FL_BAD_GID2NAME) { |
1336 | 0 | gidcache_add(gid, res); |
1337 | 0 | } |
1338 | 0 | } |
1339 | |
|
1340 | 0 | return res; |
1341 | 0 | } |
1342 | | |
1343 | 0 | uid_t pr_auth_name2uid(pool *p, const char *name) { |
1344 | 0 | cmd_rec *cmd = NULL; |
1345 | 0 | modret_t *mr = NULL; |
1346 | 0 | uid_t res = (uid_t) -1; |
1347 | 0 | unsigned int cache_lookup_flags = (PR_AUTH_CACHE_FL_NAME2UID|PR_AUTH_CACHE_FL_BAD_NAME2UID); |
1348 | 0 | int have_id = FALSE; |
1349 | |
|
1350 | 0 | if (p == NULL || |
1351 | 0 | name == NULL) { |
1352 | 0 | errno = EINVAL; |
1353 | 0 | return (uid_t) -1; |
1354 | 0 | } |
1355 | | |
1356 | 0 | usercache_create(); |
1357 | |
|
1358 | 0 | if (auth_caching & cache_lookup_flags) { |
1359 | 0 | uid_t cache_uid; |
1360 | |
|
1361 | 0 | if (usercache_get(name, &cache_uid) == 0) { |
1362 | 0 | res = cache_uid; |
1363 | |
|
1364 | 0 | if (res == (uid_t) -1) { |
1365 | 0 | errno = ENOENT; |
1366 | 0 | } |
1367 | |
|
1368 | 0 | return res; |
1369 | 0 | } |
1370 | 0 | } |
1371 | | |
1372 | 0 | cmd = make_cmd(p, 1, name); |
1373 | 0 | mr = dispatch_auth(cmd, "name2uid", NULL); |
1374 | |
|
1375 | 0 | if (MODRET_ISHANDLED(mr) && |
1376 | 0 | MODRET_HASDATA(mr)) { |
1377 | 0 | res = *((uid_t *) mr->data); |
1378 | |
|
1379 | 0 | if (auth_caching & PR_AUTH_CACHE_FL_NAME2UID) { |
1380 | 0 | usercache_add(name, res); |
1381 | 0 | } |
1382 | |
|
1383 | 0 | have_id = TRUE; |
1384 | |
|
1385 | 0 | } else { |
1386 | 0 | errno = ENOENT; |
1387 | 0 | } |
1388 | |
|
1389 | 0 | if (cmd->tmp_pool) { |
1390 | 0 | destroy_pool(cmd->tmp_pool); |
1391 | 0 | cmd->tmp_pool = NULL; |
1392 | 0 | } |
1393 | |
|
1394 | 0 | if (!have_id && |
1395 | 0 | (auth_caching & PR_AUTH_CACHE_FL_BAD_NAME2UID)) { |
1396 | 0 | usercache_add(name, res); |
1397 | 0 | } |
1398 | |
|
1399 | 0 | return res; |
1400 | 0 | } |
1401 | | |
1402 | 0 | gid_t pr_auth_name2gid(pool *p, const char *name) { |
1403 | 0 | cmd_rec *cmd = NULL; |
1404 | 0 | modret_t *mr = NULL; |
1405 | 0 | gid_t res = (gid_t) -1; |
1406 | 0 | unsigned int cache_lookup_flags = (PR_AUTH_CACHE_FL_NAME2GID|PR_AUTH_CACHE_FL_BAD_NAME2GID); |
1407 | 0 | int have_id = FALSE; |
1408 | |
|
1409 | 0 | if (p == NULL || |
1410 | 0 | name == NULL) { |
1411 | 0 | errno = EINVAL; |
1412 | 0 | return (gid_t) -1; |
1413 | 0 | } |
1414 | | |
1415 | 0 | groupcache_create(); |
1416 | |
|
1417 | 0 | if (auth_caching & cache_lookup_flags) { |
1418 | 0 | gid_t cache_gid; |
1419 | |
|
1420 | 0 | if (groupcache_get(name, &cache_gid) == 0) { |
1421 | 0 | res = cache_gid; |
1422 | |
|
1423 | 0 | if (res == (gid_t) -1) { |
1424 | 0 | errno = ENOENT; |
1425 | 0 | } |
1426 | |
|
1427 | 0 | return res; |
1428 | 0 | } |
1429 | 0 | } |
1430 | | |
1431 | 0 | cmd = make_cmd(p, 1, name); |
1432 | 0 | mr = dispatch_auth(cmd, "name2gid", NULL); |
1433 | |
|
1434 | 0 | if (MODRET_ISHANDLED(mr) && |
1435 | 0 | MODRET_HASDATA(mr)) { |
1436 | 0 | res = *((gid_t *) mr->data); |
1437 | |
|
1438 | 0 | if (auth_caching & PR_AUTH_CACHE_FL_NAME2GID) { |
1439 | 0 | groupcache_add(name, res); |
1440 | 0 | } |
1441 | |
|
1442 | 0 | have_id = TRUE; |
1443 | |
|
1444 | 0 | } else { |
1445 | 0 | errno = ENOENT; |
1446 | 0 | } |
1447 | |
|
1448 | 0 | if (cmd->tmp_pool) { |
1449 | 0 | destroy_pool(cmd->tmp_pool); |
1450 | 0 | cmd->tmp_pool = NULL; |
1451 | 0 | } |
1452 | |
|
1453 | 0 | if (!have_id && |
1454 | 0 | (auth_caching & PR_AUTH_CACHE_FL_BAD_NAME2GID)) { |
1455 | 0 | groupcache_add(name, res); |
1456 | 0 | } |
1457 | |
|
1458 | 0 | return res; |
1459 | 0 | } |
1460 | | |
1461 | | int pr_auth_getgroups(pool *p, const char *name, array_header **group_ids, |
1462 | 0 | array_header **group_names) { |
1463 | 0 | cmd_rec *cmd = NULL; |
1464 | 0 | modret_t *mr = NULL; |
1465 | 0 | int res = -1; |
1466 | |
|
1467 | 0 | if (p == NULL || |
1468 | 0 | name == NULL) { |
1469 | 0 | errno = EINVAL; |
1470 | 0 | return -1; |
1471 | 0 | } |
1472 | | |
1473 | | /* Allocate memory for the array_headers of GIDs and group names. */ |
1474 | 0 | if (group_ids) { |
1475 | 0 | *group_ids = make_array(permanent_pool, 2, sizeof(gid_t)); |
1476 | 0 | } |
1477 | |
|
1478 | 0 | if (group_names) { |
1479 | 0 | *group_names = make_array(permanent_pool, 2, sizeof(char *)); |
1480 | 0 | } |
1481 | |
|
1482 | 0 | cmd = make_cmd(p, 3, name, group_ids ? *group_ids : NULL, |
1483 | 0 | group_names ? *group_names : NULL); |
1484 | |
|
1485 | 0 | mr = dispatch_auth(cmd, "getgroups", NULL); |
1486 | |
|
1487 | 0 | if (MODRET_ISHANDLED(mr) && |
1488 | 0 | MODRET_HASDATA(mr)) { |
1489 | 0 | res = *((int *) mr->data); |
1490 | | |
1491 | | /* Note: the number of groups returned should, barring error, |
1492 | | * always be at least 1, as per getgroups(2) behavior. This one |
1493 | | * ID is present because it is the primary group membership set in |
1494 | | * struct passwd, from /etc/passwd. This will need to be documented |
1495 | | * for the benefit of auth_getgroup() implementors. |
1496 | | */ |
1497 | |
|
1498 | 0 | if (group_ids) { |
1499 | 0 | register unsigned int i; |
1500 | 0 | char *strgids = ""; |
1501 | 0 | gid_t *gids = (*group_ids)->elts; |
1502 | |
|
1503 | 0 | for (i = 0; i < (*group_ids)->nelts; i++) { |
1504 | 0 | pr_signals_handle(); |
1505 | 0 | strgids = pstrcat(p, strgids, i != 0 ? ", " : "", |
1506 | 0 | pr_gid2str(NULL, gids[i]), NULL); |
1507 | 0 | } |
1508 | |
|
1509 | 0 | pr_log_debug(DEBUG10, "retrieved group %s: %s", |
1510 | 0 | (*group_ids)->nelts == 1 ? "ID" : "IDs", |
1511 | 0 | *strgids ? strgids : "(None; corrupted group file?)"); |
1512 | 0 | } |
1513 | |
|
1514 | 0 | if (group_names) { |
1515 | 0 | register unsigned int i; |
1516 | 0 | char *strgroups = ""; |
1517 | 0 | char **groups = (*group_names)->elts; |
1518 | |
|
1519 | 0 | for (i = 0; i < (*group_names)->nelts; i++) { |
1520 | 0 | pr_signals_handle(); |
1521 | 0 | strgroups = pstrcat(p, strgroups, i != 0 ? ", " : "", groups[i], NULL); |
1522 | 0 | } |
1523 | |
|
1524 | 0 | pr_log_debug(DEBUG10, "retrieved group %s: %s", |
1525 | 0 | (*group_names)->nelts == 1 ? "name" : "names", |
1526 | 0 | *strgroups ? strgroups : "(None; corrupted group file?)"); |
1527 | 0 | } |
1528 | 0 | } |
1529 | |
|
1530 | 0 | if (cmd->tmp_pool) { |
1531 | 0 | destroy_pool(cmd->tmp_pool); |
1532 | 0 | cmd->tmp_pool = NULL; |
1533 | 0 | } |
1534 | |
|
1535 | 0 | return res; |
1536 | 0 | } |
1537 | | |
1538 | | /* This is one messy function. Yuck. Yay legacy code. */ |
1539 | | config_rec *pr_auth_get_anon_config(pool *p, const char **login_user, |
1540 | 0 | char **real_user, char **anon_name) { |
1541 | 0 | config_rec *c = NULL, *alias_config = NULL, *anon_config = NULL; |
1542 | 0 | char *config_user_name = NULL, *config_anon_name = NULL; |
1543 | 0 | unsigned char is_alias = FALSE, *auth_alias_only = NULL; |
1544 | 0 | unsigned long config_flags = (PR_CONFIG_FIND_FL_SKIP_DIR|PR_CONFIG_FIND_FL_SKIP_LIMIT|PR_CONFIG_FIND_FL_SKIP_DYNDIR); |
1545 | | |
1546 | | /* Precedence rules: |
1547 | | * 1. Search for UserAlias directive. |
1548 | | * 2. Search for Anonymous directive. |
1549 | | * 3. Normal user login |
1550 | | */ |
1551 | |
|
1552 | 0 | config_user_name = get_param_ptr(main_server->conf, "UserName", FALSE); |
1553 | 0 | if (config_user_name != NULL && |
1554 | 0 | real_user != NULL) { |
1555 | 0 | *real_user = config_user_name; |
1556 | 0 | } |
1557 | | |
1558 | | /* If the main_server->conf->set list is large (e.g. there are many |
1559 | | * config_recs in the list, as can happen if MANY <Directory> sections are |
1560 | | * configured), the login can timeout because this find_config() call takes |
1561 | | * a long time. The reason this issue strikes HERE first in the login |
1562 | | * process is that this appears to the first find_config() call which has |
1563 | | * a TRUE recurse flag. |
1564 | | * |
1565 | | * The find_config() call below is looking for a UserAlias directive |
1566 | | * anywhere in the configuration, no matter how deeply buried in nested |
1567 | | * config contexts it might be. |
1568 | | */ |
1569 | |
|
1570 | 0 | c = find_config2(main_server->conf, CONF_PARAM, "UserAlias", TRUE, |
1571 | 0 | config_flags); |
1572 | 0 | if (c != NULL) { |
1573 | 0 | do { |
1574 | 0 | const char *alias; |
1575 | |
|
1576 | 0 | pr_signals_handle(); |
1577 | |
|
1578 | 0 | alias = c->argv[0]; |
1579 | 0 | if (strcmp(alias, "*") == 0 || |
1580 | 0 | strcmp(alias, *login_user) == 0) { |
1581 | 0 | is_alias = TRUE; |
1582 | 0 | alias_config = c; |
1583 | 0 | break; |
1584 | 0 | } |
1585 | |
|
1586 | 0 | } while ((c = find_config_next2(c, c->next, CONF_PARAM, "UserAlias", |
1587 | 0 | TRUE, config_flags)) != NULL); |
1588 | 0 | } |
1589 | | |
1590 | | /* This is where things get messy, rapidly. */ |
1591 | 0 | if (is_alias == TRUE) { |
1592 | 0 | c = alias_config; |
1593 | 0 | } |
1594 | |
|
1595 | 0 | while (c != NULL && |
1596 | 0 | c->parent != NULL && |
1597 | 0 | (auth_alias_only = get_param_ptr(c->parent->subset, "AuthAliasOnly", FALSE))) { |
1598 | |
|
1599 | 0 | pr_signals_handle(); |
1600 | | |
1601 | | /* If AuthAliasOnly is on, ignore this one and continue. */ |
1602 | 0 | if (auth_alias_only != NULL && |
1603 | 0 | *auth_alias_only == TRUE) { |
1604 | 0 | c = find_config_next2(c, c->next, CONF_PARAM, "UserAlias", TRUE, |
1605 | 0 | config_flags); |
1606 | 0 | continue; |
1607 | 0 | } |
1608 | | |
1609 | | /* At this point, we have found an "AuthAliasOnly off" config in |
1610 | | * c->parent->set (which means that we cannot use the UserAlias, and thus |
1611 | | * is_alias is set to false). See if there's a UserAlias in the same |
1612 | | * config set. |
1613 | | */ |
1614 | | |
1615 | 0 | is_alias = FALSE; |
1616 | |
|
1617 | 0 | find_config_set_top(alias_config); |
1618 | 0 | c = find_config_next2(c, c->next, CONF_PARAM, "UserAlias", TRUE, |
1619 | 0 | config_flags); |
1620 | |
|
1621 | 0 | if (c != NULL && |
1622 | 0 | (strcmp(c->argv[0], "*") == 0 || |
1623 | 0 | strcmp(c->argv[0], *login_user) == 0)) { |
1624 | 0 | is_alias = TRUE; |
1625 | 0 | alias_config = c; |
1626 | 0 | } |
1627 | 0 | } |
1628 | | |
1629 | | /* At this point in time, c is guaranteed (if not null) to be pointing at |
1630 | | * a UserAlias config, either the original OR one found in the AuthAliasOnly |
1631 | | * config set. |
1632 | | */ |
1633 | 0 | if (c != NULL) { |
1634 | 0 | *login_user = c->argv[1]; |
1635 | | |
1636 | | /* If the alias is applied inside an <Anonymous> context, we have found |
1637 | | * our <Anonymous> section. |
1638 | | */ |
1639 | 0 | if (c->parent && |
1640 | 0 | c->parent->config_type == CONF_ANON) { |
1641 | 0 | anon_config = c->parent; |
1642 | |
|
1643 | 0 | } else { |
1644 | 0 | c = NULL; |
1645 | 0 | } |
1646 | 0 | } |
1647 | | |
1648 | | /* Next, search for an anonymous entry. */ |
1649 | 0 | if (anon_config == NULL) { |
1650 | 0 | c = find_config(main_server->conf, CONF_ANON, NULL, FALSE); |
1651 | |
|
1652 | 0 | } else { |
1653 | 0 | find_config_set_top(anon_config); |
1654 | 0 | c = anon_config; |
1655 | 0 | } |
1656 | | |
1657 | | /* If anon_config is null here but c is not null, then we may have found |
1658 | | * a candidate <Anonymous> section. Let's examine it more closely. |
1659 | | */ |
1660 | 0 | if (c != NULL) { |
1661 | 0 | config_rec *starting_c; |
1662 | |
|
1663 | 0 | starting_c = c; |
1664 | 0 | do { |
1665 | 0 | pr_signals_handle(); |
1666 | |
|
1667 | 0 | config_anon_name = get_param_ptr(c->subset, "UserName", FALSE); |
1668 | 0 | if (config_anon_name == NULL) { |
1669 | 0 | config_anon_name = config_user_name; |
1670 | 0 | } |
1671 | |
|
1672 | 0 | if (config_anon_name != NULL && |
1673 | 0 | strcmp(config_anon_name, *login_user) == 0) { |
1674 | | |
1675 | | /* We found our <Anonymous> section. */ |
1676 | 0 | anon_config = c; |
1677 | |
|
1678 | 0 | if (anon_name != NULL) { |
1679 | 0 | *anon_name = config_anon_name; |
1680 | 0 | } |
1681 | 0 | break; |
1682 | 0 | } |
1683 | |
|
1684 | 0 | } while ((c = find_config_next(c, c->next, CONF_ANON, NULL, |
1685 | 0 | FALSE)) != NULL); |
1686 | | |
1687 | 0 | c = starting_c; |
1688 | 0 | } |
1689 | | |
1690 | 0 | if (is_alias == FALSE) { |
1691 | 0 | auth_alias_only = get_param_ptr(c ? c->subset : main_server->conf, |
1692 | 0 | "AuthAliasOnly", FALSE); |
1693 | |
|
1694 | 0 | if (auth_alias_only != NULL && |
1695 | 0 | *auth_alias_only == TRUE) { |
1696 | 0 | if (c != NULL && |
1697 | 0 | c->config_type == CONF_ANON) { |
1698 | 0 | c = NULL; |
1699 | |
|
1700 | 0 | } else { |
1701 | 0 | *login_user = NULL; |
1702 | 0 | } |
1703 | | |
1704 | | /* Note: We only need to look for AuthAliasOnly in main_server IFF |
1705 | | * c is NOT null. If c IS null, then we will already have looked up |
1706 | | * AuthAliasOnly in main_server above. |
1707 | | */ |
1708 | 0 | if (c != NULL) { |
1709 | 0 | auth_alias_only = get_param_ptr(main_server->conf, "AuthAliasOnly", |
1710 | 0 | FALSE); |
1711 | 0 | } |
1712 | |
|
1713 | 0 | if (login_user != NULL && |
1714 | 0 | auth_alias_only != NULL && |
1715 | 0 | *auth_alias_only == TRUE) { |
1716 | 0 | *login_user = NULL; |
1717 | 0 | } |
1718 | |
|
1719 | 0 | if ((login_user == NULL || anon_config == NULL) && |
1720 | 0 | anon_name != NULL) { |
1721 | 0 | *anon_name = NULL; |
1722 | 0 | } |
1723 | 0 | } |
1724 | |
|
1725 | 0 | } else { |
1726 | 0 | config_rec *alias_parent_config = NULL; |
1727 | | |
1728 | | /* We have found a matching UserAlias for the USER name sent by the client. |
1729 | | * But we need to properly handle any AuthAliasOnly directives in that |
1730 | | * config as well (Bug#2070). |
1731 | | */ |
1732 | 0 | if (alias_config != NULL) { |
1733 | 0 | alias_parent_config = alias_config->parent; |
1734 | 0 | } |
1735 | |
|
1736 | 0 | auth_alias_only = get_param_ptr(alias_parent_config ? |
1737 | 0 | alias_parent_config->subset : main_server->conf, "AuthAliasOnly", FALSE); |
1738 | |
|
1739 | 0 | if (auth_alias_only != NULL && |
1740 | 0 | *auth_alias_only == TRUE) { |
1741 | 0 | if (alias_parent_config != NULL && |
1742 | 0 | alias_parent_config->config_type == CONF_ANON) { |
1743 | 0 | anon_config = alias_parent_config; |
1744 | 0 | } |
1745 | 0 | } |
1746 | 0 | } |
1747 | |
|
1748 | 0 | if (anon_config != NULL) { |
1749 | 0 | config_user_name = get_param_ptr(anon_config->subset, "UserName", FALSE); |
1750 | 0 | if (config_user_name != NULL && |
1751 | 0 | real_user != NULL) { |
1752 | 0 | *real_user = config_user_name; |
1753 | 0 | } |
1754 | 0 | } |
1755 | |
|
1756 | 0 | return anon_config; |
1757 | 0 | } |
1758 | | |
1759 | 0 | int pr_auth_banned_by_ftpusers(xaset_t *ctx, const char *user) { |
1760 | 0 | int res = FALSE; |
1761 | 0 | unsigned char *use_ftp_users; |
1762 | |
|
1763 | 0 | if (user == NULL) { |
1764 | 0 | return res; |
1765 | 0 | } |
1766 | | |
1767 | 0 | use_ftp_users = get_param_ptr(ctx, "UseFtpUsers", FALSE); |
1768 | 0 | if (use_ftp_users == NULL || |
1769 | 0 | *use_ftp_users == TRUE) { |
1770 | 0 | FILE *fh = NULL; |
1771 | 0 | char buf[512]; |
1772 | 0 | int xerrno; |
1773 | |
|
1774 | 0 | PRIVS_ROOT |
1775 | 0 | fh = fopen(PR_FTPUSERS_PATH, "r"); |
1776 | 0 | xerrno = errno; |
1777 | 0 | PRIVS_RELINQUISH |
1778 | |
|
1779 | 0 | if (fh == NULL) { |
1780 | 0 | pr_trace_msg(trace_channel, 14, |
1781 | 0 | "error opening '%s' for checking user '%s': %s", PR_FTPUSERS_PATH, |
1782 | 0 | user, strerror(xerrno)); |
1783 | 0 | return res; |
1784 | 0 | } |
1785 | | |
1786 | 0 | memset(buf, '\0', sizeof(buf)); |
1787 | |
|
1788 | 0 | while (fgets(buf, sizeof(buf)-1, fh) != NULL) { |
1789 | 0 | char *ptr; |
1790 | |
|
1791 | 0 | pr_signals_handle(); |
1792 | |
|
1793 | 0 | buf[sizeof(buf)-1] = '\0'; |
1794 | 0 | CHOP(buf); |
1795 | |
|
1796 | 0 | ptr = buf; |
1797 | 0 | while (PR_ISSPACE(*ptr) && *ptr) { |
1798 | 0 | ptr++; |
1799 | 0 | } |
1800 | |
|
1801 | 0 | if (!*ptr || |
1802 | 0 | *ptr == '#') { |
1803 | 0 | continue; |
1804 | 0 | } |
1805 | | |
1806 | 0 | if (strcmp(ptr, user) == 0 ) { |
1807 | 0 | res = TRUE; |
1808 | 0 | break; |
1809 | 0 | } |
1810 | | |
1811 | 0 | memset(buf, '\0', sizeof(buf)); |
1812 | 0 | } |
1813 | |
|
1814 | 0 | fclose(fh); |
1815 | 0 | } |
1816 | | |
1817 | 0 | return res; |
1818 | 0 | } |
1819 | | |
1820 | 0 | int pr_auth_is_valid_shell(xaset_t *ctx, const char *shell) { |
1821 | 0 | int res = TRUE; |
1822 | 0 | unsigned char *require_valid_shell; |
1823 | |
|
1824 | 0 | if (shell == NULL) { |
1825 | 0 | return res; |
1826 | 0 | } |
1827 | | |
1828 | 0 | require_valid_shell = get_param_ptr(ctx, "RequireValidShell", FALSE); |
1829 | |
|
1830 | 0 | if (require_valid_shell == NULL || |
1831 | 0 | *require_valid_shell == TRUE) { |
1832 | 0 | FILE *fh = NULL; |
1833 | 0 | char buf[256]; |
1834 | |
|
1835 | 0 | fh = fopen(PR_VALID_SHELL_PATH, "r"); |
1836 | 0 | if (fh == NULL) { |
1837 | 0 | return res; |
1838 | 0 | } |
1839 | | |
1840 | 0 | res = FALSE; |
1841 | 0 | memset(buf, '\0', sizeof(buf)); |
1842 | |
|
1843 | 0 | while (fgets(buf, sizeof(buf)-1, fh) != NULL) { |
1844 | 0 | pr_signals_handle(); |
1845 | |
|
1846 | 0 | buf[sizeof(buf)-1] = '\0'; |
1847 | 0 | CHOP(buf); |
1848 | |
|
1849 | 0 | if (strcmp(buf, shell) == 0) { |
1850 | 0 | res = TRUE; |
1851 | 0 | break; |
1852 | 0 | } |
1853 | | |
1854 | 0 | memset(buf, '\0', sizeof(buf)); |
1855 | 0 | } |
1856 | |
|
1857 | 0 | fclose(fh); |
1858 | 0 | } |
1859 | | |
1860 | 0 | return res; |
1861 | 0 | } |
1862 | | |
1863 | 0 | int pr_auth_chroot(const char *path) { |
1864 | 0 | int res, xerrno = 0; |
1865 | 0 | time_t now; |
1866 | 0 | char *tz = NULL; |
1867 | 0 | const char *default_tz; |
1868 | 0 | pool *tmp_pool; |
1869 | 0 | pr_error_t *err = NULL; |
1870 | |
|
1871 | 0 | if (path == NULL) { |
1872 | 0 | errno = EINVAL; |
1873 | 0 | return -1; |
1874 | 0 | } |
1875 | | |
1876 | 0 | #if defined(__GLIBC__) && \ |
1877 | 0 | defined(__GLIBC_MINOR__) && \ |
1878 | 0 | __GLIBC__ == 2 && __GLIBC_MINOR__ >= 3 |
1879 | 0 | default_tz = tzname[0]; |
1880 | | #else |
1881 | | /* Per the tzset(3) man page, this should be the assumed default. */ |
1882 | | default_tz = ":/etc/localtime"; |
1883 | | #endif |
1884 | |
|
1885 | 0 | tz = pr_env_get(session.pool, "TZ"); |
1886 | 0 | if (tz == NULL) { |
1887 | 0 | if (pr_env_set(session.pool, "TZ", pstrdup(permanent_pool, |
1888 | 0 | default_tz)) < 0) { |
1889 | 0 | pr_log_debug(DEBUG0, "error setting TZ environment variable to " |
1890 | 0 | "'%s': %s", default_tz, strerror(errno)); |
1891 | |
|
1892 | 0 | } else { |
1893 | 0 | pr_log_debug(DEBUG10, "set TZ environment variable to '%s'", default_tz); |
1894 | 0 | } |
1895 | |
|
1896 | 0 | } else { |
1897 | 0 | pr_log_debug(DEBUG10, "TZ environment variable already set to '%s'", tz); |
1898 | 0 | } |
1899 | |
|
1900 | 0 | pr_log_debug(DEBUG1, "Preparing to chroot to directory '%s'", path); |
1901 | | |
1902 | | /* Prepare for chroots and the ensuing timezone chicanery by calling |
1903 | | * our pr_localtime() routine now, which will cause libc (via localtime(2)) |
1904 | | * to load the tzinfo data into memory, and hopefully retain it (Bug#3431). |
1905 | | */ |
1906 | 0 | tmp_pool = make_sub_pool(session.pool); |
1907 | 0 | now = time(NULL); |
1908 | 0 | (void) pr_localtime(tmp_pool, &now); |
1909 | |
|
1910 | 0 | pr_event_generate("core.chroot", path); |
1911 | |
|
1912 | 0 | PRIVS_ROOT |
1913 | 0 | res = pr_fsio_chroot_with_error(tmp_pool, path, &err); |
1914 | 0 | xerrno = errno; |
1915 | 0 | PRIVS_RELINQUISH |
1916 | |
|
1917 | 0 | if (res < 0) { |
1918 | 0 | pr_error_set_where(err, NULL, __FILE__, __LINE__ - 5); |
1919 | 0 | pr_error_set_why(err, pstrcat(tmp_pool, "chroot to directory '", path, |
1920 | 0 | "'", NULL)); |
1921 | |
|
1922 | 0 | if (err != NULL) { |
1923 | 0 | pr_log_pri(PR_LOG_ERR, "%s", pr_error_strerror(err, 0)); |
1924 | 0 | pr_error_destroy(err); |
1925 | 0 | err = NULL; |
1926 | |
|
1927 | 0 | } else { |
1928 | 0 | pr_log_pri(PR_LOG_ERR, "chroot to '%s' failed for user '%s': %s", path, |
1929 | 0 | session.user ? session.user : "(unknown)", strerror(xerrno)); |
1930 | 0 | } |
1931 | |
|
1932 | 0 | destroy_pool(tmp_pool); |
1933 | 0 | errno = xerrno; |
1934 | 0 | return -1; |
1935 | 0 | } |
1936 | | |
1937 | 0 | pr_log_debug(DEBUG1, "Environment successfully chroot()ed"); |
1938 | 0 | destroy_pool(tmp_pool); |
1939 | 0 | return 0; |
1940 | 0 | } |
1941 | | |
1942 | 0 | int set_groups(pool *p, gid_t primary_gid, array_header *suppl_gids) { |
1943 | 0 | int res = 0; |
1944 | 0 | pool *tmp_pool = NULL; |
1945 | |
|
1946 | 0 | #ifdef HAVE_SETGROUPS |
1947 | 0 | register unsigned int i = 0; |
1948 | 0 | gid_t *gids = NULL, *proc_gids = NULL; |
1949 | 0 | size_t ngids = 0, nproc_gids = 0; |
1950 | 0 | char *strgids = ""; |
1951 | 0 | int have_root_privs = TRUE; |
1952 | | |
1953 | | /* First, check to see whether we even CAN set the process GIDs, which |
1954 | | * requires root privileges. |
1955 | | */ |
1956 | 0 | if (getuid() != PR_ROOT_UID) { |
1957 | 0 | have_root_privs = FALSE; |
1958 | 0 | } |
1959 | |
|
1960 | 0 | if (have_root_privs == FALSE) { |
1961 | 0 | pr_trace_msg(trace_channel, 3, |
1962 | 0 | "unable to set groups due to lack of root privs"); |
1963 | 0 | errno = ENOSYS; |
1964 | 0 | return -1; |
1965 | 0 | } |
1966 | | |
1967 | | /* sanity check */ |
1968 | 0 | if (p == NULL || |
1969 | 0 | suppl_gids == NULL) { |
1970 | |
|
1971 | 0 | # ifndef PR_DEVEL_COREDUMP |
1972 | | /* Set the primary GID of the process. */ |
1973 | 0 | res = setgid(primary_gid); |
1974 | 0 | # endif /* PR_DEVEL_COREDUMP */ |
1975 | |
|
1976 | 0 | return res; |
1977 | 0 | } |
1978 | | |
1979 | 0 | ngids = suppl_gids->nelts; |
1980 | 0 | gids = suppl_gids->elts; |
1981 | |
|
1982 | 0 | if (ngids == 0 || |
1983 | 0 | gids == NULL) { |
1984 | | /* No supplemental GIDs to process. */ |
1985 | |
|
1986 | 0 | # ifndef PR_DEVEL_COREDUMP |
1987 | | /* Set the primary GID of the process. */ |
1988 | 0 | res = setgid(primary_gid); |
1989 | 0 | # endif /* PR_DEVEL_COREDUMP */ |
1990 | |
|
1991 | 0 | return res; |
1992 | 0 | } |
1993 | | |
1994 | 0 | tmp_pool = make_sub_pool(p); |
1995 | 0 | pr_pool_tag(tmp_pool, "set_groups() tmp pool"); |
1996 | |
|
1997 | 0 | proc_gids = pcalloc(tmp_pool, sizeof(gid_t) * (ngids)); |
1998 | | |
1999 | | /* Note: the list of supplemental GIDs may contain duplicates. Sort |
2000 | | * through the list and keep only the unique IDs - this should help avoid |
2001 | | * running into the NGROUPS limit when possible. This algorithm may slow |
2002 | | * things down some; optimize it if/when possible. |
2003 | | */ |
2004 | 0 | proc_gids[nproc_gids++] = gids[0]; |
2005 | |
|
2006 | 0 | for (i = 1; i < ngids; i++) { |
2007 | 0 | register unsigned int j = 0; |
2008 | 0 | unsigned char skip_gid = FALSE; |
2009 | | |
2010 | | /* This duplicate ID search only needs to be done after the first GID |
2011 | | * in the given list is examined, as the first GID cannot be a duplicate. |
2012 | | */ |
2013 | 0 | for (j = 0; j < nproc_gids; j++) { |
2014 | 0 | if (proc_gids[j] == gids[i]) { |
2015 | 0 | skip_gid = TRUE; |
2016 | 0 | break; |
2017 | 0 | } |
2018 | 0 | } |
2019 | |
|
2020 | 0 | if (!skip_gid) { |
2021 | 0 | proc_gids[nproc_gids++] = gids[i]; |
2022 | 0 | } |
2023 | 0 | } |
2024 | |
|
2025 | 0 | for (i = 0; i < nproc_gids; i++) { |
2026 | 0 | char buf[64]; |
2027 | 0 | pr_snprintf(buf, sizeof(buf)-1, "%lu", (unsigned long) proc_gids[i]); |
2028 | 0 | buf[sizeof(buf)-1] = '\0'; |
2029 | |
|
2030 | 0 | strgids = pstrcat(p, strgids, i != 0 ? ", " : "", buf, NULL); |
2031 | 0 | } |
2032 | |
|
2033 | 0 | pr_log_debug(DEBUG10, "setting group %s: %s", nproc_gids == 1 ? "ID" : "IDs", |
2034 | 0 | strgids); |
2035 | | |
2036 | | /* Set the supplemental groups. */ |
2037 | 0 | res = setgroups(nproc_gids, proc_gids); |
2038 | 0 | if (res < 0) { |
2039 | 0 | int xerrno = errno; |
2040 | |
|
2041 | 0 | destroy_pool(tmp_pool); |
2042 | |
|
2043 | 0 | errno = xerrno; |
2044 | 0 | return res; |
2045 | 0 | } |
2046 | 0 | #endif /* !HAVE_SETGROUPS */ |
2047 | | |
2048 | 0 | #ifndef PR_DEVEL_COREDUMP |
2049 | | /* Set the primary GID of the process. */ |
2050 | 0 | res = setgid(primary_gid); |
2051 | 0 | if (res < 0) { |
2052 | 0 | int xerrno = errno; |
2053 | |
|
2054 | 0 | if (tmp_pool != NULL) { |
2055 | 0 | destroy_pool(tmp_pool); |
2056 | 0 | } |
2057 | |
|
2058 | 0 | errno = xerrno; |
2059 | 0 | return res; |
2060 | 0 | } |
2061 | 0 | #endif /* PR_DEVEL_COREDUMP */ |
2062 | | |
2063 | 0 | if (tmp_pool != NULL) { |
2064 | 0 | destroy_pool(tmp_pool); |
2065 | 0 | } |
2066 | |
|
2067 | 0 | return res; |
2068 | 0 | } |
2069 | | |
2070 | 0 | void pr_auth_cache_clear(void) { |
2071 | 0 | if (auth_tab != NULL) { |
2072 | 0 | pr_table_empty(auth_tab); |
2073 | 0 | pr_table_free(auth_tab); |
2074 | 0 | auth_tab = NULL; |
2075 | 0 | } |
2076 | |
|
2077 | 0 | if (uid_tab != NULL) { |
2078 | 0 | pr_table_empty(uid_tab); |
2079 | 0 | pr_table_free(uid_tab); |
2080 | 0 | uid_tab = NULL; |
2081 | 0 | } |
2082 | |
|
2083 | 0 | if (user_tab != NULL) { |
2084 | 0 | pr_table_empty(user_tab); |
2085 | 0 | pr_table_free(user_tab); |
2086 | 0 | user_tab = NULL; |
2087 | 0 | } |
2088 | |
|
2089 | 0 | if (gid_tab != NULL) { |
2090 | 0 | pr_table_empty(gid_tab); |
2091 | 0 | pr_table_free(gid_tab); |
2092 | 0 | gid_tab = NULL; |
2093 | 0 | } |
2094 | |
|
2095 | 0 | if (group_tab != NULL) { |
2096 | 0 | pr_table_empty(group_tab); |
2097 | 0 | pr_table_free(group_tab); |
2098 | 0 | group_tab = NULL; |
2099 | 0 | } |
2100 | 0 | } |
2101 | | |
2102 | 0 | int pr_auth_cache_set(int enable, unsigned int flags) { |
2103 | 0 | if (enable != FALSE && |
2104 | 0 | enable != TRUE) { |
2105 | 0 | errno = EINVAL; |
2106 | 0 | return -1; |
2107 | 0 | } |
2108 | | |
2109 | 0 | if (enable == FALSE) { |
2110 | 0 | if (flags & PR_AUTH_CACHE_FL_UID2NAME) { |
2111 | 0 | auth_caching &= ~PR_AUTH_CACHE_FL_UID2NAME; |
2112 | 0 | pr_trace_msg(trace_channel, 7, "UID-to-name caching (uidcache) disabled"); |
2113 | 0 | } |
2114 | |
|
2115 | 0 | if (flags & PR_AUTH_CACHE_FL_GID2NAME) { |
2116 | 0 | auth_caching &= ~PR_AUTH_CACHE_FL_GID2NAME; |
2117 | 0 | pr_trace_msg(trace_channel, 7, "GID-to-name caching (gidcache) disabled"); |
2118 | 0 | } |
2119 | |
|
2120 | 0 | if (flags & PR_AUTH_CACHE_FL_AUTH_MODULE) { |
2121 | 0 | auth_caching &= ~PR_AUTH_CACHE_FL_AUTH_MODULE; |
2122 | 0 | pr_trace_msg(trace_channel, 7, |
2123 | 0 | "auth module caching (authcache) disabled"); |
2124 | 0 | } |
2125 | |
|
2126 | 0 | if (flags & PR_AUTH_CACHE_FL_NAME2UID) { |
2127 | 0 | auth_caching &= ~PR_AUTH_CACHE_FL_NAME2UID; |
2128 | 0 | pr_trace_msg(trace_channel, 7, |
2129 | 0 | "name-to-UID caching (usercache) disabled"); |
2130 | 0 | } |
2131 | |
|
2132 | 0 | if (flags & PR_AUTH_CACHE_FL_NAME2GID) { |
2133 | 0 | auth_caching &= ~PR_AUTH_CACHE_FL_NAME2GID; |
2134 | 0 | pr_trace_msg(trace_channel, 7, |
2135 | 0 | "name-to-GID caching (groupcache) disabled"); |
2136 | 0 | } |
2137 | |
|
2138 | 0 | if (flags & PR_AUTH_CACHE_FL_BAD_UID2NAME) { |
2139 | 0 | auth_caching &= ~PR_AUTH_CACHE_FL_BAD_UID2NAME; |
2140 | 0 | pr_trace_msg(trace_channel, 7, |
2141 | 0 | "UID-to-name negative caching (uidcache) disabled"); |
2142 | 0 | } |
2143 | |
|
2144 | 0 | if (flags & PR_AUTH_CACHE_FL_BAD_GID2NAME) { |
2145 | 0 | auth_caching &= ~PR_AUTH_CACHE_FL_BAD_GID2NAME; |
2146 | 0 | pr_trace_msg(trace_channel, 7, |
2147 | 0 | "GID-to-name negative caching (gidcache) disabled"); |
2148 | 0 | } |
2149 | |
|
2150 | 0 | if (flags & PR_AUTH_CACHE_FL_BAD_NAME2UID) { |
2151 | 0 | auth_caching &= ~PR_AUTH_CACHE_FL_BAD_NAME2UID; |
2152 | 0 | pr_trace_msg(trace_channel, 7, |
2153 | 0 | "name-to-UID negative caching (usercache) disabled"); |
2154 | 0 | } |
2155 | |
|
2156 | 0 | if (flags & PR_AUTH_CACHE_FL_BAD_NAME2GID) { |
2157 | 0 | auth_caching &= ~PR_AUTH_CACHE_FL_BAD_NAME2GID; |
2158 | 0 | pr_trace_msg(trace_channel, 7, |
2159 | 0 | "name-to-GID negative caching (groupcache) disabled"); |
2160 | 0 | } |
2161 | 0 | } |
2162 | |
|
2163 | 0 | if (enable == TRUE) { |
2164 | 0 | if (flags & PR_AUTH_CACHE_FL_UID2NAME) { |
2165 | 0 | auth_caching |= PR_AUTH_CACHE_FL_UID2NAME; |
2166 | 0 | pr_trace_msg(trace_channel, 7, "UID-to-name caching (uidcache) enabled"); |
2167 | 0 | } |
2168 | |
|
2169 | 0 | if (flags & PR_AUTH_CACHE_FL_GID2NAME) { |
2170 | 0 | auth_caching |= PR_AUTH_CACHE_FL_GID2NAME; |
2171 | 0 | pr_trace_msg(trace_channel, 7, "GID-to-name caching (gidcache) enabled"); |
2172 | 0 | } |
2173 | |
|
2174 | 0 | if (flags & PR_AUTH_CACHE_FL_AUTH_MODULE) { |
2175 | 0 | auth_caching |= PR_AUTH_CACHE_FL_AUTH_MODULE; |
2176 | 0 | pr_trace_msg(trace_channel, 7, "auth module caching (authcache) enabled"); |
2177 | 0 | } |
2178 | |
|
2179 | 0 | if (flags & PR_AUTH_CACHE_FL_NAME2UID) { |
2180 | 0 | auth_caching |= PR_AUTH_CACHE_FL_NAME2UID; |
2181 | 0 | pr_trace_msg(trace_channel, 7, "name-to-UID caching (usercache) enabled"); |
2182 | 0 | } |
2183 | |
|
2184 | 0 | if (flags & PR_AUTH_CACHE_FL_NAME2GID) { |
2185 | 0 | auth_caching |= PR_AUTH_CACHE_FL_NAME2GID; |
2186 | 0 | pr_trace_msg(trace_channel, 7, |
2187 | 0 | "name-to-GID caching (groupcache) enabled"); |
2188 | 0 | } |
2189 | |
|
2190 | 0 | if (flags & PR_AUTH_CACHE_FL_BAD_UID2NAME) { |
2191 | 0 | auth_caching |= PR_AUTH_CACHE_FL_BAD_UID2NAME; |
2192 | 0 | pr_trace_msg(trace_channel, 7, |
2193 | 0 | "UID-to-name negative caching (uidcache) enabled"); |
2194 | 0 | } |
2195 | |
|
2196 | 0 | if (flags & PR_AUTH_CACHE_FL_BAD_GID2NAME) { |
2197 | 0 | auth_caching |= PR_AUTH_CACHE_FL_BAD_GID2NAME; |
2198 | 0 | pr_trace_msg(trace_channel, 7, |
2199 | 0 | "GID-to-name negative caching (gidcache) enabled"); |
2200 | 0 | } |
2201 | |
|
2202 | 0 | if (flags & PR_AUTH_CACHE_FL_BAD_NAME2UID) { |
2203 | 0 | auth_caching |= PR_AUTH_CACHE_FL_BAD_NAME2UID; |
2204 | 0 | pr_trace_msg(trace_channel, 7, |
2205 | 0 | "name-to-UID negative caching (usercache) enabled"); |
2206 | 0 | } |
2207 | |
|
2208 | 0 | if (flags & PR_AUTH_CACHE_FL_BAD_NAME2GID) { |
2209 | 0 | auth_caching |= PR_AUTH_CACHE_FL_BAD_NAME2GID; |
2210 | 0 | pr_trace_msg(trace_channel, 7, |
2211 | 0 | "name-to-GID negative caching (groupcache) enabled"); |
2212 | 0 | } |
2213 | 0 | } |
2214 | |
|
2215 | 0 | return 0; |
2216 | 0 | } |
2217 | | |
2218 | 0 | int pr_auth_add_auth_only_module(const char *name) { |
2219 | 0 | struct auth_module_elt *elt = NULL; |
2220 | |
|
2221 | 0 | if (name == NULL) { |
2222 | 0 | errno = EINVAL; |
2223 | 0 | return -1; |
2224 | 0 | } |
2225 | | |
2226 | 0 | if (auth_pool == NULL) { |
2227 | 0 | auth_pool = make_sub_pool(permanent_pool); |
2228 | 0 | pr_pool_tag(auth_pool, "Auth API"); |
2229 | 0 | } |
2230 | |
|
2231 | 0 | if (!(auth_caching & PR_AUTH_CACHE_FL_AUTH_MODULE)) { |
2232 | | /* We won't be using the auth-only module cache, so there's no need to |
2233 | | * accept this. |
2234 | | */ |
2235 | 0 | pr_trace_msg(trace_channel, 9, "not adding '%s' to the auth-only list: " |
2236 | 0 | "caching of auth-only modules disabled", name); |
2237 | 0 | return 0; |
2238 | 0 | } |
2239 | | |
2240 | 0 | if (auth_module_list == NULL) { |
2241 | 0 | auth_module_list = xaset_create(auth_pool, NULL); |
2242 | 0 | } |
2243 | | |
2244 | | /* Prevent duplicates; they could lead to a memory leak. */ |
2245 | 0 | for (elt = (struct auth_module_elt *) auth_module_list->xas_list; elt; |
2246 | 0 | elt = elt->next) { |
2247 | 0 | if (strcmp(elt->name, name) == 0) { |
2248 | 0 | errno = EEXIST; |
2249 | 0 | return -1; |
2250 | 0 | } |
2251 | 0 | } |
2252 | | |
2253 | 0 | elt = pcalloc(auth_pool, sizeof(struct auth_module_elt)); |
2254 | 0 | elt->name = pstrdup(auth_pool, name); |
2255 | |
|
2256 | 0 | if (xaset_insert_end(auth_module_list, (xasetmember_t *) elt) < 0) { |
2257 | 0 | pr_trace_msg(trace_channel, 1, "error adding '%s' to auth-only " |
2258 | 0 | "module set: %s", name, strerror(errno)); |
2259 | 0 | return -1; |
2260 | 0 | } |
2261 | | |
2262 | 0 | pr_trace_msg(trace_channel, 5, "added '%s' to auth-only module list", name); |
2263 | 0 | return 0; |
2264 | 0 | } |
2265 | | |
2266 | 0 | int pr_auth_clear_auth_only_modules(void) { |
2267 | 0 | if (auth_module_list == NULL) { |
2268 | 0 | errno = EPERM; |
2269 | 0 | return -1; |
2270 | 0 | } |
2271 | | |
2272 | 0 | auth_module_list = NULL; |
2273 | 0 | pr_trace_msg(trace_channel, 5, "cleared auth-only module list"); |
2274 | 0 | return 0; |
2275 | 0 | } |
2276 | | |
2277 | 0 | int pr_auth_remove_auth_only_module(const char *name) { |
2278 | 0 | struct auth_module_elt *elt = NULL; |
2279 | |
|
2280 | 0 | if (name == NULL) { |
2281 | 0 | errno = EINVAL; |
2282 | 0 | return -1; |
2283 | 0 | } |
2284 | | |
2285 | 0 | if (!(auth_caching & PR_AUTH_CACHE_FL_AUTH_MODULE)) { |
2286 | | /* We won't be using the auth-only module cache, so there's no need to |
2287 | | * accept this. |
2288 | | */ |
2289 | 0 | pr_trace_msg(trace_channel, 9, "not removing '%s' from the auth-only list: " |
2290 | 0 | "caching of auth-only modules disabled", name); |
2291 | 0 | return 0; |
2292 | 0 | } |
2293 | | |
2294 | 0 | if (auth_module_list == NULL) { |
2295 | 0 | pr_trace_msg(trace_channel, 9, "not removing '%s' from list: " |
2296 | 0 | "empty auth-only module list", name); |
2297 | 0 | errno = EPERM; |
2298 | 0 | return -1; |
2299 | 0 | } |
2300 | | |
2301 | 0 | for (elt = (struct auth_module_elt *) auth_module_list->xas_list; elt; |
2302 | 0 | elt = elt->next) { |
2303 | 0 | if (strcmp(elt->name, name) == 0) { |
2304 | 0 | if (xaset_remove(auth_module_list, (xasetmember_t *) elt) < 0) { |
2305 | 0 | pr_trace_msg(trace_channel, 1, "error removing '%s' from auth-only " |
2306 | 0 | "module set: %s", name, strerror(errno)); |
2307 | 0 | return -1; |
2308 | 0 | } |
2309 | | |
2310 | 0 | pr_trace_msg(trace_channel, 5, "removed '%s' from auth-only module list", |
2311 | 0 | name); |
2312 | 0 | return 0; |
2313 | 0 | } |
2314 | 0 | } |
2315 | | |
2316 | 0 | errno = ENOENT; |
2317 | 0 | return -1; |
2318 | 0 | } |
2319 | | |
2320 | 0 | const char *pr_auth_get_home(pool *p, const char *pw_dir) { |
2321 | 0 | config_rec *c; |
2322 | 0 | const char *home_dir; |
2323 | |
|
2324 | 0 | if (p == NULL || |
2325 | 0 | pw_dir == NULL) { |
2326 | 0 | errno = EINVAL; |
2327 | 0 | return NULL; |
2328 | 0 | } |
2329 | | |
2330 | 0 | home_dir = pw_dir; |
2331 | |
|
2332 | 0 | c = find_config(main_server->conf, CONF_PARAM, "RewriteHome", FALSE); |
2333 | 0 | if (c == NULL) { |
2334 | 0 | return home_dir; |
2335 | 0 | } |
2336 | | |
2337 | 0 | if (*((int *) c->argv[0]) == FALSE) { |
2338 | 0 | return home_dir; |
2339 | 0 | } |
2340 | | |
2341 | | /* Rather than using a cmd_rec dispatched to mod_rewrite's PRE_CMD handler, |
2342 | | * we use an approach with looser coupling to mod_rewrite: stash the |
2343 | | * home directory in the session.notes table, and generate an event. |
2344 | | * The mod_rewrite module will listen for this event, rewrite the stashed |
2345 | | * home directory as necessary, and be done. |
2346 | | * |
2347 | | * Thus after the event has been generated, we retrieve (and remove) the |
2348 | | * (possibly rewritten) home directory from the session.notes table. |
2349 | | * This approach means that other modules which wish to get involved |
2350 | | * in the rewriting of the home directory can also do so. |
2351 | | */ |
2352 | | |
2353 | 0 | (void) pr_table_remove(session.notes, "mod_auth.home-dir", NULL); |
2354 | 0 | if (pr_table_add(session.notes, "mod_auth.home-dir", |
2355 | 0 | pstrdup(p, pw_dir), 0) < 0) { |
2356 | 0 | pr_trace_msg(trace_channel, 3, |
2357 | 0 | "error stashing home dir in session.notes: %s", strerror(errno)); |
2358 | 0 | return home_dir; |
2359 | 0 | } |
2360 | | |
2361 | 0 | pr_event_generate("mod_auth.rewrite-home", NULL); |
2362 | |
|
2363 | 0 | home_dir = pr_table_get(session.notes, "mod_auth.home-dir", NULL); |
2364 | 0 | if (home_dir == NULL) { |
2365 | 0 | pr_trace_msg(trace_channel, 3, |
2366 | 0 | "error getting home dir from session.notes: %s", strerror(errno)); |
2367 | 0 | return pw_dir; |
2368 | 0 | } |
2369 | | |
2370 | 0 | (void) pr_table_remove(session.notes, "mod_auth.home-dir", NULL); |
2371 | |
|
2372 | 0 | pr_log_debug(DEBUG9, "returning rewritten home directory '%s' for original " |
2373 | 0 | "home directory '%s'", home_dir, pw_dir); |
2374 | 0 | pr_trace_msg(trace_channel, 9, "returning rewritten home directory '%s' " |
2375 | 0 | "for original home directory '%s'", home_dir, pw_dir); |
2376 | |
|
2377 | 0 | return home_dir; |
2378 | 0 | } |
2379 | | |
2380 | 0 | size_t pr_auth_set_max_password_len(pool *p, size_t len) { |
2381 | 0 | size_t prev_len; |
2382 | |
|
2383 | 0 | prev_len = auth_max_passwd_len; |
2384 | |
|
2385 | 0 | if (len == 0) { |
2386 | | /* Restore default. */ |
2387 | 0 | auth_max_passwd_len = PR_TUNABLE_PASSWORD_MAX; |
2388 | |
|
2389 | 0 | } else { |
2390 | 0 | auth_max_passwd_len = len; |
2391 | 0 | } |
2392 | |
|
2393 | 0 | return prev_len; |
2394 | 0 | } |
2395 | | |
2396 | | char *pr_auth_bcrypt(pool *p, const char *key, const char *salt, |
2397 | 0 | size_t *hashed_len) { |
2398 | 0 | char hashed[128], *res; |
2399 | |
|
2400 | 0 | if (p == NULL || |
2401 | 0 | key == NULL || |
2402 | 0 | salt == NULL || |
2403 | 0 | hashed_len == NULL) { |
2404 | 0 | errno = EINVAL; |
2405 | 0 | return NULL; |
2406 | 0 | } |
2407 | | |
2408 | 0 | if (bcrypt_hashpass(key, salt, hashed, sizeof(hashed)) != 0) { |
2409 | 0 | return NULL; |
2410 | 0 | } |
2411 | | |
2412 | 0 | res = palloc(p, sizeof(hashed)); |
2413 | 0 | memcpy(res, hashed, sizeof(hashed)); |
2414 | 0 | *hashed_len = sizeof(hashed); |
2415 | |
|
2416 | 0 | return res; |
2417 | 0 | } |
2418 | | |
2419 | | /* Internal use only. To be called in the session process. */ |
2420 | 0 | int init_auth(void) { |
2421 | 0 | if (auth_pool == NULL) { |
2422 | 0 | auth_pool = make_sub_pool(permanent_pool); |
2423 | 0 | pr_pool_tag(auth_pool, "Auth API"); |
2424 | 0 | } |
2425 | |
|
2426 | 0 | return 0; |
2427 | 0 | } |