aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAndrew Morgan <morgan@kernel.org>2007-08-13 20:49:08 -0700
committerAndrew Morgan <morgan@kernel.org>2007-08-13 23:33:40 -0700
commitfa0a8b847d6038b538762b8420cabe4569ecaada (patch)
tree95b083d8e076c189416cff2e120f03b10f58b40b
parent454914bc73f15ede78c2ce6882561bf41ea5c512 (diff)
downloadlibcap-fa0a8b847d6038b538762b8420cabe4569ecaada.tar.gz
I'm installing the pam_cap code here.
Since I wrote it, and reserve all rights, I'm going to rebrand it with the same license as libcap. (Will fix this an compiling etc. on the next commit.)
-rw-r--r--pam_cap/License41
-rw-r--r--pam_cap/Makefile13
-rw-r--r--pam_cap/capability.conf30
-rw-r--r--pam_cap/pam_cap.c311
-rw-r--r--pam_cap/test.c6
5 files changed, 401 insertions, 0 deletions
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..03d2597
--- /dev/null
+++ b/pam_cap/Makefile
@@ -0,0 +1,13 @@
+# simple make file for the pam_cap module
+
+pam_cap.so: pam_cap.o
+ ld -x --shared -o pam_cap.so $< -lcap
+
+pam_cap.o: pam_cap.c
+ $(CC) -fPIC -c $<
+
+test: test.c pam_cap.o
+ $(CC) -o test test.c pam_cap.o -lpam -ldl -lcap
+
+clean:
+ rm -f *.o *.so test
diff --git a/pam_cap/capability.conf b/pam_cap/capability.conf
new file mode 100644
index 0000000..30e4984
--- /dev/null
+++ b/pam_cap/capability.conf
@@ -0,0 +1,30 @@
+#
+# /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:
+#
+# ftp://linux.kernel.org/pub/linux/libs/security/linux-privs/kernel-2.2/
+#
+# despite evidence to the contrary, the 2-2 library should be used for 2.3
+# kernels too.]
+#
+# Here are some sample lines (remove the preceding '#' if you want to
+# use them
+
+## user 'morgan' gets all of the available inheritable capabilities
+# all morgan
+
+## user 'luser' just inherits two capabilities (CAP_NET_RAW and CAP_FOWNER)
+# cap_net_raw,cap_fowner luser
+
+## 'everyone else' gets no inheritable capabilities
+# none *
+
+## if there is no '*' entry, all users not explicitly mentioned will
+## get all available capabilities. This is a permissive default, and
+## probably not what you want...
diff --git a/pam_cap/pam_cap.c b/pam_cap/pam_cap.c
new file mode 100644
index 0000000..2b887fc
--- /dev/null
+++ b/pam_cap/pam_cap.c
@@ -0,0 +1,311 @@
+/*
+ * Copyright (c) Andrew G. Morgan <morgan@linux.kernel.org>
+ *
+ * The purpose of this module is to enforce inheritable capability sets
+ * for a specified user.
+ *
+ * $Id$ <- no version yet ;)
+ */
+
+/* #define DEBUG */
+
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <stdarg.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 (!strcmp(*argv, "config=")) {
+ pcs->conf_filename = strlen("config=") + *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..692ac28
--- /dev/null
+++ b/pam_cap/test.c
@@ -0,0 +1,6 @@
+#include <stdio.h>
+
+main()
+{
+ pam_sm_authenticate(NULL, 0, NULL, 0);
+}