summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndrew G. Morgan <morgan@kernel.org>2021-09-13 21:08:42 -0700
committerAndrew G. Morgan <morgan@kernel.org>2021-09-13 21:08:42 -0700
commitfc6253b9de68dafae1927b2bcbfcef9e9ec6e05a (patch)
tree4ff7116780d9bc5ab717f4c4a539e97a5380af17
parenta3446b5c6e0879b289287c9a87a57cbdc95e99da (diff)
downloadlibcap2-fc6253b9de68dafae1927b2bcbfcef9e9ec6e05a.tar.gz
Add PAM "session" support to pam_cap.so.
This is an attempt to address: https://bugzilla.kernel.org/show_bug.cgi?id=214377 The basic structure is you configure PAM with a config like this: #%PAM-1.0 auth required pam_cap.so use_session keepcaps auth required pam_unix.so account required pam_unix.so password required pam_unix.so session required pam_unix.so session optional pam_cap.so Here the "auth" part prepares the application with "keepcaps", and the "use_session" instructs the module to apply any IAB tuple for the user at session open time and not during the setcred (auth) flow. This has been tested against the contrib/sucap implementation of su. The "use_session" support should work with more standard PAM enabled apps too, but I'll wait for some positive feedback (see the bug) before declaring it stable. FWIW the contrib/sucap/su app also supports this config for Ambient vector setting (without a "session" invocation of pam_cap.so): #%PAM-1.0 auth required pam_cap.so auth required pam_unix.so account required pam_unix.so password required pam_unix.so session required pam_unix.so but that is because the sucap/su app is more tightly integrated with libcap than the standard PAM apps. Signed-off-by: Andrew G. Morgan <morgan@kernel.org>
-rw-r--r--pam_cap/execable.c3
-rw-r--r--pam_cap/pam_cap.c77
-rw-r--r--pam_cap/test_pam_cap.c18
3 files changed, 79 insertions, 19 deletions
diff --git a/pam_cap/execable.c b/pam_cap/execable.c
index 05f18d8..8e5028d 100644
--- a/pam_cap/execable.c
+++ b/pam_cap/execable.c
@@ -48,6 +48,7 @@ SO_MAIN(int argc, char **argv)
"config=<file> - override the default config with file\n"
"keepcaps - workaround for apps that setuid without this\n"
"autoauth - pam_cap.so to always succeed for the 'auth' phase\n"
- "default=<iab> - fallback IAB value if there is no '*' rule\n",
+ "default=<iab> - fallback IAB value if there is no '*' rule\n"
+ "use_session - apply IAB value during session open, not setcred\n",
cmd);
}
diff --git a/pam_cap/pam_cap.c b/pam_cap/pam_cap.c
index 17ad83e..80eb40b 100644
--- a/pam_cap/pam_cap.c
+++ b/pam_cap/pam_cap.c
@@ -39,12 +39,17 @@ struct pam_cap_s {
int debug;
int keepcaps;
int autoauth;
+ int session;
const char *user;
const char *conf_filename;
const char *fallback;
};
/*
+ * pam_cap_iab_s is used to capture any cap_iab_t value if it needs to be applied during pam_sm_
+ */
+
+/*
* load_groups obtains the list all of the groups associated with the
* requested user: gid & supplemental groups.
*/
@@ -252,6 +257,12 @@ static int set_capabilities(struct pam_cap_s *cs)
* Best effort to set keep caps - this may help work around
* situations where applications are using a capabilities
* unaware setuid() call.
+ *
+ * It isn't needed unless you want to support Ambient vector
+ * values in the IAB. In this case, it will likely also
+ * require you use the "use_session" module argument and
+ * include a "session" line in your PAM config that points to
+ * pam_cap.so.
*/
D(("setting keepcaps"));
(void) cap_prctlw(PR_SET_KEEPCAPS, 1, 0, 0, 0, 0);
@@ -299,6 +310,8 @@ static void parse_args(int argc, const char **argv, struct pam_cap_s *pcs)
pcs->autoauth = 1;
} else if (!strncmp(*argv, "default=", 8)) {
pcs->fallback = 8 + *argv;
+ } else if (!strcmp(*argv, "use_session")) {
+ pcs->session = 1;
} else {
_pam_log(LOG_ERR, "unknown option; %s", *argv);
}
@@ -358,23 +371,23 @@ int pam_sm_authenticate(pam_handle_t *pamh, int flags,
_pam_drop(conf_caps);
return PAM_SUCCESS;
-
- } else {
-
- D(("there are no capabilities restrictions on this user"));
- return PAM_IGNORE;
-
}
+
+ D(("there are no capabilities restrictions on this user"));
+ return PAM_IGNORE;
}
/*
- * pam_sm_setcred applies inheritable capabilities loaded by the
- * pam_sm_authenticate pass for the user.
+ * pam_sm_setcred optionally applies inheritable capabilities loaded
+ * by the pam_sm_authenticate pass for the user. If it doesn't apply
+ * them directly (because of the "use_session" module argument), it
+ * caches the cap_iab_t value for later use during the
+ * pam_sm_open_session() call.
*/
int pam_sm_setcred(pam_handle_t *pamh, int flags,
int argc, const char **argv)
{
- int retval;
+ int retval = 0;
struct pam_cap_s pcs;
if (!(flags & (PAM_ESTABLISH_CRED | PAM_REINITIALIZE_CRED))) {
@@ -390,8 +403,50 @@ int pam_sm_setcred(pam_handle_t *pamh, int flags,
return PAM_AUTH_ERR;
}
- retval = set_capabilities(&pcs);
- memset(&pcs, 0, sizeof(pcs));
+ if (!pcs.session) {
+ retval = set_capabilities(&pcs);
+ memset(&pcs, 0, sizeof(pcs));
+ }
return (retval ? PAM_SUCCESS:PAM_IGNORE);
}
+
+/*
+ * pam_sm_open_session supports applying the configured cap_iab_t
+ * tuple after the user credentials have been fully applied. This is
+ * for programs where pam_cap.so's auth configuration includes the
+ * "use_session" and "keepcaps" module arguments. Typically, the
+ * latter is needed to coax the application into persisting the
+ * ability to apply the IAB value after a setuid() call by the
+ * application.
+ *
+ * Note, this is only needed in cases where the local system needs to
+ * support adding Ambient capability vector values. For Inheritable
+ * capabilities which survive setuid() in all modes, the existing
+ * module works fine.
+ */
+int pam_sm_open_session(pam_handle_t *pamh, int flags, int argc,
+ const char **argv)
+{
+ int retval;
+ struct pam_cap_s 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_USER_UNKNOWN;
+ }
+
+ set_capabilities(&pcs);
+ memset(&pcs, 0, sizeof(pcs));
+ return PAM_SUCCESS;
+}
+
+/* pam_sm_close_session is mostly a no-op; it always returns PAM_SUCCESS. */
+int pam_sm_close_session(pam_handle_t *pamh, int flags, int argc,
+ const char **argv)
+{
+ return PAM_SUCCESS;
+}
diff --git a/pam_cap/test_pam_cap.c b/pam_cap/test_pam_cap.c
index 4c67cad..0a58da6 100644
--- a/pam_cap/test_pam_cap.c
+++ b/pam_cap/test_pam_cap.c
@@ -134,31 +134,35 @@ struct vargs {
static int test_arg_parsing(void) {
static struct vargs vs[] = {
{
- { 1, 0, 0, NULL, NULL, NULL },
+ { 1, 0, 0, 0, NULL, NULL, NULL },
{ "debug", NULL }
},
{
- { 0, 1, 0, NULL, NULL, NULL },
+ { 0, 1, 0, 0, NULL, NULL, NULL },
{ "keepcaps", NULL }
},
{
- { 0, 0, 1, NULL, NULL, NULL },
+ { 0, 0, 1, 0, NULL, NULL, NULL },
{ "autoauth", NULL }
},
{
- { 1, 0, 1, NULL, NULL, NULL },
+ { 1, 0, 1, 0, NULL, NULL, NULL },
{ "autoauth", "debug", NULL }
},
{
- { 0, 0, 0, NULL, "/over/there", NULL },
+ { 0, 0, 0, 0, NULL, "/over/there", NULL },
{ "config=/over/there", NULL }
},
{
- { 0, 0, 0, NULL, NULL, "^cap_setfcap" },
+ { 0, 0, 0, 0, NULL, NULL, "^cap_setfcap" },
{ "default=^cap_setfcap", NULL }
},
{
- { 0, 0, 0, NULL, NULL, NULL },
+ { 0, 0, 0, 1, NULL, NULL, NULL },
+ { "use_session", NULL }
+ },
+ {
+ { 0, 0, 0, 0, NULL, NULL, NULL },
{ NULL }
}
};