diff --git a/SOURCES/libcap-2.26-ambient-caps.patch b/SOURCES/libcap-2.26-ambient-caps.patch
new file mode 100644
index 0000000..b8eeedd
--- /dev/null
+++ b/SOURCES/libcap-2.26-ambient-caps.patch
@@ -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
+
diff --git a/SPECS/libcap.spec b/SPECS/libcap.spec
index 648f4f8..1b0361f 100644
--- a/SPECS/libcap.spec
+++ b/SPECS/libcap.spec
@@ -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