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