import libcap-2.48-2.el8
This commit is contained in:
parent
681b14fcc8
commit
b45a5f117e
2
.gitignore
vendored
2
.gitignore
vendored
@ -1 +1 @@
|
||||
SOURCES/libcap-2.26.tar.gz
|
||||
SOURCES/libcap-2.48.tar.gz
|
||||
|
@ -1 +1 @@
|
||||
e667d815755f3f6a5819eb383827dd358372dda1 SOURCES/libcap-2.26.tar.gz
|
||||
c81102815c481257e53168e83b8849bc9f154d54 SOURCES/libcap-2.48.tar.gz
|
||||
|
@ -1,23 +0,0 @@
|
||||
.\" Hey, EMACS: -*- nroff -*-
|
||||
.TH GETPCAPS 8 "2001-05-29"
|
||||
.\" Please adjust this date whenever revising the manpage.
|
||||
.SH NAME
|
||||
getpcaps \- display process capabilities
|
||||
.SH SYNOPSIS
|
||||
.B getpcaps
|
||||
.IR pid ...
|
||||
.SH DESCRIPTION
|
||||
.B getpcaps
|
||||
displays the capabilities on the processes indicated by the
|
||||
.I pid
|
||||
value(s) given on the commandline. The capabilities
|
||||
are displayed in the
|
||||
.BR cap_from_text (3)
|
||||
format.
|
||||
.SH SEE ALSO
|
||||
.BR execcap (8).
|
||||
.br
|
||||
.SH AUTHOR
|
||||
This manual page was written by Robert Bihlmeyer <robbe@debian.org>,
|
||||
for the Debian GNU/Linux system (but may be used by others).
|
||||
|
@ -1,22 +0,0 @@
|
||||
diff -up libcap-2.25/Make.Rules.rh libcap-2.25/Make.Rules
|
||||
--- libcap-2.25/Make.Rules.rh 2016-04-11 18:52:01.418065682 +0200
|
||||
+++ libcap-2.25/Make.Rules 2016-04-11 18:52:10.790113866 +0200
|
||||
@@ -49,7 +49,8 @@ KERNEL_HEADERS := $(topdir)/libcap/inclu
|
||||
IPATH += -fPIC -I$(KERNEL_HEADERS) -I$(topdir)/libcap/include
|
||||
|
||||
CC := gcc
|
||||
-CFLAGS := -O2 -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64
|
||||
+CFLAGS := $(RPM_OPT_FLAGS) -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64
|
||||
+
|
||||
BUILD_CC := $(CC)
|
||||
BUILD_CFLAGS := $(CFLAGS) $(IPATH)
|
||||
AR := ar
|
||||
@@ -60,7 +61,7 @@ WARNINGS=-Wall -Wwrite-strings \
|
||||
-Wstrict-prototypes -Wmissing-prototypes \
|
||||
-Wnested-externs -Winline -Wshadow
|
||||
LD=$(CC) -Wl,-x -shared
|
||||
-LDFLAGS := #-g
|
||||
+LDFLAGS := $(RPM_LD_FLAGS) #-g
|
||||
BUILD_GPERF := $(shell which gperf >/dev/null 2>/dev/null && echo yes)
|
||||
|
||||
SYSTEM_HEADERS = /usr/include
|
@ -1,952 +0,0 @@
|
||||
From 99c995b84ef2974426b0acfa584d75e9a7d82028 Mon Sep 17 00:00:00 2001
|
||||
From: "Andrew G. Morgan" <morgan@kernel.org>
|
||||
Date: Sun, 22 Dec 2019 08:08:48 -0800
|
||||
Subject: Add group, ambient and bound setting support to pam_cap.
|
||||
|
||||
Rewrote the pam_cap config file parsing to support:
|
||||
|
||||
- @group syntax for identifying groups of users
|
||||
- ^cap_foo support for raising both inheritable and ambient caps
|
||||
- !cap_bar support for dropping bounding capabilities
|
||||
|
||||
Updated documentation for pre-existing libcap's ambient support.
|
||||
|
||||
This pam_cap feature upgrade was done in collaboration with
|
||||
Knut Omang and Christoph Lameter.
|
||||
|
||||
Signed-off-by: Andrew G. Morgan <morgan@kernel.org>
|
||||
---
|
||||
doc/cap_get_ambient.3 | 1 +
|
||||
doc/cap_get_proc.3 | 46 ++++++-
|
||||
doc/cap_reset_ambient.3 | 1 +
|
||||
doc/cap_set_ambient.3 | 1 +
|
||||
pam_cap/.gitignore | 3 +-
|
||||
pam_cap/Makefile | 21 ++-
|
||||
pam_cap/pam_cap.c | 355 +++++++++++++++++++++++++++++++++++-------------
|
||||
pam_cap/sudotest.conf | 23 ++++
|
||||
pam_cap/test_pam_cap.c | 200 +++++++++++++++++++++++++++
|
||||
10 files changed, 556 insertions(+), 113 deletions(-)
|
||||
create mode 100644 doc/cap_get_ambient.3
|
||||
create mode 100644 doc/cap_reset_ambient.3
|
||||
create mode 100644 doc/cap_set_ambient.3
|
||||
create mode 100644 pam_cap/sudotest.conf
|
||||
create mode 100644 pam_cap/test_pam_cap.c
|
||||
|
||||
diff --git a/doc/cap_get_ambient.3 b/doc/cap_get_ambient.3
|
||||
new file mode 100644
|
||||
index 0000000..65ea3e4
|
||||
--- /dev/null
|
||||
+++ b/doc/cap_get_ambient.3
|
||||
@@ -0,0 +1 @@
|
||||
+.so man3/cap_get_proc.3
|
||||
diff --git a/doc/cap_get_proc.3 b/doc/cap_get_proc.3
|
||||
index ed87fb7..712b3ff 100644
|
||||
--- a/doc/cap_get_proc.3
|
||||
+++ b/doc/cap_get_proc.3
|
||||
@@ -3,7 +3,8 @@
|
||||
.\"
|
||||
.TH CAP_GET_PROC 3 "2008-05-11" "" "Linux Programmer's Manual"
|
||||
.SH NAME
|
||||
-cap_get_proc, cap_set_proc, capgetp, cap_get_bound, cap_drop_bound \-
|
||||
+cap_get_proc, cap_set_proc, capgetp, cap_get_bound, cap_drop_bound \
|
||||
+cap_get_ambient, cap_set_ambient, cap_reset_ambient, \-
|
||||
capability manipulation on processes
|
||||
.SH SYNOPSIS
|
||||
.B #include <sys/capability.h>
|
||||
@@ -18,6 +19,14 @@
|
||||
.sp
|
||||
.BI "int cap_drop_bound(cap_value_t " cap );
|
||||
.sp
|
||||
+.BI "int cap_get_ambient(cap_value_t " cap );
|
||||
+.sp
|
||||
+.BI "int cap_set_ambient(cap_value_t " cap ", cap_flag_value_t " value);
|
||||
+.sp
|
||||
+.B int cap_reset_ambient(void);
|
||||
+.sp
|
||||
+.BI CAP_AMBIENT_SUPPORTED();
|
||||
+.sp
|
||||
.B #include <sys/types.h>
|
||||
.sp
|
||||
.BI "cap_t cap_get_pid(pid_t " pid );
|
||||
@@ -75,11 +84,38 @@
|
||||
.PP
|
||||
.BR cap_drop_bound ()
|
||||
can be used to lower the specified bounding set capability,
|
||||
-.BR cap ,
|
||||
+.BR cap .
|
||||
To complete successfully, the prevailing
|
||||
.I effective
|
||||
capability set must have a raised
|
||||
.BR CAP_SETPCAP .
|
||||
+.BR cap_get_ambient ()
|
||||
+returns the prevailing value of the specified ambient capability, or
|
||||
+-1 if the capability is not supported by the running kernel. A macro
|
||||
+.BR CAP_AMBIENT_SUPPORTED ()
|
||||
+uses this function to determine if ambient capabilities are supported
|
||||
+by the kernel.
|
||||
+.PP
|
||||
+.BR cap_set_ambient ()
|
||||
+sets the specified ambient capability to a specific value. To complete
|
||||
+successfully, the prevailing
|
||||
+.I effective
|
||||
+capability set must have a raised
|
||||
+.BR CAP_SETPCAP .
|
||||
+.PP
|
||||
+.BR cap_reset_ambient ()
|
||||
+resets all of the ambient capabilities for the current process to
|
||||
+their lowered value. To complete successfully, the prevailing
|
||||
+.I effective
|
||||
+capability set must have a raised
|
||||
+.BR CAP_SETPCAP .
|
||||
+Note, the ambient set is intended to operate in a legacy environment
|
||||
+where the application has limited awareness of capabilities in
|
||||
+general. Executing a file with associated filesystem capabilities, the
|
||||
+kernel will implicitly reset the ambient set of the process. Also,
|
||||
+changes to the inheritable set by the program code without explicitly
|
||||
+fixing up the ambient set can also drop ambient bits.
|
||||
+.PP
|
||||
.SH "RETURN VALUE"
|
||||
The functions
|
||||
.BR cap_get_proc ()
|
||||
diff --git a/doc/cap_reset_ambient.3 b/doc/cap_reset_ambient.3
|
||||
new file mode 100644
|
||||
index 0000000..65ea3e4
|
||||
--- /dev/null
|
||||
+++ b/doc/cap_reset_ambient.3
|
||||
@@ -0,0 +1 @@
|
||||
+.so man3/cap_get_proc.3
|
||||
diff --git a/doc/cap_set_ambient.3 b/doc/cap_set_ambient.3
|
||||
new file mode 100644
|
||||
index 0000000..65ea3e4
|
||||
--- /dev/null
|
||||
+++ b/doc/cap_set_ambient.3
|
||||
@@ -0,0 +1 @@
|
||||
+.so man3/cap_get_proc.3
|
||||
diff --git a/pam_cap/.gitignore b/pam_cap/.gitignore
|
||||
index 11806f5..05e9bbf 100644
|
||||
--- a/pam_cap/.gitignore
|
||||
+++ b/pam_cap/.gitignore
|
||||
@@ -1,2 +1,3 @@
|
||||
pam_cap.so
|
||||
-testcompile
|
||||
+testlink
|
||||
+test_pam_cap
|
||||
diff --git a/pam_cap/Makefile b/pam_cap/Makefile
|
||||
index 22f0f81..56604fd 100644
|
||||
--- a/pam_cap/Makefile
|
||||
+++ b/pam_cap/Makefile
|
||||
@@ -10,7 +10,7 @@
|
||||
LDLIBS += -L../libcap -lcap
|
||||
|
||||
all: pam_cap.so
|
||||
- $(MAKE) testcompile
|
||||
+ $(MAKE) testlink
|
||||
|
||||
install: all
|
||||
mkdir -p -m 0755 $(FAKEROOT)$(LIBDIR)/security
|
||||
@@ -22,8 +22,23 @@
|
||||
pam_cap.o: pam_cap.c
|
||||
$(CC) $(CFLAGS) $(IPATH) -c $< -o $@
|
||||
|
||||
-testcompile: test.c pam_cap.o
|
||||
+test_pam_cap: test_pam_cap.c pam_cap.c
|
||||
+ $(CC) $(CFLAGS) $(IPATH) -o $@ test_pam_cap.c $(LIBCAPLIB) $(LDFLAGS) --static
|
||||
+
|
||||
+testlink: test.c pam_cap.o
|
||||
$(CC) $(CFLAGS) $(LDFLAGS) -o $@ $+ -lpam -ldl $(LDLIBS)
|
||||
|
||||
+test: pam_cap.so
|
||||
+ make testlink
|
||||
+
|
||||
+sudotest: test test_pam_cap
|
||||
+ sudo ./test_pam_cap root 0x0 0x0 0x0 config=./capability.conf
|
||||
+ sudo ./test_pam_cap root 0x0 0x0 0x0 config=./sudotest.conf
|
||||
+ sudo ./test_pam_cap alpha 0x0 0x0 0x0 config=./capability.conf
|
||||
+ sudo ./test_pam_cap alpha 0x0 0x1 0x80 config=./sudotest.conf
|
||||
+ sudo ./test_pam_cap beta 0x0 0x1 0x0 config=./sudotest.conf
|
||||
+ sudo ./test_pam_cap gamma 0x0 0x0 0x81 config=./sudotest.conf
|
||||
+ sudo ./test_pam_cap delta 0x41 0x80 0x41 config=./sudotest.conf
|
||||
+
|
||||
clean:
|
||||
- rm -f *.o *.so testcompile *~
|
||||
+ rm -f *.o *.so testlink test_pam_cap *~
|
||||
diff --git a/pam_cap/pam_cap.c b/pam_cap/pam_cap.c
|
||||
index b1cc5cb..58ffe4a 100644
|
||||
--- a/pam_cap/pam_cap.c
|
||||
+++ b/pam_cap/pam_cap.c
|
||||
@@ -1,20 +1,23 @@
|
||||
/*
|
||||
- * Copyright (c) 1999,2007 Andrew G. Morgan <morgan@kernel.org>
|
||||
+ * Copyright (c) 1999,2007,2019 Andrew G. Morgan <morgan@kernel.org>
|
||||
*
|
||||
- * The purpose of this module is to enforce inheritable capability sets
|
||||
- * for a specified user.
|
||||
+ * The purpose of this module is to enforce inheritable, bounding and
|
||||
+ * ambient capability sets for a specified user.
|
||||
*/
|
||||
|
||||
/* #define DEBUG */
|
||||
|
||||
-#include <stdio.h>
|
||||
-#include <string.h>
|
||||
#include <errno.h>
|
||||
+#include <grp.h>
|
||||
+#include <limits.h>
|
||||
+#include <pwd.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdlib.h>
|
||||
+#include <stdio.h>
|
||||
+#include <string.h>
|
||||
#include <syslog.h>
|
||||
-
|
||||
#include <sys/capability.h>
|
||||
+#include <sys/types.h>
|
||||
|
||||
#include <security/pam_modules.h>
|
||||
#include <security/_pam_macros.h>
|
||||
@@ -22,8 +25,6 @@
|
||||
#define USER_CAP_FILE "/etc/security/capability.conf"
|
||||
#define CAP_FILE_BUFFER_SIZE 4096
|
||||
#define CAP_FILE_DELIMITERS " \t\n"
|
||||
-#define CAP_COMBINED_FORMAT "%s all-i %s+i"
|
||||
-#define CAP_DROP_ALL "%s all-i"
|
||||
|
||||
struct pam_cap_s {
|
||||
int debug;
|
||||
@@ -31,25 +32,71 @@ struct pam_cap_s {
|
||||
const char *conf_filename;
|
||||
};
|
||||
|
||||
+/*
|
||||
+ * load_groups obtains the list all of the groups associated with the
|
||||
+ * requested user: gid & supplemental groups.
|
||||
+ */
|
||||
+static int load_groups(const char *user, char ***groups, int *groups_n) {
|
||||
+ struct passwd *pwd;
|
||||
+ gid_t grps[NGROUPS_MAX];
|
||||
+ int ngrps = NGROUPS_MAX;
|
||||
+
|
||||
+ *groups = NULL;
|
||||
+ *groups_n = 0;
|
||||
+
|
||||
+ pwd = getpwnam(user);
|
||||
+ if (pwd == NULL) {
|
||||
+ return -1;
|
||||
+ }
|
||||
+
|
||||
+ /* must include at least pwd->pw_gid, hence < 1 test. */
|
||||
+ if (getgrouplist(user, pwd->pw_gid, grps, &ngrps) < 1) {
|
||||
+ return -1;
|
||||
+ }
|
||||
+
|
||||
+ *groups = calloc(ngrps, sizeof(char *));
|
||||
+ int g_n = 0;
|
||||
+ for (int i = 0; i < ngrps; i++) {
|
||||
+ const struct group *g = getgrgid(grps[i]);
|
||||
+ if (g == NULL) {
|
||||
+ continue;
|
||||
+ }
|
||||
+ D(("noting [%s] is a member of [%s]", user, g->gr_name));
|
||||
+ (*groups)[g_n++] = strdup(g->gr_name);
|
||||
+ }
|
||||
+
|
||||
+ *groups_n = g_n;
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
/* obtain the inheritable capabilities for the current user */
|
||||
|
||||
static char *read_capabilities_for_user(const char *user, const char *source)
|
||||
{
|
||||
char *cap_string = NULL;
|
||||
char buffer[CAP_FILE_BUFFER_SIZE], *line;
|
||||
+ char **groups;
|
||||
+ int groups_n;
|
||||
FILE *cap_file;
|
||||
|
||||
+ if (load_groups(user, &groups, &groups_n)) {
|
||||
+ D(("unknown user [%s]", user));
|
||||
+ return NULL;
|
||||
+ }
|
||||
+
|
||||
cap_file = fopen(source, "r");
|
||||
if (cap_file == NULL) {
|
||||
D(("failed to open capability file"));
|
||||
- return NULL;
|
||||
+ goto defer;
|
||||
}
|
||||
|
||||
- while ((line = fgets(buffer, CAP_FILE_BUFFER_SIZE, cap_file))) {
|
||||
- int found_one = 0;
|
||||
+ int found_one = 0;
|
||||
+ while (!found_one &&
|
||||
+ (line = fgets(buffer, CAP_FILE_BUFFER_SIZE, cap_file))) {
|
||||
const char *cap_text;
|
||||
|
||||
- cap_text = strtok(line, CAP_FILE_DELIMITERS);
|
||||
+ char *next = NULL;
|
||||
+ cap_text = strtok_r(line, CAP_FILE_DELIMITERS, &next);
|
||||
|
||||
if (cap_text == NULL) {
|
||||
D(("empty line"));
|
||||
@@ -60,38 +107,63 @@ static char *read_capabilities_for_user(const char *user, const char *source)
|
||||
continue;
|
||||
}
|
||||
|
||||
- while ((line = strtok(NULL, CAP_FILE_DELIMITERS))) {
|
||||
-
|
||||
+ /*
|
||||
+ * Explore whether any of the ids are a match for the current
|
||||
+ * user.
|
||||
+ */
|
||||
+ while ((line = strtok_r(next, CAP_FILE_DELIMITERS, &next))) {
|
||||
if (strcmp("*", line) == 0) {
|
||||
D(("wildcard matched"));
|
||||
found_one = 1;
|
||||
- cap_string = strdup(cap_text);
|
||||
break;
|
||||
}
|
||||
|
||||
if (strcmp(user, line) == 0) {
|
||||
D(("exact match for user"));
|
||||
found_one = 1;
|
||||
- cap_string = strdup(cap_text);
|
||||
break;
|
||||
}
|
||||
|
||||
- D(("user is not [%s] - skipping", line));
|
||||
- }
|
||||
+ if (line[0] != '@') {
|
||||
+ D(("user [%s] is not [%s] - skipping", user, line));
|
||||
+ }
|
||||
|
||||
- cap_text = NULL;
|
||||
- line = NULL;
|
||||
+ for (int i=0; i < groups_n; i++) {
|
||||
+ if (!strcmp(groups[i], line+1)) {
|
||||
+ D(("user group matched [%s]", line));
|
||||
+ found_one = 1;
|
||||
+ break;
|
||||
+ }
|
||||
+ }
|
||||
+ if (found_one) {
|
||||
+ break;
|
||||
+ }
|
||||
+ }
|
||||
|
||||
if (found_one) {
|
||||
+ cap_string = strdup(cap_text);
|
||||
D(("user [%s] matched - caps are [%s]", user, cap_string));
|
||||
- break;
|
||||
}
|
||||
+
|
||||
+ cap_text = NULL;
|
||||
+ line = NULL;
|
||||
}
|
||||
|
||||
fclose(cap_file);
|
||||
|
||||
+defer:
|
||||
memset(buffer, 0, CAP_FILE_BUFFER_SIZE);
|
||||
|
||||
+ for (int i = 0; i < groups_n; i++) {
|
||||
+ char *g = groups[i];
|
||||
+ _pam_overwrite(g);
|
||||
+ _pam_drop(g);
|
||||
+ }
|
||||
+ if (groups != NULL) {
|
||||
+ memset(groups, 0, groups_n * sizeof(char *));
|
||||
+ _pam_drop(groups);
|
||||
+ }
|
||||
+
|
||||
return cap_string;
|
||||
}
|
||||
|
||||
@@ -100,15 +172,16 @@ static char *read_capabilities_for_user(const char *user, const char *source)
|
||||
* permitted+executable sets combined with the configured inheritable
|
||||
* set.
|
||||
*/
|
||||
-
|
||||
static int set_capabilities(struct pam_cap_s *cs)
|
||||
{
|
||||
cap_t cap_s;
|
||||
- ssize_t length = 0;
|
||||
- char *conf_icaps;
|
||||
- char *proc_epcaps;
|
||||
- char *combined_caps;
|
||||
+ char *conf_caps;
|
||||
int ok = 0;
|
||||
+ int has_ambient = 0, has_bound = 0;
|
||||
+ int *bound = NULL, *ambient = NULL;
|
||||
+ cap_flag_value_t had_setpcap = 0;
|
||||
+ cap_value_t max_caps = 0;
|
||||
+ const cap_value_t wanted_caps[] = { CAP_SETPCAP };
|
||||
|
||||
cap_s = cap_get_proc();
|
||||
if (cap_s == NULL) {
|
||||
@@ -116,82 +189,170 @@ static int set_capabilities(struct pam_cap_s *cs)
|
||||
strerror(errno)));
|
||||
return 0;
|
||||
}
|
||||
+ if (cap_get_flag(cap_s, CAP_SETPCAP, CAP_EFFECTIVE, &had_setpcap)) {
|
||||
+ D(("failed to read a e capability: %s", strerror(errno)));
|
||||
+ goto cleanup_cap_s;
|
||||
+ }
|
||||
+ if (cap_set_flag(cap_s, CAP_EFFECTIVE, 1, wanted_caps, CAP_SET) != 0) {
|
||||
+ D(("unable to raise CAP_SETPCAP: %s", strerrno(errno)));
|
||||
+ goto cleanup_cap_s;
|
||||
+ }
|
||||
|
||||
- conf_icaps =
|
||||
- read_capabilities_for_user(cs->user,
|
||||
- cs->conf_filename
|
||||
- ? cs->conf_filename:USER_CAP_FILE );
|
||||
- if (conf_icaps == NULL) {
|
||||
+ conf_caps = read_capabilities_for_user(cs->user,
|
||||
+ cs->conf_filename
|
||||
+ ? cs->conf_filename:USER_CAP_FILE );
|
||||
+ if (conf_caps == NULL) {
|
||||
D(("no capabilities found for user [%s]", cs->user));
|
||||
goto cleanup_cap_s;
|
||||
}
|
||||
|
||||
- proc_epcaps = cap_to_text(cap_s, &length);
|
||||
- if (proc_epcaps == NULL) {
|
||||
- D(("unable to convert process capabilities to text"));
|
||||
- goto cleanup_icaps;
|
||||
+ ssize_t conf_caps_length = strlen(conf_caps);
|
||||
+ if (!strcmp(conf_caps, "all")) {
|
||||
+ /*
|
||||
+ * all here is interpreted as no change/pass through, which is
|
||||
+ * likely to be the same as none for sensible system defaults.
|
||||
+ */
|
||||
+ ok = 1;
|
||||
+ goto cleanup_caps;
|
||||
}
|
||||
|
||||
- /*
|
||||
- * This is a pretty inefficient way to combine
|
||||
- * capabilities. However, it seems to be the most straightforward
|
||||
- * one, given the limitations of the POSIX.1e draft spec. The spec
|
||||
- * is optimized for applications that know the capabilities they
|
||||
- * want to manipulate at compile time.
|
||||
- */
|
||||
-
|
||||
- combined_caps = malloc(1+strlen(CAP_COMBINED_FORMAT)
|
||||
- +strlen(proc_epcaps)+strlen(conf_icaps));
|
||||
- if (combined_caps == NULL) {
|
||||
- D(("unable to combine capabilities into one string - no memory"));
|
||||
- goto cleanup_epcaps;
|
||||
+ if (cap_set_proc(cap_s) != 0) {
|
||||
+ D(("unable to use CAP_SETPCAP: %s", strerrno(errno)));
|
||||
+ goto cleanup_caps;
|
||||
+ }
|
||||
+ if (cap_reset_ambient() == 0) {
|
||||
+ // Ambient set fully declared by this config.
|
||||
+ has_ambient = 1;
|
||||
}
|
||||
|
||||
- if (!strcmp(conf_icaps, "none")) {
|
||||
- sprintf(combined_caps, CAP_DROP_ALL, proc_epcaps);
|
||||
- } else if (!strcmp(conf_icaps, "all")) {
|
||||
- /* no change */
|
||||
- sprintf(combined_caps, "%s", proc_epcaps);
|
||||
+ if (!strcmp(conf_caps, "none")) {
|
||||
+ /* clearing CAP_INHERITABLE will also clear the ambient caps. */
|
||||
+ cap_clear_flag(cap_s, CAP_INHERITABLE);
|
||||
} else {
|
||||
- sprintf(combined_caps, CAP_COMBINED_FORMAT, proc_epcaps, conf_icaps);
|
||||
- }
|
||||
- D(("combined_caps=[%s]", combined_caps));
|
||||
+ /*
|
||||
+ * we know we have to perform some capability operations and
|
||||
+ * we need to know how many capabilities there are to do it
|
||||
+ * successfully.
|
||||
+ */
|
||||
+ while (cap_get_bound(max_caps) >= 0) {
|
||||
+ max_caps++;
|
||||
+ }
|
||||
+ has_bound = (max_caps != 0);
|
||||
+ if (has_bound) {
|
||||
+ bound = calloc(max_caps, sizeof(int));
|
||||
+ if (has_ambient) {
|
||||
+ // In kernel lineage, bound came first.
|
||||
+ ambient = calloc(max_caps, sizeof(int));
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ /*
|
||||
+ * Scan the configured capability string for:
|
||||
+ *
|
||||
+ * cap_name: add to cap_s' inheritable vector
|
||||
+ * ^cap_name: add to cap_s' inheritable vector and ambient set
|
||||
+ * !cap_name: drop from bounding set
|
||||
+ *
|
||||
+ * Setting ambient capabilities requires that we first enable
|
||||
+ * the corresponding inheritable capability to set them. So,
|
||||
+ * there is an order we use: parse the config line, building
|
||||
+ * the inheritable, ambient and bounding sets in three separate
|
||||
+ * arrays. Then, set I set A set B. Finally, at the end, we
|
||||
+ * restore the E value for CAP_SETPCAP.
|
||||
+ */
|
||||
+ char *token = NULL;
|
||||
+ char *next = conf_caps;
|
||||
+ while ((token = strtok_r(next, ",", &next))) {
|
||||
+ if (strlen(token) < 4) {
|
||||
+ D(("bogus cap: [%s] - ignored\n", token));
|
||||
+ goto cleanup_caps;
|
||||
+ }
|
||||
+ int is_a = 0, is_b = 0;
|
||||
+ if (*token == '^') {
|
||||
+ if (!has_ambient) {
|
||||
+ D(("want ambient [%s] but kernel has no support", token));
|
||||
+ goto cleanup_caps;
|
||||
+ }
|
||||
+ is_a = 1;
|
||||
+ token++;
|
||||
+ } else if (*token == '!') {
|
||||
+ if (!has_bound) {
|
||||
+ D(("want bound [%s] dropped - no kernel support", token));
|
||||
+ }
|
||||
+ is_b = 1;
|
||||
+ token++;
|
||||
+ }
|
||||
+
|
||||
+ cap_value_t c;
|
||||
+ if (cap_from_name(token, &c) != 0) {
|
||||
+ D(("unrecognized name [%s]: %s - ignored", token,
|
||||
+ strerror(errno)));
|
||||
+ goto cleanup_caps;
|
||||
+ }
|
||||
|
||||
- cap_free(cap_s);
|
||||
- cap_s = cap_from_text(combined_caps);
|
||||
- _pam_overwrite(combined_caps);
|
||||
- _pam_drop(combined_caps);
|
||||
+ if (is_b) {
|
||||
+ bound[c] = 1;
|
||||
+ } else {
|
||||
+ if (cap_set_flag(cap_s, CAP_INHERITABLE, 1, &c, CAP_SET)) {
|
||||
+ D(("failed to raise inheritable [%s]: %s", token,
|
||||
+ strerror(errno)));
|
||||
+ goto cleanup_caps;
|
||||
+ }
|
||||
+ if (is_a) {
|
||||
+ ambient[c] = 1;
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
|
||||
#ifdef DEBUG
|
||||
- {
|
||||
- char *temp = cap_to_text(cap_s, NULL);
|
||||
- D(("abbreviated caps for process will be [%s]", temp));
|
||||
- cap_free(temp);
|
||||
- }
|
||||
+ {
|
||||
+ char *temp = cap_to_text(cap_s, NULL);
|
||||
+ D(("abbreviated caps for process will be [%s]", temp));
|
||||
+ cap_free(temp);
|
||||
+ }
|
||||
#endif /* DEBUG */
|
||||
+ }
|
||||
|
||||
- if (cap_s == NULL) {
|
||||
- D(("no capabilies to set"));
|
||||
- } else if (cap_set_proc(cap_s) == 0) {
|
||||
- D(("capabilities were set correctly"));
|
||||
- ok = 1;
|
||||
- } else {
|
||||
+ if (cap_set_proc(cap_s)) {
|
||||
D(("failed to set specified capabilities: %s", strerror(errno)));
|
||||
+ } else {
|
||||
+ for (cap_value_t c = 0; c < max_caps; c++) {
|
||||
+ if (ambient != NULL && ambient[c]) {
|
||||
+ cap_set_ambient(c, CAP_SET);
|
||||
+ }
|
||||
+ if (bound != NULL && bound[c]) {
|
||||
+ cap_drop_bound(c);
|
||||
+ }
|
||||
+ }
|
||||
+ ok = 1;
|
||||
}
|
||||
|
||||
-cleanup_epcaps:
|
||||
- cap_free(proc_epcaps);
|
||||
-
|
||||
-cleanup_icaps:
|
||||
- _pam_overwrite(conf_icaps);
|
||||
- _pam_drop(conf_icaps);
|
||||
+cleanup_caps:
|
||||
+ if (has_ambient) {
|
||||
+ memset(ambient, 0, max_caps * sizeof(*ambient));
|
||||
+ _pam_drop(ambient);
|
||||
+ ambient = NULL;
|
||||
+ }
|
||||
+ if (has_bound) {
|
||||
+ memset(bound, 0, max_caps * sizeof(*bound));
|
||||
+ _pam_drop(bound);
|
||||
+ bound = NULL;
|
||||
+ }
|
||||
+ memset(conf_caps, 0, conf_caps_length);
|
||||
+ _pam_drop(conf_caps);
|
||||
|
||||
cleanup_cap_s:
|
||||
+ if (!had_setpcap) {
|
||||
+ /* Only need to lower if it wasn't raised by caller */
|
||||
+ if (!cap_set_flag(cap_s, CAP_EFFECTIVE, 1, wanted_caps,
|
||||
+ CAP_CLEAR)) {
|
||||
+ cap_set_proc(cap_s);
|
||||
+ }
|
||||
+ }
|
||||
if (cap_s) {
|
||||
cap_free(cap_s);
|
||||
cap_s = NULL;
|
||||
}
|
||||
-
|
||||
return ok;
|
||||
}
|
||||
|
||||
@@ -210,11 +371,8 @@ static void _pam_log(int err, const char *format, ...)
|
||||
|
||||
static void parse_args(int argc, const char **argv, struct pam_cap_s *pcs)
|
||||
{
|
||||
- int ctrl=0;
|
||||
-
|
||||
/* step through arguments */
|
||||
- for (ctrl=0; argc-- > 0; ++argv) {
|
||||
-
|
||||
+ for (; argc-- > 0; ++argv) {
|
||||
if (!strcmp(*argv, "debug")) {
|
||||
pcs->debug = 1;
|
||||
} else if (!memcmp(*argv, "config=", 7)) {
|
||||
@@ -222,23 +380,25 @@ static void parse_args(int argc, const char **argv, struct pam_cap_s *pcs)
|
||||
} else {
|
||||
_pam_log(LOG_ERR, "unknown option; %s", *argv);
|
||||
}
|
||||
-
|
||||
}
|
||||
}
|
||||
|
||||
+/*
|
||||
+ * pam_sm_authenticate parses the config file with respect to the user
|
||||
+ * being authenticated and determines if they are covered by any
|
||||
+ * capability inheritance rules.
|
||||
+ */
|
||||
int pam_sm_authenticate(pam_handle_t *pamh, int flags,
|
||||
int argc, const char **argv)
|
||||
{
|
||||
int retval;
|
||||
struct pam_cap_s pcs;
|
||||
- char *conf_icaps;
|
||||
+ char *conf_caps;
|
||||
|
||||
memset(&pcs, 0, sizeof(pcs));
|
||||
-
|
||||
parse_args(argc, argv, &pcs);
|
||||
|
||||
retval = pam_get_user(pamh, &pcs.user, NULL);
|
||||
-
|
||||
if (retval == PAM_CONV_AGAIN) {
|
||||
D(("user conversation is not available yet"));
|
||||
memset(&pcs, 0, sizeof(pcs));
|
||||
@@ -251,24 +411,22 @@ int pam_sm_authenticate(pam_handle_t *pamh, int flags,
|
||||
return PAM_AUTH_ERR;
|
||||
}
|
||||
|
||||
- conf_icaps =
|
||||
- read_capabilities_for_user(pcs.user,
|
||||
- pcs.conf_filename
|
||||
- ? pcs.conf_filename:USER_CAP_FILE );
|
||||
-
|
||||
+ conf_caps = read_capabilities_for_user(pcs.user,
|
||||
+ pcs.conf_filename
|
||||
+ ? pcs.conf_filename:USER_CAP_FILE );
|
||||
memset(&pcs, 0, sizeof(pcs));
|
||||
|
||||
- if (conf_icaps) {
|
||||
+ if (conf_caps) {
|
||||
D(("it appears that there are capabilities for this user [%s]",
|
||||
- conf_icaps));
|
||||
+ conf_caps));
|
||||
|
||||
/* We could also store this as a pam_[gs]et_data item for use
|
||||
by the setcred call to follow. As it is, there is a small
|
||||
race associated with a redundant read. Oh well, if you
|
||||
care, send me a patch.. */
|
||||
|
||||
- _pam_overwrite(conf_icaps);
|
||||
- _pam_drop(conf_icaps);
|
||||
+ _pam_overwrite(conf_caps);
|
||||
+ _pam_drop(conf_caps);
|
||||
|
||||
return PAM_SUCCESS;
|
||||
|
||||
@@ -280,6 +438,10 @@ int pam_sm_authenticate(pam_handle_t *pamh, int flags,
|
||||
}
|
||||
}
|
||||
|
||||
+/*
|
||||
+ * pam_sm_setcred applies inheritable capabilities loaded by the
|
||||
+ * pam_sm_authenticate pass for the user.
|
||||
+ */
|
||||
int pam_sm_setcred(pam_handle_t *pamh, int flags,
|
||||
int argc, const char **argv)
|
||||
{
|
||||
@@ -292,18 +454,15 @@ int pam_sm_setcred(pam_handle_t *pamh, int flags,
|
||||
}
|
||||
|
||||
memset(&pcs, 0, sizeof(pcs));
|
||||
-
|
||||
parse_args(argc, argv, &pcs);
|
||||
|
||||
retval = pam_get_item(pamh, PAM_USER, (const void **)&pcs.user);
|
||||
if ((retval != PAM_SUCCESS) || (pcs.user == NULL) || !(pcs.user[0])) {
|
||||
-
|
||||
D(("user's name is not set"));
|
||||
return PAM_AUTH_ERR;
|
||||
}
|
||||
|
||||
retval = set_capabilities(&pcs);
|
||||
-
|
||||
memset(&pcs, 0, sizeof(pcs));
|
||||
|
||||
return (retval ? PAM_SUCCESS:PAM_IGNORE );
|
||||
diff --git a/pam_cap/sudotest.conf b/pam_cap/sudotest.conf
|
||||
new file mode 100644
|
||||
index 0000000..ff528ce
|
||||
--- /dev/null
|
||||
+++ b/pam_cap/sudotest.conf
|
||||
@@ -0,0 +1,23 @@
|
||||
+# only root
|
||||
+all root
|
||||
+
|
||||
+# this should fire for beta only
|
||||
+!cap_chown beta
|
||||
+
|
||||
+# the next one should snag gamma since beta done
|
||||
+cap_setuid,cap_chown @three
|
||||
+
|
||||
+# neither of these should fire
|
||||
+cap_chown beta gamma
|
||||
+
|
||||
+# just alpha
|
||||
+!cap_chown,cap_setuid @one
|
||||
+
|
||||
+# not this one
|
||||
+^cap_setuid alpha
|
||||
+
|
||||
+# this should fire
|
||||
+^cap_chown,^cap_setgid,!cap_setuid delta
|
||||
+
|
||||
+# not this one
|
||||
+cap_setuid @four
|
||||
diff --git a/pam_cap/test_pam_cap.c b/pam_cap/test_pam_cap.c
|
||||
new file mode 100644
|
||||
index 0000000..2f519f1
|
||||
--- /dev/null
|
||||
+++ b/pam_cap/test_pam_cap.c
|
||||
@@ -0,0 +1,200 @@
|
||||
+/*
|
||||
+ * Copyright (c) 2019 Andrew G. Morgan <morgan@kernel.org>
|
||||
+ *
|
||||
+ * This test inlines the pam_cap module and runs test vectors against
|
||||
+ * it.
|
||||
+ */
|
||||
+
|
||||
+#include "./pam_cap.c"
|
||||
+
|
||||
+const char *test_groups[] = {
|
||||
+ "root", "one", "two", "three", "four", "five", "six", "seven"
|
||||
+};
|
||||
+#define n_groups sizeof(test_groups)/sizeof(*test_groups)
|
||||
+
|
||||
+const char *test_users[] = {
|
||||
+ "root", "alpha", "beta", "gamma", "delta"
|
||||
+};
|
||||
+#define n_users sizeof(test_users)/sizeof(*test_users)
|
||||
+
|
||||
+// Note about memberships:
|
||||
+//
|
||||
+// user gid suppl groups
|
||||
+// root root
|
||||
+// alpha one two
|
||||
+// beta two three four
|
||||
+// gamma three four five six
|
||||
+// delta four five six seven [eight]
|
||||
+//
|
||||
+
|
||||
+static char *test_user;
|
||||
+
|
||||
+int pam_get_user(pam_handle_t *pamh, const char **user, const char *prompt) {
|
||||
+ *user = test_user;
|
||||
+ if (*user == NULL) {
|
||||
+ return PAM_CONV_AGAIN;
|
||||
+ }
|
||||
+ return PAM_SUCCESS;
|
||||
+}
|
||||
+
|
||||
+int pam_get_item(const pam_handle_t *pamh, int item_type, const void **item) {
|
||||
+ if (item_type != PAM_USER) {
|
||||
+ errno = EINVAL;
|
||||
+ return -1;
|
||||
+ }
|
||||
+ *item = test_user;
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+int getgrouplist(const char *user, gid_t group, gid_t *groups, int *ngroups) {
|
||||
+ int i,j;
|
||||
+ for (i = 0; i < n_users; i++) {
|
||||
+ if (strcmp(user, test_users[i]) == 0) {
|
||||
+ *ngroups = i+1;
|
||||
+ break;
|
||||
+ }
|
||||
+ }
|
||||
+ if (i == n_users) {
|
||||
+ return -1;
|
||||
+ }
|
||||
+ groups[0] = i;
|
||||
+ for (j = 1; j < *ngroups; j++) {
|
||||
+ groups[j] = i+j;
|
||||
+ }
|
||||
+ return *ngroups;
|
||||
+}
|
||||
+
|
||||
+static struct group gr;
|
||||
+struct group *getgrgid(gid_t gid) {
|
||||
+ if (gid >= n_groups) {
|
||||
+ errno = EINVAL;
|
||||
+ return NULL;
|
||||
+ }
|
||||
+ gr.gr_name = strdup(test_groups[gid]);
|
||||
+ return &gr;
|
||||
+}
|
||||
+
|
||||
+static struct passwd pw;
|
||||
+struct passwd *getpwnam(const char *name) {
|
||||
+ for (int i = 0; i < n_users; i++) {
|
||||
+ if (strcmp(name, test_users[i]) == 0) {
|
||||
+ pw.pw_gid = i;
|
||||
+ return &pw;
|
||||
+ }
|
||||
+ }
|
||||
+ return NULL;
|
||||
+}
|
||||
+
|
||||
+/* we'll use these to keep track of the three vectors - only use
|
||||
+ lowest 64 bits */
|
||||
+
|
||||
+#define A 0
|
||||
+#define B 1
|
||||
+#define I 2
|
||||
+
|
||||
+/*
|
||||
+ * load_vectors caches a copy of the lowest 64 bits of the inheritable
|
||||
+ * cap vectors
|
||||
+ */
|
||||
+static void load_vectors(unsigned long int bits[3]) {
|
||||
+ memset(bits, 0, 3*sizeof(unsigned long int));
|
||||
+ cap_t prev = cap_get_proc();
|
||||
+ for (int i = 0; i < 64; i++) {
|
||||
+ unsigned long int mask = (1ULL << i);
|
||||
+ int v = cap_get_bound(i);
|
||||
+ if (v < 0) {
|
||||
+ break;
|
||||
+ }
|
||||
+ bits[B] |= v ? mask : 0;
|
||||
+ cap_flag_value_t u;
|
||||
+ if (cap_get_flag(prev, i, CAP_INHERITABLE, &u) != 0) {
|
||||
+ break;
|
||||
+ }
|
||||
+ bits[I] |= u ? mask : 0;
|
||||
+ v = cap_get_ambient(i);
|
||||
+ if (v > 0) {
|
||||
+ bits[A] |= mask;
|
||||
+ }
|
||||
+ }
|
||||
+ cap_free(prev);
|
||||
+}
|
||||
+
|
||||
+/*
|
||||
+ * args: user a b i config-args...
|
||||
+ */
|
||||
+int main(int argc, char *argv[]) {
|
||||
+ unsigned long int before[3], change[3], after[3];
|
||||
+
|
||||
+ /*
|
||||
+ * Start out with a cleared inheritable set.
|
||||
+ */
|
||||
+ cap_t orig = cap_get_proc();
|
||||
+ cap_clear_flag(orig, CAP_INHERITABLE);
|
||||
+ cap_set_proc(orig);
|
||||
+
|
||||
+ change[A] = strtoul(argv[2], NULL, 0);
|
||||
+ change[B] = strtoul(argv[3], NULL, 0);
|
||||
+ change[I] = strtoul(argv[4], NULL, 0);
|
||||
+
|
||||
+ void* args_for_pam = argv+4;
|
||||
+
|
||||
+ int status = pam_sm_authenticate(NULL, 0, argc-4,
|
||||
+ (const char **) args_for_pam);
|
||||
+ if (status != PAM_INCOMPLETE) {
|
||||
+ printf("failed to recognize no username\n");
|
||||
+ exit(1);
|
||||
+ }
|
||||
+
|
||||
+ test_user = argv[1];
|
||||
+
|
||||
+ status = pam_sm_authenticate(NULL, 0, argc-4, (const char **) args_for_pam);
|
||||
+ if (status == PAM_IGNORE) {
|
||||
+ if (strcmp(test_user, "root") == 0) {
|
||||
+ exit(0);
|
||||
+ }
|
||||
+ printf("unconfigured non-root user: %s\n", test_user);
|
||||
+ exit(1);
|
||||
+ }
|
||||
+ if (status != PAM_SUCCESS) {
|
||||
+ printf("failed to recognize username\n");
|
||||
+ exit(1);
|
||||
+ }
|
||||
+
|
||||
+ // Now it is time to execute the credential setting
|
||||
+ load_vectors(before);
|
||||
+
|
||||
+ status = pam_sm_setcred(NULL, PAM_ESTABLISH_CRED, argc-4,
|
||||
+ (const char **) args_for_pam);
|
||||
+
|
||||
+ load_vectors(after);
|
||||
+
|
||||
+ printf("before: A=0x%016lx B=0x%016lx I=0x%016lx\n",
|
||||
+ before[A], before[B], before[I]);
|
||||
+
|
||||
+ long unsigned int dA = before[A] ^ after[A];
|
||||
+ long unsigned int dB = before[B] ^ after[B];
|
||||
+ long unsigned int dI = before[I] ^ after[I];
|
||||
+
|
||||
+ printf("diff : A=0x%016lx B=0x%016lx I=0x%016lx\n", dA, dB, dI);
|
||||
+ printf("after : A=0x%016lx B=0x%016lx I=0x%016lx\n",
|
||||
+ after[A], after[B], after[I]);
|
||||
+
|
||||
+ int failure = 0;
|
||||
+ if (after[A] != change[A]) {
|
||||
+ printf("Ambient set error: got=0x%016lx, want=0x%016lx\n",
|
||||
+ after[A], change[A]);
|
||||
+ failure = 1;
|
||||
+ }
|
||||
+ if (dB != change[B]) {
|
||||
+ printf("Bounding set error: got=0x%016lx, want=0x%016lx\n",
|
||||
+ after[B], before[B] ^ change[B]);
|
||||
+ failure = 1;
|
||||
+ }
|
||||
+ if (after[I] != change[I]) {
|
||||
+ printf("Inheritable set error: got=0x%016lx, want=0x%016lx\n",
|
||||
+ after[I], change[I]);
|
||||
+ failure = 1;
|
||||
+ }
|
||||
+
|
||||
+ exit(failure);
|
||||
+}
|
||||
--
|
||||
cgit 1.2.3-1.el7
|
||||
|
34
SOURCES/libcap-2.48-buildflags.patch
Normal file
34
SOURCES/libcap-2.48-buildflags.patch
Normal file
@ -0,0 +1,34 @@
|
||||
diff --color -ru a/Make.Rules b/Make.Rules
|
||||
--- a/Make.Rules 2021-02-05 06:52:17.000000000 +0100
|
||||
+++ b/Make.Rules 2021-12-13 17:09:11.225308225 +0100
|
||||
@@ -56,10 +56,10 @@
|
||||
|
||||
CC := $(CROSS_COMPILE)gcc
|
||||
DEFINES := -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64
|
||||
-COPTS ?= -O2
|
||||
+COPTS ?= $(RPM_OPT_FLAGS)
|
||||
CFLAGS ?= $(COPTS) $(DEFINES)
|
||||
BUILD_CC ?= $(CC)
|
||||
-BUILD_COPTS ?= -O2
|
||||
+BUILD_COPTS ?= $(RPM_OPT_FLAGS)
|
||||
BUILD_CFLAGS ?= $(BUILD_COPTS) $(DEFINES) $(IPATH)
|
||||
AR := $(CROSS_COMPILE)ar
|
||||
RANLIB := $(CROSS_COMPILE)ranlib
|
||||
@@ -69,7 +69,7 @@
|
||||
-Wstrict-prototypes -Wmissing-prototypes \
|
||||
-Wnested-externs -Winline -Wshadow
|
||||
LD=$(CC) -Wl,-x -shared
|
||||
-LDFLAGS ?= #-g
|
||||
+LDFLAGS ?= $(RPM_LD_FLAGS)
|
||||
LIBCAPLIB := -L$(topdir)/libcap -lcap
|
||||
PSXLINKFLAGS := -lpthread -Wl,-wrap,pthread_create
|
||||
LIBPSXLIB := -L$(topdir)/libcap -lpsx $(PSXLINKFLAGS)
|
||||
@@ -104,7 +104,7 @@
|
||||
|
||||
ifeq ($(PTHREADS),yes)
|
||||
GO ?= go
|
||||
-GOLANG ?= $(shell if [ -n "$(shell $(GO) version 2>/dev/null)" ]; then echo yes ; else echo no ; fi)
|
||||
+GOLANG ?= no
|
||||
ifeq ($(GOLANG),yes)
|
||||
GOROOT ?= $(shell $(GO) env GOROOT)
|
||||
GOCGO ?= $(shell if [ "$(shell $(GO) env CGO_ENABLED)" = 1 ]; then echo yes ; else echo no ; fi)
|
@ -1,12 +0,0 @@
|
||||
diff -urN libcap-2.25/pam_cap/pam_cap.c libcap-2.25_patched/pam_cap/pam_cap.c
|
||||
--- libcap-2.25/pam_cap/pam_cap.c 2013-12-16 05:46:28.000000000 +0100
|
||||
+++ libcap-2.25_patched/pam_cap/pam_cap.c 2019-03-04 16:18:23.440525062 +0100
|
||||
@@ -286,7 +286,7 @@
|
||||
int retval;
|
||||
struct pam_cap_s pcs;
|
||||
|
||||
- if (!(flags & PAM_ESTABLISH_CRED)) {
|
||||
+ if (!(flags & (PAM_ESTABLISH_CRED | PAM_REINITIALIZE_CRED))) {
|
||||
D(("we don't handle much in the way of credentials"));
|
||||
return PAM_IGNORE;
|
||||
}
|
37
SOURCES/libcap-abi-compatibility.patch
Normal file
37
SOURCES/libcap-abi-compatibility.patch
Normal file
@ -0,0 +1,37 @@
|
||||
diff --color -ru a/libcap/cap_text.c b/libcap/cap_text.c
|
||||
--- a/libcap/cap_text.c 2021-02-05 06:52:17.000000000 +0100
|
||||
+++ b/libcap/cap_text.c 2021-12-15 13:03:44.993774400 +0100
|
||||
@@ -15,7 +15,7 @@
|
||||
#define LIBCAP_PLEASE_INCLUDE_ARRAY
|
||||
#include "libcap.h"
|
||||
|
||||
-static char const *_cap_names[__CAP_BITS] = LIBCAP_CAP_NAMES;
|
||||
+extern char const *_cap_names[__CAP_BITS];
|
||||
|
||||
#include <ctype.h>
|
||||
#include <limits.h>
|
||||
diff --color -ru a/libcap/_makenames.c b/libcap/_makenames.c
|
||||
--- a/libcap/_makenames.c 2021-02-05 06:52:17.000000000 +0100
|
||||
+++ b/libcap/_makenames.c 2021-12-15 12:47:07.921408357 +0100
|
||||
@@ -66,17 +66,17 @@
|
||||
"#define __CAP_NAME_SIZE %d\n"
|
||||
"\n"
|
||||
"#ifdef LIBCAP_PLEASE_INCLUDE_ARRAY\n"
|
||||
- "#define LIBCAP_CAP_NAMES { \\\n", maxcaps, maxlength+1);
|
||||
+ " char const *_cap_names[__CAP_BITS] = {\n", maxcaps, maxlength+1);
|
||||
|
||||
for (i=0; i<maxcaps; ++i) {
|
||||
if (pointers[i]) {
|
||||
- printf(" /* %d */\t\"%s\", \\\n", i, pointers[i]);
|
||||
+ printf(" /* %d */\t\"%s\",\n", i, pointers[i]);
|
||||
} else {
|
||||
- printf(" /* %d */\tNULL,\t\t/* - presently unused */ \\\n", i);
|
||||
+ printf(" /* %d */\tNULL,\t\t/* - presently unused */\n", i);
|
||||
}
|
||||
}
|
||||
|
||||
- printf(" }\n"
|
||||
+ printf(" };\n"
|
||||
"#endif /* LIBCAP_PLEASE_INCLUDE_ARRAY */\n"
|
||||
"\n"
|
||||
"/* END OF FILE */\n");
|
@ -1,96 +0,0 @@
|
||||
diff --color -ruN a/distcheck.sh b/distcheck.sh
|
||||
--- a/distcheck.sh 1970-01-01 01:00:00.000000000 +0100
|
||||
+++ b/distcheck.sh 2021-06-10 10:06:19.618284780 +0200
|
||||
@@ -0,0 +1,13 @@
|
||||
+#!/bin/bash
|
||||
+
|
||||
+actual=$(wget -o/dev/null -O/dev/stdout https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/plain/include/uapi/linux/capability.h | grep "#define.CAP_LAST_CAP"|awk '{print $3}')
|
||||
+working=$(grep "#define.CAP_LAST_CAP" libcap/include/uapi/linux/capability.h|awk '{print $3}')
|
||||
+
|
||||
+if [[ ${actual} = ${working} ]]; then
|
||||
+ echo "up to date with officially named caps"
|
||||
+ exit 0
|
||||
+fi
|
||||
+
|
||||
+echo "want: ${actual}"
|
||||
+echo "have: ${working}"
|
||||
+exit 1
|
||||
diff --color -ruN a/libcap/include/uapi/linux/capability.h b/libcap/include/uapi/linux/capability.h
|
||||
--- a/libcap/include/uapi/linux/capability.h 2018-09-09 20:06:40.000000000 +0200
|
||||
+++ b/libcap/include/uapi/linux/capability.h 2021-06-10 10:05:19.729202015 +0200
|
||||
@@ -331,6 +331,8 @@
|
||||
|
||||
#define CAP_AUDIT_CONTROL 30
|
||||
|
||||
+/* Set capabilities on files. */
|
||||
+
|
||||
#define CAP_SETFCAP 31
|
||||
|
||||
/* Override MAC access.
|
||||
@@ -366,8 +368,50 @@
|
||||
|
||||
#define CAP_AUDIT_READ 37
|
||||
|
||||
+/* Allow system performance and observability privileged operations using
|
||||
+ * perf_events, i915_perf and other kernel subsystems. */
|
||||
+
|
||||
+#define CAP_PERFMON 38
|
||||
+
|
||||
+/*
|
||||
+ * CAP_BPF allows the following BPF operations:
|
||||
+ * - Creating all types of BPF maps
|
||||
+ * - Advanced verifier features
|
||||
+ * - Indirect variable access
|
||||
+ * - Bounded loops
|
||||
+ * - BPF to BPF function calls
|
||||
+ * - Scalar precision tracking
|
||||
+ * - Larger complexity limits
|
||||
+ * - Dead code elimination
|
||||
+ * - And potentially other features
|
||||
+ * - Loading BPF Type Format (BTF) data
|
||||
+ * - Retrieve xlated and JITed code of BPF programs
|
||||
+ * - Use bpf_spin_lock() helper
|
||||
+ *
|
||||
+ * CAP_PERFMON relaxes the verifier checks further:
|
||||
+ * - BPF progs can use of pointer-to-integer conversions
|
||||
+ * - speculation attack hardening measures are bypassed
|
||||
+ * - bpf_probe_read to read arbitrary kernel memory is allowed
|
||||
+ * - bpf_trace_printk to print kernel memory is allowed
|
||||
+ *
|
||||
+ * CAP_SYS_ADMIN is required to use bpf_probe_write_user.
|
||||
+ *
|
||||
+ * CAP_SYS_ADMIN is required to iterate system wide loaded
|
||||
+ * programs, maps, links, BTFs and convert their IDs to file descriptors.
|
||||
+ *
|
||||
+ * CAP_PERFMON and CAP_BPF are required to load tracing programs.
|
||||
+ * CAP_NET_ADMIN and CAP_BPF are required to load networking programs.
|
||||
+ */
|
||||
+
|
||||
+#define CAP_BPF 39
|
||||
+
|
||||
+/* Allow checkpoint/restore related operations */
|
||||
+/* Allow PID selection during clone3() */
|
||||
+/* Allow writing to ns_last_pid */
|
||||
+
|
||||
+#define CAP_CHECKPOINT_RESTORE 40
|
||||
|
||||
-#define CAP_LAST_CAP CAP_AUDIT_READ
|
||||
+#define CAP_LAST_CAP CAP_CHECKPOINT_RESTORE
|
||||
|
||||
#define cap_valid(x) ((x) >= 0 && (x) <= CAP_LAST_CAP)
|
||||
|
||||
diff --color -ruN a/Makefile b/Makefile
|
||||
--- a/Makefile 2018-09-15 23:51:38.000000000 +0200
|
||||
+++ b/Makefile 2021-06-10 10:07:30.872573023 +0200
|
||||
@@ -33,7 +33,10 @@
|
||||
test: all
|
||||
cd progs && sudo ./quicktest.sh
|
||||
|
||||
-morganrelease: distclean
|
||||
+distcheck:
|
||||
+ ./distcheck.sh
|
||||
+
|
||||
+morganrelease: distclean distcheck
|
||||
@echo "sign the tag twice: older DSA key; and newer RSA kernel.org key"
|
||||
git tag -u D41A6DF2 -s libcap-$(VERSION).$(MINOR) -m "This is libcap-$(VERSION).$(MINOR)"
|
||||
git tag -u E2CCF3F4 -s libcap-korg-$(VERSION).$(MINOR) -m "This is libcap-$(VERSION).$(MINOR)"
|
147
SOURCES/libcap-fix-ambient-caps.patch
Normal file
147
SOURCES/libcap-fix-ambient-caps.patch
Normal file
@ -0,0 +1,147 @@
|
||||
diff --color -ru a/libcap/cap_proc.c b/libcap/cap_proc.c
|
||||
--- a/libcap/cap_proc.c 2021-12-22 12:33:20.739126763 +0100
|
||||
+++ b/libcap/cap_proc.c 2021-12-22 12:33:53.195733115 +0100
|
||||
@@ -406,6 +406,29 @@
|
||||
}
|
||||
|
||||
/*
|
||||
+ * cap_prctl performs a prctl() 6 argument call on the current
|
||||
+ * thread. Use cap_prctlw() if you want to perform a POSIX semantics
|
||||
+ * prctl() system call.
|
||||
+ */
|
||||
+int cap_prctl(long int pr_cmd, long int arg1, long int arg2,
|
||||
+ long int arg3, long int arg4, long int arg5)
|
||||
+{
|
||||
+ return prctl(pr_cmd, arg1, arg2, arg3, arg4, arg5);
|
||||
+}
|
||||
+
|
||||
+/*
|
||||
+ * cap_prctlw performs a POSIX semantics prctl() call. That is a 6 arg
|
||||
+ * prctl() call that executes on all available threads when libpsx is
|
||||
+ * linked. The suffix 'w' refers to the fact one only ever needs to
|
||||
+ * invoke this is if the call will write some kernel state.
|
||||
+ */
|
||||
+int cap_prctlw(long int pr_cmd, long int arg1, long int arg2,
|
||||
+ long int arg3, long int arg4, long int arg5)
|
||||
+{
|
||||
+ return _libcap_wprctl6(&multithread, pr_cmd, arg1, arg2, arg3, arg4, arg5);
|
||||
+}
|
||||
+
|
||||
+/*
|
||||
* Some predefined constants
|
||||
*/
|
||||
#define CAP_SECURED_BITS_BASIC \
|
||||
diff --color -ru a/libcap/include/sys/capability.h b/libcap/include/sys/capability.h
|
||||
--- a/libcap/include/sys/capability.h 2021-02-05 06:52:17.000000000 +0100
|
||||
+++ b/libcap/include/sys/capability.h 2021-12-22 12:33:53.196733134 +0100
|
||||
@@ -175,6 +175,11 @@
|
||||
extern unsigned cap_get_secbits(void);
|
||||
extern int cap_set_secbits(unsigned bits);
|
||||
|
||||
+extern int cap_prctl(long int pr_cmd, long int arg1, long int arg2,
|
||||
+ long int arg3, long int arg4, long int arg5);
|
||||
+extern int cap_prctlw(long int pr_cmd, long int arg1, long int arg2,
|
||||
+ long int arg3, long int arg4, long int arg5);
|
||||
+
|
||||
extern int cap_setuid(uid_t uid);
|
||||
extern int cap_setgroups(gid_t gid, size_t ngroups, const gid_t groups[]);
|
||||
|
||||
diff --color -ru a/pam_cap/pam_cap.c b/pam_cap/pam_cap.c
|
||||
--- a/pam_cap/pam_cap.c 2021-12-22 12:33:20.740126781 +0100
|
||||
+++ b/pam_cap/pam_cap.c 2021-12-22 12:33:53.196733134 +0100
|
||||
@@ -21,6 +21,7 @@
|
||||
#include <string.h>
|
||||
#include <syslog.h>
|
||||
#include <sys/capability.h>
|
||||
+#include <sys/prctl.h>
|
||||
#include <sys/types.h>
|
||||
#include <linux/limits.h>
|
||||
|
||||
@@ -33,8 +34,11 @@
|
||||
|
||||
struct pam_cap_s {
|
||||
int debug;
|
||||
+ int keepcaps;
|
||||
+ int defer;
|
||||
const char *user;
|
||||
const char *conf_filename;
|
||||
+ pam_handle_t *pamh;
|
||||
};
|
||||
|
||||
/*
|
||||
@@ -178,6 +182,33 @@
|
||||
}
|
||||
|
||||
/*
|
||||
+ * This is the "defer" cleanup function that actually applies the IAB
|
||||
+ * tuple. This happens really late in the PAM session, hopefully after
|
||||
+ * the application has performed its setuid() function.
|
||||
+ */
|
||||
+static void iab_apply(pam_handle_t *pamh, void *data, int error_status)
|
||||
+{
|
||||
+ cap_iab_t iab = data;
|
||||
+ int retval = error_status & ~(PAM_DATA_REPLACE|PAM_DATA_SILENT);
|
||||
+
|
||||
+ data = NULL;
|
||||
+ if (error_status & PAM_DATA_REPLACE) {
|
||||
+ goto done;
|
||||
+ }
|
||||
+
|
||||
+ if (retval != PAM_SUCCESS || !(error_status & PAM_DATA_SILENT)) {
|
||||
+ goto done;
|
||||
+ }
|
||||
+
|
||||
+ if (cap_iab_set_proc(iab) != 0) {
|
||||
+ D(("IAB setting failed"));
|
||||
+ }
|
||||
+
|
||||
+done:
|
||||
+ cap_free(iab);
|
||||
+}
|
||||
+
|
||||
+/*
|
||||
* Set capabilities for current process to match the current
|
||||
* permitted+executable sets combined with the configured inheritable
|
||||
* set.
|
||||
@@ -230,12 +261,21 @@
|
||||
goto cleanup_conf;
|
||||
}
|
||||
|
||||
- if (!cap_iab_set_proc(iab)) {
|
||||
+ if (cs->defer) {
|
||||
+ D(("configured to delay applying IAB"));
|
||||
+ pam_set_data(cs->pamh, "pam_cap_iab", iab, iab_apply);
|
||||
+ iab = NULL;
|
||||
+ } else if (!cap_iab_set_proc(iab)) {
|
||||
D(("able to set the IAB [%s] value", conf_caps));
|
||||
ok = 1;
|
||||
}
|
||||
cap_free(iab);
|
||||
|
||||
+ if (cs->keepcaps) {
|
||||
+ D(("setting keepcaps"));
|
||||
+ (void) cap_prctlw(PR_SET_KEEPCAPS, 1, 0, 0, 0, 0);
|
||||
+ }
|
||||
+
|
||||
cleanup_conf:
|
||||
memset(conf_caps, 0, conf_caps_length);
|
||||
_pam_drop(conf_caps);
|
||||
@@ -268,6 +308,10 @@
|
||||
pcs->debug = 1;
|
||||
} else if (!strncmp(*argv, "config=", 7)) {
|
||||
pcs->conf_filename = 7 + *argv;
|
||||
+ } else if (!strcmp(*argv, "keepcaps")) {
|
||||
+ pcs->keepcaps = 1;
|
||||
+ } else if (!strcmp(*argv, "defer")) {
|
||||
+ pcs->defer = 1;
|
||||
} else {
|
||||
_pam_log(LOG_ERR, "unknown option; %s", *argv);
|
||||
}
|
||||
@@ -353,6 +397,7 @@
|
||||
return PAM_AUTH_ERR;
|
||||
}
|
||||
|
||||
+ pcs.pamh = pamh;
|
||||
retval = set_capabilities(&pcs);
|
||||
memset(&pcs, 0, sizeof(pcs));
|
||||
|
494
SOURCES/libcap-static-analysis.patch
Normal file
494
SOURCES/libcap-static-analysis.patch
Normal file
@ -0,0 +1,494 @@
|
||||
diff --color -ru a/libcap/cap_proc.c b/libcap/cap_proc.c
|
||||
--- a/libcap/cap_proc.c 2022-01-28 12:42:39.726331628 +0100
|
||||
+++ b/libcap/cap_proc.c 2022-01-28 12:44:05.007936110 +0100
|
||||
@@ -712,6 +712,10 @@
|
||||
cap_value_t c;
|
||||
int raising = 0;
|
||||
|
||||
+ if (temp == NULL) {
|
||||
+ return -1;
|
||||
+ }
|
||||
+
|
||||
for (i = 0; i < _LIBCAP_CAPABILITY_U32S; i++) {
|
||||
__u32 newI = iab->i[i];
|
||||
__u32 oldIP = temp->u[i].flat[CAP_INHERITABLE] |
|
||||
diff --color -ru a/libcap/cap_text.c b/libcap/cap_text.c
|
||||
--- a/libcap/cap_text.c 2022-01-28 12:42:39.725331609 +0100
|
||||
+++ b/libcap/cap_text.c 2022-01-28 12:44:05.008936129 +0100
|
||||
@@ -160,6 +160,7 @@
|
||||
cap_blks = _LINUX_CAPABILITY_U32S_3;
|
||||
break;
|
||||
default:
|
||||
+ cap_free(res);
|
||||
errno = EINVAL;
|
||||
return NULL;
|
||||
}
|
||||
@@ -398,6 +399,9 @@
|
||||
for (n = 0; n < cmb; n++) {
|
||||
if (getstateflags(caps, n) == t) {
|
||||
char *this_cap_name = cap_to_name(n);
|
||||
+ if (this_cap_name == NULL) {
|
||||
+ return NULL;
|
||||
+ }
|
||||
if ((strlen(this_cap_name) + (p - buf)) > CAP_TEXT_SIZE) {
|
||||
cap_free(this_cap_name);
|
||||
errno = ERANGE;
|
||||
@@ -450,6 +454,9 @@
|
||||
for (n = cmb; n < __CAP_MAXBITS; n++) {
|
||||
if (getstateflags(caps, n) == t) {
|
||||
char *this_cap_name = cap_to_name(n);
|
||||
+ if (this_cap_name == NULL) {
|
||||
+ return NULL;
|
||||
+ }
|
||||
if ((strlen(this_cap_name) + (p - buf)) > CAP_TEXT_SIZE) {
|
||||
cap_free(this_cap_name);
|
||||
errno = ERANGE;
|
||||
@@ -549,6 +556,9 @@
|
||||
cap_iab_t cap_iab_from_text(const char *text)
|
||||
{
|
||||
cap_iab_t iab = cap_iab_init();
|
||||
+ if (iab == NULL) {
|
||||
+ return iab;
|
||||
+ }
|
||||
if (text != NULL) {
|
||||
unsigned flags;
|
||||
for (flags = 0; *text; text++) {
|
||||
diff --color -ru a/libcap/_makenames.c b/libcap/_makenames.c
|
||||
--- a/libcap/_makenames.c 2022-01-28 12:42:39.725331609 +0100
|
||||
+++ b/libcap/_makenames.c 2022-01-28 13:07:28.700817691 +0100
|
||||
@@ -45,10 +45,14 @@
|
||||
if (maxcaps <= list[i].index) {
|
||||
maxcaps = list[i].index + 1;
|
||||
}
|
||||
- if (list[i].index >= pointers_avail) {
|
||||
+ if (pointers == NULL || list[i].index >= pointers_avail) {
|
||||
int was = pointers_avail * sizeof(char *);
|
||||
pointers_avail = 2 * list[i].index + 1;
|
||||
pointers = recalloc(pointers, was, pointers_avail * sizeof(char *));
|
||||
+ if (pointers == NULL) {
|
||||
+ perror("unable to continue");
|
||||
+ exit(1);
|
||||
+ }
|
||||
}
|
||||
pointers[list[i].index] = list[i].name;
|
||||
int n = strlen(list[i].name);
|
||||
diff --color -ru a/pam_cap/pam_cap.c b/pam_cap/pam_cap.c
|
||||
--- a/pam_cap/pam_cap.c 2022-01-28 12:42:39.726331628 +0100
|
||||
+++ b/pam_cap/pam_cap.c 2022-01-28 12:44:05.009936148 +0100
|
||||
@@ -64,6 +64,9 @@
|
||||
}
|
||||
|
||||
*groups = calloc(ngrps, sizeof(char *));
|
||||
+ if (*groups == NULL) {
|
||||
+ return -1;
|
||||
+ }
|
||||
int g_n = 0, i;
|
||||
for (i = 0; i < ngrps; i++) {
|
||||
const struct group *g = getgrgid(grps[i]);
|
||||
@@ -249,7 +252,7 @@
|
||||
if (!cap_set_proc(cap_s)) {
|
||||
ok = 1;
|
||||
}
|
||||
- goto cleanup_cap_s;
|
||||
+ goto cleanup_conf;
|
||||
}
|
||||
|
||||
iab = cap_iab_from_text(conf_caps);
|
||||
@@ -278,10 +281,9 @@
|
||||
_pam_drop(conf_caps);
|
||||
|
||||
cleanup_cap_s:
|
||||
- if (cap_s) {
|
||||
- cap_free(cap_s);
|
||||
- cap_s = NULL;
|
||||
- }
|
||||
+ cap_free(cap_s);
|
||||
+ cap_s = NULL;
|
||||
+
|
||||
return ok;
|
||||
}
|
||||
|
||||
diff --color -ru a/progs/capsh.c b/progs/capsh.c
|
||||
--- a/progs/capsh.c 2021-02-05 06:52:17.000000000 +0100
|
||||
+++ b/progs/capsh.c 2022-01-28 13:06:15.803465885 +0100
|
||||
@@ -34,6 +34,35 @@
|
||||
|
||||
#define MAX_GROUPS 100 /* max number of supplementary groups for user */
|
||||
|
||||
+/* parse a non-negative integer with some error handling */
|
||||
+static unsigned long nonneg_uint(const char *text, const char *prefix, int *ok)
|
||||
+{
|
||||
+ char *remains;
|
||||
+ unsigned long value;
|
||||
+ ssize_t len = strlen(text);
|
||||
+
|
||||
+ if (len == 0 || *text == '-') {
|
||||
+ goto fail;
|
||||
+ }
|
||||
+ value = strtoul(text, &remains, 0);
|
||||
+ if (*remains) {
|
||||
+ goto fail;
|
||||
+ }
|
||||
+ if (ok != NULL) {
|
||||
+ *ok = 1;
|
||||
+ }
|
||||
+ return value;
|
||||
+
|
||||
+fail:
|
||||
+ if (ok == NULL) {
|
||||
+ fprintf(stderr, "%s: want non-negative integer, got \"%s\"\n",
|
||||
+ prefix, text);
|
||||
+ exit(1);
|
||||
+ }
|
||||
+ *ok = 0;
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
static char *binary(unsigned long value)
|
||||
{
|
||||
static char string[8*sizeof(unsigned long) + 1];
|
||||
@@ -100,7 +129,16 @@
|
||||
display_prctl_set("Bounding", cap_get_bound);
|
||||
display_prctl_set("Ambient", cap_get_ambient);
|
||||
iab = cap_iab_get_proc();
|
||||
+ if (iab == NULL) {
|
||||
+ perror("failed to get IAB for process");
|
||||
+ exit(1);
|
||||
+ }
|
||||
text = cap_iab_to_text(iab);
|
||||
+ if (text == NULL) {
|
||||
+ perror("failed to obtain text for IAB");
|
||||
+ cap_free(iab);
|
||||
+ exit(1);
|
||||
+ }
|
||||
printf("Current IAB: %s\n", text);
|
||||
cap_free(text);
|
||||
cap_free(iab);
|
||||
@@ -336,8 +374,8 @@
|
||||
*/
|
||||
static char *find_self(const char *arg0)
|
||||
{
|
||||
- int i;
|
||||
- char *parts, *dir, *scratch;
|
||||
+ int i, status=1;
|
||||
+ char *p = NULL, *parts, *dir, *scratch;
|
||||
const char *path;
|
||||
|
||||
for (i = strlen(arg0)-1; i >= 0 && arg0[i] != '/'; i--);
|
||||
@@ -352,21 +390,45 @@
|
||||
}
|
||||
|
||||
parts = strdup(path);
|
||||
+ if (parts == NULL) {
|
||||
+ fprintf(stderr, "insufficient memory for parts of path\n");
|
||||
+ exit(1);
|
||||
+ }
|
||||
+
|
||||
scratch = malloc(2+strlen(path)+strlen(arg0));
|
||||
- if (parts == NULL || scratch == NULL) {
|
||||
+ if (scratch == NULL) {
|
||||
fprintf(stderr, "insufficient memory for path building\n");
|
||||
- exit(1);
|
||||
+ goto free_parts;
|
||||
}
|
||||
|
||||
- for (i=0; (dir = strtok(parts, ":")); parts = NULL) {
|
||||
+ for (p = parts; (dir = strtok(p, ":")); p = NULL) {
|
||||
sprintf(scratch, "%s/%s", dir, arg0);
|
||||
if (access(scratch, X_OK) == 0) {
|
||||
- return scratch;
|
||||
+ status = 0;
|
||||
+ break;
|
||||
}
|
||||
}
|
||||
+ if (status) {
|
||||
+ fprintf(stderr, "unable to find executable '%s' in PATH\n", arg0);
|
||||
+ free(scratch);
|
||||
+ }
|
||||
|
||||
- fprintf(stderr, "unable to find executable '%s' in PATH\n", arg0);
|
||||
- exit(1);
|
||||
+free_parts:
|
||||
+ free(parts);
|
||||
+ if (status) {
|
||||
+ exit(status);
|
||||
+ }
|
||||
+ return scratch;
|
||||
+}
|
||||
+
|
||||
+static long safe_sysconf(int name)
|
||||
+{
|
||||
+ long ans = sysconf(name);
|
||||
+ if (ans <= 0) {
|
||||
+ fprintf(stderr, "sysconf(%d) returned a non-positive number: %ld\n", name, ans);
|
||||
+ exit(1);
|
||||
+ }
|
||||
+ return ans;
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[], char *envp[])
|
||||
@@ -378,6 +440,10 @@
|
||||
child = 0;
|
||||
|
||||
char *temp_name = cap_to_name(cap_max_bits() - 1);
|
||||
+ if (temp_name == NULL) {
|
||||
+ perror("obtaining highest capability name");
|
||||
+ exit(1);
|
||||
+ }
|
||||
if (temp_name[0] != 'c') {
|
||||
printf("WARNING: libcap needs an update (cap=%d should have a name).\n",
|
||||
cap_max_bits() - 1);
|
||||
@@ -573,7 +639,7 @@
|
||||
unsigned value;
|
||||
int set;
|
||||
|
||||
- value = strtoul(argv[i]+7, NULL, 0);
|
||||
+ value = nonneg_uint(argv[i]+7, "invalid --keep value", NULL);
|
||||
set = prctl(PR_SET_KEEPCAPS, value);
|
||||
if (set < 0) {
|
||||
fprintf(stderr, "prctl(PR_SET_KEEPCAPS, %u) failed: %s\n",
|
||||
@@ -617,7 +683,9 @@
|
||||
* Given we are now in a new directory tree, its good practice
|
||||
* to start off in a sane location
|
||||
*/
|
||||
- status = chdir("/");
|
||||
+ if (status == 0) {
|
||||
+ status = chdir("/");
|
||||
+ }
|
||||
|
||||
cap_free(orig);
|
||||
|
||||
@@ -628,7 +696,7 @@
|
||||
} else if (!strncmp("--secbits=", argv[i], 10)) {
|
||||
unsigned value;
|
||||
int status;
|
||||
- value = strtoul(argv[i]+10, NULL, 0);
|
||||
+ value = nonneg_uint(argv[i]+10, "invalid --secbits value", NULL);
|
||||
status = cap_set_secbits(value);
|
||||
if (status < 0) {
|
||||
fprintf(stderr, "failed to set securebits to 0%o/0x%x\n",
|
||||
@@ -641,7 +709,7 @@
|
||||
fprintf(stderr, "already forked\n");
|
||||
exit(1);
|
||||
}
|
||||
- value = strtoul(argv[i]+10, NULL, 0);
|
||||
+ value = nonneg_uint(argv[i]+10, "invalid --forkfor value", NULL);
|
||||
if (value == 0) {
|
||||
goto usage;
|
||||
}
|
||||
@@ -657,7 +725,8 @@
|
||||
pid_t result;
|
||||
unsigned value;
|
||||
|
||||
- value = strtoul(argv[i]+9, NULL, 0);
|
||||
+ value = nonneg_uint(argv[i]+9, "invalid --killit signo value",
|
||||
+ NULL);
|
||||
if (!child) {
|
||||
fprintf(stderr, "no forked process to kill\n");
|
||||
exit(1);
|
||||
@@ -683,7 +752,7 @@
|
||||
unsigned value;
|
||||
int status;
|
||||
|
||||
- value = strtoul(argv[i]+6, NULL, 0);
|
||||
+ value = nonneg_uint(argv[i]+6, "invalid --uid value", NULL);
|
||||
status = setuid(value);
|
||||
if (status < 0) {
|
||||
fprintf(stderr, "Failed to set uid=%u: %s\n",
|
||||
@@ -694,7 +763,7 @@
|
||||
unsigned value;
|
||||
int status;
|
||||
|
||||
- value = strtoul(argv[i]+10, NULL, 0);
|
||||
+ value = nonneg_uint(argv[i]+10, "invalid --cap-uid value", NULL);
|
||||
status = cap_setuid(value);
|
||||
if (status < 0) {
|
||||
fprintf(stderr, "Failed to cap_setuid(%u): %s\n",
|
||||
@@ -705,7 +774,7 @@
|
||||
unsigned value;
|
||||
int status;
|
||||
|
||||
- value = strtoul(argv[i]+6, NULL, 0);
|
||||
+ value = nonneg_uint(argv[i]+6, "invalid --gid value", NULL);
|
||||
status = setgid(value);
|
||||
if (status < 0) {
|
||||
fprintf(stderr, "Failed to set gid=%u: %s\n",
|
||||
@@ -718,14 +787,14 @@
|
||||
gid_t *group_list;
|
||||
int g_count;
|
||||
|
||||
- length = sysconf(_SC_GETGR_R_SIZE_MAX);
|
||||
+ length = safe_sysconf(_SC_GETGR_R_SIZE_MAX);
|
||||
buf = calloc(1, length);
|
||||
if (NULL == buf) {
|
||||
fprintf(stderr, "No memory for [%s] operation\n", argv[i]);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
- max_groups = sysconf(_SC_NGROUPS_MAX);
|
||||
+ max_groups = safe_sysconf(_SC_NGROUPS_MAX);
|
||||
group_list = calloc(max_groups, sizeof(gid_t));
|
||||
if (NULL == group_list) {
|
||||
fprintf(stderr, "No memory for gid list\n");
|
||||
@@ -741,8 +810,7 @@
|
||||
}
|
||||
if (!isdigit(*ptr)) {
|
||||
struct group *g, grp;
|
||||
- getgrnam_r(ptr, &grp, buf, length, &g);
|
||||
- if (NULL == g) {
|
||||
+ if (getgrnam_r(ptr, &grp, buf, length, &g) || NULL == g) {
|
||||
fprintf(stderr, "Failed to identify gid for group [%s]\n", ptr);
|
||||
exit(1);
|
||||
}
|
||||
@@ -835,6 +903,7 @@
|
||||
argv[argc] = NULL;
|
||||
execve(argv[i], argv+i, envp);
|
||||
fprintf(stderr, "execve '%s' failed!\n", argv[i]);
|
||||
+ free(argv[i]);
|
||||
exit(1);
|
||||
} else if (!strncmp("--shell=", argv[i], 8)) {
|
||||
shell = argv[i]+8;
|
||||
@@ -885,7 +954,7 @@
|
||||
} else if (!strncmp("--is-uid=", argv[i], 9)) {
|
||||
unsigned value;
|
||||
uid_t uid;
|
||||
- value = strtoul(argv[i]+9, NULL, 0);
|
||||
+ value = nonneg_uint(argv[i]+9, "invalid --is-uid value", NULL);
|
||||
uid = getuid();
|
||||
if (uid != value) {
|
||||
fprintf(stderr, "uid: got=%d, want=%d\n", uid, value);
|
||||
@@ -894,7 +963,7 @@
|
||||
} else if (!strncmp("--is-gid=", argv[i], 9)) {
|
||||
unsigned value;
|
||||
gid_t gid;
|
||||
- value = strtoul(argv[i]+9, NULL, 0);
|
||||
+ value = nonneg_uint(argv[i]+9, "invalid --is-gid value", NULL);
|
||||
gid = getgid();
|
||||
if (gid != value) {
|
||||
fprintf(stderr, "gid: got=%d, want=%d\n", gid, value);
|
||||
diff --color -ru a/progs/getcap.c b/progs/getcap.c
|
||||
--- a/progs/getcap.c 2021-02-05 06:52:17.000000000 +0100
|
||||
+++ b/progs/getcap.c 2022-01-28 12:44:05.009936148 +0100
|
||||
@@ -110,11 +110,11 @@
|
||||
|
||||
for (i=optind; argv[i] != NULL; i++) {
|
||||
struct stat stbuf;
|
||||
-
|
||||
- if (lstat(argv[i], &stbuf) != 0) {
|
||||
- fprintf(stderr, "%s (%s)\n", argv[i], strerror(errno));
|
||||
+ char *arg = argv[i];
|
||||
+ if (lstat(arg, &stbuf) != 0) {
|
||||
+ fprintf(stderr, "%s (%s)\n", arg, strerror(errno));
|
||||
} else if (recursive) {
|
||||
- nftw(argv[i], do_getcap, 20, FTW_PHYS);
|
||||
+ nftw(arg, do_getcap, 20, FTW_PHYS);
|
||||
} else {
|
||||
int tflag = S_ISREG(stbuf.st_mode) ? FTW_F :
|
||||
(S_ISLNK(stbuf.st_mode) ? FTW_SL : FTW_NS);
|
||||
diff --color -ru a/progs/setcap.c b/progs/setcap.c
|
||||
--- a/progs/setcap.c 2021-02-05 06:52:17.000000000 +0100
|
||||
+++ b/progs/setcap.c 2022-01-28 12:44:05.009936148 +0100
|
||||
@@ -166,9 +166,12 @@
|
||||
}
|
||||
|
||||
cap_on_file = cap_get_file(*++argv);
|
||||
-
|
||||
if (cap_on_file == NULL) {
|
||||
cap_on_file = cap_from_text("=");
|
||||
+ if (cap_on_file == NULL) {
|
||||
+ perror("unable to use missing capability");
|
||||
+ exit(1);
|
||||
+ }
|
||||
}
|
||||
|
||||
cmp = cap_compare(cap_on_file, cap_d);
|
||||
diff --color -ru a/psx/psx.c b/psx/psx.c
|
||||
--- a/psx/psx.c 2021-02-05 06:52:17.000000000 +0100
|
||||
+++ b/psx/psx.c 2022-01-28 12:44:05.009936148 +0100
|
||||
@@ -107,6 +107,10 @@
|
||||
*/
|
||||
static void *psx_do_registration(void) {
|
||||
registered_thread_t *node = calloc(1, sizeof(registered_thread_t));
|
||||
+ if (node == NULL) {
|
||||
+ perror("unable to register psx handler");
|
||||
+ exit(1);
|
||||
+ }
|
||||
pthread_mutex_init(&node->mu, NULL);
|
||||
node->thread = pthread_self();
|
||||
pthread_setspecific(psx_action_key, node);
|
||||
@@ -454,6 +458,10 @@
|
||||
int __wrap_pthread_create(pthread_t *thread, const pthread_attr_t *attr,
|
||||
void *(*start_routine) (void *), void *arg) {
|
||||
psx_starter_t *starter = calloc(1, sizeof(psx_starter_t));
|
||||
+ if (starter == NULL) {
|
||||
+ perror("failed at thread creation");
|
||||
+ exit(1);
|
||||
+ }
|
||||
starter->fn = start_routine;
|
||||
starter->arg = arg;
|
||||
/*
|
||||
diff --color -ru a/tests/libcap_launch_test.c b/tests/libcap_launch_test.c
|
||||
--- a/tests/libcap_launch_test.c 2021-02-05 06:52:17.000000000 +0100
|
||||
+++ b/tests/libcap_launch_test.c 2022-01-28 12:44:05.010936167 +0100
|
||||
@@ -93,6 +93,10 @@
|
||||
printf("[%d] test should %s\n", i,
|
||||
v->result ? "generate error" : "work");
|
||||
cap_launch_t attr = cap_new_launcher(v->args[0], v->args, v->envp);
|
||||
+ if (attr == NULL) {
|
||||
+ perror("failed to obtain launcher");
|
||||
+ exit(1);
|
||||
+ }
|
||||
if (v->chroot) {
|
||||
cap_launcher_set_chroot(attr, v->chroot);
|
||||
}
|
||||
diff --color -ru a/tests/libcap_psx_test.c b/tests/libcap_psx_test.c
|
||||
--- a/tests/libcap_psx_test.c 2021-02-05 06:52:17.000000000 +0100
|
||||
+++ b/tests/libcap_psx_test.c 2022-01-28 12:55:55.887807887 +0100
|
||||
@@ -16,8 +16,15 @@
|
||||
usleep(1234);
|
||||
pid_t pid = fork();
|
||||
cap_t start = cap_get_proc();
|
||||
+ if (start == NULL) {
|
||||
+ perror("FAILED: unable to start");
|
||||
+ exit(1);
|
||||
+ }
|
||||
if (pid == 0) {
|
||||
- cap_set_proc(start);
|
||||
+ if (cap_set_proc(start)) {
|
||||
+ perror("setting empty caps failed");
|
||||
+ exit(1);
|
||||
+ }
|
||||
exit(0);
|
||||
}
|
||||
int res;
|
||||
@@ -27,6 +34,7 @@
|
||||
exit(1);
|
||||
}
|
||||
cap_set_proc(start);
|
||||
+ cap_free(start);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@@ -35,6 +43,10 @@
|
||||
printf("hello libcap and libpsx ");
|
||||
fflush(stdout);
|
||||
cap_t start = cap_get_proc();
|
||||
+ if (start == NULL) {
|
||||
+ perror("FAILED: to actually start");
|
||||
+ exit(1);
|
||||
+ }
|
||||
pthread_t ignored[10];
|
||||
for (i = 0; i < 10; i++) {
|
||||
pthread_create(&ignored[i], NULL, thread_fork_exit, NULL);
|
||||
@@ -42,7 +54,10 @@
|
||||
for (i = 0; i < 10; i++) {
|
||||
printf("."); /* because of fork, this may print double */
|
||||
fflush(stdout); /* try to limit the above effect */
|
||||
- cap_set_proc(start);
|
||||
+ if (cap_set_proc(start)) {
|
||||
+ perror("failed to set proc");
|
||||
+ exit(1);
|
||||
+ }
|
||||
usleep(1000);
|
||||
}
|
||||
printf(" PASSED\n");
|
@ -1,20 +1,19 @@
|
||||
Name: libcap
|
||||
Version: 2.26
|
||||
Release: 5%{?dist}
|
||||
Version: 2.48
|
||||
Release: 2%{?dist}
|
||||
Summary: Library for getting and setting POSIX.1e capabilities
|
||||
URL: https://sites.google.com/site/fullycapable/
|
||||
License: GPLv2
|
||||
License: BSD or GPLv2
|
||||
Group: System Environment/Libraries
|
||||
|
||||
Source: https://git.kernel.org/pub/scm/libs/libcap/libcap.git/snapshot/%{name}-%{version}.tar.gz
|
||||
# http://manned.org/getpcaps/299a4949/src:
|
||||
Source1: getpcaps.8
|
||||
Patch0: %{name}-2.25-buildflags.patch
|
||||
Patch1: %{name}-PAM_REINITIALIZE_CRED.patch
|
||||
Patch2: %{name}-2.26-ambient-caps.patch
|
||||
Patch3: %{name}-add-new-caps.patch
|
||||
Patch0: %{name}-2.48-buildflags.patch
|
||||
Patch1: %{name}-abi-compatibility.patch
|
||||
Patch2: %{name}-static-analysis.patch
|
||||
Patch3: %{name}-fix-ambient-caps.patch
|
||||
|
||||
BuildRequires: libattr-devel pam-devel perl-interpreter
|
||||
BuildRequires: make
|
||||
|
||||
%description
|
||||
libcap is a library for getting and setting POSIX.1e (formerly POSIX 6)
|
||||
@ -47,11 +46,7 @@ Install libcap-devel if you want to develop or compile applications using
|
||||
libcap.
|
||||
|
||||
%prep
|
||||
%setup -q
|
||||
%patch0 -p1
|
||||
%patch1 -p1
|
||||
%patch2 -p1
|
||||
%patch3 -p1
|
||||
%autosetup -p1
|
||||
|
||||
%build
|
||||
# libcap can not be build with _smp_mflags:
|
||||
@ -67,7 +62,6 @@ make install RAISE_SETFCAP=no \
|
||||
|
||||
mkdir -p %{buildroot}/%{_mandir}/man{2,3,8}
|
||||
mv -f doc/*.3 %{buildroot}/%{_mandir}/man3/
|
||||
cp -f %{SOURCE1} %{buildroot}/%{_mandir}/man8/
|
||||
|
||||
chmod +x %{buildroot}/%{_libdir}/*.so.*
|
||||
|
||||
@ -85,14 +79,22 @@ chmod +x %{buildroot}/%{_libdir}/*.so.*
|
||||
|
||||
%files static
|
||||
%{_libdir}/libcap.a
|
||||
%{_libdir}/libpsx.a
|
||||
|
||||
%files devel
|
||||
%{_includedir}/*
|
||||
%{_libdir}/*.so
|
||||
%{_mandir}/man3/*
|
||||
%{_libdir}/pkgconfig/libcap.pc
|
||||
%{_libdir}/pkgconfig/libpsx.pc
|
||||
|
||||
%changelog
|
||||
* Fri Jan 28 2022 Zoltan Fridrich <zfridric@redhat.com> - 2.48-2
|
||||
- rebase to 2.48
|
||||
resolves: rhbz#2032813
|
||||
- fix ambient capabilities for non-root users
|
||||
resolves: rhbz#1950187
|
||||
|
||||
* Thu Jun 10 2021 Zoltan Fridrich <zfridric@redhat.com> - 2.26-5
|
||||
- added CAP_PERFMON, CAP_BPF and CAP_CHECKPOINT_RESTORE capabilities
|
||||
resolves: rhbz#1946982 rhbz#1921576
|
||||
|
Loading…
Reference in New Issue
Block a user