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