import libcap-2.48-2.el8

This commit is contained in:
CentOS Sources 2022-05-10 03:19:42 -04:00 committed by Stepan Oksanichenko
parent 681b14fcc8
commit b45a5f117e
12 changed files with 731 additions and 1122 deletions

2
.gitignore vendored
View File

@ -1 +1 @@
SOURCES/libcap-2.26.tar.gz
SOURCES/libcap-2.48.tar.gz

View File

@ -1 +1 @@
e667d815755f3f6a5819eb383827dd358372dda1 SOURCES/libcap-2.26.tar.gz
c81102815c481257e53168e83b8849bc9f154d54 SOURCES/libcap-2.48.tar.gz

View File

@ -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).

View File

@ -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

View File

@ -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

View 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)

View File

@ -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;
}

View 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");

View File

@ -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)"

View 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));

View 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");

View File

@ -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