diff options
author | Anas Nashif <anas.nashif@intel.com> | 2012-11-03 20:31:18 -0700 |
---|---|---|
committer | Anas Nashif <anas.nashif@intel.com> | 2012-11-03 20:31:18 -0700 |
commit | b138da4a4b9d57b850ca4d0061969f5e3299861d (patch) | |
tree | 3e20a6f4a29bfe91b2b51f416673d9fad1e0b7c7 /pam_cap | |
download | libcap-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/.gitignore | 2 | ||||
-rw-r--r-- | pam_cap/License | 41 | ||||
-rw-r--r-- | pam_cap/Makefile | 29 | ||||
-rw-r--r-- | pam_cap/capability.conf | 45 | ||||
-rw-r--r-- | pam_cap/pam_cap.c | 310 | ||||
-rw-r--r-- | pam_cap/test.c | 12 |
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); +} |