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