summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndrew G. Morgan <morgan@kernel.org>2021-05-04 20:28:37 -0700
committerAndrew G. Morgan <morgan@kernel.org>2021-05-07 20:06:36 -0700
commitfe4c27de243b13973acff3cda2c8c8ff4a768855 (patch)
tree0f117b5def631a8df86eb66d2d797c579159ae62
parentb08b523364b133d7e158968892eba48a18827142 (diff)
downloadlibcap2-fe4c27de243b13973acff3cda2c8c8ff4a768855.tar.gz
Add a module argument to pam_cap.so to assist with ambient support
Some PAM applications drop privilege when they change UID, which has the side effect of dropping ambient capabilities. We add support for the "keepcaps" argument which can be used in an attempt by the module to not drop permitted capabilities when performing a setuid() call. Some experimentation may be needed to see if this works for any given application. To not be a security bug vector, it requires the application so configured perform an exec() to launch a user-specific operation. This is an attempt to provide some Adminstrator support for working around the issue observed in this bug (report by Zoltan Fridrich): https://bugzilla.kernel.org/show_bug.cgi?id=212945 Signed-off-by: Andrew G. Morgan <morgan@kernel.org>
-rw-r--r--doc/cap_get_proc.317
-rw-r--r--libcap/cap_proc.c23
-rw-r--r--libcap/include/sys/capability.h4
-rw-r--r--pam_cap/capability.conf32
-rw-r--r--pam_cap/pam_cap.c21
5 files changed, 80 insertions, 17 deletions
diff --git a/doc/cap_get_proc.3 b/doc/cap_get_proc.3
index 05a6e7d..496c06e 100644
--- a/doc/cap_get_proc.3
+++ b/doc/cap_get_proc.3
@@ -3,7 +3,7 @@
cap_get_proc, cap_set_proc, capgetp, cap_get_bound, cap_drop_bound, \
cap_get_ambient, cap_set_ambient, cap_reset_ambient, \
cap_get_secbits, cap_set_secbits, cap_get_mode, cap_set_mode, \
-cap_mode_name, cap_get_pid, cap_setuid, cap_setgroups \
+cap_mode_name, cap_get_pid, cap_setuid, cap_prctl, cap_prctlw, cap_setgroups \
\- capability manipulation on processes
.SH SYNOPSIS
.nf
@@ -25,6 +25,10 @@ unsigned cap_get_secbits(void);
int cap_set_secbits(unsigned bits);
cap_mode_t cap_get_mode(void);
const char *cap_mode_name(cap_mode_t mode);
+int cap_prctl(long int pr_cmd, long int arg1, long int arg2,
+ long int arg3, long int arg4, long int arg5);
+int cap_prctlw(long int pr_cmd, long int arg1, long int arg2,
+ long int arg3, long int arg4, long int arg5);
int cap_set_mode(cap_mode_t mode);
#include <sys/types.h>
@@ -161,6 +165,12 @@ identifies as
Supported modes are:
.BR CAP_MODE_NOPRIV ", " CAP_MODE_PURE1E_INIT " and " CAP_MODE_PURE1E .
.PP
+.BR cap_prctl ()
+can be used to read state via the \fBprctl\fI()\fP system call.
+.PP
+.BR cap_prctlw ()
+can be used to write state via the \fBprctl\fI()\fP system call.
+.PP
.BR cap_set_mode ()
can be used to set the desired mode. The permitted capability
.B CAP_SETPCAP
@@ -239,7 +249,10 @@ or,
.sp
When linked this way, due to linker magic, libcap uses
.BR psx_syscall "(3) and " psx_syscall6 (3)
-to perform state setting system calls.
+to perform state setting system calls. Notably, this also ensures that
+.BI cap_prctlw ()
+can be used to ensure process control bits are shared over all threads
+of a single process.
.SS capgetp() and capsetp()
The library also supports the deprecated functions:
.PP
diff --git a/libcap/cap_proc.c b/libcap/cap_proc.c
index 5173730..c0f66a9 100644
--- a/libcap/cap_proc.c
+++ b/libcap/cap_proc.c
@@ -406,6 +406,29 @@ static void _cap_set_no_new_privs(struct syscaller_s *sc)
}
/*
+ * 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 --git a/libcap/include/sys/capability.h b/libcap/include/sys/capability.h
index 5f43127..300fd3c 100644
--- a/libcap/include/sys/capability.h
+++ b/libcap/include/sys/capability.h
@@ -175,6 +175,10 @@ extern const char *cap_mode_name(cap_mode_t flavor);
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 --git a/pam_cap/capability.conf b/pam_cap/capability.conf
index 09517f8..7c22bf6 100644
--- a/pam_cap/capability.conf
+++ b/pam_cap/capability.conf
@@ -6,14 +6,20 @@
#
# In order to use this module, it must have been linked with libcap
# and thus you'll know about Linux's capability support.
-# [If you don't know about libcap, the sources for it are here:
+# [If you don't know about libcap, read more about it here:
#
-# http://www.kernel.org/pub/linux/libs/security/linux-privs/
+# https://sites.google.com/site/fullycapable/
#
# .]
#
# Here are some sample lines (remove the preceding '#' if you want to
-# use them
+# use them.
+#
+# The pam_cap.so module accepts the following arguments:
+#
+# debug - be more verbose logging things (unused by pam_cap for now)
+# config=<file> - override the default config for the module with file
+# keepcaps - workaround for applications that setuid without this
## user 'morgan' gets the CAP_SETFCAP inheritable capability (commented out!)
#cap_setfcap morgan
@@ -25,19 +31,21 @@
none *
## if there is no '*' entry, all users not explicitly mentioned will
-## get all available capabilities. This is a permissive default, and
-## possibly not what you want... On first reading, you might think this
-## is a security problem waiting to happen, but it defaults to not being
-## so in this sample file! Further, by 'get', we mean 'get in their inheritable
-## set'. That is, if you look at a random process, even one run by root,
-## you will see it has no inheritable capabilities (by default):
+## get all available inheritable capabilities. This is a permissive
+## default, and possibly not what you want... On first reading, you
+## might think this is a security problem waiting to happen, but it
+## defaults to not being so in this sample file! Further, by 'get', we
+## mean 'get in their IAB sets'. That is, if you look at a random
+## process, even one run by root, you will see it has no IAB
+## capabilities (by default):
##
## $ /sbin/capsh --decode=$(grep CapInh /proc/1/status|awk '{print $2}')
## 0000000000000000=
##
-## The pam_cap module simply alters the value of this capability
-## set. Including the 'none *' forces use of this module with an
-## unspecified user to have their inheritable set forced to zero.
+## The pam_cap module simply alters the value of the inheritable
+## capability vactors (IAB). Including the 'none *' forces use of this
+## module with an unspecified user to have their inheritable set
+## forced to zero.
##
## Omitting the line will cause the inheritable set to be unmodified
## from what the parent process had (which is generally 0 unless the
diff --git a/pam_cap/pam_cap.c b/pam_cap/pam_cap.c
index 5b48b06..9ddd069 100644
--- a/pam_cap/pam_cap.c
+++ b/pam_cap/pam_cap.c
@@ -1,11 +1,11 @@
/*
- * Copyright (c) 1999,2007,19,20 Andrew G. Morgan <morgan@kernel.org>
+ * Copyright (c) 1999,2007,2019-21 Andrew G. Morgan <morgan@kernel.org>
*
* The purpose of this module is to enforce inheritable, bounding and
* ambient capability sets for a specified user.
*/
-/* #define DEBUG */
+/* #define PAM_DEBUG */
#ifndef _DEFAULT_SOURCE
#define _DEFAULT_SOURCE
@@ -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,6 +34,7 @@
struct pam_cap_s {
int debug;
+ int keepcaps;
const char *user;
const char *conf_filename;
};
@@ -233,6 +235,16 @@ static int set_capabilities(struct pam_cap_s *cs)
}
cap_free(iab);
+ if (cs->keepcaps) {
+ /*
+ * Best effort to set keep caps - this may help work around
+ * situations where applications are using a capabilities
+ * unaware setuid() call.
+ */
+ 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);
@@ -259,12 +271,15 @@ static void _pam_log(int err, const char *format, ...)
static void parse_args(int argc, const char **argv, struct pam_cap_s *pcs)
{
+ D(("parsing %d module arg(s)", argc));
/* step through arguments */
for (; argc-- > 0; ++argv) {
if (!strcmp(*argv, "debug")) {
pcs->debug = 1;
} else if (!strncmp(*argv, "config=", 7)) {
pcs->conf_filename = 7 + *argv;
+ } else if (!strcmp(*argv, "keepcaps")) {
+ pcs->keepcaps = 1;
} else {
_pam_log(LOG_ERR, "unknown option; %s", *argv);
}
@@ -353,5 +368,5 @@ int pam_sm_setcred(pam_handle_t *pamh, int flags,
retval = set_capabilities(&pcs);
memset(&pcs, 0, sizeof(pcs));
- return (retval ? PAM_SUCCESS:PAM_IGNORE );
+ return (retval ? PAM_SUCCESS:PAM_IGNORE);
}