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