#include #include #include #include #include #include #include #include #include #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; bool active; }; static struct auth_module_t modules_g[] = { { .name = "unix", .path = "/usr/lib/security/pam_unix.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); modules_g[i].active = true; if (modules_g[i].pid == -1) { return PAM_SYSTEM_ERR; } #undef STACK_SIZE } siginfo_t infos; int modules_terminated = 0; int ret = PAM_AUTH_ERR; do { /* wait for a first child to exit or be killed */ waitid(P_ALL, 0, &infos, WEXITED); ++modules_terminated; /* find the process matching the pid */ for (int i = 0; i < countof(modules_g); ++i) { struct auth_module_t *mod = &modules_g[i]; if (mod->pid == infos.si_pid) { mod->active = false; break; } } if (infos.si_status == PAM_SUCCESS || infos.si_status == PAM_AUTH_ERR) { ret = infos.si_status; break; } } while (modules_terminated < countof(modules_g)); for (int i = 0; i < countof(modules_g); ++i) { struct auth_module_t *mod = &modules_g[i]; if (!mod->active) continue; kill(mod->pid, SIGKILL); waitpid(mod->pid, NULL, 0); } return ret; } 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; }