summaryrefslogtreecommitdiff
path: root/pam_cap
diff options
context:
space:
mode:
authorAnas Nashif <anas.nashif@intel.com>2012-11-03 20:31:18 -0700
committerAnas Nashif <anas.nashif@intel.com>2012-11-03 20:31:18 -0700
commitb138da4a4b9d57b850ca4d0061969f5e3299861d (patch)
tree3e20a6f4a29bfe91b2b51f416673d9fad1e0b7c7 /pam_cap
downloadlibcap-b138da4a4b9d57b850ca4d0061969f5e3299861d.tar.gz
libcap-b138da4a4b9d57b850ca4d0061969f5e3299861d.tar.bz2
libcap-b138da4a4b9d57b850ca4d0061969f5e3299861d.zip
Imported Upstream version 2.22upstream/2.22
Diffstat (limited to 'pam_cap')
-rw-r--r--pam_cap/.gitignore2
-rw-r--r--pam_cap/License41
-rw-r--r--pam_cap/Makefile29
-rw-r--r--pam_cap/capability.conf45
-rw-r--r--pam_cap/pam_cap.c310
-rw-r--r--pam_cap/test.c12
6 files changed, 439 insertions, 0 deletions
diff --git a/pam_cap/.gitignore b/pam_cap/.gitignore
new file mode 100644
index 0000000..11806f5
--- /dev/null
+++ b/pam_cap/.gitignore
@@ -0,0 +1,2 @@
+pam_cap.so
+testcompile
diff --git a/pam_cap/License b/pam_cap/License
new file mode 100644
index 0000000..e88aa3f
--- /dev/null
+++ b/pam_cap/License
@@ -0,0 +1,41 @@
+Unless otherwise *explicitly* stated the following text describes the
+licensed conditions under which the contents of this module release
+may be distributed:
+
+-------------------------------------------------------------------------
+Redistribution and use in source and binary forms of this module, with
+or without modification, are permitted provided that the following
+conditions are met:
+
+1. Redistributions of source code must retain any existing copyright
+ notice, and this entire permission notice in its entirety,
+ including the disclaimer of warranties.
+
+2. Redistributions in binary form must reproduce all prior and current
+ copyright notices, this list of conditions, and the following
+ disclaimer in the documentation and/or other materials provided
+ with the distribution.
+
+3. The name of any author may not be used to endorse or promote
+ products derived from this software without their specific prior
+ written permission.
+
+ALTERNATIVELY, this product may be distributed under the terms of the
+GNU Library General Public License, in which case the provisions of
+the GNU LGPL are required INSTEAD OF the above restrictions. (This
+clause is necessary due to a potential conflict between the GNU LGPL
+and the restrictions contained in a BSD-style copyright.)
+
+THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
+TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+DAMAGE.
+-------------------------------------------------------------------------
+
diff --git a/pam_cap/Makefile b/pam_cap/Makefile
new file mode 100644
index 0000000..9ca5bef
--- /dev/null
+++ b/pam_cap/Makefile
@@ -0,0 +1,29 @@
+# simple make file for the pam_cap module
+
+topdir=$(shell pwd)/..
+include ../Make.Rules
+
+# Note (as the author of much of the Linux-PAM library, I am confident
+# that this next line does *not* require -lpam on it.) If you think it
+# does, *verify that it does*, and if you observe that it fails as
+# written (and you know why it fails), email me and explain why. Thanks!
+LDLIBS += -L../libcap -lcap
+
+all: pam_cap.so
+ $(MAKE) testcompile
+
+install: all
+ mkdir -p -m 0755 $(LIBDIR)/security
+ install -m 0755 pam_cap.so $(LIBDIR)/security
+
+pam_cap.so: pam_cap.o
+ $(LD) $(LDFLAGS) -o pam_cap.so $< $(LDLIBS)
+
+pam_cap.o: pam_cap.c
+ $(CC) $(CFLAGS) $(IPATH) -c $< -o $@
+
+testcompile: test.c pam_cap.o
+ $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $+ -lpam -ldl $(LDLIBS)
+
+clean:
+ rm -f *.o *.so testcompile *~
diff --git a/pam_cap/capability.conf b/pam_cap/capability.conf
new file mode 100644
index 0000000..dd93ea7
--- /dev/null
+++ b/pam_cap/capability.conf
@@ -0,0 +1,45 @@
+#
+# /etc/security/capability.conf
+#
+# this is a sample capability file (to be used in conjunction with
+# the pam_cap.so module)
+#
+# 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:
+#
+# http://linux.kernel.org/pub/linux/libs/security/linux-privs/
+#
+# .]
+#
+# Here are some sample lines (remove the preceding '#' if you want to
+# use them
+
+## user 'morgan' gets the CAP_SETFCAP inheritable capability (commented out!)
+#cap_setfcap morgan
+
+## user 'luser' inherits the CAP_DAC_OVERRIDE capability (commented out!)
+#cap_dac_override luser
+
+## 'everyone else' gets no inheritable capabilities (restrictive config)
+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):
+##
+## $ /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.
+##
+## Omitting the line will cause the inheritable set to be unmodified
+## from what the parent process had (which is generally 0 unless the
+## invoking user was bestowed with some inheritable capabilities by a
+## previous invocation).
diff --git a/pam_cap/pam_cap.c b/pam_cap/pam_cap.c
new file mode 100644
index 0000000..e6ebbe9
--- /dev/null
+++ b/pam_cap/pam_cap.c
@@ -0,0 +1,310 @@
+/*
+ * Copyright (c) 1999,2007 Andrew G. Morgan <morgan@kernel.org>
+ *
+ * The purpose of this module is to enforce inheritable capability sets
+ * for a specified user.
+ */
+
+/* #define DEBUG */
+
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <syslog.h>
+
+#include <sys/capability.h>
+
+#include <security/pam_modules.h>
+#include <security/_pam_macros.h>
+
+#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;
+ const char *user;
+ const char *conf_filename;
+};
+
+/* 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;
+ FILE *cap_file;
+
+ cap_file = fopen(source, "r");
+ if (cap_file == NULL) {
+ D(("failed to open capability file"));
+ return NULL;
+ }
+
+ while ((line = fgets(buffer, CAP_FILE_BUFFER_SIZE, cap_file))) {
+ int found_one = 0;
+ const char *cap_text;
+
+ cap_text = strtok(line, CAP_FILE_DELIMITERS);
+
+ if (cap_text == NULL) {
+ D(("empty line"));
+ continue;
+ }
+ if (*cap_text == '#') {
+ D(("comment line"));
+ continue;
+ }
+
+ while ((line = strtok(NULL, CAP_FILE_DELIMITERS))) {
+
+ 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));
+ }
+
+ cap_text = NULL;
+ line = NULL;
+
+ if (found_one) {
+ D(("user [%s] matched - caps are [%s]", user, cap_string));
+ break;
+ }
+ }
+
+ fclose(cap_file);
+
+ memset(buffer, 0, CAP_FILE_BUFFER_SIZE);
+
+ return cap_string;
+}
+
+/*
+ * Set capabilities for current process to match the current
+ * 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;
+ int ok = 0;
+
+ cap_s = cap_get_proc();
+ if (cap_s == NULL) {
+ D(("your kernel is capability challenged - upgrade: %s",
+ strerror(errno)));
+ return 0;
+ }
+
+ conf_icaps =
+ read_capabilities_for_user(cs->user,
+ cs->conf_filename
+ ? cs->conf_filename:USER_CAP_FILE );
+ if (conf_icaps == 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;
+ }
+
+ /*
+ * 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 (!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);
+ } else {
+ sprintf(combined_caps, CAP_COMBINED_FORMAT, proc_epcaps, conf_icaps);
+ }
+ D(("combined_caps=[%s]", combined_caps));
+
+ cap_free(cap_s);
+ cap_s = cap_from_text(combined_caps);
+ _pam_overwrite(combined_caps);
+ _pam_drop(combined_caps);
+
+#ifdef DEBUG
+ {
+ 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 {
+ D(("failed to set specified capabilities: %s", strerror(errno)));
+ }
+
+cleanup_epcaps:
+ cap_free(proc_epcaps);
+
+cleanup_icaps:
+ _pam_overwrite(conf_icaps);
+ _pam_drop(conf_icaps);
+
+cleanup_cap_s:
+ if (cap_s) {
+ cap_free(cap_s);
+ cap_s = NULL;
+ }
+
+ return ok;
+}
+
+/* log errors */
+
+static void _pam_log(int err, const char *format, ...)
+{
+ va_list args;
+
+ va_start(args, format);
+ openlog("pam_cap", LOG_CONS|LOG_PID, LOG_AUTH);
+ vsyslog(err, format, args);
+ va_end(args);
+ closelog();
+}
+
+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) {
+
+ if (!strcmp(*argv, "debug")) {
+ pcs->debug = 1;
+ } else if (!memcmp(*argv, "config=", 7)) {
+ pcs->conf_filename = 7 + *argv;
+ } else {
+ _pam_log(LOG_ERR, "unknown option; %s", *argv);
+ }
+
+ }
+}
+
+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;
+
+ 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));
+ return PAM_INCOMPLETE;
+ }
+
+ if (retval != PAM_SUCCESS) {
+ D(("pam_get_user failed: %s", pam_strerror(pamh, retval)));
+ memset(&pcs, 0, sizeof(pcs));
+ return PAM_AUTH_ERR;
+ }
+
+ conf_icaps =
+ read_capabilities_for_user(pcs.user,
+ pcs.conf_filename
+ ? pcs.conf_filename:USER_CAP_FILE );
+
+ memset(&pcs, 0, sizeof(pcs));
+
+ if (conf_icaps) {
+ D(("it appears that there are capabilities for this user [%s]",
+ conf_icaps));
+
+ /* 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);
+
+ return PAM_SUCCESS;
+
+ } else {
+
+ D(("there are no capabilities restrctions on this user"));
+ return PAM_IGNORE;
+
+ }
+}
+
+int pam_sm_setcred(pam_handle_t *pamh, int flags,
+ int argc, const char **argv)
+{
+ int retval;
+ struct pam_cap_s pcs;
+
+ if (!(flags & PAM_ESTABLISH_CRED)) {
+ D(("we don't handle much in the way of credentials"));
+ return PAM_IGNORE;
+ }
+
+ 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/test.c b/pam_cap/test.c
new file mode 100644
index 0000000..5150ba5
--- /dev/null
+++ b/pam_cap/test.c
@@ -0,0 +1,12 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <security/pam_modules.h>
+
+int main(int argc, char **argv)
+{
+ if (pam_sm_authenticate(NULL, 0, 0, NULL) != PAM_SUCCESS) {
+ printf("failed to authenticate\n");
+ exit(1);
+ }
+ exit(0);
+}