import libcap-2.26-4.el8
This commit is contained in:
parent
90cf38fe94
commit
5258b1382c
952
SOURCES/libcap-2.26-ambient-caps.patch
Normal file
952
SOURCES/libcap-2.26-ambient-caps.patch
Normal file
@ -0,0 +1,952 @@
|
||||
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
|
||||
|
@ -1,6 +1,6 @@
|
||||
Name: libcap
|
||||
Version: 2.26
|
||||
Release: 3%{?dist}
|
||||
Release: 4%{?dist}
|
||||
Summary: Library for getting and setting POSIX.1e capabilities
|
||||
URL: https://sites.google.com/site/fullycapable/
|
||||
License: GPLv2
|
||||
@ -11,6 +11,7 @@ Source: https://git.kernel.org/pub/scm/libs/libcap/libcap.git/snapshot/%{name}-%
|
||||
Source1: getpcaps.8
|
||||
Patch0: %{name}-2.25-buildflags.patch
|
||||
Patch1: %{name}-PAM_REINITIALIZE_CRED.patch
|
||||
Patch2: %{name}-2.26-ambient-caps.patch
|
||||
|
||||
BuildRequires: libattr-devel pam-devel perl-interpreter
|
||||
|
||||
@ -48,6 +49,7 @@ libcap.
|
||||
%setup -q
|
||||
%patch0 -p1
|
||||
%patch1 -p1
|
||||
%patch2 -p1
|
||||
|
||||
%build
|
||||
# libcap can not be build with _smp_mflags:
|
||||
@ -89,6 +91,10 @@ chmod +x %{buildroot}/%{_libdir}/*.so.*
|
||||
%{_libdir}/pkgconfig/libcap.pc
|
||||
|
||||
%changelog
|
||||
* Fri May 22 2020 Jiri Vymazal <jvymazal@redhat.com> - 2.26-4
|
||||
- added patch implementing support for ambient capabilities
|
||||
resolves: rhbz#1487388
|
||||
|
||||
* Tue Oct 15 2019 Marek Tamaskovic <mtamasko@redhat.com> - 2.26-3
|
||||
- changed url
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user