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