static int send_text (pam_handle_t *pamh, const char *text, int debug) { if (debug) pam_syslog(pamh, LOG_NOTICE, "%s", text); return pam_info (pamh, "%s", text); }
static int _pam_echo(pam_handle_t *pamh, int flags, int argc, const char *argv[]) { char msg[PAM_MAX_MSG_SIZE]; const void *str; const char *p, *q; int err, i, item; size_t len; if (flags & PAM_SILENT) return (PAM_SUCCESS); for (i = 0, len = 0; i < argc && len < sizeof(msg) - 1; ++i) { if (i > 0) msg[len++] = ' '; for (p = argv[i]; *p != '\0' && len < sizeof(msg) - 1; ++p) { if (*p != '%' || p[1] == '\0') { msg[len++] = *p; continue; } switch (*++p) { case 'H': item = PAM_RHOST; break; case 'h': /* not implemented */ item = -1; break; case 's': item = PAM_SERVICE; break; case 't': item = PAM_TTY; break; case 'U': item = PAM_RUSER; break; case 'u': item = PAM_USER; break; default: item = -1; msg[len++] = *p; break; } if (item == -1) continue; err = pam_get_item(pamh, item, &str); if (err != PAM_SUCCESS) return (err); if (str == NULL) str = "(null)"; for (q = str; *q != '\0' && len < sizeof(msg) - 1; ++q) msg[len++] = *q; } } msg[len] = '\0'; return (pam_info(pamh, "%s", msg)); }
static int report_mail(pam_handle_t *pamh, int ctrl, int type, const char *folder) { int retval; if (!(ctrl & PAM_MAIL_SILENT) || ((ctrl & PAM_QUIET_MAIL) && type == HAVE_NEW_MAIL)) { if (ctrl & PAM_STANDARD_MAIL) switch (type) { case HAVE_NO_MAIL: retval = pam_info (pamh, "%s", _("No mail.")); break; case HAVE_NEW_MAIL: retval = pam_info (pamh, "%s", _("You have new mail.")); break; case HAVE_OLD_MAIL: retval = pam_info (pamh, "%s", _("You have old mail.")); break; case HAVE_MAIL: default: retval = pam_info (pamh, "%s", _("You have mail.")); break; } else switch (type) { case HAVE_NO_MAIL: retval = pam_info (pamh, _("You have no mail in folder %s."), folder); break; case HAVE_NEW_MAIL: retval = pam_info (pamh, _("You have new mail in folder %s."), folder); break; case HAVE_OLD_MAIL: retval = pam_info (pamh, _("You have old mail in folder %s."), folder); break; case HAVE_MAIL: default: retval = pam_info (pamh, _("You have mail in folder %s."), folder); break; } } else { D(("keeping quiet")); retval = PAM_SUCCESS; } D(("returning %s", pam_strerror(pamh, retval))); return retval; }
static int read_fd(pam_handle_t *pamh, const char *file, int fd) { char buf[1024], *p; ssize_t rd; size_t total = 0; size_t level = 0; while (total < max_output_size) { size_t rdsize = sizeof(buf) - level - 1; if (total + rdsize >= max_output_size && (rdsize = max_output_size - total) == 0) break; rd = read(fd, buf + level, rdsize); if (rd <= 0) break; total += rd; level += rd; buf[level] = 0; p = strrchr(buf, '\n'); if (p) *p++ = 0; pam_info(pamh, "%s", buf); if (p && *p) { level = strlen(p); memmove(buf, p, level); } else level = 0; } if (level) { buf[level] = 0; pam_info(pamh, "%s", buf); } if (rd < 0) { _pam_log(LOG_ERR, "error reading file %s: %s", file, strerror(errno)); return PAM_SYSTEM_ERR; } return PAM_SUCCESS; }
static int state(pam_handle_t *pamh, const char *text) { int retval; retval = pam_info (pamh, "%s", text); if (retval != PAM_SUCCESS) { D(("pam_info failed")); } return retval; }
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); }
static int replace_and_print (pam_handle_t *pamh, const char *mesg) { char *output; size_t length = strlen (mesg) + PAM_MAX_MSG_SIZE; char myhostname[HOST_NAME_MAX+1]; const void *str = NULL; const char *p, *q; int item; size_t len; output = malloc (length); if (output == NULL) { pam_syslog (pamh, LOG_ERR, "running out of memory"); return PAM_BUF_ERR; } for (p = mesg, len = 0; *p != '\0' && len < length - 1; ++p) { if (*p != '%' || p[1] == '\0') { output[len++] = *p; continue; } switch (*++p) { case 'H': item = PAM_RHOST; break; case 'h': item = -2; /* aka PAM_LOCALHOST */ break; case 's': item = PAM_SERVICE; break; case 't': item = PAM_TTY; break; case 'U': item = PAM_RUSER; break; case 'u': item = PAM_USER; break; default: output[len++] = *p; continue; } if (item == -2) { if (gethostname (myhostname, sizeof (myhostname)) == -1) str = NULL; else str = &myhostname; } else { if (pam_get_item (pamh, item, &str) != PAM_SUCCESS) str = NULL; } if (str == NULL) str = "(null)"; for (q = str; *q != '\0' && len < length - 1; ++q) output[len++] = *q; } output[len] = '\0'; pam_info (pamh, "%s", output); free (output); return PAM_SUCCESS; }
static void __duo_status(void *arg, const char *msg) { pam_info((pam_handle_t *)arg, "%s", msg); }
PAM_EXTERN int pam_sm_authenticate(pam_handle_t *pamh, int pam_flags, int argc, const char *argv[]) { struct duo_config cfg; struct passwd *pw; struct in_addr addr; duo_t *duo; duo_code_t code; duopam_const char *config, *cmd, *p, *service, *user; const char *ip, *host; int i, flags, pam_err, matched; duo_config_default(&cfg); /* Parse configuration */ config = DUO_CONF; for (i = 0; i < argc; i++) { if (strncmp("conf=", argv[i], 5) == 0) { config = argv[i] + 5; } else if (strcmp("debug", argv[i]) == 0) { duo_debug = 1; } else { duo_syslog(LOG_ERR, "Invalid pam_duo option: '%s'", argv[i]); return (PAM_SERVICE_ERR); } } i = duo_parse_config(config, __ini_handler, &cfg); if (i == -2) { duo_syslog(LOG_ERR, "%s must be readable only by user 'root'", config); return (cfg.failmode == DUO_FAIL_SAFE ? PAM_SUCCESS : PAM_SERVICE_ERR); } else if (i == -1) { duo_syslog(LOG_ERR, "Couldn't open %s: %s", config, strerror(errno)); return (cfg.failmode == DUO_FAIL_SAFE ? PAM_SUCCESS : PAM_SERVICE_ERR); } else if (i > 0) { duo_syslog(LOG_ERR, "Parse error in %s, line %d", config, i); return (cfg.failmode == DUO_FAIL_SAFE ? PAM_SUCCESS : PAM_SERVICE_ERR); } else if (!cfg.apihost || !cfg.apihost[0] || !cfg.skey || !cfg.skey[0] || !cfg.ikey || !cfg.ikey[0]) { duo_syslog(LOG_ERR, "Missing host, ikey, or skey in %s", config); return (cfg.failmode == DUO_FAIL_SAFE ? PAM_SUCCESS : PAM_SERVICE_ERR); } /* Check user */ if (pam_get_user(pamh, &user, NULL) != PAM_SUCCESS || (pw = getpwnam(user)) == NULL) { return (PAM_USER_UNKNOWN); } /* XXX - Service-specific behavior */ flags = 0; cmd = NULL; if (pam_get_item(pamh, PAM_SERVICE, (duopam_const void **) (duopam_const void *)&service) != PAM_SUCCESS) { return (PAM_SERVICE_ERR); } if (strcmp(service, "sshd") == 0) { /* * Disable incremental status reporting for sshd :-( * OpenSSH accumulates PAM_TEXT_INFO from modules to send in * an SSH_MSG_USERAUTH_BANNER post-auth, not real-time! */ flags |= DUO_FLAG_SYNC; } else if (strcmp(service, "sudo") == 0) { cmd = getenv("SUDO_COMMAND"); } else if (strcmp(service, "su") == 0) { /* Check calling user for Duo auth, just like sudo */ if ((pw = getpwuid(getuid())) == NULL) { return (PAM_USER_UNKNOWN); } user = pw->pw_name; } /* Check group membership */ matched = duo_check_groups(pw, cfg.groups, cfg.groups_cnt); if (matched == -1) { return (PAM_SERVICE_ERR); } else if (matched == 0) { return (PAM_SUCCESS); } /* Grab the remote host */ ip = NULL; pam_get_item(pamh, PAM_RHOST, (duopam_const void **)(duopam_const void *)&ip); host = ip; /* PAM is weird, check to see if PAM_RHOST is IP or hostname */ if (ip == NULL) { ip = ""; /* XXX inet_addr needs a non-null IP */ } if (!inet_aton(ip, &addr)) { /* We have a hostname, don't try to resolve, check fallback */ ip = (cfg.local_ip_fallback ? duo_local_ip() : NULL); } /* Honor configured http_proxy */ if (cfg.http_proxy != NULL) { setenv("http_proxy", cfg.http_proxy, 1); } /* Try Duo auth */ if ((duo = duo_open(cfg.apihost, cfg.ikey, cfg.skey, "pam_duo/" PACKAGE_VERSION, cfg.noverify ? "" : cfg.cafile, DUO_NO_TIMEOUT)) == NULL) { duo_log(LOG_ERR, "Couldn't open Duo API handle", user, host, NULL); return (PAM_SERVICE_ERR); } duo_set_conv_funcs(duo, __duo_prompt, __duo_status, pamh); if (cfg.autopush) { flags |= DUO_FLAG_AUTO; } pam_err = PAM_SERVICE_ERR; for (i = 0; i < cfg.prompts; i++) { code = duo_login(duo, user, host, flags, cfg.pushinfo ? cmd : NULL, cfg.suffix); if (code == DUO_FAIL) { duo_log(LOG_WARNING, "Failed Duo login", user, host, duo_geterr(duo)); if ((flags & DUO_FLAG_SYNC) == 0) { pam_info(pamh, "%s", ""); } /* Keep going */ continue; } /* Terminal conditions */ if (code == DUO_OK) { if ((p = duo_geterr(duo)) != NULL) { duo_log(LOG_WARNING, "Skipped Duo login", user, host, p); } else { duo_log(LOG_INFO, "Successful Duo login", user, host, NULL); } pam_err = PAM_SUCCESS; } else if (code == DUO_ABORT) { duo_log(LOG_WARNING, "Aborted Duo login", user, host, duo_geterr(duo)); pam_err = PAM_ABORT; } else if (cfg.failmode == DUO_FAIL_SAFE && (code == DUO_CONN_ERROR || code == DUO_CLIENT_ERROR || code == DUO_SERVER_ERROR)) { duo_log(LOG_WARNING, "Failsafe Duo login", user, host, duo_geterr(duo)); pam_err = PAM_SUCCESS; } else { duo_log(LOG_ERR, "Error in Duo login", user, host, duo_geterr(duo)); pam_err = PAM_SERVICE_ERR; } break; } if (i == MAX_PROMPTS) { pam_err = PAM_MAXTRIES; } duo_close(duo); return (pam_err); }
static int last_login_failed(pam_handle_t *pamh, int announce, const char *user, time_t lltime) { int retval; int fd; struct utmp ut; struct utmp utuser; int failed = 0; char the_time[256]; char *date = NULL; char *host = NULL; char *line = NULL; if (strlen(user) > UT_NAMESIZE) { pam_syslog(pamh, LOG_WARNING, "username too long, output might be inaccurate"); } /* obtain the failed login attempt records from btmp */ fd = open(_PATH_BTMP, O_RDONLY); if (fd < 0) { int save_errno = errno; pam_syslog(pamh, LOG_ERR, "unable to open %s: %m", _PATH_BTMP); D(("unable to open %s file", _PATH_BTMP)); if (save_errno == ENOENT) return PAM_SUCCESS; else return PAM_SERVICE_ERR; } while ((retval=pam_modutil_read(fd, (void *)&ut, sizeof(ut))) == sizeof(ut)) { if (ut.ut_tv.tv_sec >= lltime && strncmp(ut.ut_user, user, UT_NAMESIZE) == 0) { memcpy(&utuser, &ut, sizeof(utuser)); failed++; } } if (failed) { /* we want the date? */ if (announce & LASTLOG_DATE) { struct tm *tm, tm_buf; time_t lf_time; lf_time = utuser.ut_tv.tv_sec; tm = localtime_r (&lf_time, &tm_buf); strftime (the_time, sizeof (the_time), /* TRANSLATORS: "strftime options for date of last login" */ _(" %a %b %e %H:%M:%S %Z %Y"), tm); date = the_time; } /* we want & have the host? */ if ((announce & LASTLOG_HOST) && (utuser.ut_host[0] != '\0')) { /* TRANSLATORS: " from <host>" */ if (asprintf(&host, _(" from %.*s"), UT_HOSTSIZE, utuser.ut_host) < 0) { pam_syslog(pamh, LOG_ERR, "out of memory"); retval = PAM_BUF_ERR; goto cleanup; } } /* we want and have the terminal? */ if ((announce & LASTLOG_LINE) && (utuser.ut_line[0] != '\0')) { /* TRANSLATORS: " on <terminal>" */ if (asprintf(&line, _(" on %.*s"), UT_LINESIZE, utuser.ut_line) < 0) { pam_syslog(pamh, LOG_ERR, "out of memory"); retval = PAM_BUF_ERR; goto cleanup; } } if (line != NULL || date != NULL || host != NULL) { /* TRANSLATORS: "Last failed login: <date> from <host> on <terminal>" */ pam_info(pamh, _("Last failed login:%s%s%s"), date ? date : "", host ? host : "", line ? line : ""); } _pam_drop(line); #if defined HAVE_DNGETTEXT && defined ENABLE_NLS retval = asprintf (&line, dngettext(PACKAGE, "There was %d failed login attempt since the last successful login.", "There were %d failed login attempts since the last successful login.", failed), failed); #else if (failed == 1) retval = asprintf(&line, _("There was %d failed login attempt since the last successful login."), failed); else retval = asprintf(&line, /* TRANSLATORS: only used if dngettext is not supported */ _("There were %d failed login attempts since the last successful login."), failed); #endif if (retval >= 0) retval = pam_info(pamh, "%s", line); else { retval = PAM_BUF_ERR; line = NULL; } } cleanup: free(host); free(line); close(fd); D(("all done with btmp")); return retval; }
static int last_login_read(pam_handle_t *pamh, int announce, int last_fd, uid_t uid, time_t *lltime) { struct flock last_lock; struct lastlog last_login; int retval = PAM_SUCCESS; char the_time[256]; char *date = NULL; char *host = NULL; char *line = NULL; memset(&last_lock, 0, sizeof(last_lock)); last_lock.l_type = F_RDLCK; last_lock.l_whence = SEEK_SET; last_lock.l_start = sizeof(last_login) * (off_t) uid; last_lock.l_len = sizeof(last_login); if (fcntl(last_fd, F_SETLK, &last_lock) < 0) { D(("locking %s failed..(waiting a little)", _PATH_LASTLOG)); pam_syslog(pamh, LOG_WARNING, "file %s is locked/read", _PATH_LASTLOG); sleep(LASTLOG_IGNORE_LOCK_TIME); } if (pam_modutil_read(last_fd, (char *) &last_login, sizeof(last_login)) != sizeof(last_login)) { memset(&last_login, 0, sizeof(last_login)); } last_lock.l_type = F_UNLCK; (void) fcntl(last_fd, F_SETLK, &last_lock); /* unlock */ *lltime = last_login.ll_time; if (!last_login.ll_time) { if (announce & LASTLOG_DEBUG) { pam_syslog(pamh, LOG_DEBUG, "first login for user with uid %lu", (unsigned long int)uid); } } if (!(announce & LASTLOG_QUIET)) { if (last_login.ll_time) { /* we want the date? */ if (announce & LASTLOG_DATE) { struct tm *tm, tm_buf; time_t ll_time; ll_time = last_login.ll_time; tm = localtime_r (&ll_time, &tm_buf); strftime (the_time, sizeof (the_time), /* TRANSLATORS: "strftime options for date of last login" */ _(" %a %b %e %H:%M:%S %Z %Y"), tm); date = the_time; } /* we want & have the host? */ if ((announce & LASTLOG_HOST) && (last_login.ll_host[0] != '\0')) { /* TRANSLATORS: " from <host>" */ if (asprintf(&host, _(" from %.*s"), UT_HOSTSIZE, last_login.ll_host) < 0) { pam_syslog(pamh, LOG_ERR, "out of memory"); retval = PAM_BUF_ERR; goto cleanup; } } /* we want and have the terminal? */ if ((announce & LASTLOG_LINE) && (last_login.ll_line[0] != '\0')) { /* TRANSLATORS: " on <terminal>" */ if (asprintf(&line, _(" on %.*s"), UT_LINESIZE, last_login.ll_line) < 0) { pam_syslog(pamh, LOG_ERR, "out of memory"); retval = PAM_BUF_ERR; goto cleanup; } } if (date != NULL || host != NULL || line != NULL) /* TRANSLATORS: "Last login: <date> from <host> on <terminal>" */ retval = pam_info(pamh, _("Last login:%s%s%s"), date ? date : "", host ? host : "", line ? line : ""); } else if (announce & LASTLOG_NEVER) { D(("this is the first time this user has logged in")); retval = pam_info(pamh, "%s", _("Welcome to your new account!")); } } /* cleanup */ cleanup: memset(&last_login, 0, sizeof(last_login)); _pam_overwrite(date); _pam_overwrite(host); _pam_drop(host); _pam_overwrite(line); _pam_drop(line); return retval; }
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_authenticate(pam_handle_t *pamh, int pam_flags, int argc, const char *argv[]) { struct duo_config cfg; struct passwd *pw; duo_t *duo; duo_code_t code; duopam_const char *config, *cmd, *ip, *p, *service, *user; int i, flags, pam_err; memset(&cfg, 0, sizeof(cfg)); cfg.failmode = DUO_FAIL_SAFE; /* Parse configuration */ config = DUO_CONF; for (i = 0; i < argc; i++) { if (strncmp("conf=", argv[i], 5) == 0) { config = argv[i] + 5; } else if (strcmp("debug", argv[i]) == 0) { options |= PAM_OPT_DEBUG; } else if (strcmp("try_first_pass", argv[i]) == 0) { options |= PAM_OPT_TRY_FIRST_PASS; } else if (strcmp("use_first_pass", argv[i]) == 0) { options |= PAM_OPT_USE_FIRST_PASS|PAM_OPT_TRY_FIRST_PASS; } else if (strcmp("use_uid", argv[i]) == 0) { options |= PAM_OPT_USE_UID; } else if (strcmp("push", argv[i]) == 0) { options |= PAM_OPT_PUSH; } else { _syslog(LOG_ERR, "Invalid pam_duo option: '%s'", argv[i]); return (PAM_SERVICE_ERR); } } i = duo_parse_config(config, __ini_handler, &cfg); if (i == -2) { _syslog(LOG_ERR, "%s must be readable only by user 'root'", config); return (PAM_SERVICE_ERR); } else if (i == -1) { _syslog(LOG_ERR, "Couldn't open %s: %s", config, strerror(errno)); return (PAM_SERVICE_ERR); } else if (i > 0) { _syslog(LOG_ERR, "Parse error in %s, line %d", config, i); return (PAM_SERVICE_ERR); } else if (!cfg.host || !cfg.host[0] || !cfg.skey || !cfg.skey[0] || !cfg.ikey || !cfg.ikey[0]) { _syslog(LOG_ERR, "Missing host, ikey, or skey in %s", config); return (PAM_SERVICE_ERR); } /* Check user */ if (pam_get_user(pamh, &user, NULL) != PAM_SUCCESS || (pw = getpwnam(user)) == NULL) { return (PAM_USER_UNKNOWN); } /* XXX - Service-specific behavior */ flags = 0; cmd = NULL; if (pam_get_item(pamh, PAM_SERVICE, (duopam_const void **) (duopam_const void *)&service) != PAM_SUCCESS) { return (PAM_SERVICE_ERR); } if (options & PAM_OPT_USE_UID) { /* Check calling user for Duo auth, just like sudo */ if ((pw = getpwuid(getuid())) == NULL) { return (PAM_USER_UNKNOWN); } user = pw->pw_name; } if (strcmp(service, "sshd") == 0) { /* * Disable incremental status reporting for sshd :-( * OpenSSH accumulates PAM_TEXT_INFO from modules to send in * an SSH_MSG_USERAUTH_BANNER post-auth, not real-time! */ flags |= DUO_FLAG_SYNC; } else if (strcmp(service, "sudo") == 0) { cmd = getenv("SUDO_COMMAND"); } /* Check group membership */ if (cfg.groups_cnt > 0) { int matched = 0; if (ga_init(pw->pw_name, pw->pw_gid) < 0) { _log(LOG_ERR, "Couldn't get groups", pw->pw_name, NULL, strerror(errno)); return (PAM_SERVICE_ERR); } for (i = 0; i < cfg.groups_cnt; i++) { if (ga_match_pattern_list(cfg.groups[i])) { matched = 1; break; } } ga_free(); /* User in configured groups for Duo auth? */ if (!matched) return (PAM_SUCCESS); } ip = NULL; pam_get_item(pamh, PAM_RHOST, (duopam_const void **)(duopam_const void *)&ip); /* Honor configured http_proxy */ if (cfg.http_proxy != NULL) { setenv("http_proxy", cfg.http_proxy, 1); } /* Try Duo auth */ if ((duo = duo_open(cfg.host, cfg.ikey, cfg.skey, "pam_duo/" PACKAGE_VERSION, cfg.noverify ? "" : cfg.cafile)) == NULL) { _log(LOG_ERR, "Couldn't open Duo API handle", user, ip, NULL); return (PAM_SERVICE_ERR); } duo_set_conv_funcs(duo, __duo_prompt, __duo_status, pamh); pam_err = PAM_SERVICE_ERR; for (i = 0; i < MAX_RETRIES; i++) { code = duo_login(duo, user, ip, flags, cfg.pushinfo ? cmd : NULL); if (code == DUO_FAIL) { _log(LOG_WARNING, "Failed Duo login", user, ip, duo_geterr(duo)); if ((flags & DUO_FLAG_SYNC) == 0) { pam_info(pamh, "%s", ""); } /* Keep going */ continue; } /* Terminal conditions */ if (code == DUO_OK) { if ((p = duo_geterr(duo)) != NULL) { _log(LOG_WARNING, "Skipped Duo login", user, ip, p); } else { _log(LOG_INFO, "Successful Duo login", user, ip, NULL); } pam_err = PAM_SUCCESS; } else if (code == DUO_ABORT) { _log(LOG_WARNING, "Aborted Duo login", user, ip, duo_geterr(duo)); pam_err = PAM_ABORT; } else if (cfg.failmode == DUO_FAIL_SAFE && (code == DUO_CONN_ERROR || code == DUO_CLIENT_ERROR || code == DUO_SERVER_ERROR)) { _log(LOG_WARNING, "Failsafe Duo login", user, ip, duo_geterr(duo)); pam_err = PAM_SUCCESS; } else { _log(LOG_ERR, "Error in Duo login", user, ip, duo_geterr(duo)); pam_err = PAM_SERVICE_ERR; } break; } if (i == MAX_RETRIES) { pam_err = PAM_MAXTRIES; } duo_close(duo); return (pam_err); }
/* Tell the user that access has been granted. */ static void verbose_success(pam_handle_t *pamh, long diff) { pam_info(pamh, _("Access granted (last access was %ld seconds ago)."), diff); }
// 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; }
static int exec_file(pam_handle_t *pamh, char **argv, const char *logfile) { pid_t pid, rc; int p[2]; char buf[1024]; long ttl; time_t start; int i, status, intr; fd_set rd; struct timeval tv; size_t total = 0; if (pipe(p)) { _pam_log(LOG_ERR, "pipe: %s", strerror(errno)); return PAM_SYSTEM_ERR; } pid = fork(); if (pid == -1) { close(p[0]); close(p[1]); _pam_log(LOG_ERR, "fork: %s", strerror(errno)); return PAM_SYSTEM_ERR; } if (pid == 0) { /* child */ if (dup2(p[1], 1) == -1) { _pam_log(LOG_ERR, "dup2: %s", strerror(errno)); _exit(127); } for (i = sysconf(_SC_OPEN_MAX); i >= 0; i--) { if (i != 1) close(i); } open("/dev/null", O_RDONLY); if (logfile) { if (open(logfile, O_CREAT|O_APPEND|O_WRONLY, 0644) == -1) { _pam_log(LOG_ERR, "open(%s): %s", logfile, strerror(errno)); _exit(127); } } else dup2(1, 2); execv(argv[0], argv); _exit(127); } /* master */ close(p[1]); start = time(NULL); intr = 0; rc = 0; status = 0; for (i = 0; total < max_output_size;) { FD_ZERO(&rd); FD_SET(p[0], &rd); if (intr) { rc = waitpid(pid, &status, WNOHANG); if (rc == pid) break; if (rc == (pid_t)-1) { _pam_log(LOG_ERR, "waitpid: %s", strerror(errno)); break; } intr = 0; } ttl = timeout_option - (time(NULL) - start); if (ttl <= 0) { _pam_log(LOG_ERR, "timed out reading from %s", argv[0]); break; } tv.tv_sec = ttl; tv.tv_usec = 0; rc = select(p[0] + 1, &rd, NULL, NULL, &tv); if (rc < 0) { if (errno == EINTR || errno == EAGAIN) { intr = 1; continue; } _pam_log(LOG_ERR, "select: %s", strerror(errno)); } if (i == sizeof(buf) - 1) { char *p; buf[i] = 0; p = strrchr(buf, '\n'); if (p) *p++ = 0; pam_info(pamh, "%s", buf); if (p && *p) { i = strlen(p); memmove(buf, p, i); } } if (FD_ISSET(p[0], &rd)) { char c; rc = read(p[0], &c, 1); if (rc == 1) { buf[i++] = c; total++; } else if (rc == 0 || errno == EINTR || errno == EAGAIN) { intr = 1; continue; } else { _pam_log(LOG_ERR, "read: %s", strerror(errno)); break; } } } if (i) { buf[i] = 0; pam_info(pamh, "%s", buf); } close(p[0]); if (rc != pid) { _pam_log(LOG_NOTICE, "killing %s (pid %lu)", argv[0], (unsigned long) pid); kill(pid, SIGKILL); while ((rc = waitpid(pid, &status, 0)) == -1 && errno == EINTR); if (rc == (pid_t)-1) { _pam_log(LOG_ERR, "waitpid: %s", strerror(errno)); return PAM_SYSTEM_ERR; } } else if (WIFEXITED(status)) { status = WEXITSTATUS(status); if (status) { _pam_log(LOG_ERR, "%s exited with status %d", argv[0], status); return PAM_SYSTEM_ERR; } } else if (WIFSIGNALED(status)) { status = WTERMSIG(status); _pam_log(LOG_ERR, "%s got signal %d", argv[0], status); return PAM_SYSTEM_ERR; } else if (status) { _pam_log(LOG_ERR, "%s failed: unknown status 0x%x", argv[0], status); return PAM_SYSTEM_ERR; } return PAM_SUCCESS; }