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