parent
faa6ce91f9
commit
c90b071719
@ -0,0 +1,144 @@
|
||||
From 244b46908df930626535c0cd7c2867407fe8714a Mon Sep 17 00:00:00 2001
|
||||
From: Thorsten Kukuk <kukuk@suse.com>
|
||||
Date: Tue, 14 Feb 2023 14:57:40 +0100
|
||||
Subject: [PATCH] libpam: use getlogin() from libc and not utmp
|
||||
|
||||
utmp uses 32bit time_t for compatibility with 32bit userland on some
|
||||
64bit systems and is thus not Y2038 safe. Use getlogin() from libc
|
||||
which avoids using utmp and is more safe than the old utmp-based
|
||||
implementation by using /proc/self/loginuid.
|
||||
|
||||
* libpam/pam_modutil_getlogin.c: Use getlogin() instead of parsing utmp
|
||||
---
|
||||
libpam/pam_modutil_getlogin.c | 52 ++++++++---------------------------
|
||||
1 file changed, 11 insertions(+), 41 deletions(-)
|
||||
|
||||
diff --git a/libpam/pam_modutil_getlogin.c b/libpam/pam_modutil_getlogin.c
|
||||
index 04a20fd8..633dd676 100644
|
||||
--- a/libpam/pam_modutil_getlogin.c
|
||||
+++ b/libpam/pam_modutil_getlogin.c
|
||||
@@ -10,7 +10,6 @@
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
-#include <utmp.h>
|
||||
|
||||
#define _PAMMODUTIL_GETLOGIN "_pammodutil_getlogin"
|
||||
|
||||
@@ -19,62 +18,33 @@ pam_modutil_getlogin(pam_handle_t *pamh)
|
||||
{
|
||||
int status;
|
||||
const void *logname;
|
||||
- const void *void_curr_tty;
|
||||
- const char *curr_tty;
|
||||
char *curr_user;
|
||||
- struct utmp *ut, line;
|
||||
+ size_t curr_user_len;
|
||||
|
||||
status = pam_get_data(pamh, _PAMMODUTIL_GETLOGIN, &logname);
|
||||
if (status == PAM_SUCCESS) {
|
||||
return logname;
|
||||
}
|
||||
|
||||
- status = pam_get_item(pamh, PAM_TTY, &void_curr_tty);
|
||||
- if ((status != PAM_SUCCESS) || (void_curr_tty == NULL))
|
||||
- curr_tty = ttyname(0);
|
||||
- else
|
||||
- curr_tty = (const char*)void_curr_tty;
|
||||
-
|
||||
- if (curr_tty == NULL) {
|
||||
- return NULL;
|
||||
- }
|
||||
-
|
||||
- if (curr_tty[0] == '/') { /* full path */
|
||||
- const char *t;
|
||||
- curr_tty++;
|
||||
- if ((t = strchr(curr_tty, '/')) != NULL) {
|
||||
- curr_tty = t + 1;
|
||||
- }
|
||||
+ logname = getlogin();
|
||||
+ if (logname == NULL) {
|
||||
+ return NULL;
|
||||
}
|
||||
- logname = NULL;
|
||||
|
||||
- setutent();
|
||||
- strncpy(line.ut_line, curr_tty, sizeof(line.ut_line));
|
||||
-
|
||||
- if ((ut = getutline(&line)) == NULL) {
|
||||
- goto clean_up_and_go_home;
|
||||
- }
|
||||
-
|
||||
- curr_user = calloc(sizeof(line.ut_user)+1, 1);
|
||||
+ curr_user_len = strlen(logname)+1;
|
||||
+ curr_user = calloc(curr_user_len, 1);
|
||||
if (curr_user == NULL) {
|
||||
- goto clean_up_and_go_home;
|
||||
+ return NULL;
|
||||
}
|
||||
|
||||
- strncpy(curr_user, ut->ut_user, sizeof(ut->ut_user));
|
||||
- /* calloc already zeroed the memory */
|
||||
+ memcpy(curr_user, logname, curr_user_len);
|
||||
|
||||
status = pam_set_data(pamh, _PAMMODUTIL_GETLOGIN, curr_user,
|
||||
pam_modutil_cleanup);
|
||||
if (status != PAM_SUCCESS) {
|
||||
- free(curr_user);
|
||||
- goto clean_up_and_go_home;
|
||||
+ free(curr_user);
|
||||
+ return NULL;
|
||||
}
|
||||
|
||||
- logname = curr_user;
|
||||
-
|
||||
-clean_up_and_go_home:
|
||||
-
|
||||
- endutent();
|
||||
-
|
||||
- return logname;
|
||||
+ return curr_user;
|
||||
}
|
||||
--
|
||||
2.43.0
|
||||
|
||||
From f26d873435be9f35fa7953493cc07a9bc4e31876 Mon Sep 17 00:00:00 2001
|
||||
From: =?UTF-8?q?Christian=20G=C3=B6ttsche?= <cgzones@googlemail.com>
|
||||
Date: Sat, 18 Feb 2023 14:37:04 +0100
|
||||
Subject: [PATCH] libpam: simplify string copying using strdup
|
||||
|
||||
---
|
||||
libpam/pam_modutil_getlogin.c | 6 +-----
|
||||
1 file changed, 1 insertion(+), 5 deletions(-)
|
||||
|
||||
diff --git a/libpam/pam_modutil_getlogin.c b/libpam/pam_modutil_getlogin.c
|
||||
index 633dd676..2e7a0116 100644
|
||||
--- a/libpam/pam_modutil_getlogin.c
|
||||
+++ b/libpam/pam_modutil_getlogin.c
|
||||
@@ -19,7 +19,6 @@ pam_modutil_getlogin(pam_handle_t *pamh)
|
||||
int status;
|
||||
const void *logname;
|
||||
char *curr_user;
|
||||
- size_t curr_user_len;
|
||||
|
||||
status = pam_get_data(pamh, _PAMMODUTIL_GETLOGIN, &logname);
|
||||
if (status == PAM_SUCCESS) {
|
||||
@@ -31,14 +30,11 @@ pam_modutil_getlogin(pam_handle_t *pamh)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
- curr_user_len = strlen(logname)+1;
|
||||
- curr_user = calloc(curr_user_len, 1);
|
||||
+ curr_user = strdup(logname);
|
||||
if (curr_user == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
- memcpy(curr_user, logname, curr_user_len);
|
||||
-
|
||||
status = pam_set_data(pamh, _PAMMODUTIL_GETLOGIN, curr_user,
|
||||
pam_modutil_cleanup);
|
||||
if (status != PAM_SUCCESS) {
|
||||
--
|
||||
2.43.0
|
||||
|
@ -0,0 +1,168 @@
|
||||
diff -up Linux-PAM-1.5.1/modules/pam_access/pam_access.c.access-handle-hostnames Linux-PAM-1.5.1/modules/pam_access/pam_access.c
|
||||
--- Linux-PAM-1.5.1/modules/pam_access/pam_access.c.access-handle-hostnames 2020-11-25 17:57:02.000000000 +0100
|
||||
+++ Linux-PAM-1.5.1/modules/pam_access/pam_access.c 2024-01-22 15:56:09.977868880 +0100
|
||||
@@ -662,7 +662,7 @@ from_match (pam_handle_t *pamh UNUSED, c
|
||||
}
|
||||
}
|
||||
} else {
|
||||
- /* Assume network/netmask with a IP of a host. */
|
||||
+ /* Assume network/netmask, IP address or hostname. */
|
||||
if (network_netmask_match(pamh, tok, string, item))
|
||||
return YES;
|
||||
}
|
||||
@@ -684,7 +684,7 @@ string_match (pam_handle_t *pamh, const
|
||||
/*
|
||||
* If the token has the magic value "ALL" the match always succeeds.
|
||||
* Otherwise, return YES if the token fully matches the string.
|
||||
- * "NONE" token matches NULL string.
|
||||
+ * "NONE" token matches NULL string.
|
||||
*/
|
||||
|
||||
if (strcasecmp(tok, "ALL") == 0) { /* all: always matches */
|
||||
@@ -702,7 +702,8 @@ string_match (pam_handle_t *pamh, const
|
||||
|
||||
/* network_netmask_match - match a string against one token
|
||||
* where string is a hostname or ip (v4,v6) address and tok
|
||||
- * represents either a single ip (v4,v6) address or a network/netmask
|
||||
+ * represents either a hostname, a single ip (v4,v6) address
|
||||
+ * or a network/netmask
|
||||
*/
|
||||
static int
|
||||
network_netmask_match (pam_handle_t *pamh,
|
||||
@@ -711,10 +712,12 @@ network_netmask_match (pam_handle_t *pam
|
||||
char *netmask_ptr;
|
||||
char netmask_string[MAXHOSTNAMELEN + 1];
|
||||
int addr_type;
|
||||
+ struct addrinfo *ai = NULL;
|
||||
|
||||
if (item->debug)
|
||||
- pam_syslog (pamh, LOG_DEBUG,
|
||||
+ pam_syslog (pamh, LOG_DEBUG,
|
||||
"network_netmask_match: tok=%s, item=%s", tok, string);
|
||||
+
|
||||
/* OK, check if tok is of type addr/mask */
|
||||
if ((netmask_ptr = strchr(tok, '/')) != NULL)
|
||||
{
|
||||
@@ -748,54 +751,108 @@ network_netmask_match (pam_handle_t *pam
|
||||
netmask_ptr = number_to_netmask(netmask, addr_type,
|
||||
netmask_string, MAXHOSTNAMELEN);
|
||||
}
|
||||
- }
|
||||
+
|
||||
+ /*
|
||||
+ * Construct an addrinfo list from the IP address.
|
||||
+ * This should not fail as the input is a correct IP address...
|
||||
+ */
|
||||
+ if (getaddrinfo (tok, NULL, NULL, &ai) != 0)
|
||||
+ {
|
||||
+ return NO;
|
||||
+ }
|
||||
+ }
|
||||
else
|
||||
- /* NO, then check if it is only an addr */
|
||||
- if (isipaddr(tok, NULL, NULL) != YES)
|
||||
+ {
|
||||
+ /*
|
||||
+ * It is either an IP address or a hostname.
|
||||
+ * Let getaddrinfo sort everything out
|
||||
+ */
|
||||
+ if (getaddrinfo (tok, NULL, NULL, &ai) != 0)
|
||||
{
|
||||
+ pam_syslog(pamh, LOG_ERR, "cannot resolve hostname \"%s\"", tok);
|
||||
+
|
||||
return NO;
|
||||
}
|
||||
+ netmask_ptr = NULL;
|
||||
+ }
|
||||
|
||||
if (isipaddr(string, NULL, NULL) != YES)
|
||||
{
|
||||
- /* Assume network/netmask with a name of a host. */
|
||||
struct addrinfo hint;
|
||||
|
||||
+ /* Assume network/netmask with a name of a host. */
|
||||
memset (&hint, '\0', sizeof (hint));
|
||||
hint.ai_flags = AI_CANONNAME;
|
||||
hint.ai_family = AF_UNSPEC;
|
||||
|
||||
if (item->gai_rv != 0)
|
||||
+ {
|
||||
+ freeaddrinfo(ai);
|
||||
return NO;
|
||||
+ }
|
||||
else if (!item->res &&
|
||||
(item->gai_rv = getaddrinfo (string, NULL, &hint, &item->res)) != 0)
|
||||
+ {
|
||||
+ freeaddrinfo(ai);
|
||||
return NO;
|
||||
+ }
|
||||
else
|
||||
{
|
||||
struct addrinfo *runp = item->res;
|
||||
+ struct addrinfo *runp1;
|
||||
|
||||
while (runp != NULL)
|
||||
{
|
||||
char buf[INET6_ADDRSTRLEN];
|
||||
|
||||
- DIAG_PUSH_IGNORE_CAST_ALIGN;
|
||||
- inet_ntop (runp->ai_family,
|
||||
- runp->ai_family == AF_INET
|
||||
- ? (void *) &((struct sockaddr_in *) runp->ai_addr)->sin_addr
|
||||
- : (void *) &((struct sockaddr_in6 *) runp->ai_addr)->sin6_addr,
|
||||
- buf, sizeof (buf));
|
||||
- DIAG_POP_IGNORE_CAST_ALIGN;
|
||||
+ if (getnameinfo (runp->ai_addr, runp->ai_addrlen, buf, sizeof (buf), NULL, 0, NI_NUMERICHOST) != 0)
|
||||
+ {
|
||||
+ freeaddrinfo(ai);
|
||||
+ return NO;
|
||||
+ }
|
||||
|
||||
- if (are_addresses_equal(buf, tok, netmask_ptr))
|
||||
+ for (runp1 = ai; runp1 != NULL; runp1 = runp1->ai_next)
|
||||
{
|
||||
- return YES;
|
||||
+ char buf1[INET6_ADDRSTRLEN];
|
||||
+
|
||||
+ if (runp->ai_family != runp1->ai_family)
|
||||
+ continue;
|
||||
+
|
||||
+ if (getnameinfo (runp1->ai_addr, runp1->ai_addrlen, buf1, sizeof (buf1), NULL, 0, NI_NUMERICHOST) != 0)
|
||||
+ {
|
||||
+ freeaddrinfo(ai);
|
||||
+ return NO;
|
||||
+ }
|
||||
+
|
||||
+ if (are_addresses_equal (buf, buf1, netmask_ptr))
|
||||
+ {
|
||||
+ freeaddrinfo(ai);
|
||||
+ return YES;
|
||||
+ }
|
||||
}
|
||||
runp = runp->ai_next;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
- return (are_addresses_equal(string, tok, netmask_ptr));
|
||||
+ {
|
||||
+ struct addrinfo *runp1;
|
||||
+
|
||||
+ for (runp1 = ai; runp1 != NULL; runp1 = runp1->ai_next)
|
||||
+ {
|
||||
+ char buf1[INET6_ADDRSTRLEN];
|
||||
+
|
||||
+ (void) getnameinfo (runp1->ai_addr, runp1->ai_addrlen, buf1, sizeof (buf1), NULL, 0, NI_NUMERICHOST);
|
||||
+
|
||||
+ if (are_addresses_equal(string, buf1, netmask_ptr))
|
||||
+ {
|
||||
+ freeaddrinfo(ai);
|
||||
+ return YES;
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ freeaddrinfo(ai);
|
||||
|
||||
return NO;
|
||||
}
|
@ -0,0 +1,72 @@
|
||||
From c85513220c1bd3150e39c6277422d29cfa44acc7 Mon Sep 17 00:00:00 2001
|
||||
From: Steve Grubb <sgrubb@redhat.com>
|
||||
Date: Thu, 27 Jul 2023 13:14:42 -0400
|
||||
Subject: [PATCH 1/2] pam_faillock: fix formatting of audit messages
|
||||
|
||||
pam_faillock uses audit_log_user_message to write to the audit system.
|
||||
It does not take an op argument, so you have to add one yourself. Otherwise
|
||||
the pam_faillock part of the message is lost because it's not in key=value
|
||||
format.
|
||||
|
||||
Also, we can't use uid in that event because the kernel already adds that
|
||||
field. What we normally do is use 'suid' (meaning sender uid) as the
|
||||
field name.
|
||||
---
|
||||
modules/pam_faillock/pam_faillock.c | 4 ++--
|
||||
1 file changed, 2 insertions(+), 2 deletions(-)
|
||||
|
||||
diff --git a/modules/pam_faillock/pam_faillock.c b/modules/pam_faillock/pam_faillock.c
|
||||
index ca1c7035..a89909ab 100644
|
||||
--- a/modules/pam_faillock/pam_faillock.c
|
||||
+++ b/modules/pam_faillock/pam_faillock.c
|
||||
@@ -248,7 +248,7 @@ check_tally(pam_handle_t *pamh, struct options *opts, struct tally_data *tallies
|
||||
|
||||
(void)pam_get_item(pamh, PAM_TTY, &tty);
|
||||
(void)pam_get_item(pamh, PAM_RHOST, &rhost);
|
||||
- snprintf(buf, sizeof(buf), "pam_faillock uid=%u ", opts->uid);
|
||||
+ snprintf(buf, sizeof(buf), "op=pam_faillock suid=%u ", opts->uid);
|
||||
audit_log_user_message(audit_fd, AUDIT_RESP_ACCT_UNLOCK_TIMED, buf,
|
||||
rhost, NULL, tty, 1);
|
||||
}
|
||||
@@ -364,7 +364,7 @@ write_tally(pam_handle_t *pamh, struct options *opts, struct tally_data *tallies
|
||||
errno == EAFNOSUPPORT))
|
||||
return PAM_SYSTEM_ERR;
|
||||
|
||||
- snprintf(buf, sizeof(buf), "pam_faillock uid=%u ", opts->uid);
|
||||
+ snprintf(buf, sizeof(buf), "op=pam_faillock suid=%u ", opts->uid);
|
||||
audit_log_user_message(audit_fd, AUDIT_ANOM_LOGIN_FAILURES, buf,
|
||||
NULL, NULL, NULL, 1);
|
||||
|
||||
--
|
||||
2.41.0
|
||||
|
||||
|
||||
From 1648734a69c31e9ce834da70144ac9a453296807 Mon Sep 17 00:00:00 2001
|
||||
From: Steve Grubb <sgrubb@redhat.com>
|
||||
Date: Fri, 4 Aug 2023 17:45:45 -0400
|
||||
Subject: [PATCH 2/2] pam_selinux: fix formatting of audit messages
|
||||
|
||||
pam_selinux uses audit_log_user_message to write to the audit system.
|
||||
It does not take an op argument, so you have to add one yourself. Otherwise
|
||||
the pam_selinux part of the message is lost because it's not in key=value
|
||||
format.
|
||||
---
|
||||
modules/pam_selinux/pam_selinux.c | 2 +-
|
||||
1 file changed, 1 insertion(+), 1 deletion(-)
|
||||
|
||||
diff --git a/modules/pam_selinux/pam_selinux.c b/modules/pam_selinux/pam_selinux.c
|
||||
index e52e0fc4..713b3f73 100644
|
||||
--- a/modules/pam_selinux/pam_selinux.c
|
||||
+++ b/modules/pam_selinux/pam_selinux.c
|
||||
@@ -97,7 +97,7 @@ send_audit_message(const pam_handle_t *pamh, int success, const char *default_co
|
||||
pam_syslog(pamh, LOG_ERR, "Error translating selected context '%s'.", selected_context);
|
||||
selected_raw = NULL;
|
||||
}
|
||||
- if (asprintf(&msg, "pam: default-context=%s selected-context=%s",
|
||||
+ if (asprintf(&msg, "op=pam_selinux default-context=%s selected-context=%s",
|
||||
default_raw ? default_raw : (default_context ? default_context : "?"),
|
||||
selected_raw ? selected_raw : (selected_context ? selected_context : "?")) < 0) {
|
||||
msg = NULL; /* asprintf leaves msg in undefined state on failure */
|
||||
--
|
||||
2.41.0
|
||||
|
@ -0,0 +1,36 @@
|
||||
From d54870f993e97fe75e2cd0470a3701d5af22877c Mon Sep 17 00:00:00 2001
|
||||
From: Changqing Li <changqing.li@windriver.com>
|
||||
Date: Tue, 12 Jan 2021 14:45:34 +0800
|
||||
Subject: [PATCH] faillock: create tallydir before creating tallyfile
|
||||
|
||||
The default tallydir is "/var/run/faillock", and this default
|
||||
tallydir may not exist.
|
||||
|
||||
Function open may fail as tallydir does not exist when creating
|
||||
the tallyfile. Therefore, faillock will not work well.
|
||||
|
||||
Fix this problem by creating tallydir before creating tallyfile
|
||||
when the tallydir does not exist.
|
||||
|
||||
Signed-off-by: Changqing Li <changqing.li@windriver.com>
|
||||
---
|
||||
modules/pam_faillock/faillock.c | 3 +++
|
||||
1 file changed, 3 insertions(+)
|
||||
|
||||
diff --git a/modules/pam_faillock/faillock.c b/modules/pam_faillock/faillock.c
|
||||
index 4ea94cbe..091f253a 100644
|
||||
--- a/modules/pam_faillock/faillock.c
|
||||
+++ b/modules/pam_faillock/faillock.c
|
||||
@@ -74,6 +74,9 @@ open_tally (const char *dir, const char *user, uid_t uid, int create)
|
||||
|
||||
if (create) {
|
||||
flags |= O_CREAT;
|
||||
+ if (access(dir, F_OK) != 0) {
|
||||
+ mkdir(dir, 0755);
|
||||
+ }
|
||||
}
|
||||
|
||||
fd = open(path, flags, 0660);
|
||||
--
|
||||
2.43.0
|
||||
|
@ -0,0 +1,55 @@
|
||||
diff -up Linux-PAM-1.5.1/configure.ac.libpam-close-range Linux-PAM-1.5.1/configure.ac
|
||||
--- Linux-PAM-1.5.1/configure.ac.libpam-close-range 2023-11-10 10:35:00.142833269 +0100
|
||||
+++ Linux-PAM-1.5.1/configure.ac 2023-11-10 10:36:29.158987392 +0100
|
||||
@@ -552,6 +552,7 @@ AC_CHECK_FUNCS(inet_ntop inet_pton innet
|
||||
AC_CHECK_FUNCS(quotactl)
|
||||
AC_CHECK_FUNCS(unshare)
|
||||
AC_CHECK_FUNCS([ruserok_af ruserok], [break])
|
||||
+AC_CHECK_FUNCS(close_range)
|
||||
BACKUP_LIBS=$LIBS
|
||||
LIBS="$LIBS -lutil"
|
||||
AC_CHECK_FUNCS([logwtmp])
|
||||
diff -up Linux-PAM-1.5.1/libpam/pam_modutil_sanitize.c.libpam-close-range Linux-PAM-1.5.1/libpam/pam_modutil_sanitize.c
|
||||
--- Linux-PAM-1.5.1/libpam/pam_modutil_sanitize.c.libpam-close-range 2020-11-25 17:57:02.000000000 +0100
|
||||
+++ Linux-PAM-1.5.1/libpam/pam_modutil_sanitize.c 2023-11-10 10:35:00.142833269 +0100
|
||||
@@ -11,6 +11,10 @@
|
||||
#include <syslog.h>
|
||||
#include <sys/resource.h>
|
||||
|
||||
+#ifndef CLOSE_RANGE_UNSHARE
|
||||
+#define CLOSE_RANGE_UNSHARE (1U << 1)
|
||||
+#endif /* CLOSE_RANGE_UNSHARE */
|
||||
+
|
||||
/*
|
||||
* Creates a pipe, closes its write end, redirects fd to its read end.
|
||||
* Returns fd on success, -1 otherwise.
|
||||
@@ -84,9 +88,8 @@ redirect_out(pam_handle_t *pamh, enum pa
|
||||
return fd;
|
||||
}
|
||||
|
||||
-/* Closes all descriptors after stderr. */
|
||||
static void
|
||||
-close_fds(void)
|
||||
+close_fds_iteratively(void)
|
||||
{
|
||||
/*
|
||||
* An arbitrary upper limit for the maximum file descriptor number
|
||||
@@ -111,6 +114,18 @@ close_fds(void)
|
||||
close(fd);
|
||||
}
|
||||
|
||||
+/* Closes all descriptors after stderr. */
|
||||
+static void
|
||||
+close_fds(void)
|
||||
+{
|
||||
+#ifdef HAVE_CLOSE_RANGE
|
||||
+ if (close_range(STDERR_FILENO+1, -1U, CLOSE_RANGE_UNSHARE) == 0)
|
||||
+ return;
|
||||
+#endif /* HAVE_CLOSE_RANGE */
|
||||
+
|
||||
+ close_fds_iteratively();
|
||||
+}
|
||||
+
|
||||
int
|
||||
pam_modutil_sanitize_helper_fds(pam_handle_t *pamh,
|
||||
enum pam_modutil_redirect_fd stdin_mode,
|
@ -0,0 +1,654 @@
|
||||
diff -up Linux-PAM-1.5.1/libpam/pam_handlers.c.libpam-support-long-lines Linux-PAM-1.5.1/libpam/pam_handlers.c
|
||||
--- Linux-PAM-1.5.1/libpam/pam_handlers.c.libpam-support-long-lines 2020-11-25 17:57:02.000000000 +0100
|
||||
+++ Linux-PAM-1.5.1/libpam/pam_handlers.c 2024-06-18 10:07:12.434785557 +0200
|
||||
@@ -17,21 +17,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 */
|
||||
|
||||
@@ -59,12 +68,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;
|
||||
@@ -74,7 +86,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) {
|
||||
@@ -233,10 +245,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;
|
||||
}
|
||||
|
||||
@@ -557,88 +570,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';
|
||||
+}
|
||||
+
|
||||
+/*
|
||||
+ * 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';
|
||||
+
|
||||
+ 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)
|
||||
+{
|
||||
+ int ret = 1;
|
||||
+ char *p = *end;
|
||||
+
|
||||
+ /*
|
||||
+ * 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;
|
||||
+ }
|
||||
+
|
||||
+ return ret;
|
||||
+}
|
||||
+
|
||||
+/*
|
||||
+ * 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;
|
||||
+
|
||||
+ *start = line;
|
||||
+ *end = line + len;
|
||||
+
|
||||
+ ret = _pam_str_unescnl(*start, end) || strchr(*start, '#') != NULL;
|
||||
+
|
||||
+ *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, char *buffer, int buf_len)
|
||||
+static int _pam_assemble_line(FILE *f, struct line_buffer *buffer)
|
||||
{
|
||||
- char *p = buffer;
|
||||
- char *endp = buffer + buf_len;
|
||||
- char *s, *os;
|
||||
- int used = 0;
|
||||
+ int ret = 0;
|
||||
|
||||
/* loop broken with a 'break' when a non-'\\n' ended line is read */
|
||||
|
||||
D(("called."));
|
||||
+
|
||||
+ _pam_buffer_shift(buffer);
|
||||
+
|
||||
for (;;) {
|
||||
- if (p >= endp) {
|
||||
- /* Overflow */
|
||||
- D(("_pam_assemble_line: overflow"));
|
||||
- return -1;
|
||||
- }
|
||||
- if (fgets(p, endp - p, f) == NULL) {
|
||||
- if (used) {
|
||||
+ char *start, *end;
|
||||
+ ssize_t n;
|
||||
+ int eol;
|
||||
+
|
||||
+ if ((n = getline(&buffer->chunk, &buffer->chunk_size, f)) == -1) {
|
||||
+ if (ret) {
|
||||
/* Incomplete read */
|
||||
- return -1;
|
||||
+ ret = -1;
|
||||
} else {
|
||||
/* EOF */
|
||||
- return 0;
|
||||
+ ret = 0;
|
||||
}
|
||||
+ break;
|
||||
}
|
||||
|
||||
- /* skip leading spaces --- line may be blank */
|
||||
-
|
||||
- s = p + strspn(p, " \n\t");
|
||||
- if (*s && (*s != '#')) {
|
||||
- os = s;
|
||||
-
|
||||
- /*
|
||||
- * we are only interested in characters before the first '#'
|
||||
- * character
|
||||
- */
|
||||
-
|
||||
- while (*s && *s != '#')
|
||||
- ++s;
|
||||
- if (*s == '#') {
|
||||
- *s = '\0';
|
||||
- used += strlen(os);
|
||||
- break; /* the line has been read */
|
||||
- }
|
||||
-
|
||||
- s = os;
|
||||
-
|
||||
- /*
|
||||
- * 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!
|
||||
- */
|
||||
-
|
||||
- s += strlen(s);
|
||||
- while (s > os && ((*--s == ' ') || (*s == '\t')
|
||||
- || (*s == '\n')));
|
||||
-
|
||||
- /* 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 ... */
|
||||
- } else {
|
||||
- /* End of the line! */
|
||||
- used += strlen(os);
|
||||
- break; /* this is the complete line */
|
||||
- }
|
||||
+ 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 *
|
||||
@@ -777,7 +945,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.5.1/libpam/pam_misc.c.libpam-support-long-lines Linux-PAM-1.5.1/libpam/pam_misc.c
|
||||
--- Linux-PAM-1.5.1/libpam/pam_misc.c.libpam-support-long-lines 2024-06-18 09:52:38.726482849 +0200
|
||||
+++ Linux-PAM-1.5.1/libpam/pam_misc.c 2024-06-18 10:02:13.132973447 +0200
|
||||
@@ -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 +164,55 @@ char *_pam_memdup(const char *x, int len
|
||||
/* Generate argv, argc from s */
|
||||
/* caller must free(argv) */
|
||||
|
||||
-int _pam_mkargv(const 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.5.1/libpam/pam_private.h.libpam-support-long-lines Linux-PAM-1.5.1/libpam/pam_private.h
|
||||
--- Linux-PAM-1.5.1/libpam/pam_private.h.libpam-support-long-lines 2020-11-25 17:57:02.000000000 +0100
|
||||
+++ Linux-PAM-1.5.1/libpam/pam_private.h 2024-06-18 09:52:38.726482849 +0200
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
#include "config.h"
|
||||
|
||||
+#include <stddef.h>
|
||||
#include <syslog.h>
|
||||
|
||||
#include <security/pam_appl.h>
|
||||
@@ -272,7 +273,7 @@ char *_pam_strdup(const char *s);
|
||||
|
||||
char *_pam_memdup(const char *s, int len);
|
||||
|
||||
-int _pam_mkargv(const 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.5.1/modules/pam_exec/pam_exec.c.libpam-support-long-lines Linux-PAM-1.5.1/modules/pam_exec/pam_exec.c
|
||||
--- Linux-PAM-1.5.1/modules/pam_exec/pam_exec.c.libpam-support-long-lines 2020-11-25 17:57:02.000000000 +0100
|
||||
+++ Linux-PAM-1.5.1/modules/pam_exec/pam_exec.c 2024-06-18 09:52:38.725482846 +0200
|
||||
@@ -407,7 +407,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.5.1/modules/pam_filter/pam_filter.c.libpam-support-long-lines Linux-PAM-1.5.1/modules/pam_filter/pam_filter.c
|
||||
--- Linux-PAM-1.5.1/modules/pam_filter/pam_filter.c.libpam-support-long-lines 2024-06-18 09:52:38.725482846 +0200
|
||||
+++ Linux-PAM-1.5.1/modules/pam_filter/pam_filter.c 2024-06-18 09:54:17.102732759 +0200
|
||||
@@ -96,7 +96,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.5.1/modules/pam_motd/pam_motd.c.libpam-support-long-lines Linux-PAM-1.5.1/modules/pam_motd/pam_motd.c
|
||||
--- Linux-PAM-1.5.1/modules/pam_motd/pam_motd.c.libpam-support-long-lines 2020-11-25 17:57:02.000000000 +0100
|
||||
+++ Linux-PAM-1.5.1/modules/pam_motd/pam_motd.c 2024-06-18 09:55:44.530954883 +0200
|
||||
@@ -71,14 +71,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;
|
||||
@@ -172,13 +172,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) {
|
||||
@@ -304,9 +304,8 @@ static int drop_privileges(pam_handle_t
|
||||
}
|
||||
|
||||
static int try_to_display(pam_handle_t *pamh, char **motd_path_split,
|
||||
- unsigned int num_motd_paths,
|
||||
- char **motd_dir_path_split,
|
||||
- unsigned int num_motd_dir_paths, int report_missing)
|
||||
+ size_t num_motd_paths, char **motd_dir_path_split,
|
||||
+ size_t num_motd_dir_paths, int report_missing)
|
||||
{
|
||||
PAM_MODUTIL_DEF_PRIVS(privs);
|
||||
|
||||
@@ -316,7 +315,7 @@ static int try_to_display(pam_handle_t *
|
||||
}
|
||||
|
||||
if (motd_path_split != NULL) {
|
||||
- unsigned int i;
|
||||
+ size_t i;
|
||||
|
||||
for (i = 0; i < num_motd_paths; i++) {
|
||||
int fd = open(motd_path_split[i], O_RDONLY, 0);
|
||||
@@ -354,11 +353,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;
|
||||
|
||||
diff -up Linux-PAM-1.5.1/modules/pam_permit/tst-pam_permit-retval.c.libpam-support-long-lines Linux-PAM-1.5.1/modules/pam_permit/tst-pam_permit-retval.c
|
||||
--- Linux-PAM-1.5.1/modules/pam_permit/tst-pam_permit-retval.c.libpam-support-long-lines 2020-11-25 17:57:02.000000000 +0100
|
||||
+++ Linux-PAM-1.5.1/modules/pam_permit/tst-pam_permit-retval.c 2024-06-18 09:52:38.726482849 +0200
|
||||
@@ -52,6 +52,35 @@ main(void)
|
||||
ASSERT_EQ(PAM_SUCCESS, pam_end(pamh, 0));
|
||||
pamh = NULL;
|
||||
|
||||
+ /* Perform a test dedicated to configuration file parsing. */
|
||||
+ ASSERT_NE(NULL, fp = fopen(service_file, "w"));
|
||||
+ ASSERT_LT(0, fprintf(fp, "#%%PAM-1.0\n"
|
||||
+ "# ignore escaped newlines in comments \\\n"
|
||||
+ "auth required \\\n"
|
||||
+ " %s/.libs/%s.so\n"
|
||||
+ "# allow unneeded whitespaces\n"
|
||||
+ " account required %s/.libs/%s.so%c\\\n"
|
||||
+ "line after NUL byte continues up to here\n"
|
||||
+ "password required %s/.libs/%s.so # eol comment\n"
|
||||
+ "session required %s/.libs/%s.so",
|
||||
+ cwd, MODULE_NAME,
|
||||
+ cwd, MODULE_NAME, '\0',
|
||||
+ cwd, MODULE_NAME,
|
||||
+ cwd, MODULE_NAME));
|
||||
+ ASSERT_EQ(0, fclose(fp));
|
||||
+
|
||||
+ ASSERT_EQ(PAM_SUCCESS,
|
||||
+ pam_start_confdir(service_file, user_name, &conv, ".", &pamh));
|
||||
+ ASSERT_NE(NULL, pamh);
|
||||
+ ASSERT_EQ(PAM_SUCCESS, pam_authenticate(pamh, 0));
|
||||
+ ASSERT_EQ(PAM_SUCCESS, pam_setcred(pamh, 0));
|
||||
+ ASSERT_EQ(PAM_SUCCESS, pam_acct_mgmt(pamh, 0));
|
||||
+ ASSERT_EQ(PAM_SUCCESS, pam_chauthtok(pamh, 0));
|
||||
+ ASSERT_EQ(PAM_SUCCESS, pam_open_session(pamh, 0));
|
||||
+ ASSERT_EQ(PAM_SUCCESS, pam_close_session(pamh, 0));
|
||||
+ ASSERT_EQ(PAM_SUCCESS, pam_end(pamh, 0));
|
||||
+ pamh = NULL;
|
||||
+
|
||||
ASSERT_EQ(0, unlink(service_file));
|
||||
|
||||
return 0;
|
@ -0,0 +1,58 @@
|
||||
From 031bb5a5d0d950253b68138b498dc93be69a64cb Mon Sep 17 00:00:00 2001
|
||||
From: Matthias Gerstner <matthias.gerstner@suse.de>
|
||||
Date: Wed, 27 Dec 2023 14:01:59 +0100
|
||||
Subject: [PATCH] pam_namespace: protect_dir(): use O_DIRECTORY to prevent
|
||||
local DoS situations
|
||||
|
||||
Without O_DIRECTORY the path crawling logic is subject to e.g. FIFOs
|
||||
being placed in user controlled directories, causing the PAM module to
|
||||
block indefinitely during `openat()`.
|
||||
|
||||
Pass O_DIRECTORY to cause the `openat()` to fail if the path does not
|
||||
refer to a directory.
|
||||
|
||||
With this the check whether the final path element is a directory
|
||||
becomes unnecessary, drop it.
|
||||
---
|
||||
modules/pam_namespace/pam_namespace.c | 18 +-----------------
|
||||
1 file changed, 1 insertion(+), 17 deletions(-)
|
||||
|
||||
diff --git a/modules/pam_namespace/pam_namespace.c b/modules/pam_namespace/pam_namespace.c
|
||||
index 2528cff8..f72d6718 100644
|
||||
--- a/modules/pam_namespace/pam_namespace.c
|
||||
+++ b/modules/pam_namespace/pam_namespace.c
|
||||
@@ -1201,7 +1201,7 @@ static int protect_dir(const char *path, mode_t mode, int do_mkdir,
|
||||
int dfd = AT_FDCWD;
|
||||
int dfd_next;
|
||||
int save_errno;
|
||||
- int flags = O_RDONLY;
|
||||
+ int flags = O_RDONLY | O_DIRECTORY;
|
||||
int rv = -1;
|
||||
struct stat st;
|
||||
|
||||
@@ -1255,22 +1255,6 @@ static int protect_dir(const char *path, mode_t mode, int do_mkdir,
|
||||
rv = openat(dfd, dir, flags);
|
||||
}
|
||||
|
||||
- if (rv != -1) {
|
||||
- if (fstat(rv, &st) != 0) {
|
||||
- save_errno = errno;
|
||||
- close(rv);
|
||||
- rv = -1;
|
||||
- errno = save_errno;
|
||||
- goto error;
|
||||
- }
|
||||
- if (!S_ISDIR(st.st_mode)) {
|
||||
- close(rv);
|
||||
- errno = ENOTDIR;
|
||||
- rv = -1;
|
||||
- goto error;
|
||||
- }
|
||||
- }
|
||||
-
|
||||
if (flags & O_NOFOLLOW) {
|
||||
/* we are inside user-owned dir - protect */
|
||||
if (protect_mount(rv, p, idata) == -1) {
|
||||
--
|
||||
2.43.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,114 @@
|
||||
diff -up Linux-PAM-1.5.1/modules/pam_unix/passverify.c.fail1 Linux-PAM-1.5.1/modules/pam_unix/passverify.c
|
||||
--- Linux-PAM-1.5.1/modules/pam_unix/passverify.c.fail1 2024-11-04 11:42:51.962791265 +0100
|
||||
+++ Linux-PAM-1.5.1/modules/pam_unix/passverify.c 2024-11-04 11:45:18.246218579 +0100
|
||||
@@ -239,17 +239,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;
|
||||
|
||||
|
||||
From 8d0c575336ad301cd14e16ad2fdec6fe621764b8 Mon Sep 17 00:00:00 2001
|
||||
From: Sergei Trofimovich <slyich@gmail.com>
|
||||
Date: Thu, 28 Mar 2024 21:58:35 +0000
|
||||
Subject: [PATCH] pam_unix: allow empty passwords with non-empty hashes
|
||||
|
||||
Before the change pam_unix has different behaviours for a user with
|
||||
empty password for these two `/etc/shadow` entries:
|
||||
|
||||
nulloktest:$6$Yy4ty2jJ$bsVQWo8qlXC6UHq1/qTC3UR60ZJKmKApJ3Wj7DreAy8FxlVKtlDnplFQ7jMLVlDqordE7e4t49GvTb.aI59TP0:1::::::
|
||||
nulloktest::1::::::
|
||||
|
||||
The entry with a hash was rejected and the entry without was accepted.
|
||||
|
||||
The rejection happened because 9e74e90147c "pam_unix: avoid determining
|
||||
if user exists" introduced the following rejection check (slightly
|
||||
simplified):
|
||||
|
||||
...
|
||||
} else if (p[0] == '\0' && nullok) {
|
||||
if (hash[0] != '\0') {
|
||||
retval = PAM_AUTH_ERR;
|
||||
}
|
||||
|
||||
We should not reject the user with a hash assuming it's non-empty.
|
||||
The change does that by pushing empty password check into
|
||||
`verify_pwd_hash()`.
|
||||
|
||||
`NixOS` generates such hashed entries for empty passwords as if they
|
||||
were non-empty using the following perl code:
|
||||
|
||||
sub hashPassword {
|
||||
my ($password) = @_;
|
||||
my $salt = "";
|
||||
my @chars = ('.', '/', 0..9, 'A'..'Z', 'a'..'z');
|
||||
$salt .= $chars[rand 64] for (1..8);
|
||||
return crypt($password, '$6$' . $salt . '$');
|
||||
}
|
||||
|
||||
Resolves: https://github.com/linux-pam/linux-pam/issues/758
|
||||
Fixes: 9e74e90147c "pam_unix: avoid determining if user exists"
|
||||
Signed-off-by: Sergei Trofimovich <slyich@gmail.com>
|
||||
---
|
||||
modules/pam_unix/passverify.c | 14 ++++++--------
|
||||
1 file changed, 6 insertions(+), 8 deletions(-)
|
||||
|
||||
diff --git a/modules/pam_unix/passverify.c b/modules/pam_unix/passverify.c
|
||||
index 30045333..1c83f1aa 100644
|
||||
--- a/modules/pam_unix/passverify.c
|
||||
+++ b/modules/pam_unix/passverify.c
|
||||
@@ -76,9 +76,13 @@ PAMH_ARG_DECL(int verify_pwd_hash,
|
||||
|
||||
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 {
|
||||
@@ -1109,12 +1113,6 @@ helper_verify_password(const char *name, const char *p, int nullok)
|
||||
if (pwd == NULL || hash == NULL) {
|
||||
helper_log_err(LOG_NOTICE, "check pass; user unknown");
|
||||
retval = PAM_USER_UNKNOWN;
|
||||
- } else if (p[0] == '\0' && nullok) {
|
||||
- if (hash[0] == '\0') {
|
||||
- retval = PAM_SUCCESS;
|
||||
- } else {
|
||||
- retval = PAM_AUTH_ERR;
|
||||
- }
|
||||
} else {
|
||||
retval = verify_pwd_hash(p, hash, nullok);
|
||||
}
|
||||
--
|
||||
2.47.0
|
||||
|
Loading…
Reference in new issue