Merge branch 'i8c' with version 1.3.1-36 into 'i8'

i8 changed/i8/pam-1.3.1-36.el8_10.inferit
Sergey Cherevko 4 weeks ago
commit fc55f81471
Signed by: scherevko
GPG Key ID: D87CBBC16D2E4A72

@ -0,0 +1,595 @@
diff -up Linux-PAM-1.3.1/libpam/pam_handlers.c.libpam-support-long-lines Linux-PAM-1.3.1/libpam/pam_handlers.c
--- Linux-PAM-1.3.1/libpam/pam_handlers.c.libpam-support-long-lines 2024-11-04 13:17:27.395991844 +0100
+++ Linux-PAM-1.3.1/libpam/pam_handlers.c 2024-11-04 14:45:22.583441849 +0100
@@ -16,21 +16,30 @@
#include <fcntl.h>
#include <unistd.h>
-#define BUF_SIZE 1024
#define MODULE_CHUNK 4
#define UNKNOWN_MODULE "<*unknown module*>"
#ifndef _PAM_ISA
#define _PAM_ISA "."
#endif
-static int _pam_assemble_line(FILE *f, char *buf, int buf_len);
+struct line_buffer {
+ char *assembled;
+ char *chunk;
+ size_t chunk_size;
+ size_t len;
+ size_t size;
+};
+
+static void _pam_buffer_init(struct line_buffer *buffer);
+
+static int _pam_assemble_line(FILE *f, struct line_buffer *buf);
static void _pam_free_handlers_aux(struct handler **hp);
static int _pam_add_handler(pam_handle_t *pamh
, int must_fail, int other, int stack_level, int type
, int *actions, const char *mod_path
- , int argc, char **argv, int argvlen);
+ , int argc, char **argv, size_t argvlen);
/* Values for module type */
@@ -58,12 +67,15 @@ static int _pam_parse_conf_file(pam_hand
#endif /* PAM_READ_BOTH_CONFS */
)
{
- char buf[BUF_SIZE];
+ struct line_buffer buffer;
int x; /* read a line from the FILE *f ? */
+
+ _pam_buffer_init(&buffer);
/*
* read a line from the configuration (FILE *) f
*/
- while ((x = _pam_assemble_line(f, buf, BUF_SIZE)) > 0) {
+ while ((x = _pam_assemble_line(f, &buffer)) > 0) {
+ char *buf = buffer.assembled;
char *tok, *nexttok=NULL;
const char *this_service;
const char *mod_path;
@@ -73,7 +85,7 @@ static int _pam_parse_conf_file(pam_hand
int handler_type = PAM_HT_MODULE; /* regular handler from a module */
int argc;
char **argv;
- int argvlen;
+ size_t argvlen;
D(("_pam_init_handler: LINE: %s", buf));
if (known_service != NULL) {
@@ -232,10 +244,11 @@ static int _pam_parse_conf_file(pam_hand
if (nexttok != NULL) {
D(("list: %s",nexttok));
argvlen = _pam_mkargv(nexttok, &argv, &argc);
- D(("argvlen = %d",argvlen));
+ D(("argvlen = %zu",argvlen));
} else { /* there are no arguments so fix by hand */
D(("_pam_init_handlers: empty argument list"));
- argvlen = argc = 0;
+ argvlen = 0;
+ argc = 0;
argv = NULL;
}
@@ -548,88 +561,243 @@ int _pam_init_handlers(pam_handle_t *pam
return PAM_SUCCESS;
}
+static int _pam_buffer_add(struct line_buffer *buffer, char *start, char *end)
+{
+ size_t len = end - start;
+
+ D(("assembled: [%zu/%zu] '%s', adding [%zu] '%s'",
+ buffer->len, buffer->size,
+ buffer->assembled == NULL ? "" : buffer->assembled, len, start));
+
+ if (start == end)
+ return 0;
+
+ if (buffer->assembled == NULL && buffer->chunk == start) {
+ /* no extra allocation needed, just move chunk to assembled */
+ buffer->assembled = buffer->chunk;
+ buffer->len = len;
+ buffer->size = buffer->chunk_size;
+
+ buffer->chunk = NULL;
+ buffer->chunk_size = 0;
+
+ D(("exiting with quick exchange"));
+ return 0;
+ }
+
+ if (buffer->len + len + 1 > buffer->size) {
+ size_t size;
+ char *p;
+
+ size = buffer->len + len + 1;
+ if ((p = realloc(buffer->assembled, size)) == NULL)
+ return -1;
+
+ buffer->assembled = p;
+ buffer->size = size;
+ }
+
+ memcpy(buffer->assembled + buffer->len, start, len);
+ buffer->len += len;
+ buffer->assembled[buffer->len] = '\0';
+
+ D(("exiting"));
+ return 0;
+}
+
+static inline int _pam_buffer_add_eol(struct line_buffer *buffer,
+ char *start, char *end)
+{
+ if (buffer->assembled != NULL || (*start != '\0' && *start != '\n'))
+ return _pam_buffer_add(buffer, start, end);
+ return 0;
+}
+
+static void _pam_buffer_clear(struct line_buffer *buffer)
+{
+ _pam_drop(buffer->assembled);
+ _pam_drop(buffer->chunk);
+ buffer->chunk_size = 0;
+ buffer->len = 0;
+ buffer->size = 0;
+}
+
+static void _pam_buffer_init(struct line_buffer *buffer)
+{
+ buffer->assembled = NULL;
+ buffer->chunk = NULL;
+ _pam_buffer_clear(buffer);
+}
+
+static void _pam_buffer_purge(struct line_buffer *buffer)
+{
+ _pam_drop(buffer->chunk);
+ buffer->chunk_size = 0;
+}
+
+static void _pam_buffer_shift(struct line_buffer *buffer)
+{
+ if (buffer->assembled == NULL)
+ return;
+
+ _pam_buffer_purge(buffer);
+ buffer->chunk = buffer->assembled;
+ buffer->chunk_size = buffer->size;
+
+ buffer->assembled = NULL;
+ buffer->size = 0;
+ buffer->len = 0;
+}
+
+static inline int _pam_buffer_valid(struct line_buffer *buffer)
+{
+ return buffer->assembled != NULL && *buffer->assembled != '\0';
+}
+
/*
- * This is where we read a line of the PAM config file. The line may be
- * preceeded by lines of comments and also extended with "\\\n"
+ * Trim string to relevant parts of a configuration line.
+ *
+ * Preceding whitespaces are skipped and comment (#) marks the end of
+ * configuration line.
+ *
+ * Returns start of configuration line.
*/
+static inline char *_pam_str_trim(char *str)
+{
+ /* skip leading spaces */
+ str += strspn(str, " \t");
+ /*
+ * we are only interested in characters before the first '#'
+ * character
+ */
+ str[strcspn(str, "#")] = '\0';
-static int _pam_assemble_line(FILE *f, char *buffer, int buf_len)
+ return str;
+}
+
+/*
+ * Remove escaped newline from end of string.
+ *
+ * Configuration lines may span across multiple lines in a file
+ * by ending a line with a backslash (\).
+ *
+ * If an escaped newline is encountered, the backslash will be
+ * replaced with a blank ' ' and the newline itself removed.
+ * Then the variable "end" will point to the new end of line.
+ *
+ * Returns 0 if escaped newline was found and replaced, 1 otherwise.
+ */
+static inline int _pam_str_unescnl(char *start, char **end)
{
- char *p = buffer;
- char *endp = buffer + buf_len;
- char *s, *os;
- int used = 0;
+ int ret = 1;
+ char *p = *end;
- /* loop broken with a 'break' when a non-'\\n' ended line is read */
+ /*
+ * Check for backslash by scanning back from the end of
+ * the entered line, the '\n' should be included since
+ * normally a line is terminated with this character.
+ */
+ while (p > start && ((*--p == ' ') || (*p == '\t') || (*p == '\n')))
+ ;
+ if (*p == '\\') {
+ *p++ = ' '; /* replace backslash with ' ' */
+ *p = '\0'; /* truncate the line here */
+ *end = p;
+ ret = 0;
+ }
- D(("called."));
- for (;;) {
- if (p >= endp) {
- /* Overflow */
- D(("_pam_assemble_line: overflow"));
- return -1;
- }
- if (fgets(p, endp - p, f) == NULL) {
- if (used) {
- /* Incomplete read */
- return -1;
- } else {
- /* EOF */
- return 0;
- }
- }
+ return ret;
+}
- /* skip leading spaces --- line may be blank */
+/*
+ * Prepare line from file for configuration line parsing.
+ *
+ * A configuration line may span across multiple lines in a file.
+ * Remove comments and skip preceding whitespaces.
+ *
+ * Returns 0 if line spans across multiple lines, 1 if
+ * end of line is encountered.
+ */
+static inline int _pam_str_prepare(char *line, ssize_t len,
+ char **start, char **end)
+{
+ int ret;
- s = p + strspn(p, " \n\t");
- if (*s && (*s != '#')) {
- os = s;
+ *start = line;
+ *end = line + len;
- /*
- * we are only interested in characters before the first '#'
- * character
- */
+ ret = _pam_str_unescnl(*start, end) || strchr(*start, '#') != NULL;
- while (*s && *s != '#')
- ++s;
- if (*s == '#') {
- *s = '\0';
- used += strlen(os);
- break; /* the line has been read */
- }
+ *start = _pam_str_trim(*start);
+
+ return ret;
+}
+
+/*
+ * This is where we read a line of the PAM config file. The line may be
+ * preceded by lines of comments and also extended with "\\\n"
+ *
+ * Returns 0 on EOF, 1 on successful line parsing, or -1 on error.
+ */
+
+static int _pam_assemble_line(FILE *f, struct line_buffer *buffer)
+{
+ int ret = 0;
+
+ /* loop broken with a 'break' when a non-'\\n' ended line is read */
- s = os;
+ D(("called."));
- /*
- * Check for backslash by scanning back from the end of
- * the entered line, the '\n' has been included since
- * normally a line is terminated with this
- * character. fgets() should only return one though!
- */
+ _pam_buffer_shift(buffer);
- s += strlen(s);
- while (s > os && ((*--s == ' ') || (*s == '\t')
- || (*s == '\n')));
+ for (;;) {
+ char *start, *end;
+ ssize_t n;
+ int eol;
- /* check if it ends with a backslash */
- if (*s == '\\') {
- *s++ = ' '; /* replace backslash with ' ' */
- *s = '\0'; /* truncate the line here */
- used += strlen(os);
- p = s; /* there is more ... */
+ if ((n = getline(&buffer->chunk, &buffer->chunk_size, f)) == -1) {
+ if (ret) {
+ /* Incomplete read */
+ ret = -1;
} else {
- /* End of the line! */
- used += strlen(os);
- break; /* this is the complete line */
+ /* EOF */
+ ret = 0;
}
+ break;
+ }
+
+ eol = _pam_str_prepare(buffer->chunk, n, &start, &end);
+ if (eol) {
+ if (_pam_buffer_add_eol(buffer, start, end)) {
+ ret = -1;
+ break;
+ }
+ if (_pam_buffer_valid(buffer)) {
+ /* Successfully parsed a line */
+ ret = 1;
+ break;
+ }
+ /* Start parsing next line */
+ _pam_buffer_shift(buffer);
+ ret = 0;
} else {
- /* Nothing in this line */
- /* Don't move p */
+ /* Configuration line spans across multiple lines in file */
+ if (_pam_buffer_add(buffer, start, end)) {
+ ret = -1;
+ break;
+ }
+ /* Keep parsing line */
+ ret = 1;
}
}
- return used;
+ if (ret == 1)
+ _pam_buffer_purge(buffer);
+ else
+ _pam_buffer_clear(buffer);
+
+ return ret;
}
static char *
@@ -761,7 +929,7 @@ _pam_load_module(pam_handle_t *pamh, con
int _pam_add_handler(pam_handle_t *pamh
, int handler_type, int other, int stack_level, int type
, int *actions, const char *mod_path
- , int argc, char **argv, int argvlen)
+ , int argc, char **argv, size_t argvlen)
{
struct loaded_module *mod = NULL;
struct handler **handler_p;
diff -up Linux-PAM-1.3.1/libpam/pam_misc.c.libpam-support-long-lines Linux-PAM-1.3.1/libpam/pam_misc.c
--- Linux-PAM-1.3.1/libpam/pam_misc.c.libpam-support-long-lines 2017-02-10 11:10:15.000000000 +0100
+++ Linux-PAM-1.3.1/libpam/pam_misc.c 2024-11-04 14:47:31.147672755 +0100
@@ -39,6 +39,8 @@
#include <stdarg.h>
#include <stdlib.h>
+#include <stdint.h>
+#include <limits.h>
#include <stdio.h>
#include <string.h>
#include <syslog.h>
@@ -163,60 +165,55 @@ char *_pam_memdup(const char *x, int len
/* Generate argv, argc from s */
/* caller must free(argv) */
-int _pam_mkargv(char *s, char ***argv, int *argc)
+size_t _pam_mkargv(const char *s, char ***argv, int *argc)
{
- int l;
- int argvlen = 0;
- char *sbuf, *sbuf_start;
+ size_t l;
+ size_t argvlen = 0;
char **our_argv = NULL;
- char **argvbuf;
- char *argvbufp;
-#ifdef PAM_DEBUG
- int count=0;
-#endif
- D(("_pam_mkargv called: %s",s));
+ D(("called: %s",s));
*argc = 0;
l = strlen(s);
- if (l) {
- if ((sbuf = sbuf_start = _pam_strdup(s)) == NULL) {
- pam_syslog(NULL, LOG_CRIT,
- "pam_mkargv: null returned by _pam_strdup");
- D(("arg NULL"));
+ if (l && l < SIZE_MAX / (sizeof(char) + sizeof(char *))) {
+ char **argvbuf;
+ /* Overkill on the malloc, but not large */
+ argvlen = (l + 1) * (sizeof(char) + sizeof(char *));
+ if ((our_argv = argvbuf = malloc(argvlen)) == NULL) {
+ pam_syslog(NULL, LOG_CRIT, "pam_mkargv: null returned by malloc");
+ argvlen = 0;
} else {
- /* Overkill on the malloc, but not large */
- argvlen = (l + 1) * ((sizeof(char)) + sizeof(char *));
- if ((our_argv = argvbuf = malloc(argvlen)) == NULL) {
- pam_syslog(NULL, LOG_CRIT,
- "pam_mkargv: null returned by malloc");
- } else {
- char *tmp=NULL;
-
- argvbufp = (char *) argvbuf + (l * sizeof(char *));
- D(("[%s]",sbuf));
- while ((sbuf = _pam_StrTok(sbuf, " \n\t", &tmp))) {
- D(("arg #%d",++count));
- D(("->[%s]",sbuf));
- strcpy(argvbufp, sbuf);
- D(("copied token"));
- *argvbuf = argvbufp;
- argvbufp += strlen(argvbufp) + 1;
- D(("stepped in argvbufp"));
- (*argc)++;
- argvbuf++;
- sbuf = NULL;
- D(("loop again?"));
+ char *argvbufp;
+ char *tmp=NULL;
+ char *tok;
+#ifdef PAM_DEBUG
+ unsigned count=0;
+#endif
+ argvbufp = (char *) argvbuf + (l * sizeof(char *));
+ strcpy(argvbufp, s);
+ D(("[%s]",argvbufp));
+ while ((tok = _pam_StrTok(argvbufp, " \n\t", &tmp))) {
+ D(("arg #%u",++count));
+ D(("->[%s]",tok));
+ *argvbuf++ = tok;
+ if (*argc == INT_MAX) {
+ pam_syslog(NULL, LOG_CRIT,
+ "pam_mkargv: too many arguments");
+ argvlen = 0;
+ _pam_drop(our_argv);
+ break;
}
+ (*argc)++;
+ argvbufp = NULL;
+ D(("loop again?"));
}
- _pam_drop(sbuf_start);
}
}
*argv = our_argv;
- D(("_pam_mkargv returned"));
+ D(("exiting"));
return(argvlen);
}
diff -up Linux-PAM-1.3.1/libpam/pam_private.h.libpam-support-long-lines Linux-PAM-1.3.1/libpam/pam_private.h
--- Linux-PAM-1.3.1/libpam/pam_private.h.libpam-support-long-lines 2024-11-04 13:17:27.404991868 +0100
+++ Linux-PAM-1.3.1/libpam/pam_private.h 2024-11-04 14:51:31.654635328 +0100
@@ -16,6 +16,7 @@
#include "config.h"
+#include <stddef.h>
#include <syslog.h>
#include <security/pam_appl.h>
@@ -266,7 +267,7 @@ char *_pam_strdup(const char *s);
char *_pam_memdup(const char *s, int len);
-int _pam_mkargv(char *s, char ***argv, int *argc);
+size_t _pam_mkargv(const char *s, char ***argv, int *argc);
void _pam_sanitize(pam_handle_t *pamh);
diff -up Linux-PAM-1.3.1/modules/pam_exec/pam_exec.c.libpam-support-long-lines Linux-PAM-1.3.1/modules/pam_exec/pam_exec.c
--- Linux-PAM-1.3.1/modules/pam_exec/pam_exec.c.libpam-support-long-lines 2017-02-10 11:10:15.000000000 +0100
+++ Linux-PAM-1.3.1/modules/pam_exec/pam_exec.c 2024-11-04 13:17:27.434991946 +0100
@@ -408,7 +408,7 @@ call_exec (const char *pam_type, pam_han
_exit (err);
}
- arggv = calloc (argc + 4, sizeof (char *));
+ arggv = calloc ((size_t) argc + 1, sizeof (char *));
if (arggv == NULL)
_exit (ENOMEM);
diff -up Linux-PAM-1.3.1/modules/pam_filter/pam_filter.c.libpam-support-long-lines Linux-PAM-1.3.1/modules/pam_filter/pam_filter.c
--- Linux-PAM-1.3.1/modules/pam_filter/pam_filter.c.libpam-support-long-lines 2017-02-10 11:10:15.000000000 +0100
+++ Linux-PAM-1.3.1/modules/pam_filter/pam_filter.c 2024-11-04 13:17:27.434991946 +0100
@@ -101,7 +101,8 @@ static int process_args(pam_handle_t *pa
char **levp;
const char *user = NULL;
const void *tmp;
- int i,size, retval;
+ int i, retval;
+ size_t size;
*filtername = *++argv;
if (ctrl & FILTER_DEBUG) {
diff -up Linux-PAM-1.3.1/modules/pam_motd/pam_motd.c.libpam-support-long-lines Linux-PAM-1.3.1/modules/pam_motd/pam_motd.c
--- Linux-PAM-1.3.1/modules/pam_motd/pam_motd.c.libpam-support-long-lines 2024-11-04 13:17:27.423991918 +0100
+++ Linux-PAM-1.3.1/modules/pam_motd/pam_motd.c 2024-11-04 13:17:27.434991946 +0100
@@ -83,14 +83,14 @@ static void try_to_display_fd(pam_handle
* Returns 0 in case of error, 1 in case of success.
*/
static int pam_split_string(const pam_handle_t *pamh, char *arg, char delim,
- char ***out_arg_split, unsigned int *out_num_strs)
+ char ***out_arg_split, size_t *out_num_strs)
{
char *arg_extracted = NULL;
const char *arg_ptr = arg;
char **arg_split = NULL;
char delim_str[2];
- unsigned int i = 0;
- unsigned int num_strs = 0;
+ size_t i = 0;
+ size_t num_strs = 0;
int retval = 0;
delim_str[0] = delim;
@@ -184,13 +184,13 @@ static int filter_dirents(const struct d
}
static void try_to_display_directories_with_overrides(pam_handle_t *pamh,
- char **motd_dir_path_split, unsigned int num_motd_dirs, int report_missing)
+ char **motd_dir_path_split, size_t num_motd_dirs, int report_missing)
{
struct dirent ***dirscans = NULL;
unsigned int *dirscans_sizes = NULL;
unsigned int dirscans_size_total = 0;
char **dirnames_all = NULL;
- unsigned int i;
+ size_t i;
int i_dirnames = 0;
if (pamh == NULL || motd_dir_path_split == NULL) {
@@ -302,11 +302,11 @@ int pam_sm_open_session(pam_handle_t *pa
int retval = PAM_IGNORE;
const char *motd_path = NULL;
char *motd_path_copy = NULL;
- unsigned int num_motd_paths = 0;
+ size_t num_motd_paths = 0;
char **motd_path_split = NULL;
const char *motd_dir_path = NULL;
char *motd_dir_path_copy = NULL;
- unsigned int num_motd_dir_paths = 0;
+ size_t num_motd_dir_paths = 0;
char **motd_dir_path_split = NULL;
int report_missing;

@ -0,0 +1,215 @@
From 08992030c56c940c0707ccbc442b1c325aa01e6d Mon Sep 17 00:00:00 2001
From: Tomas Mraz <tmraz@fedoraproject.org>
Date: Tue, 6 Apr 2021 12:27:38 +0200
Subject: [PATCH] pam_access: clean up the remote host matching code
* modules/pam_access/pam_access.c (from_match): Split out remote_match()
function and avoid calling it when matching against LOCAL keyword.
There is also no point in doing domain match against TTY or SERVICE.
---
modules/pam_access/pam_access.c | 44 +++++++++++++++++++++------------
1 file changed, 28 insertions(+), 16 deletions(-)
diff --git a/modules/pam_access/pam_access.c b/modules/pam_access/pam_access.c
index 98848c54..277192b9 100644
--- a/modules/pam_access/pam_access.c
+++ b/modules/pam_access/pam_access.c
@@ -160,6 +160,7 @@ static int list_match (pam_handle_t *, char *, char *, struct login_info *,
static int user_match (pam_handle_t *, char *, struct login_info *);
static int group_match (pam_handle_t *, const char *, const char *, int);
static int from_match (pam_handle_t *, char *, struct login_info *);
+static int remote_match (pam_handle_t *, char *, struct login_info *);
static int string_match (pam_handle_t *, const char *, const char *, int);
static int network_netmask_match (pam_handle_t *, const char *, const char *, struct login_info *);
@@ -589,11 +590,9 @@ group_match (pam_handle_t *pamh, const char *tok, const char* usr,
/* from_match - match a host or tty against a list of tokens */
static int
-from_match (pam_handle_t *pamh UNUSED, char *tok, struct login_info *item)
+from_match (pam_handle_t *pamh, char *tok, struct login_info *item)
{
const char *string = item->from;
- int tok_len;
- int str_len;
int rv;
if (item->debug)
@@ -616,14 +615,29 @@ from_match (pam_handle_t *pamh UNUSED, char *tok, struct login_info *item)
} else if ((rv = string_match(pamh, tok, string, item->debug)) != NO) {
/* ALL or exact match */
return rv;
- } else if (tok[0] == '.') { /* domain: match last fields */
- if ((str_len = strlen(string)) > (tok_len = strlen(tok))
- && strcasecmp(tok, string + str_len - tok_len) == 0)
- return (YES);
- } else if (item->from_remote_host == 0) { /* local: no PAM_RHOSTS */
- if (strcasecmp(tok, "LOCAL") == 0)
- return (YES);
- } else if (tok[(tok_len = strlen(tok)) - 1] == '.') {
+ } else if (strcasecmp(tok, "LOCAL") == 0) {
+ /* LOCAL matches only local accesses */
+ if (!item->from_remote_host)
+ return YES;
+ return NO;
+ } else if (item->from_remote_host) {
+ return remote_match(pamh, tok, item);
+ }
+ return NO;
+}
+
+static int
+remote_match (pam_handle_t *pamh, char *tok, struct login_info *item)
+{
+ const char *string = item->from;
+ size_t tok_len = strlen(tok);
+ size_t str_len;
+
+ if (tok[0] == '.') { /* domain: match last fields */
+ if ((str_len = strlen(string)) > tok_len
+ && strcasecmp(tok, string + str_len - tok_len) == 0)
+ return YES;
+ } else if (tok[tok_len - 1] == '.') {
struct addrinfo hint;
memset (&hint, '\0', sizeof (hint));
@@ -661,13 +675,11 @@ from_match (pam_handle_t *pamh UNUSED, char *tok, struct login_info *item)
runp = runp->ai_next;
}
}
- } else {
- /* Assume network/netmask with a IP of a host. */
- if (network_netmask_match(pamh, tok, string, item))
- return YES;
+ return NO;
}
- return NO;
+ /* Assume network/netmask with an IP of a host. */
+ return network_netmask_match(pamh, tok, string, item);
}
/* string_match - match a string against one token */
--
2.47.0
From ecaaf4456e5aeacae1acdb1775bb5aadd3b19e13 Mon Sep 17 00:00:00 2001
From: Iker Pedrosa <ipedrosa@redhat.com>
Date: Wed, 16 Oct 2024 12:41:09 +0200
Subject: [PATCH 1/2] pam_access: always match local address
* modules/pam_access/pam_access.c: match the local address regardless of
the IP version in use.
In some circumstances the `localhost` may be translated to IPv4 or IPv6,
but the configuration file only indicated the address for one of the two
versions. Since the originating value is set in `PAM_RHOST` and PAM has
no control over it, let's match the local addresses regardless of the IP
version in use.
Resolves: https://issues.redhat.com/browse/RHEL-23018
Signed-off-by: Iker Pedrosa <ipedrosa@redhat.com>
---
modules/pam_access/pam_access.c | 30 ++++++++++++++++++++++++++++--
1 file changed, 28 insertions(+), 2 deletions(-)
diff --git a/modules/pam_access/pam_access.c b/modules/pam_access/pam_access.c
index bfbc6d57..48e7c7e9 100644
--- a/modules/pam_access/pam_access.c
+++ b/modules/pam_access/pam_access.c
@@ -306,6 +306,23 @@ isipaddr (const char *string, int *addr_type,
return is_ip;
}
+/* is_local_addr - checks if the IP address is local */
+static int
+is_local_addr (const char *string, int addr_type)
+{
+ if (addr_type == AF_INET) {
+ if (strcmp(string, "127.0.0.1") == 0) {
+ return YES;
+ }
+ } else if (addr_type == AF_INET6) {
+ if (strcmp(string, "::1") == 0) {
+ return YES;
+ }
+ }
+
+ return NO;
+}
+
/* are_addresses_equal - translate IP address strings to real IP
* addresses and compare them to find out if they are equal.
@@ -327,9 +344,18 @@ are_addresses_equal (const char *ipaddr0, const char *ipaddr1,
if (isipaddr (ipaddr1, &addr_type1, &addr1) == NO)
return NO;
- if (addr_type0 != addr_type1)
- /* different address types */
+ if (addr_type0 != addr_type1) {
+ /* different address types, but there is still a possibility that they are
+ * both local addresses
+ */
+ int local1 = is_local_addr(ipaddr0, addr_type0);
+ int local2 = is_local_addr(ipaddr1, addr_type1);
+
+ if (local1 == YES && local2 == YES)
+ return YES;
+
return NO;
+ }
if (netmask != NULL) {
/* Got a netmask, so normalize addresses? */
--
2.47.0
From 641dfd1084508c63f3590e93a35b80ffc50774e5 Mon Sep 17 00:00:00 2001
From: Iker Pedrosa <ipedrosa@redhat.com>
Date: Fri, 18 Oct 2024 10:27:07 +0200
Subject: [PATCH 2/2] pam_access: clarify `LOCAL` keyword behaviour
* modules/pam_access/access.conf.5.xml: `LOCAL` keyword behaviour
explanation was focused on the development internals. Let's clarify it
by rephrasing it to something a sysadmin can understand.
Resolves: https://issues.redhat.com/browse/RHEL-39943
Signed-off-by: Iker Pedrosa <ipedrosa@redhat.com>
---
modules/pam_access/access.conf.5.xml | 17 ++++++-----------
1 file changed, 6 insertions(+), 11 deletions(-)
diff --git a/modules/pam_access/access.conf.5.xml b/modules/pam_access/access.conf.5.xml
index 35a1a8fe..0b93db00 100644
--- a/modules/pam_access/access.conf.5.xml
+++ b/modules/pam_access/access.conf.5.xml
@@ -79,17 +79,12 @@
with network mask (where network mask can be a decimal number or an
internet address also), <emphasis>ALL</emphasis> (which always matches)
or <emphasis>LOCAL</emphasis>. The <emphasis>LOCAL</emphasis>
- keyword matches if and only if
- <citerefentry><refentrytitle>pam_get_item</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
- when called with an <parameter>item_type</parameter> of
- <emphasis>PAM_RHOST</emphasis>, returns <code>NULL</code> or an
- empty string (and therefore the
- <replaceable>origins</replaceable> field is compared against the
- return value of
- <citerefentry><refentrytitle>pam_get_item</refentrytitle><manvolnum>3</manvolnum></citerefentry>
- called with an <parameter>item_type</parameter> of
- <emphasis>PAM_TTY</emphasis> or, absent that,
- <emphasis>PAM_SERVICE</emphasis>).
+ keyword matches when the user connects without a network
+ connection (e.g., <emphasis>su</emphasis>,
+ <emphasis>login</emphasis>). A connection through the loopback
+ device (e.g., <command>ssh user@localhost</command>) is
+ considered a network connection, and thus, the
+ <emphasis>LOCAL</emphasis> keyword does not match.
</para>
<para>
--
2.47.0

@ -0,0 +1,211 @@
diff -up Linux-PAM-1.5.1/modules/pam_access/access.conf.5.xml.pam-access-resolve-ip Linux-PAM-1.5.1/modules/pam_access/access.conf.5.xml
--- Linux-PAM-1.5.1/modules/pam_access/access.conf.5.xml.pam-access-resolve-ip 2020-11-25 17:57:02.000000000 +0100
+++ Linux-PAM-1.5.1/modules/pam_access/access.conf.5.xml 2024-11-21 10:04:58.553127026 +0100
@@ -226,6 +226,14 @@
item and the line will be most probably ignored. For this reason, it is not
recommended to put spaces around the ':' characters.
</para>
+ <para>
+ An IPv6 link local host address must contain the interface
+ identifier. IPv6 link local network/netmask is not supported.
+ </para>
+ <para>
+ Hostnames should be written as Fully-Qualified Host Name (FQHN) to avoid
+ confusion with device names or PAM service names.
+ </para>
</refsect1>
<refsect1 id="access.conf-see_also">
diff -up Linux-PAM-1.5.1/modules/pam_access/pam_access.8.xml.pam-access-resolve-ip Linux-PAM-1.5.1/modules/pam_access/pam_access.8.xml
--- Linux-PAM-1.5.1/modules/pam_access/pam_access.8.xml.pam-access-resolve-ip 2020-11-25 17:57:02.000000000 +0100
+++ Linux-PAM-1.5.1/modules/pam_access/pam_access.8.xml 2024-11-21 10:04:58.553127026 +0100
@@ -25,11 +25,14 @@
<arg choice="opt">
debug
</arg>
+ <arg choice="opt" rep="norepeat">
+ noaudit
+ </arg>
<arg choice="opt">
nodefgroup
</arg>
- <arg choice="opt">
- noaudit
+ <arg choice="opt" rep="norepeat">
+ nodns
</arg>
<arg choice="opt">
accessfile=<replaceable>file</replaceable>
@@ -114,7 +117,46 @@
<varlistentry>
<term>
- <option>fieldsep=<replaceable>separators</replaceable></option>
+ nodefgroup
+ </term>
+ <listitem>
+ <para>
+ User tokens which are not enclosed in parentheses will not be
+ matched against the group database. The backwards compatible default is
+ to try the group database match even for tokens not enclosed
+ in parentheses.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>
+ nodns
+ </term>
+ <listitem>
+ <para>
+ Do not try to resolve tokens as hostnames, only IPv4 and IPv6
+ addresses will be resolved. Which means to allow login from a
+ remote host, the IP addresses need to be specified in <filename>access.conf</filename>.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>
+ quiet_log
+ </term>
+ <listitem>
+ <para>
+ Do not log denials with
+ <citerefentry><refentrytitle>syslog</refentrytitle><manvolnum>3</manvolnum></citerefentry>.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>
+ fieldsep=separators
</term>
<listitem>
<para>
@@ -152,20 +194,6 @@
</para>
</listitem>
</varlistentry>
-
- <varlistentry>
- <term>
- <option>nodefgroup</option>
- </term>
- <listitem>
- <para>
- User tokens which are not enclosed in parentheses will not be
- matched against the group database. The backwards compatible default is
- to try the group database match even for tokens not enclosed
- in parentheses.
- </para>
- </listitem>
- </varlistentry>
</variablelist>
</refsect1>
diff -up Linux-PAM-1.5.1/modules/pam_access/pam_access.c.pam-access-resolve-ip Linux-PAM-1.5.1/modules/pam_access/pam_access.c
--- Linux-PAM-1.5.1/modules/pam_access/pam_access.c.pam-access-resolve-ip 2024-11-21 10:04:58.547127010 +0100
+++ Linux-PAM-1.5.1/modules/pam_access/pam_access.c 2024-11-21 10:04:58.553127026 +0100
@@ -92,6 +92,7 @@ struct login_info {
int debug; /* Print debugging messages. */
int only_new_group_syntax; /* Only allow group entries of the form "(xyz)" */
int noaudit; /* Do not audit denials */
+ int nodns; /* Do not try to resolve tokens as hostnames */
const char *fs; /* field separator */
const char *sep; /* list-element separator */
int from_remote_host; /* If PAM_RHOST was used for from */
@@ -143,6 +144,8 @@ parse_args(pam_handle_t *pamh, struct lo
loginfo->only_new_group_syntax = YES;
} else if (strcmp (argv[i], "noaudit") == 0) {
loginfo->noaudit = YES;
+ } else if (strcmp (argv[i], "nodns") == 0) {
+ loginfo->nodns = YES;
} else {
pam_syslog(pamh, LOG_ERR, "unrecognized option [%s]", argv[i]);
}
@@ -700,6 +703,39 @@ string_match (pam_handle_t *pamh, const
}
+static int
+is_device (pam_handle_t *pamh, const char *tok)
+{
+ struct stat st;
+ const char *dev = "/dev/";
+ char *devname;
+
+ devname = malloc (strlen(dev) + strlen (tok) + 1);
+ if (devname == NULL) {
+ pam_syslog(pamh, LOG_ERR, "Cannot allocate memory for device name: %m");
+ /*
+ * We should return an error and abort, but pam_access has no good
+ * error handling.
+ */
+ return NO;
+ }
+
+ char *cp = stpcpy (devname, dev);
+ strcpy (cp, tok);
+
+ if (lstat(devname, &st) != 0)
+ {
+ free (devname);
+ return NO;
+ }
+ free (devname);
+
+ if (S_ISCHR(st.st_mode))
+ return YES;
+
+ return NO;
+}
+
/* network_netmask_match - match a string against one token
* where string is a hostname or ip (v4,v6) address and tok
* represents either a hostname, a single ip (v4,v6) address
@@ -761,10 +797,42 @@ network_netmask_match (pam_handle_t *pam
return NO;
}
}
+ else if (isipaddr(tok, NULL, NULL) == YES)
+ {
+ if (getaddrinfo (tok, NULL, NULL, &ai) != 0)
+ {
+ if (item->debug)
+ pam_syslog(pamh, LOG_DEBUG, "cannot resolve IP address \"%s\"", tok);
+
+ return NO;
+ }
+ netmask_ptr = NULL;
+ }
+ else if (item->nodns)
+ {
+ /* Only hostnames are left, which we would need to resolve via DNS */
+ return NO;
+ }
else
{
+ /* Bail out on X11 Display entries and ttys. */
+ if (tok[0] == ':')
+ {
+ if (item->debug)
+ pam_syslog (pamh, LOG_DEBUG,
+ "network_netmask_match: tok=%s is X11 display", tok);
+ return NO;
+ }
+ if (is_device (pamh, tok))
+ {
+ if (item->debug)
+ pam_syslog (pamh, LOG_DEBUG,
+ "network_netmask_match: tok=%s is a TTY", tok);
+ return NO;
+ }
+
/*
- * It is either an IP address or a hostname.
+ * It is most likely a hostname.
* Let getaddrinfo sort everything out
*/
if (getaddrinfo (tok, NULL, NULL, &ai) != 0)

@ -0,0 +1,47 @@
diff -up Linux-PAM-1.3.1/modules/pam_unix/passverify.c.pam-unix-shadow-password Linux-PAM-1.3.1/modules/pam_unix/passverify.c
--- Linux-PAM-1.3.1/modules/pam_unix/passverify.c.pam-unix-shadow-password 2024-11-05 13:02:14.637785962 +0100
+++ Linux-PAM-1.3.1/modules/pam_unix/passverify.c 2024-11-05 13:02:14.643785975 +0100
@@ -73,9 +73,13 @@ verify_pwd_hash(const char *p, char *has
strip_hpux_aging(hash);
hash_len = strlen(hash);
- if (!hash_len) {
+
+ if (p && p[0] == '\0' && !nullok) {
+ /* The passed password is empty */
+ retval = PAM_AUTH_ERR;
+ } else if (!hash_len) {
/* the stored password is NULL */
- if (nullok) { /* this means we've succeeded */
+ if (p && p[0] == '\0' && nullok) { /* this means we've succeeded */
D(("user has empty password - access granted"));
retval = PAM_SUCCESS;
} else {
@@ -192,17 +196,21 @@ PAMH_ARG_DECL(int get_account_info,
return PAM_UNIX_RUN_HELPER;
#endif
} else if (is_pwd_shadowed(*pwd)) {
+#ifdef HELPER_COMPILE
/*
- * ...and shadow password file entry for this user,
+ * shadow password file entry for this user,
* if shadowing is enabled
*/
-#ifndef HELPER_COMPILE
- if (geteuid() || SELINUX_ENABLED)
- return PAM_UNIX_RUN_HELPER;
-#endif
- *spwdent = pam_modutil_getspnam(pamh, name);
+ *spwdent = getspnam(name);
if (*spwdent == NULL || (*spwdent)->sp_pwdp == NULL)
return PAM_AUTHINFO_UNAVAIL;
+#else
+ /*
+ * The helper has to be invoked to deal with
+ * the shadow password file entry.
+ */
+ return PAM_UNIX_RUN_HELPER;
+#endif
}
} else {
return PAM_USER_UNKNOWN;

@ -3,7 +3,7 @@
Summary: An extensible library which provides authentication for applications
Name: pam
Version: 1.3.1
Release: 34%{?dist}.inferit
Release: 36%{?dist}.inferit
# The library is BSD licensed with option to relicense as GPLv2+
# - this option is redundant as the BSD license allows that anyway.
# pam_timestamp, pam_loginuid, and pam_console modules are GPLv2+.
@ -113,6 +113,16 @@ Patch70: pam-1.3.1-namespace-protect-dir.patch
# https://github.com/linux-pam/linux-pam/commit/c85513220c1bd3150e39c6277422d29cfa44acc7
# https://github.com/linux-pam/linux-pam/commit/1648734a69c31e9ce834da70144ac9a453296807
Patch71: pam-1.3.1-audit-messages-formatting.patch
# https://github.com/linux-pam/linux-pam/commit/b3020da7da384d769f27a8713257fbe1001878be
# https://github.com/linux-pam/linux-pam/commit/8d0c575336ad301cd14e16ad2fdec6fe621764b8
Patch72: pam-1.3.1-pam-unix-shadow-password.patch
# https://github.com/linux-pam/linux-pam/commit/08992030c56c940c0707ccbc442b1c325aa01e6d
# https://github.com/linux-pam/linux-pam/commit/641dfd1084508c63f3590e93a35b80ffc50774e5
Patch73: pam-1.3.1-pam-access-local.patch
# https://github.com/linux-pam/linux-pam/commit/ec1fb9ddc6c252d8c61379e9385ca19c036fcb96
Patch74: pam-1.3.1-libpam-support-long-lines.patch
# https://github.com/linux-pam/linux-pam/commit/940747f88c16e029b69a74e80a2e94f65cb3e628
Patch75: pam-1.3.1-pam-access-resolve-ip.patch
Patch1000: 0001-Fix-Russian-translation.patch
@ -234,6 +244,10 @@ cp %{SOURCE18} .
%patch69 -p1 -b .access-handle-hostnames
%patch70 -p1 -b .namespace-protect-dir
%patch71 -p1 -b .audit-messages-formatting
%patch72 -p1 -b .pam-unix-shadow-password
%patch73 -p1 -b .pam-access-local
%patch74 -p1 -b .libpam-support-long-lines
%patch75 -p1 -b .pam-access-resolve-ip
%patch1000 -p1
autoreconf -i
@ -489,6 +503,20 @@ done
%doc doc/specs/rfc86.0.txt
%changelog
* Wed Nov 27 2024 Sergey Cherevko <s.cherevko@msvsphere-os.ru> - 1.3.1-36.inferit
- Update to 1.3.1-36
* Mon Nov 25 2024 Iker Pedrosa <ipedrosa@redhat.com> - 1.3.1-36
- pam_access: rework resolving of tokens as hostname.
Resolves: CVE-2024-10963 and RHEL-66242
* Mon Nov 4 2024 Iker Pedrosa <ipedrosa@redhat.com> - 1.3.1-35
- pam_unix: always run the helper to obtain shadow password file entries.
CVE-2024-10041. Resolves: RHEL-62877
- pam_access: always match local address and clarify LOCAL keyword behaviour.
Resolves: RHEL-23018
- libpam: support long lines in service files. Resolves: RHEL-5051
* Wed Jul 03 2024 Sergey Cherevko <s.cherevko@msvsphere-os.ru> - 1.3.1-34.inferit
- Update to 1.3.1-34

Loading…
Cancel
Save