From de848d9e9146434817c65d74d1d0313e9d729462 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sat, 27 Apr 2024 14:01:37 +0200 Subject: Adding upstream version 1.4.0. Signed-off-by: Daniel Baumann --- modules/pam_issue/pam_issue.c | 306 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 306 insertions(+) create mode 100644 modules/pam_issue/pam_issue.c (limited to 'modules/pam_issue/pam_issue.c') diff --git a/modules/pam_issue/pam_issue.c b/modules/pam_issue/pam_issue.c new file mode 100644 index 0000000..5b6a466 --- /dev/null +++ b/modules/pam_issue/pam_issue.c @@ -0,0 +1,306 @@ +/* + * pam_issue module - a simple /etc/issue parser to set PAM_USER_PROMPT + * + * Copyright 1999 by Ben Collins + * + * Needs to be called before any other auth modules so we can setup the + * user prompt before it's first used. Allows one argument option, which + * is the full path to a file to be used for issue (uses /etc/issue as a + * default) such as "issue=/etc/issue.telnet". + * + * We can also parse escapes within the the issue file (enabled by + * default, but can be disabled with the "noesc" option). It's the exact + * same parsing as util-linux's agetty program performs. + * + * Released under the GNU LGPL version 2 or later + */ + +#include "config.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include "pam_inline.h" + +static int _user_prompt_set = 0; + +static int read_issue_raw(pam_handle_t *pamh, FILE *fp, char **prompt); +static int read_issue_quoted(pam_handle_t *pamh, FILE *fp, char **prompt); + +/* --- authentication management functions (only) --- */ + +int +pam_sm_authenticate (pam_handle_t *pamh, int flags UNUSED, + int argc, const char **argv) +{ + int retval = PAM_SERVICE_ERR; + FILE *fp; + const char *issue_file = NULL; + int parse_esc = 1; + const void *item = NULL; + const char *cur_prompt; + char *issue_prompt = NULL; + + /* If we've already set the prompt, don't set it again */ + if(_user_prompt_set) + return PAM_IGNORE; + + /* We set this here so if we fail below, we won't get further + than this next time around (only one real failure) */ + _user_prompt_set = 1; + + for ( ; argc-- > 0 ; ++argv ) { + const char *str; + + if ((str = pam_str_skip_prefix(*argv, "issue=")) != NULL) { + issue_file = str; + D(("set issue_file to: %s", issue_file)); + } else if (!strcmp(*argv,"noesc")) { + parse_esc = 0; + D(("turning off escape parsing by request")); + } else + D(("unknown option passed: %s", *argv)); + } + + if (issue_file == NULL) + issue_file = "/etc/issue"; + + if ((fp = fopen(issue_file, "r")) == NULL) { + pam_syslog(pamh, LOG_ERR, "error opening %s: %m", issue_file); + return PAM_SERVICE_ERR; + } + + if ((retval = pam_get_item(pamh, PAM_USER_PROMPT, &item)) != PAM_SUCCESS) { + fclose(fp); + return retval; + } + + cur_prompt = item; + if (cur_prompt == NULL) + cur_prompt = ""; + + if (parse_esc) + retval = read_issue_quoted(pamh, fp, &issue_prompt); + else + retval = read_issue_raw(pamh, fp, &issue_prompt); + + fclose(fp); + + if (retval != PAM_SUCCESS) + goto out; + + { + size_t size = strlen(issue_prompt) + strlen(cur_prompt) + 1; + char *new_prompt = realloc(issue_prompt, size); + + if (new_prompt == NULL) { + pam_syslog(pamh, LOG_CRIT, "out of memory"); + retval = PAM_BUF_ERR; + goto out; + } + issue_prompt = new_prompt; + } + + strcat(issue_prompt, cur_prompt); + retval = pam_set_item(pamh, PAM_USER_PROMPT, + (const void *) issue_prompt); + out: + _pam_drop(issue_prompt); + return (retval == PAM_SUCCESS) ? PAM_IGNORE : retval; +} + +int +pam_sm_setcred (pam_handle_t *pamh UNUSED, int flags UNUSED, + int argc UNUSED, const char **argv UNUSED) +{ + return PAM_IGNORE; +} + +static int +read_issue_raw(pam_handle_t *pamh, FILE *fp, char **prompt) +{ + char *issue; + struct stat st; + + *prompt = NULL; + + if (fstat(fileno(fp), &st) < 0) { + pam_syslog(pamh, LOG_ERR, "stat error: %m"); + return PAM_SERVICE_ERR; + } + + if ((issue = malloc(st.st_size + 1)) == NULL) { + pam_syslog(pamh, LOG_CRIT, "out of memory"); + return PAM_BUF_ERR; + } + + if ((off_t)fread(issue, 1, st.st_size, fp) != st.st_size) { + pam_syslog(pamh, LOG_ERR, "read error: %m"); + _pam_drop(issue); + return PAM_SERVICE_ERR; + } + + issue[st.st_size] = '\0'; + *prompt = issue; + return PAM_SUCCESS; +} + +static int +read_issue_quoted(pam_handle_t *pamh, FILE *fp, char **prompt) +{ + int c; + size_t size = 1024; + size_t issue_len = 0; + char *issue; + struct utsname uts; + + *prompt = NULL; + + if ((issue = malloc(size)) == NULL) { + pam_syslog(pamh, LOG_CRIT, "out of memory"); + return PAM_BUF_ERR; + } + + (void) uname(&uts); + + while ((c = getc(fp)) != EOF) { + const char *src = NULL; + size_t len = 0; + char buf[1024] = ""; + + if (c == '\\') { + if ((c = getc(fp)) == EOF) + break; + switch (c) { + case 's': + src = uts.sysname; + len = strnlen(uts.sysname, sizeof(uts.sysname)); + break; + case 'n': + src = uts.nodename; + len = strnlen(uts.nodename, sizeof(uts.nodename)); + break; + case 'r': + src = uts.release; + len = strnlen(uts.release, sizeof(uts.release)); + break; + case 'v': + src = uts.version; + len = strnlen(uts.version, sizeof(uts.version)); + break; + case 'm': + src = uts.machine; + len = strnlen(uts.machine, sizeof(uts.machine)); + break; + case 'o': +#ifdef HAVE_GETDOMAINNAME + if (getdomainname(buf, sizeof(buf)) >= 0) + buf[sizeof(buf) - 1] = '\0'; +#endif + break; + case 'd': + case 't': + { + const char *weekday[] = { + "Sun", "Mon", "Tue", "Wed", "Thu", + "Fri", "Sat" }; + const char *month[] = { + "Jan", "Feb", "Mar", "Apr", "May", + "Jun", "Jul", "Aug", "Sep", "Oct", + "Nov", "Dec" }; + time_t now; + struct tm *tm; + + (void) time (&now); + tm = localtime(&now); + + if (c == 'd') + snprintf (buf, sizeof buf, "%s %s %d %d", + weekday[tm->tm_wday], month[tm->tm_mon], + tm->tm_mday, tm->tm_year + 1900); + else + snprintf (buf, sizeof buf, "%02d:%02d:%02d", + tm->tm_hour, tm->tm_min, tm->tm_sec); + } + break; + case 'l': + { + const char *ttyn = ttyname(1); + if (ttyn) { + const char *str = pam_str_skip_prefix(ttyn, "/dev/"); + if (str != NULL) + ttyn = str; + src = ttyn; + len = strlen(ttyn); + } + } + break; + case 'u': + case 'U': + { + unsigned int users = 0; + struct utmp *ut; + setutent(); + while ((ut = getutent())) { + if (ut->ut_type == USER_PROCESS) + ++users; + } + endutent(); + if (c == 'U') + snprintf (buf, sizeof buf, "%u %s", users, + (users == 1) ? "user" : "users"); + else + snprintf (buf, sizeof buf, "%u", users); + break; + } + default: + buf[0] = c; buf[1] = '\0'; + } + } else { + buf[0] = c; buf[1] = '\0'; + } + + if (src == NULL) { + src = buf; + len = strlen(buf); + } + if (issue_len + len + 1 > size) { + char *new_issue; + + size += len + 1; + new_issue = realloc (issue, size); + if (new_issue == NULL) { + _pam_drop(issue); + return PAM_BUF_ERR; + } + issue = new_issue; + } + memcpy(issue + issue_len, src, len); + issue_len += len; + } + + issue[issue_len] = '\0'; + + if (ferror(fp)) { + pam_syslog(pamh, LOG_ERR, "read error: %m"); + _pam_drop(issue); + return PAM_SERVICE_ERR; + } + + *prompt = issue; + return PAM_SUCCESS; +} + +/* end of module definition */ -- cgit v1.2.3