PAM_EXTERN int pam_sm_open_session(pam_handle_t *pamh, int flags, int argc, const char **argv) { const struct passwd *pwent; const char *user; struct stat di; void (*sh)(int); pid_t child; int res; /* Who are we talking about anyway? */ res = pam_get_user(pamh, &user, NULL); if (res != PAM_SUCCESS) return res; /* Fetch passwd entry */ pwent = getpwnam(user); if (!pwent) { pam_error(pamh, "User not found in passwd?"); return PAM_CRED_INSUFFICIENT; } openlog("pam_mkhomedir", LOG_PID, LOG_AUTH); if (stat(pwent->pw_dir, &di)) return pam_mkhd_copy(pamh, pwent, "/etc/skel", pwent->pw_dir); return PAM_SUCCESS; }
context::context(const std::string& service, const std::string& username) { auto s = app::clone(service), u = username.size() ? app::clone(username) : nullptr; pam_conv conv = { despatch, this }; _M_code = pam_start(s.get(), u.get(), &conv, &_M_pamh); if(errc(_M_code) != errc::success) throw pam_error(_M_code); }
/* now the session stuff */ PAM_EXTERN int pam_sm_open_session (pam_handle_t *pamh, int flags UNUSED, int argc, const char **argv) { int retval; char *user_name; struct passwd *pwd; int ctrl; struct pam_limit_s pl; D(("called.")); memset(&pl, 0, sizeof(pl)); ctrl = _pam_parse(pamh, argc, argv, &pl); retval = pam_get_item( pamh, PAM_USER, (void*) &user_name ); if ( user_name == NULL || retval != PAM_SUCCESS ) { pam_syslog(pamh, LOG_CRIT, "open_session - error recovering username"); return PAM_SESSION_ERR; } pwd = getpwnam(user_name); if (!pwd) { if (ctrl & PAM_DEBUG_ARG) pam_syslog(pamh, LOG_WARNING, "open_session username '%s' does not exist", user_name); return PAM_SESSION_ERR; } retval = init_limits(&pl); if (retval != PAM_SUCCESS) { pam_syslog(pamh, LOG_WARNING, "cannot initialize"); return PAM_ABORT; } retval = parse_config_file(pamh, pwd->pw_name, ctrl, &pl); if (retval == PAM_IGNORE) { D(("the configuration file has an applicable '<domain> -' entry")); return PAM_SUCCESS; } if (retval != PAM_SUCCESS) { pam_syslog(pamh, LOG_WARNING, "error parsing the configuration file"); return retval; } if (ctrl & PAM_DO_SETREUID) { setreuid(pwd->pw_uid, -1); } retval = setup_limits(pamh, pwd->pw_name, pwd->pw_uid, ctrl, &pl); if (retval & LOGIN_ERR) pam_error(pamh, _("Too many logins for '%s'."), pwd->pw_name); if (retval != LIMITED_OK) { return PAM_PERM_DENIED; } return PAM_SUCCESS; }
static void test_pam_prompt(void **state) { struct pwrap_test_ctx *test_ctx; int rv; char *response; int resp_array[2]; test_ctx = (struct pwrap_test_ctx *) *state; memset(resp_array, 0, sizeof(resp_array)); test_ctx->conv.conv = pwrap_echo_conv; test_ctx->conv.appdata_ptr = resp_array; rv = pam_start("matrix", "trinity", &test_ctx->conv, &test_ctx->ph); assert_int_equal(rv, PAM_SUCCESS); rv = pam_prompt(test_ctx->ph, PAM_PROMPT_ECHO_OFF, &response, "no echo"); assert_int_equal(rv, PAM_SUCCESS); assert_string_equal(response, "echo off: no echo"); free(response); rv = vprompt_test_fn(test_ctx->ph, PAM_PROMPT_ECHO_OFF, &response, "no echo"); assert_int_equal(rv, PAM_SUCCESS); assert_string_equal(response, "echo off: no echo"); free(response); rv = pam_prompt(test_ctx->ph, PAM_PROMPT_ECHO_ON, &response, "echo"); assert_int_equal(rv, PAM_SUCCESS); assert_string_equal(response, "echo on: echo"); free(response); rv = vprompt_test_fn(test_ctx->ph, PAM_PROMPT_ECHO_ON, &response, "echo"); assert_int_equal(rv, PAM_SUCCESS); assert_string_equal(response, "echo on: echo"); free(response); assert_int_equal(resp_array[0], 0); pam_info(test_ctx->ph, "info"); assert_int_equal(resp_array[0], 1); assert_int_equal(resp_array[1], 0); pam_error(test_ctx->ph, "error"); assert_int_equal(resp_array[1], 1); }
/* now the session stuff */ PAM_EXTERN int pam_sm_open_session (pam_handle_t *pamh, int flags UNUSED, int argc, const char **argv) { int retval; int i; int glob_rc; char *user_name; struct passwd *pwd; int ctrl; struct pam_limit_s plstruct; struct pam_limit_s *pl = &plstruct; glob_t globbuf; const char *oldlocale; D(("called.")); memset(pl, 0, sizeof(*pl)); memset(&globbuf, 0, sizeof(globbuf)); ctrl = _pam_parse(pamh, argc, argv, pl); retval = pam_get_item( pamh, PAM_USER, (void*) &user_name ); if ( user_name == NULL || retval != PAM_SUCCESS ) { pam_syslog(pamh, LOG_CRIT, "open_session - error recovering username"); return PAM_SESSION_ERR; } pwd = pam_modutil_getpwnam(pamh, user_name); if (!pwd) { if (ctrl & PAM_DEBUG_ARG) pam_syslog(pamh, LOG_WARNING, "open_session username '%s' does not exist", user_name); return PAM_USER_UNKNOWN; } retval = init_limits(pamh, pl, ctrl); if (retval != PAM_SUCCESS) { pam_syslog(pamh, LOG_WARNING, "cannot initialize"); return PAM_ABORT; } retval = parse_config_file(pamh, pwd->pw_name, pwd->pw_uid, pwd->pw_gid, ctrl, pl); if (retval == PAM_IGNORE) { D(("the configuration file ('%s') has an applicable '<domain> -' entry", CONF_FILE)); return PAM_SUCCESS; } if (retval != PAM_SUCCESS || pl->conf_file != NULL) /* skip reading limits.d if config file explicitely specified */ goto out; /* Read subsequent *.conf files, if they exist. */ /* set the LC_COLLATE so the sorting order doesn't depend on system locale */ oldlocale = setlocale(LC_COLLATE, "C"); glob_rc = glob(LIMITS_CONF_GLOB, GLOB_ERR, NULL, &globbuf); if (oldlocale != NULL) setlocale (LC_COLLATE, oldlocale); if (!glob_rc) { /* Parse the *.conf files. */ for (i = 0; globbuf.gl_pathv[i] != NULL; i++) { pl->conf_file = globbuf.gl_pathv[i]; retval = parse_config_file(pamh, pwd->pw_name, pwd->pw_uid, pwd->pw_gid, ctrl, pl); if (retval == PAM_IGNORE) { D(("the configuration file ('%s') has an applicable '<domain> -' entry", pl->conf_file)); globfree(&globbuf); return PAM_SUCCESS; } if (retval != PAM_SUCCESS) goto out; } } out: globfree(&globbuf); if (retval != PAM_SUCCESS) { pam_syslog(pamh, LOG_WARNING, "error parsing the configuration file: '%s' ",CONF_FILE); return retval; } retval = setup_limits(pamh, pwd->pw_name, pwd->pw_uid, ctrl, pl); if (retval & LOGIN_ERR) pam_error(pamh, _("Too many logins for '%s'."), pwd->pw_name); if (retval != LIMITED_OK) { return PAM_PERM_DENIED; } return PAM_SUCCESS; }
int pam_sm_chauthtok (pam_handle_t *pamh, int flags, int argc, const char **argv) { struct passwd *pwd; const char *newpass; const char *user; int retval, tries; options_t options; memset (&options, 0, sizeof (options)); /* Set some default values, which could be overwritten later. */ options.remember = 10; options.tries = 1; /* Parse parameters for module */ for ( ; argc-- > 0; argv++) parse_option (pamh, *argv, &options); if (options.debug) pam_syslog (pamh, LOG_DEBUG, "pam_sm_chauthtok entered"); if (options.remember == 0) return PAM_IGNORE; retval = pam_get_user (pamh, &user, NULL); if (retval != PAM_SUCCESS) return retval; if (user == NULL || strlen (user) == 0) { if (options.debug) pam_syslog (pamh, LOG_DEBUG, "User is not known to system"); return PAM_USER_UNKNOWN; } if (flags & PAM_PRELIM_CHECK) { if (options.debug) pam_syslog (pamh, LOG_DEBUG, "pam_sm_chauthtok(PAM_PRELIM_CHECK)"); return PAM_SUCCESS; } pwd = pam_modutil_getpwnam (pamh, user); if (pwd == NULL) return PAM_USER_UNKNOWN; if ((strcmp(pwd->pw_passwd, "x") == 0) || ((pwd->pw_passwd[0] == '#') && (pwd->pw_passwd[1] == '#') && (strcmp(pwd->pw_name, pwd->pw_passwd + 2) == 0))) { struct spwd *spw = pam_modutil_getspnam (pamh, user); if (spw == NULL) return PAM_USER_UNKNOWN; retval = save_old_pass (pamh, user, pwd->pw_uid, spw->sp_pwdp, options.remember, options.debug); if (retval != PAM_SUCCESS) return retval; } else { retval = save_old_pass (pamh, user, pwd->pw_uid, pwd->pw_passwd, options.remember, options.debug); if (retval != PAM_SUCCESS) return retval; } newpass = NULL; tries = 0; while ((newpass == NULL) && (tries < options.tries)) { retval = pam_get_authtok (pamh, PAM_AUTHTOK, &newpass, NULL); if (retval != PAM_SUCCESS && retval != PAM_TRY_AGAIN) { if (retval == PAM_CONV_AGAIN) retval = PAM_INCOMPLETE; return retval; } tries++; if (options.debug) { if (newpass) pam_syslog (pamh, LOG_DEBUG, "got new auth token"); else pam_syslog (pamh, LOG_DEBUG, "got no auth token"); } if (newpass == NULL || retval == PAM_TRY_AGAIN) continue; if (options.debug) pam_syslog (pamh, LOG_DEBUG, "check against old password file"); if (check_old_pass (pamh, user, newpass, options.debug) != PAM_SUCCESS) { if (getuid() || options.enforce_for_root || (flags & PAM_CHANGE_EXPIRED_AUTHTOK)) { pam_error (pamh, _("Password has been already used. Choose another.")); newpass = NULL; /* Remove password item, else following module will use it */ pam_set_item (pamh, PAM_AUTHTOK, (void *) NULL); } else pam_info (pamh, _("Password has been already used.")); } } if (newpass == NULL && tries >= options.tries) { if (options.debug) pam_syslog (pamh, LOG_DEBUG, "Aborted, too many tries"); return PAM_MAXTRIES; } return PAM_SUCCESS; }
PAM_EXTERN int pam_sm_chauthtok(pam_handle_t *pamh, int flags, int argc, const char **argv) { int ctrl; struct module_options options; memset(&options, 0, sizeof(options)); options.retry_times = CO_RETRY_TIMES; ctrl = _pam_parse(pamh, &options, argc, argv); if (ctrl < 0) return PAM_BUF_ERR; if (flags & PAM_PRELIM_CHECK) { /* Check for passwd dictionary * We cannot do that, since the original path is compiled * into the cracklib library and we don't know it. */ return PAM_SUCCESS; } else if (flags & PAM_UPDATE_AUTHTOK) { int retval; const void *oldtoken; const char *user; int tries; retval = pam_get_user(pamh, &user, NULL); if (retval != PAM_SUCCESS || user == NULL) { if (ctrl & PAM_DEBUG_ARG) pam_syslog(pamh, LOG_ERR, "Can not get username"); return PAM_AUTHTOK_ERR; } retval = pam_get_item(pamh, PAM_OLDAUTHTOK, &oldtoken); if (retval != PAM_SUCCESS) { if (ctrl & PAM_DEBUG_ARG) pam_syslog(pamh, LOG_ERR, "Can not get old passwd"); oldtoken = NULL; } tries = 0; while (tries < options.retry_times) { void *auxerror; const char *newtoken = NULL; tries++; /* Planned modus operandi: * Get a passwd. * Verify it against libpwquality. * If okay get it a second time. * Check to be the same with the first one. * set PAM_AUTHTOK and return */ retval = pam_get_authtok_noverify(pamh, &newtoken, NULL); if (retval != PAM_SUCCESS) { pam_syslog(pamh, LOG_ERR, "pam_get_authtok_noverify returned error: %s", pam_strerror(pamh, retval)); continue; } else if (newtoken == NULL) { /* user aborted password change, quit */ return PAM_AUTHTOK_ERR; } /* now test this passwd against libpwquality */ retval = pwquality_check(options.pwq, newtoken, oldtoken, user, &auxerror); if (retval < 0) { const char *msg; char buf[PWQ_MAX_ERROR_MESSAGE_LEN]; msg = pwquality_strerror(buf, sizeof(buf), retval, auxerror); if (ctrl & PAM_DEBUG_ARG) pam_syslog(pamh, LOG_DEBUG, "bad password: %s", msg); pam_error(pamh, _("BAD PASSWORD: %s"), msg); if (getuid() || options.enforce_for_root || (flags & PAM_CHANGE_EXPIRED_AUTHTOK)) { pam_set_item(pamh, PAM_AUTHTOK, NULL); retval = PAM_AUTHTOK_ERR; continue; } } else { if (ctrl & PAM_DEBUG_ARG) pam_syslog(pamh, LOG_DEBUG, "password score: %d", retval); } retval = pam_get_authtok_verify(pamh, &newtoken, NULL); if (retval != PAM_SUCCESS) { pam_syslog(pamh, LOG_ERR, "pam_get_authtok_verify returned error: %s", pam_strerror(pamh, retval)); pam_set_item(pamh, PAM_AUTHTOK, NULL); continue; } else if (newtoken == NULL) { /* user aborted password change, quit */ return PAM_AUTHTOK_ERR; } return PAM_SUCCESS; } pam_set_item (pamh, PAM_AUTHTOK, NULL); /* if we have only one try, we can use the real reason, * else say that there were too many tries. */ if (options.retry_times > 1) return PAM_MAXTRIES; else return retval; } else { if (ctrl & PAM_DEBUG_ARG) pam_syslog(pamh, LOG_NOTICE, "UNKNOWN flags setting %02X",flags); } return PAM_SERVICE_ERR; }
static int pam_mkhd_copy(pam_handle_t *pamh, const struct passwd *pwent, const char *from, const char *to) { char *newfrom, *newto; struct dirent *dentry; struct stat di; ssize_t len; DIR *dirp; int ffd, tfd, ret; if (lstat(from, &di)) { pam_error(pamh, "Unable to stat %s: %s", from, strerror(errno)); return PAM_PERM_DENIED; } if (S_ISDIR(di.st_mode)) { if (mkdir(to, di.st_mode & 07777)) { pam_error(pamh, "Creating directory %s failed: %s", to, strerror(errno)); return PAM_PERM_DENIED; } if (chown(to, pwent->pw_uid, pwent->pw_gid)) { pam_error(pamh, "Setting ownership of %s failed: %s", to, strerror(errno)); return PAM_PERM_DENIED; } dirp = opendir(from); if (!dirp) { pam_error(pamh, "Unable to open %s: %s", from, strerror(errno)); return PAM_PERM_DENIED; } while ((dentry = readdir(dirp))) { if (dentry->d_name[0] == '.' && (dentry->d_name[1] == '\0' || (dentry->d_name[1] == '.' && dentry->d_name[2] == '\0'))) continue; newfrom = malloc(strlen(from) + strlen(dentry->d_name) + 2); if (!newfrom) { closedir(dirp); return PAM_PERM_DENIED; } memset(newfrom, 0, strlen(from) + strlen(dentry->d_name) + 2); strlcpy(newfrom, from, strlen(from) + 1); newfrom[strlen(from)] = '/'; strlcpy(newfrom + strlen(from) + 1, dentry->d_name, strlen(dentry->d_name) + 1); newfrom[strlen(from) + strlen(dentry->d_name) + 2] = '\0'; newto = malloc(strlen(to) + strlen(dentry->d_name) + 2); if (!newto) { free(newfrom); closedir(dirp); return PAM_PERM_DENIED; } strlcpy(newto, to, strlen(to) + 1); newto[strlen(to)] = '/'; strlcpy(newto + strlen(to) + 1, dentry->d_name, strlen(dentry->d_name) + 1); newto[strlen(to) + strlen(dentry->d_name) + 2] = '\0'; ret = pam_mkhd_copy(pamh, pwent, newfrom, newto); free(newto); free(newfrom); if (ret != PAM_SUCCESS) { closedir(dirp); return ret; } } closedir(dirp); } else if (S_ISLNK(di.st_mode)) { newto = malloc(di.st_size) + 1; memset(newto, 0, di.st_size + 1); if (readlink(from, newto, di.st_size + 1) < 0) { pam_error(pamh, "Readlink on %s failed: %s", from, strerror(errno)); free(newto); return PAM_PERM_DENIED; } if (symlink(newto, to)) { pam_error(pamh, "Creating symlink %s failed: %s", to, strerror(errno)); free(newto); return PAM_PERM_DENIED; } free(newto); if (lchmod(to, di.st_mode & 07777)) { pam_error(pamh, "Changing permissions of symlink %s failed: %s", to, strerror(errno)); return PAM_PERM_DENIED; } if (lchown(to, pwent->pw_uid, pwent->pw_gid)) { pam_error(pamh, "Changing ownership of symlink %s failed: %s", to, strerror(errno)); return PAM_PERM_DENIED; } } else if (S_ISREG(di.st_mode)) { ffd = open(from, O_RDONLY, 0); if (ffd == -1) { pam_error(pamh, "Opening %s for reading failed: %s", from, strerror(errno)); return PAM_PERM_DENIED; } tfd = open(to, O_WRONLY | O_CREAT | O_TRUNC, di.st_mode & 07777); if (ffd == -1) { pam_error(pamh, "Opening %s for writing failed: %s", to, strerror(errno)); close(ffd); return PAM_PERM_DENIED; } newto = malloc(65536); if (!newto) { pam_error(pamh, "Unable to allocate buffer: %s", strerror(errno)); close(tfd); close(ffd); return PAM_PERM_DENIED; } while ((len = read(ffd, newto, 65546)) > 0) { if (write(tfd, newto, len) == -1) { pam_error(pamh, "Unable to write to %s: %s", to, strerror(errno)); free(newto); close(tfd); close(ffd); return PAM_PERM_DENIED; } } free(newto); if (len == -1) { pam_error(pamh, "Unable to read from %s: %s", from, strerror(errno)); close(tfd); close(ffd); return PAM_PERM_DENIED; } if (close(tfd)) { pam_error(pamh, "Unable to close %s: %s", to, strerror(errno)); close(ffd); return PAM_PERM_DENIED; } if (close(ffd)) { pam_error(pamh, "Unable to close %s: %s", from, strerror(errno)); return PAM_PERM_DENIED; } if (chown(to, pwent->pw_uid, pwent->pw_gid)) { pam_error(pamh, "Changing ownership of symlink %s failed: %s", to, strerror(errno)); return PAM_PERM_DENIED; } } return PAM_SUCCESS; }
// CALLED BY PAM_AUTHENTICATE PAM_EXTERN int pam_sm_authenticate(pam_handle_t * pamh, int flags, int argc, const char **argv) { module_config *cfg = NULL; user_config *user_cfg = NULL; int retval; unsigned int trial; const char *authtok = NULL; retval = pam_get_item(pamh, PAM_AUTHTOK, (const void **) &authtok); if (retval != PAM_SUCCESS || (authtok != NULL && !strcmp(authtok, AUTHTOK_INCORRECT))) { D(("Previous authentication failed, let's stop here!")); return PAM_AUTH_ERR; } retval = parse_config(pamh, argc, argv, &cfg); //CHECK PAM CONFIGURATION if (retval == CONFIG_ERROR) { D(("Invalid configuration")); pam_syslog(pamh, LOG_ERR, "Invalid parameters to pam_2fa module"); pam_error(pamh, "Sorry, 2FA Pam Module is misconfigured, please contact admins!\n"); return PAM_AUTH_ERR; } // Get User configuration user_cfg = get_user_config(pamh, cfg); if(!user_cfg) { pam_syslog(pamh, LOG_INFO, "Unable to get user configuration"); // cleanup free_config(cfg); return PAM_AUTH_ERR; } const auth_mod *available_mods[4] = { NULL, NULL, NULL, NULL }; int menu_len = 0; if (cfg->gauth_enabled && user_cfg->gauth_login[0] != '\0') { #ifdef HAVE_CURL ++menu_len; available_mods[menu_len] = &gauth_auth; #else DBG(("GAuth configured, but CURL not compiled (should never happen!)")); #endif } if (cfg->sms_enabled && user_cfg->sms_mobile[0] != '\0') { ++menu_len; available_mods[menu_len] = &sms_auth; } if (cfg->yk_enabled && user_cfg->yk_publicids) { #ifdef HAVE_YKCLIENT ++menu_len; available_mods[menu_len] = &yk_auth; #else DBG(("Yubikey configured, but ykclient not compiled (should never happen!)")); #endif } retval = PAM_AUTH_ERR; for (trial = 0; trial < cfg->retry && retval != PAM_SUCCESS; ++trial) { const auth_mod *selected_auth_mod = NULL; char *user_input = NULL; if (menu_len > 1) { size_t user_input_len; int i = 1; pam_info(pamh, "Login for %s:\n", user_cfg->username); for (i = 1; i <= menu_len; ++i) { pam_info(pamh, " %d. %s", i, available_mods[i]->name); } if (pam_prompt(pamh, PAM_PROMPT_ECHO_ON, &user_input, "\nOption (1-%d): ", menu_len) != PAM_SUCCESS) { pam_syslog(pamh, LOG_INFO, "Unable to get 2nd factors for user '%s'", user_cfg->username); pam_error(pamh, "Unable to get user input"); retval = PAM_AUTH_ERR; break; } user_input_len = user_input ? strlen(user_input) : 0; for (i = 1; i <= menu_len; ++i) { if (available_mods[i]->pre_auth == NULL && available_mods[i]->otp_len) { if (user_input_len == available_mods[i]->otp_len) { selected_auth_mod = available_mods[i]; break; } } } if (selected_auth_mod == NULL) { if (user_input_len == 1 && user_input[0] >= '1' && user_input[0] <= menu_len + '0') { selected_auth_mod = available_mods[user_input[0] - '0']; free(user_input); user_input = NULL; } else { pam_error(pamh, "Invalid input"); free(user_input); user_input = NULL; } } } else if (menu_len == 1) { selected_auth_mod = available_mods[1]; } else { pam_syslog(pamh, LOG_INFO, "No supported 2nd factor for user '%s'", user_cfg->username); pam_error(pamh, "No supported 2nd factors for user '%s'", user_cfg->username); retval = PAM_AUTH_ERR; break; } if (selected_auth_mod != NULL) { void * pre_auth_data = NULL; if (selected_auth_mod->pre_auth != NULL) { pre_auth_data = selected_auth_mod->pre_auth(pamh, user_cfg, cfg); if (pre_auth_data == NULL) continue; } if (user_input == NULL) { if (pam_prompt(pamh, PAM_PROMPT_ECHO_ON, &user_input, "%s", selected_auth_mod->prompt) != PAM_SUCCESS) { pam_syslog(pamh, LOG_INFO, "Unable to get %s", selected_auth_mod->prompt); pam_error(pamh, "Unable to get user input"); free(pre_auth_data); retval = PAM_AUTH_ERR; break; } } retval = selected_auth_mod->do_auth(pamh, user_cfg, cfg, user_input, pre_auth_data); free(user_input); } } // final cleanup free_user_config(user_cfg); free_config(cfg); return retval; }