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