1 /* pam_handlers.c -- pam config file parsing and module loading */
2
3 /*
4 * created by Marc Ewing.
5 * Currently maintained by Andrew G. Morgan <morgan@kernel.org>
6 *
7 */
8
9 #include "pam_private.h"
10 #include "pam_inline.h"
11
12 #include <stdlib.h>
13 #include <stdio.h>
14 #include <string.h>
15 #include <sys/types.h>
16 #include <sys/stat.h>
17 #include <fcntl.h>
18 #include <unistd.h>
19
20 #define BUF_SIZE 1024
21 #define MODULE_CHUNK 4
22 #define UNKNOWN_MODULE "<*unknown module*>"
23 #ifndef _PAM_ISA
24 #define _PAM_ISA "."
25 #endif
26
27 static int _pam_assemble_line(FILE *f, char *buf, int buf_len);
28
29 static void _pam_free_handlers_aux(struct handler **hp);
30
31 static int _pam_add_handler(pam_handle_t *pamh
32 , int must_fail, int other, int stack_level, int type
33 , int *actions, const char *mod_path
34 , int argc, char **argv, int argvlen);
35
36 /* Values for module type */
37
38 #define PAM_T_ANY 0
39 #define PAM_T_AUTH 1
40 #define PAM_T_SESS 2
41 #define PAM_T_ACCT 4
42 #define PAM_T_PASS 8
43
44 static int _pam_load_conf_file(pam_handle_t *pamh, const char *config_name
45 , const char *service /* specific file */
46 , int module_type /* specific type */
47 , int stack_level /* level of substack */
48 #ifdef PAM_READ_BOTH_CONFS
49 , int not_other
50 #endif /* PAM_READ_BOTH_CONFS */
51 );
52
53 static int _pam_parse_conf_file(pam_handle_t *pamh, FILE *f
54 , const char *known_service /* specific file */
55 , int requested_module_type /* specific type */
56 , int stack_level /* level of substack */
57 #ifdef PAM_READ_BOTH_CONFS
58 , int not_other
59 #endif /* PAM_READ_BOTH_CONFS */
60 )
61 {
62 char buf[BUF_SIZE];
63 int x; /* read a line from the FILE *f ? */
64 /*
65 * read a line from the configuration (FILE *) f
66 */
67 while ((x = _pam_assemble_line(f, buf, BUF_SIZE)) > 0) {
68 char *tok, *nexttok=NULL;
69 const char *this_service;
70 const char *mod_path;
71 int module_type, actions[_PAM_RETURN_VALUES];
72 int other; /* set if module is for PAM_DEFAULT_SERVICE */
73 int res; /* module added successfully? */
74 int handler_type = PAM_HT_MODULE; /* regular handler from a module */
75 int argc;
76 char **argv;
77 int argvlen;
78
79 D(("_pam_init_handler: LINE: %s", buf));
80 if (known_service != NULL) {
81 nexttok = buf;
82 /* No service field: all lines are for the known service. */
83 this_service = known_service;
84 } else {
85 this_service = tok = _pam_StrTok(buf, " \n\t", &nexttok);
86 }
87
88 #ifdef PAM_READ_BOTH_CONFS
89 if (not_other)
90 other = 0;
91 else
92 #endif /* PAM_READ_BOTH_CONFS */
93 other = !strcasecmp(this_service, PAM_DEFAULT_SERVICE);
94
95 /* accept "service name" or PAM_DEFAULT_SERVICE modules */
96 if (!strcasecmp(this_service, pamh->service_name) || other) {
97 int pam_include = 0;
98 int substack = 0;
99
100 /* This is a service we are looking for */
101 D(("_pam_init_handlers: Found PAM config entry for: %s"
102 , this_service));
103
104 tok = _pam_StrTok(NULL, " \n\t", &nexttok);
105 if (tok == NULL) {
106 /* module type does not exist */
107 D(("_pam_init_handlers: empty module type for %s", this_service));
108 pam_syslog(pamh, LOG_ERR,
109 "(%s) empty module type", this_service);
110 module_type = (requested_module_type != PAM_T_ANY) ?
111 requested_module_type : PAM_T_AUTH; /* most sensitive */
112 handler_type = PAM_HT_MUST_FAIL; /* install as normal but fail when dispatched */
113 } else {
114 if (tok[0] == '-') { /* do not log module load errors */
115 handler_type = PAM_HT_SILENT_MODULE;
116 ++tok;
117 }
118 if (!strcasecmp("auth", tok)) {
119 module_type = PAM_T_AUTH;
120 } else if (!strcasecmp("session", tok)) {
121 module_type = PAM_T_SESS;
122 } else if (!strcasecmp("account", tok)) {
123 module_type = PAM_T_ACCT;
124 } else if (!strcasecmp("password", tok)) {
125 module_type = PAM_T_PASS;
126 } else {
127 /* Illegal module type */
128 D(("_pam_init_handlers: bad module type: %s", tok));
129 pam_syslog(pamh, LOG_ERR, "(%s) illegal module type: %s",
130 this_service, tok);
131 module_type = (requested_module_type != PAM_T_ANY) ?
132 requested_module_type : PAM_T_AUTH; /* most sensitive */
133 handler_type = PAM_HT_MUST_FAIL; /* install as normal but fail when dispatched */
134 }
135 }
136 D(("Using %s config entry: %s", handler_type?"BAD ":"", tok));
137 if (requested_module_type != PAM_T_ANY &&
138 module_type != requested_module_type) {
139 D(("Skipping config entry: %s (requested=%d, found=%d)",
140 tok, requested_module_type, module_type));
141 continue;
142 }
143
144 /* reset the actions to .._UNDEF's -- this is so that
145 we can work out which entries are not yet set (for default). */
146 {
147 int i;
148 for (i=0; i<_PAM_RETURN_VALUES;
149 actions[i++] = _PAM_ACTION_UNDEF);
150 }
151 tok = _pam_StrTok(NULL, " \n\t", &nexttok);
152 if (tok == NULL) {
153 /* no module name given */
154 D(("_pam_init_handlers: no control flag supplied"));
155 pam_syslog(pamh, LOG_ERR,
156 "(%s) no control flag supplied", this_service);
157 _pam_set_default_control(actions, _PAM_ACTION_BAD);
158 handler_type = PAM_HT_MUST_FAIL;
159 } else if (!strcasecmp("required", tok)) {
160 D(("*PAM_F_REQUIRED*"));
161 actions[PAM_SUCCESS] = _PAM_ACTION_OK;
162 actions[PAM_NEW_AUTHTOK_REQD] = _PAM_ACTION_OK;
163 actions[PAM_IGNORE] = _PAM_ACTION_IGNORE;
164 _pam_set_default_control(actions, _PAM_ACTION_BAD);
165 } else if (!strcasecmp("requisite", tok)) {
166 D(("*PAM_F_REQUISITE*"));
167 actions[PAM_SUCCESS] = _PAM_ACTION_OK;
168 actions[PAM_NEW_AUTHTOK_REQD] = _PAM_ACTION_OK;
169 actions[PAM_IGNORE] = _PAM_ACTION_IGNORE;
170 _pam_set_default_control(actions, _PAM_ACTION_DIE);
171 } else if (!strcasecmp("optional", tok)) {
172 D(("*PAM_F_OPTIONAL*"));
173 actions[PAM_SUCCESS] = _PAM_ACTION_OK;
174 actions[PAM_NEW_AUTHTOK_REQD] = _PAM_ACTION_OK;
175 _pam_set_default_control(actions, _PAM_ACTION_IGNORE);
176 } else if (!strcasecmp("sufficient", tok)) {
177 D(("*PAM_F_SUFFICIENT*"));
178 actions[PAM_SUCCESS] = _PAM_ACTION_DONE;
179 actions[PAM_NEW_AUTHTOK_REQD] = _PAM_ACTION_DONE;
180 _pam_set_default_control(actions, _PAM_ACTION_IGNORE);
181 } else if (!strcasecmp("include", tok)) {
182 D(("*PAM_F_INCLUDE*"));
183 pam_include = 1;
184 substack = 0;
185 } else if (!strcasecmp("substack", tok)) {
186 D(("*PAM_F_SUBSTACK*"));
187 pam_include = 1;
188 substack = 1;
189 } else {
190 D(("will need to parse %s", tok));
191 _pam_parse_control(actions, tok);
192 /* by default the default is to treat as failure */
193 _pam_set_default_control(actions, _PAM_ACTION_BAD);
194 }
195
196 tok = _pam_StrTok(NULL, " \n\t", &nexttok);
197 if (pam_include) {
198 if (substack) {
199 res = _pam_add_handler(pamh, PAM_HT_SUBSTACK, other,
200 stack_level, module_type, actions, tok,
201 0, NULL, 0);
202 if (res != PAM_SUCCESS) {
203 pam_syslog(pamh, LOG_ERR, "error adding substack %s", tok);
204 D(("failed to load module - aborting"));
205 return PAM_ABORT;
206 }
207 }
208 if (_pam_load_conf_file(pamh, tok, this_service, module_type,
209 stack_level + substack
210 #ifdef PAM_READ_BOTH_CONFS
211 , !other
212 #endif /* PAM_READ_BOTH_CONFS */
213 ) == PAM_SUCCESS)
214 continue;
215 _pam_set_default_control(actions, _PAM_ACTION_BAD);
216 mod_path = NULL;
217 handler_type = PAM_HT_MUST_FAIL;
218 nexttok = NULL;
219 } else if (tok != NULL) {
220 mod_path = tok;
221 D(("mod_path = %s",mod_path));
222 } else {
223 /* no module name given */
224 D(("_pam_init_handlers: no module name supplied"));
225 pam_syslog(pamh, LOG_ERR,
226 "(%s) no module name supplied", this_service);
227 mod_path = NULL;
228 handler_type = PAM_HT_MUST_FAIL;
229 }
230
231 /* nexttok points to remaining arguments... */
232
233 if (nexttok != NULL) {
234 D(("list: %s",nexttok));
235 argvlen = _pam_mkargv(nexttok, &argv, &argc);
236 D(("argvlen = %d",argvlen));
237 } else { /* there are no arguments so fix by hand */
238 D(("_pam_init_handlers: empty argument list"));
239 argvlen = argc = 0;
240 argv = NULL;
241 }
242
243 #ifdef PAM_DEBUG
244 {
245 int y;
246
247 D(("CONF%s: %s%s %d %s %d"
248 , handler_type==PAM_HT_MUST_FAIL?"<*will fail*>":""
249 , this_service, other ? "(backup)":""
250 , module_type
251 , mod_path, argc));
252 for (y = 0; y < argc; y++) {
253 D(("CONF: %s", argv[y]));
254 }
255 for (y = 0; y<_PAM_RETURN_VALUES; ++y) {
256 D(("RETURN %s(%d) -> %d %s",
257 _pam_token_returns[y], y, actions[y],
258 actions[y]>0 ? "jump":
259 _pam_token_actions[-actions[y]]));
260 }
261 }
262 #endif
263
264 res = _pam_add_handler(pamh, handler_type, other, stack_level
265 , module_type, actions, mod_path
266 , argc, argv, argvlen);
267 if (res != PAM_SUCCESS) {
268 pam_syslog(pamh, LOG_ERR, "error loading %s", mod_path);
269 D(("failed to load module - aborting"));
270 return PAM_ABORT;
271 }
272 }
273 }
274
275 return ( (x < 0) ? PAM_ABORT:PAM_SUCCESS );
276 }
277
278 static int
279 _pam_open_config_file(pam_handle_t *pamh
280 , const char *service
281 , char **path
282 , FILE **file)
283 {
284 const char *const pamd_dirs[] = { PAM_CONFIG_DF, PAM_CONFIG_DIST_DF
285 #ifdef VENDORDIR
286 , PAM_CONFIG_DIST2_DF
287 #endif
288 };
289 char *p = NULL;
290 FILE *f;
291 size_t i;
292
293 /* Absolute path */
294 if (service[0] == '/') {
295 p = _pam_strdup(service);
296 if (p == NULL) {
297 pam_syslog(pamh, LOG_CRIT, "strdup failed");
298 return PAM_BUF_ERR;
299 }
300 } else if (pamh->confdir != NULL) {
301 if (asprintf (&p, "%s/%s", pamh->confdir, service) < 0) {
302 pam_syslog(pamh, LOG_CRIT, "asprintf failed");
303 return PAM_BUF_ERR;
304 }
305 }
306
307 if (p != NULL) {
308 D(("opening %s", p));
309 f = fopen(p, "r");
310 if (f != NULL) {
311 *path = p;
312 *file = f;
313 return PAM_SUCCESS;
314 }
315 _pam_drop(p);
316 return PAM_ABORT;
317 }
318
319 for (i = 0; i < PAM_ARRAY_SIZE(pamd_dirs); i++) {
320 DIAG_PUSH_IGNORE_FORMAT_NONLITERAL
321 if (asprintf (&p, pamd_dirs[i], service) < 0) {
322 pam_syslog(pamh, LOG_CRIT, "asprintf failed");
323 return PAM_BUF_ERR;
324 }
325 DIAG_POP_IGNORE_FORMAT_NONLITERAL
326
327 D(("opening %s", p));
328 f = fopen(p, "r");
329 if (f != NULL) {
330 *path = p;
331 *file = f;
332 return PAM_SUCCESS;
333 }
334 _pam_drop(p);
335 }
336
337 return PAM_ABORT;
338 }
339
340 static int _pam_load_conf_file(pam_handle_t *pamh, const char *config_name
341 , const char *service /* specific file */
342 , int module_type /* specific type */
343 , int stack_level /* level of substack */
344 #ifdef PAM_READ_BOTH_CONFS
345 , int not_other
346 #endif /* PAM_READ_BOTH_CONFS */
347 )
348 {
349 FILE *f;
350 char *path = NULL;
351 int retval = PAM_ABORT;
352
353 D(("_pam_load_conf_file called"));
354
355 if (stack_level >= PAM_SUBSTACK_MAX_LEVEL) {
356 D(("maximum level of substacks reached"));
357 pam_syslog(pamh, LOG_ERR, "maximum level of substacks reached");
358 return PAM_ABORT;
359 }
360
361 if (config_name == NULL) {
362 D(("no config file supplied"));
363 pam_syslog(pamh, LOG_ERR, "(%s) no config name supplied", service);
364 return PAM_ABORT;
365 }
366
367 if (_pam_open_config_file(pamh, config_name, &path, &f) == PAM_SUCCESS) {
368 retval = _pam_parse_conf_file(pamh, f, service, module_type, stack_level
369 #ifdef PAM_READ_BOTH_CONFS
370 , not_other
371 #endif /* PAM_READ_BOTH_CONFS */
372 );
373 if (retval != PAM_SUCCESS)
374 pam_syslog(pamh, LOG_ERR,
375 "_pam_load_conf_file: error reading %s: %s",
376 path, pam_strerror(pamh, retval));
377 _pam_drop(path);
378 fclose(f);
379 } else {
380 D(("unable to open %s", config_name));
381 pam_syslog(pamh, LOG_ERR,
382 "_pam_load_conf_file: unable to open config for %s",
383 config_name);
384 }
385
386 return retval;
387 }
388
389 /* Parse config file, allocate handler structures, dlopen() */
390 int _pam_init_handlers(pam_handle_t *pamh)
391 {
392 FILE *f;
393 int retval;
394
395 D(("_pam_init_handlers called"));
396 IF_NO_PAMH("_pam_init_handlers",pamh,PAM_SYSTEM_ERR);
397
398 /* Return immediately if everything is already loaded */
399 if (pamh->handlers.handlers_loaded) {
400 return PAM_SUCCESS;
401 }
402
403 D(("_pam_init_handlers: initializing"));
404
405 /* First clean the service structure */
406
407 _pam_free_handlers(pamh);
408 if (! pamh->handlers.module) {
409 if ((pamh->handlers.module =
410 malloc(MODULE_CHUNK * sizeof(struct loaded_module))) == NULL) {
411 pam_syslog(pamh, LOG_CRIT,
412 "_pam_init_handlers: no memory loading module");
413 return PAM_BUF_ERR;
414 }
415 pamh->handlers.modules_allocated = MODULE_CHUNK;
416 pamh->handlers.modules_used = 0;
417 }
418
419 if (pamh->service_name == NULL) {
420 return PAM_BAD_ITEM; /* XXX - better error? */
421 }
422
423 #ifdef PAM_LOCKING
424 /* Is the PAM subsystem locked? */
425 {
426 int fd_tmp;
427
428 if ((fd_tmp = open( PAM_LOCK_FILE, O_RDONLY )) != -1) {
429 pam_syslog(pamh, LOG_ERR,
430 "_pam_init_handlers: PAM lockfile ("
431 PAM_LOCK_FILE ") exists - aborting");
432 (void) close(fd_tmp);
433 /*
434 * to avoid swamping the system with requests
435 */
436 _pam_start_timer(pamh);
437 pam_fail_delay(pamh, 5000000);
438 _pam_await_timer(pamh, PAM_ABORT);
439
440 return PAM_ABORT;
441 }
442 }
443 #endif /* PAM_LOCKING */
444
445 /*
446 * Now parse the config file(s) and add handlers
447 */
448 {
449 struct stat test_d;
450
451 /* Is there a PAM_CONFIG_D directory? */
452 if (pamh->confdir != NULL ||
453 (stat(PAM_CONFIG_D, &test_d) == 0 && S_ISDIR(test_d.st_mode)) ||
454 (stat(PAM_CONFIG_DIST_D, &test_d) == 0 && S_ISDIR(test_d.st_mode))
455 #ifdef PAM_CONFIG_DIST2_D
456 || (stat(PAM_CONFIG_DIST2_D, &test_d) == 0
457 && S_ISDIR(test_d.st_mode))
458 #endif
459 ) {
460 char *path = NULL;
461 int read_something=0;
462
463 if (_pam_open_config_file(pamh, pamh->service_name, &path, &f) == PAM_SUCCESS) {
464 retval = _pam_parse_conf_file(pamh, f, pamh->service_name,
465 PAM_T_ANY, 0
466 #ifdef PAM_READ_BOTH_CONFS
467 , 0
468 #endif /* PAM_READ_BOTH_CONFS */
469 );
470 if (retval != PAM_SUCCESS) {
471 pam_syslog(pamh, LOG_ERR,
472 "_pam_init_handlers: error reading %s",
473 path);
474 pam_syslog(pamh, LOG_ERR, "_pam_init_handlers: [%s]",
475 pam_strerror(pamh, retval));
476 } else {
477 read_something = 1;
478 }
479 _pam_drop(path);
480 fclose(f);
481 } else {
482 D(("unable to open configuration for %s", pamh->service_name));
483 #ifdef PAM_READ_BOTH_CONFS
484 D(("checking %s", PAM_CONFIG));
485
486 if (pamh->confdir == NULL
487 && (f = fopen(PAM_CONFIG,"r")) != NULL) {
488 retval = _pam_parse_conf_file(pamh, f, NULL, PAM_T_ANY, 0, 1);
489 fclose(f);
490 } else
491 #endif /* PAM_READ_BOTH_CONFS */
492 retval = PAM_SUCCESS;
493 /*
494 * XXX - should we log an error? Some people want to always
495 * use "other"
496 */
497 }
498
499 if (retval == PAM_SUCCESS) {
500 /* now parse the PAM_DEFAULT_SERVICE */
501
502 if (_pam_open_config_file(pamh, PAM_DEFAULT_SERVICE, &path, &f) == PAM_SUCCESS) {
503 /* would test magic here? */
504 retval = _pam_parse_conf_file(pamh, f, PAM_DEFAULT_SERVICE,
505 PAM_T_ANY, 0
506 #ifdef PAM_READ_BOTH_CONFS
507 , 0
508 #endif /* PAM_READ_BOTH_CONFS */
509 );
510 if (retval != PAM_SUCCESS) {
511 pam_syslog(pamh, LOG_ERR,
512 "_pam_init_handlers: error reading %s",
513 path);
514 pam_syslog(pamh, LOG_ERR,
515 "_pam_init_handlers: [%s]",
516 pam_strerror(pamh, retval));
517 } else {
518 read_something = 1;
519 }
520 _pam_drop(path);
521 fclose(f);
522 } else {
523 D(("unable to open %s", PAM_DEFAULT_SERVICE));
524 pam_syslog(pamh, LOG_ERR,
525 "_pam_init_handlers: no default config %s",
526 PAM_DEFAULT_SERVICE);
527 }
528 if (!read_something) { /* nothing read successfully */
529 retval = PAM_ABORT;
530 }
531 }
532 } else {
533 if ((f = fopen(PAM_CONFIG, "r")) == NULL) {
534 pam_syslog(pamh, LOG_ERR, "_pam_init_handlers: could not open "
535 PAM_CONFIG );
536 return PAM_ABORT;
537 }
538
539 retval = _pam_parse_conf_file(pamh, f, NULL, PAM_T_ANY, 0
540 #ifdef PAM_READ_BOTH_CONFS
541 , 0
542 #endif /* PAM_READ_BOTH_CONFS */
543 );
544
545 D(("closing configuration file"));
546 fclose(f);
547 }
548 }
549
550 if (retval != PAM_SUCCESS) {
551 /* Read error */
552 pam_syslog(pamh, LOG_ERR, "error reading PAM configuration file");
553 return PAM_ABORT;
554 }
555
556 pamh->handlers.handlers_loaded = 1;
557
558 D(("_pam_init_handlers exiting"));
559 return PAM_SUCCESS;
560 }
561
562 /*
563 * This is where we read a line of the PAM config file. The line may be
564 * preceded by lines of comments and also extended with "\\\n"
565 */
566
567 static int _pam_assemble_line(FILE *f, char *buffer, int buf_len)
568 {
569 char *p = buffer;
570 char *endp = buffer + buf_len;
571 char *s, *os;
572 int used = 0;
573
574 /* loop broken with a 'break' when a non-'\\n' ended line is read */
575
576 D(("called."));
577 for (;;) {
578 if (p >= endp) {
579 /* Overflow */
580 D(("_pam_assemble_line: overflow"));
581 return -1;
582 }
583 if (fgets(p, endp - p, f) == NULL) {
584 if (used) {
585 /* Incomplete read */
586 return -1;
587 } else {
588 /* EOF */
589 return 0;
590 }
591 }
592
593 /* skip leading spaces --- line may be blank */
594
595 s = p + strspn(p, " \n\t");
596 if (*s && (*s != '#')) {
597 os = s;
598
599 /*
600 * we are only interested in characters before the first '#'
601 * character
602 */
603
604 while (*s && *s != '#')
605 ++s;
606 if (*s == '#') {
607 *s = '\0';
608 used += strlen(os);
609 break; /* the line has been read */
610 }
611
612 s = os;
613
614 /*
615 * Check for backslash by scanning back from the end of
616 * the entered line, the '\n' has been included since
617 * normally a line is terminated with this
618 * character. fgets() should only return one though!
619 */
620
621 s += strlen(s);
622 while (s > os && ((*--s == ' ') || (*s == '\t')
623 || (*s == '\n')));
624
625 /* check if it ends with a backslash */
626 if (*s == '\\') {
627 *s++ = ' '; /* replace backslash with ' ' */
628 *s = '\0'; /* truncate the line here */
629 used += strlen(os);
630 p = s; /* there is more ... */
631 } else {
632 /* End of the line! */
633 used += strlen(os);
634 break; /* this is the complete line */
635 }
636
637 } else {
638 /* Nothing in this line */
639 /* Don't move p */
640 }
641 }
642
643 return used;
644 }
645
646 static char *
647 extract_modulename(const char *mod_path)
648 {
649 const char *p = strrchr (mod_path, '/');
650 char *dot, *retval;
651
652 if (p == NULL)
653 p = mod_path;
654 else
655 p++;
656
657 if ((retval = _pam_strdup (p)) == NULL)
658 return NULL;
659
660 dot = strrchr (retval, '.');
661 if (dot)
662 *dot = '\0';
663
664 if (*retval == '\0' || strcmp(retval, "?") == 0) {
665 /* do not allow empty module name or "?" to avoid confusing audit trail */
666 _pam_drop(retval);
667 return NULL;
668 }
669
670 return retval;
671 }
672
673 static struct loaded_module *
674 _pam_load_module(pam_handle_t *pamh, const char *mod_path, int handler_type)
675 {
676 int x = 0;
677 int success;
678 struct loaded_module *mod;
679
680 D(("_pam_load_module: loading module `%s'", mod_path));
681
682 mod = pamh->handlers.module;
683
684 /* First, ensure the module is loaded */
685 while (x < pamh->handlers.modules_used) {
686 if (!strcmp(mod[x].name, mod_path)) { /* case sensitive ! */
687 break;
688 }
689 x++;
690 }
691 if (x == pamh->handlers.modules_used) {
692 /* Not found */
693 if (pamh->handlers.modules_allocated == pamh->handlers.modules_used) {
694 /* will need more memory */
695 void *tmp = realloc(pamh->handlers.module,
696 (pamh->handlers.modules_allocated+MODULE_CHUNK)
697 *sizeof(struct loaded_module));
698 if (tmp == NULL) {
699 D(("cannot enlarge module pointer memory"));
700 pam_syslog(pamh, LOG_CRIT,
701 "realloc returned NULL in _pam_load_module");
702 return NULL;
703 }
704 pamh->handlers.module = tmp;
705 pamh->handlers.modules_allocated += MODULE_CHUNK;
706 }
707 mod = &(pamh->handlers.module[x]);
708 /* Be pessimistic... */
709 success = PAM_ABORT;
710
711 D(("_pam_load_module: _pam_dlopen(%s)", mod_path));
712 mod->dl_handle = _pam_dlopen(mod_path);
713 D(("_pam_load_module: _pam_dlopen'ed"));
714 D(("_pam_load_module: dlopen'ed"));
715 if (mod->dl_handle == NULL) {
716 const char *isa = strstr(mod_path, "$ISA");
717 size_t isa_len = strlen("$ISA");
718
719 if (isa != NULL) {
720 size_t pam_isa_len = strlen(_PAM_ISA);
721 char *mod_full_isa_path =
722 malloc(strlen(mod_path) - isa_len + pam_isa_len + 1);
723
724 if (mod_full_isa_path == NULL) {
725 D(("_pam_load_module: couldn't get memory for mod_path"));
726 pam_syslog(pamh, LOG_CRIT, "no memory for module path");
727 success = PAM_ABORT;
728 } else {
729 char *p = mod_full_isa_path;
730
731 memcpy(p, mod_path, isa - mod_path);
732 p += isa - mod_path;
733 memcpy(p, _PAM_ISA, pam_isa_len);
734 p += pam_isa_len;
735 strcpy(p, isa + isa_len);
736
737 mod->dl_handle = _pam_dlopen(mod_full_isa_path);
738 _pam_drop(mod_full_isa_path);
739 }
740 }
741 }
742 if (mod->dl_handle == NULL) {
743 D(("_pam_load_module: _pam_dlopen(%s) failed", mod_path));
744 if (handler_type != PAM_HT_SILENT_MODULE)
745 pam_syslog(pamh, LOG_ERR, "unable to dlopen(%s): %s", mod_path,
746 _pam_dlerror());
747 /* Don't abort yet; static code may be able to find function.
748 * But defaults to abort if nothing found below... */
749 } else {
750 D(("module added successfully"));
751 success = PAM_SUCCESS;
752 mod->type = PAM_MT_DYNAMIC_MOD;
753 pamh->handlers.modules_used++;
754 }
755
756 if (success != PAM_SUCCESS) { /* add a malformed module */
757 mod->dl_handle = NULL;
758 mod->type = PAM_MT_FAULTY_MOD;
759 pamh->handlers.modules_used++;
760 if (handler_type != PAM_HT_SILENT_MODULE)
761 pam_syslog(pamh, LOG_ERR, "adding faulty module: %s", mod_path);
762 success = PAM_SUCCESS; /* We have successfully added a module */
763 }
764
765 /* indicate its name - later we will search for it by this */
766 if ((mod->name = _pam_strdup(mod_path)) == NULL) {
767 D(("_pam_load_module: couldn't get memory for mod_path"));
768 pam_syslog(pamh, LOG_CRIT, "no memory for module path");
769 success = PAM_ABORT;
770 }
771
772 } else { /* x != pamh->handlers.modules_used */
773 mod += x; /* the located module */
774 success = PAM_SUCCESS;
775 }
776 return success == PAM_SUCCESS ? mod : NULL;
777 }
778
779 int _pam_add_handler(pam_handle_t *pamh
780 , int handler_type, int other, int stack_level, int type
781 , int *actions, const char *mod_path
782 , int argc, char **argv, int argvlen)
783 {
784 struct loaded_module *mod = NULL;
785 struct handler **handler_p;
786 struct handler **handler_p2;
787 struct handlers *the_handlers;
788 const char *sym, *sym2;
789 char *mod_full_path;
790 servicefn func, func2;
791 int mod_type = PAM_MT_FAULTY_MOD;
792
793 D(("called."));
794 IF_NO_PAMH("_pam_add_handler",pamh,PAM_SYSTEM_ERR);
795
796 D(("_pam_add_handler: adding type %d, handler_type %d, module `%s'",
797 type, handler_type, mod_path));
798
799 if ((handler_type == PAM_HT_MODULE || handler_type == PAM_HT_SILENT_MODULE) &&
800 mod_path != NULL) {
801 if (mod_path[0] == '/') {
802 mod = _pam_load_module(pamh, mod_path, handler_type);
803 } else if (asprintf(&mod_full_path, "%s%s",
804 DEFAULT_MODULE_PATH, mod_path) >= 0) {
805 mod = _pam_load_module(pamh, mod_full_path, handler_type);
806 _pam_drop(mod_full_path);
807 } else {
808 pam_syslog(pamh, LOG_CRIT, "cannot malloc full mod path");
809 return PAM_ABORT;
810 }
811
812 if (mod == NULL) {
813 /* if we get here with NULL it means allocation error */
814 return PAM_ABORT;
815 }
816
817 mod_type = mod->type;
818 }
819
820 if (mod_path == NULL)
821 mod_path = UNKNOWN_MODULE;
822
823 /*
824 * At this point 'mod' points to the stored/loaded module.
825 */
826
827 /* Now define the handler(s) based on mod->dlhandle and type */
828
829 /* decide which list of handlers to use */
830 the_handlers = (other) ? &pamh->handlers.other : &pamh->handlers.conf;
831
832 handler_p = handler_p2 = NULL;
833 func = func2 = NULL;
834 sym2 = NULL;
835
836 /* point handler_p's at the root addresses of the function stacks */
837 switch (type) {
838 case PAM_T_AUTH:
839 handler_p = &the_handlers->authenticate;
840 sym = "pam_sm_authenticate";
841 handler_p2 = &the_handlers->setcred;
842 sym2 = "pam_sm_setcred";
843 break;
844 case PAM_T_SESS:
845 handler_p = &the_handlers->open_session;
846 sym = "pam_sm_open_session";
847 handler_p2 = &the_handlers->close_session;
848 sym2 = "pam_sm_close_session";
849 break;
850 case PAM_T_ACCT:
851 handler_p = &the_handlers->acct_mgmt;
852 sym = "pam_sm_acct_mgmt";
853 break;
854 case PAM_T_PASS:
855 handler_p = &the_handlers->chauthtok;
856 sym = "pam_sm_chauthtok";
857 break;
858 default:
859 /* Illegal module type */
860 D(("_pam_add_handler: illegal module type %d", type));
861 return PAM_ABORT;
862 }
863
864 /* are the modules reliable? */
865 if (mod_type != PAM_MT_DYNAMIC_MOD &&
866 mod_type != PAM_MT_FAULTY_MOD) {
867 D(("_pam_add_handlers: illegal module library type; %d", mod_type));
868 pam_syslog(pamh, LOG_ERR,
869 "internal error: module library type not known: %s;%d",
870 sym, mod_type);
871 return PAM_ABORT;
872 }
873
874 /* now identify this module's functions - for non-faulty modules */
875
876 if ((mod_type == PAM_MT_DYNAMIC_MOD) &&
877 !(func = _pam_dlsym(mod->dl_handle, sym)) ) {
878 pam_syslog(pamh, LOG_ERR, "unable to resolve symbol: %s", sym);
879 }
880 if (sym2) {
881 if ((mod_type == PAM_MT_DYNAMIC_MOD) &&
882 !(func2 = _pam_dlsym(mod->dl_handle, sym2)) ) {
883 pam_syslog(pamh, LOG_ERR, "unable to resolve symbol: %s", sym2);
884 }
885 }
886
887 /* here func (and perhaps func2) point to the appropriate functions */
888
889 /* add new handler to end of existing list */
890 while (*handler_p != NULL) {
891 handler_p = &((*handler_p)->next);
892 }
893
894 if ((*handler_p = calloc(1, sizeof(struct handler))) == NULL) {
895 pam_syslog(pamh, LOG_CRIT, "cannot allocate struct handler #1");
896 return (PAM_ABORT);
897 }
898
899 (*handler_p)->handler_type = handler_type;
900 (*handler_p)->stack_level = stack_level;
901 (*handler_p)->func = func;
902 memcpy((*handler_p)->actions,actions,sizeof((*handler_p)->actions));
903 (*handler_p)->cached_retval = _PAM_INVALID_RETVAL;
904 (*handler_p)->cached_retval_p = &((*handler_p)->cached_retval);
905 (*handler_p)->argc = argc;
906 (*handler_p)->argv = argv; /* not a copy */
907 if (((*handler_p)->mod_name = extract_modulename(mod_path)) == NULL)
908 return PAM_ABORT;
909
910 /* some of the modules have a second calling function */
911 if (handler_p2) {
912 /* add new handler to end of existing list */
913 while (*handler_p2) {
914 handler_p2 = &((*handler_p2)->next);
915 }
916
917 if ((*handler_p2 = calloc(1, sizeof(struct handler))) == NULL) {
918 pam_syslog(pamh, LOG_CRIT, "cannot allocate struct handler #2");
919 return (PAM_ABORT);
920 }
921
922 (*handler_p2)->handler_type = handler_type;
923 (*handler_p2)->stack_level = stack_level;
924 (*handler_p2)->func = func2;
925 memcpy((*handler_p2)->actions,actions,sizeof((*handler_p2)->actions));
926 (*handler_p2)->cached_retval = _PAM_INVALID_RETVAL; /* ignored */
927 /* Note, this next entry points to the handler_p value! */
928 (*handler_p2)->cached_retval_p = &((*handler_p)->cached_retval);
929 (*handler_p2)->argc = argc;
930 if (argv) {
931 if (((*handler_p2)->argv = malloc(argvlen)) == NULL) {
932 pam_syslog(pamh, LOG_CRIT, "cannot malloc argv for handler #2");
933 return (PAM_ABORT);
934 }
935 memcpy((*handler_p2)->argv, argv, argvlen);
936 }
937 if (((*handler_p2)->mod_name = extract_modulename(mod_path)) == NULL)
938 return PAM_ABORT;
939 }
940
941 D(("_pam_add_handler: returning successfully"));
942
943 return PAM_SUCCESS;
944 }
945
946 /* Free various allocated structures and dlclose() the libs */
947 int _pam_free_handlers(pam_handle_t *pamh)
948 {
949 struct loaded_module *mod;
950
951 D(("called."));
952 IF_NO_PAMH("_pam_free_handlers",pamh,PAM_SYSTEM_ERR);
953
954 mod = pamh->handlers.module;
955
956 /* Close all loaded modules */
957
958 while (pamh->handlers.modules_used) {
959 D(("_pam_free_handlers: dlclose(%s)", mod->name));
960 free(mod->name);
961 if (mod->type == PAM_MT_DYNAMIC_MOD) {
962 _pam_dlclose(mod->dl_handle);
963 }
964 mod++;
965 pamh->handlers.modules_used--;
966 }
967
968 /* Free all the handlers */
969
970 _pam_free_handlers_aux(&(pamh->handlers.conf.authenticate));
971 _pam_free_handlers_aux(&(pamh->handlers.conf.setcred));
972 _pam_free_handlers_aux(&(pamh->handlers.conf.acct_mgmt));
973 _pam_free_handlers_aux(&(pamh->handlers.conf.open_session));
974 _pam_free_handlers_aux(&(pamh->handlers.conf.close_session));
975 _pam_free_handlers_aux(&(pamh->handlers.conf.chauthtok));
976
977 _pam_free_handlers_aux(&(pamh->handlers.other.authenticate));
978 _pam_free_handlers_aux(&(pamh->handlers.other.setcred));
979 _pam_free_handlers_aux(&(pamh->handlers.other.acct_mgmt));
980 _pam_free_handlers_aux(&(pamh->handlers.other.open_session));
981 _pam_free_handlers_aux(&(pamh->handlers.other.close_session));
982 _pam_free_handlers_aux(&(pamh->handlers.other.chauthtok));
983
984 /* no more loaded modules */
985
986 _pam_drop(pamh->handlers.module);
987
988 /* Indicate that handlers are not initialized for this pamh */
989
990 pamh->handlers.handlers_loaded = 0;
991
992 return PAM_SUCCESS;
993 }
994
995 void _pam_start_handlers(pam_handle_t *pamh)
996 {
997 D(("called."));
998 /* NB. There is no check for a NULL pamh here, since no return
999 * value to communicate the fact! */
1000
1001 /* Indicate that handlers are not initialized for this pamh */
1002 pamh->handlers.handlers_loaded = 0;
1003
1004 pamh->handlers.modules_allocated = 0;
1005 pamh->handlers.modules_used = 0;
1006 pamh->handlers.module = NULL;
1007
1008 /* initialize the .conf and .other entries */
1009
1010 pamh->handlers.conf.authenticate = NULL;
1011 pamh->handlers.conf.setcred = NULL;
1012 pamh->handlers.conf.acct_mgmt = NULL;
1013 pamh->handlers.conf.open_session = NULL;
1014 pamh->handlers.conf.close_session = NULL;
1015 pamh->handlers.conf.chauthtok = NULL;
1016
1017 pamh->handlers.other.authenticate = NULL;
1018 pamh->handlers.other.setcred = NULL;
1019 pamh->handlers.other.acct_mgmt = NULL;
1020 pamh->handlers.other.open_session = NULL;
1021 pamh->handlers.other.close_session = NULL;
1022 pamh->handlers.other.chauthtok = NULL;
1023 }
1024
1025 void _pam_free_handlers_aux(struct handler **hp)
1026 {
1027 struct handler *h = *hp;
1028 struct handler *last;
1029
1030 D(("called."));
1031 while (h) {
1032 last = h;
1033 _pam_drop(h->argv); /* This is all allocated in a single chunk */
1034 _pam_drop(h->mod_name);
1035 h = h->next;
1036 pam_overwrite_object(last);
1037 free(last);
1038 }
1039
1040 *hp = NULL;
1041 }