diff options
author | Olivier Gayot <duskcoder@gmail.com> | 2015-12-15 22:34:44 +0100 |
---|---|---|
committer | Olivier Gayot <duskcoder@gmail.com> | 2015-12-15 22:34:44 +0100 |
commit | ffdce9f16ed386cb9668a8a97967212a0acb4647 (patch) | |
tree | a4609d554ff8ade91ea07286dc7bb0ae83027673 |
first version
Signed-off-by: Olivier Gayot <duskcoder@gmail.com>
-rw-r--r-- | multi_auth.c | 152 |
1 files changed, 152 insertions, 0 deletions
diff --git a/multi_auth.c b/multi_auth.c new file mode 100644 index 0000000..ba8ec1d --- /dev/null +++ b/multi_auth.c @@ -0,0 +1,152 @@ +#include <stdio.h> +#include <stdlib.h> +#include <security/pam_appl.h> +#include <security/pam_modules.h> +#include <sys/wait.h> +#include <unistd.h> +#include <sched.h> +#include <dlfcn.h> + +#define countof(_x) ((int)((sizeof((_x)) / sizeof((_x)[0])))) + +struct module_args_t { + int (*callback)(pam_handle_t *, int, int, const char **); + pam_handle_t *handler; + int flags; + int argc; + const char **argv; +}; + +struct auth_module_t { + const char *name; + const char *path; + const char *symbol; + + struct module_args_t args; + + void *dl_handler; + + pid_t pid; +}; + +static +struct auth_module_t modules_g[] = { + { + .name = "unix", + .path = "/usr/lib/security/pam_unix.so", + .symbol = "pam_sm_authenticate", + }, { + .name = "other", + .path = "/home/camel_case/dev/fingerprint/pam_other.so", + .symbol = "pam_sm_authenticate", + }, +}; + +static int multi_auth_init(pam_handle_t *pamh, int flags, + int argc, const char **argv) +{ + for (int i = 0; i < countof(modules_g); ++i) { + struct auth_module_t *mod = &modules_g[i]; + + mod->dl_handler = dlopen(mod->path, RTLD_NOW); + + if (mod->dl_handler == NULL) { + fprintf(stderr, "%s: %m\n", mod->path); + return PAM_SYSTEM_ERR; + } + + mod->args.callback = dlsym(mod->dl_handler, mod->symbol); + mod->args.handler = pamh; + mod->args.flags = flags; + mod->args.argc = argc; + mod->args.argv = argv; + + if (mod->args.callback == NULL) { + return PAM_SYSTEM_ERR; + } + } + + return 0; +} + +static void multi_auth_fini(void) +{ + for (int i = 0; i < countof(modules_g); ++i) { + dlclose(modules_g[i].dl_handler); + } +} + +static int module_auth_main(void *_args) +{ + struct module_args_t *args = _args; + + /* the child is supposed to close after returning from this function */ + return args->callback(args->handler, args->flags, args->argc, args->argv); +} + +static int perform_auth(void) +{ + for (int i = 0; i < countof(modules_g); ++i) { +#define STACK_SIZE (1000 * 1000) + struct auth_module_t *mod = &modules_g[i]; + + void *stack = malloc(STACK_SIZE); + + if (stack == NULL) { + return PAM_AUTH_ERR; + } + + int clone_flags = CLONE_NEWUTS|SIGCHLD; + + modules_g[i].pid = clone(module_auth_main, stack + STACK_SIZE, clone_flags, &mod->args); + + if (modules_g[i].pid == -1) { + return PAM_SYSTEM_ERR; + } +#undef STACK_SIZE + } + + for (int i = 0; i < countof(modules_g); ++i) { + int status; + + do { + waitpid(-1, &status, 0); + + } while (!WIFEXITED(status) && !WIFSIGNALED(status)); + + if (WIFEXITED(status)) { + for (int j = 0; j < countof(modules_g); ++j) { + kill(modules_g[j].pid, SIGKILL); + } + + return (WEXITSTATUS(status) == 0) ? PAM_SUCCESS : PAM_AUTH_ERR; + } + } + + return PAM_AUTH_ERR; +} + + PAM_EXTERN +int pam_sm_setcred(pam_handle_t *pamh, int flags, int argc, const char **argv) +{ + (void)pamh; + (void)flags; + (void)argc; + (void)argv; + return PAM_SUCCESS; +} + + PAM_EXTERN +int pam_sm_authenticate(pam_handle_t *pamh, int flags, + int argc, const char **argv) +{ + + int ret = multi_auth_init(pamh, flags, argc, argv); + + if (ret == 0) { + ret = perform_auth(); + } + + multi_auth_fini(); + return ret; +} |