/* This software is licensed under GPL-3, as shown in the file LICENSE Author: Linux Gruppe IRB Copyright: Linux Gruppe IRB, 2024 */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* We have to make this definitions before we include the pam header files! */ #define PAM_SM_AUTH #define PAM_SM_ACCOUNT #define PAM_SM_SESSION #define PAM_SM_PASSWORD #include #include #include #include "pam_eid.h" #include "auth.h" #define PACKAGE "pam_eid" struct module_data { int pamResult; CURL *curl; }; Params params; static int parse_args(pam_handle_t *pamh, int argc, const char **argv) { params.echo_pin = PAM_PROMPT_ECHO_OFF; params.debug = 0; for (int i = 0; i < argc; i++) { if (!strcmp(argv[i], "debug")) params.debug = 1; else if (!strcmp(argv[i], "echo_pin")) params.echo_pin = PAM_PROMPT_ECHO_ON; else { pam_syslog(pamh, LOG_ERR, "Unrecognized option \"%s\"", argv[i]); return PAM_AUTH_ERR; } } return PAM_SUCCESS; } void module_data_cleanup(pam_handle_t *pamh, void *data, int error_status) { struct module_data *module_data = data; if (module_data != NULL) { if (module_data->curl) curl_easy_cleanup(module_data->curl); free(module_data); } } static int module_initialize(pam_handle_t *pamh, int flags, int argc, const char **argv, struct module_data **module_data, int pamResult) { struct module_data *data = calloc(1, sizeof *data); int r; if (data == NULL) { pam_syslog(pamh, LOG_CRIT, "calloc() failed: %s", strerror(errno)); r = PAM_BUF_ERR; goto err; } data->pamResult = pamResult; data->curl = curl_easy_init(); if (data->curl == NULL) { pam_syslog(pamh, LOG_ERR, "curl_easy_init() failed"); r = PAM_SERVICE_ERR; goto err; } r = pam_set_data(pamh, PACKAGE, data, module_data_cleanup); if (r != PAM_SUCCESS) { pam_syslog(pamh, LOG_ERR, "pam_set_data() failed"); goto err; } *module_data = data; data = NULL; err: module_data_cleanup(pamh, data, r); return r; } static int module_refresh(pam_handle_t *pamh, int flags, int argc, const char **argv, const char **user, CURL **curl, int pamResult) { struct module_data *module_data; int r; if ((pam_get_data(pamh, PACKAGE, (void *)&module_data) != PAM_SUCCESS) || (module_data == NULL)) { r = module_initialize(pamh, flags, argc, argv, &module_data, pamResult); if (r != PAM_SUCCESS) goto err; } r = pam_get_user(pamh, user, NULL); if (r != PAM_SUCCESS) { pam_syslog(pamh, LOG_ERR, "pam_get_user() failed %s", pam_strerror(pamh, r)); goto err; } *curl = module_data->curl; err: return r; } int pam_sm_authenticate(pam_handle_t *pamh, int flags, int argc, const char **argv) { PAM_MODUTIL_DEF_PRIVS(privs); const char *authtok, *user; struct passwd *passwd; CURL *curl; pid_t pid; int r, s; r = parse_args(pamh, argc, argv); if (r != PAM_SUCCESS) goto err; r = module_refresh(pamh, flags, argc, argv, &user, &curl, PAM_AUTH_ERR); if (r != PAM_SUCCESS) goto err; if (params.debug) pam_syslog(pamh, LOG_DEBUG, "Trying to authenticate user %s", user); if ((passwd = getpwnam(user)) == NULL) { pam_syslog(pamh, LOG_ERR, "getpwnam() failed: %s", strerror(errno)); r = PAM_USER_UNKNOWN; goto err; } if ((r = pam_get_authtok(pamh, PAM_AUTHTOK, &authtok, "Password: ")) != PAM_SUCCESS) { pam_syslog(pamh, LOG_ERR, "pam_get_authtok() failed"); goto err; } if ((r = pam_modutil_drop_priv(pamh, &privs, passwd)) != PAM_SUCCESS) { pam_syslog(pamh, LOG_ERR, "pam_modutil_drop_priv() failed: %s", pam_strerror(pamh, r)); goto err; } pid = fork(); switch (pid) { case -1: pam_syslog(pamh, LOG_ERR, "fork() failed: %s", strerror(errno)); r = PAM_SERVICE_ERR; break; case 0: { char *envp[3]; DIR *dir; if ((envp[0] = (char *) malloc(sizeof("AUSWEISAPP2_AUTOMATIC_PIN=123456"))) == NULL) { pam_syslog(pamh, LOG_CRIT, "malloc() failed: strerror(errno)"); exit(errno); } if ((envp[1] = (char *) malloc(sizeof("XDG_RUNTIME_DIR=/run/user/12345"))) == NULL) { pam_syslog(pamh, LOG_CRIT, "malloc() failed: strerror(errno)"); exit(errno); } sprintf(envp[0], "AUSWEISAPP2_AUTOMATIC_PIN=%s", authtok); sprintf(envp[1], "XDG_RUNTIME_DIR=/run/user/%d", (int) passwd->pw_uid); envp[2] = (char *) NULL; if ((dir = opendir(strstr(envp[1], "/"))) == NULL) if (errno == ENOENT) { mkdir(strstr(envp[1], "/"), 0700); if (chown(strstr(envp[1], "/"), passwd->pw_uid, passwd->pw_gid) != 0) if (params.debug) pam_syslog(pamh, LOG_DEBUG, "chown() failed: %s", strerror(errno)); } if (params.debug) pam_syslog(pamh, LOG_DEBUG, "Original UID is %d, original GID is %d", (int) getuid(), (int) getgid()); if (setgid(passwd->pw_gid) != 0) { pam_syslog(pamh, LOG_ERR, "setgid() failed: %s", strerror(errno)); exit(errno); } if (setuid(passwd->pw_uid) != 0) { pam_syslog(pamh, LOG_ERR, "setuid() failed: %s", strerror(errno)); exit(errno); } if (params.debug) pam_syslog(pamh, LOG_DEBUG, "New UID is %d, new GID is %d", (int) getuid(), (int) getgid()); if ((execle("/usr/bin/AusweisApp", "AusweisApp", "--ui", "websocket", "--ui", "webservice", "--ui", "automatic", "-platform", "offscreen", "--no-loghandler", "--no-logfile", "--port", "41325", (char *) NULL, envp)) == -1) { pam_syslog(pamh, LOG_ERR, "execle() failed: %s", strerror(errno)); exit(errno); } break; } default: r = auth(pamh, passwd, curl); kill(pid, SIGKILL); waitpid(pid, NULL, 0); break; } err: if ((s = pam_modutil_regain_priv(pamh, &privs)) != PAM_SUCCESS) { pam_syslog(pamh, LOG_ERR, "pam_modutil_regain_priv() failed: %s", pam_strerror(pamh, r)); if (r == PAM_SUCCESS) r = s; } if (params.debug) { if (r == PAM_SUCCESS) pam_syslog(pamh, LOG_DEBUG, "User %s was authenticated successfully", user); else pam_syslog(pamh, LOG_DEBUG, "User %s could not be authenticated", user); } module_refresh(pamh, flags, argc, argv, &user, &curl, r); return r; } int pam_sm_setcred(pam_handle_t *pamh, int flags, int argc, const char **argv) { struct module_data moduleData; int r; if ((r = pam_get_data(pamh, PACKAGE, (void *) &moduleData)) != PAM_SUCCESS) { pam_syslog(pamh, LOG_ERR, "pam_get_data() failed: %s", pam_strerror(pamh, r)); return r; } return moduleData.pamResult; } int pam_sm_acct_mgmt(pam_handle_t * pamh, int flags, int argc, const char **argv) { return PAM_SUCCESS; } int pam_sm_open_session(pam_handle_t * pamh, int flags, int argc, const char **argv) { pam_syslog(pamh, LOG_ERR, "Function pam_sm_open_session() is not implemented " "in this module"); return PAM_SERVICE_ERR; } int pam_sm_close_session(pam_handle_t * pamh, int flags, int argc, const char **argv) { pam_syslog(pamh, LOG_ERR, "Function pam_sm_close_session() is not implemented " "in this module"); return PAM_SERVICE_ERR; } int pam_sm_chauthtok(pam_handle_t * pamh, int flags, int argc, const char **argv) { pam_syslog(pamh, LOG_ERR, "Function pam_sm_chauthtok() is not implemented " "in this module"); return PAM_SERVICE_ERR; } #ifdef PAM_STATIC /* static module data */ struct pam_module _pam_group_modstruct = { PACKAGE, pam_sm_authenticate, pam_sm_setcred, pam_sm_acct_mgmt, pam_sm_open_session, pam_sm_close_session, pam_sm_chauthtok }; #endif