123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440 |
- /******************************************************************************
- * A module for Linux-PAM that allows/denies access based on SELinux state.
- *
- * Copyright (c) 2007, 2008, 2009 Red Hat, Inc.
- * Originally written by Tomas Mraz <tmraz@redhat.com>
- * Contributions by Dan Walsh <dwalsh@redhat.com>
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, and the entire permission notice in its entirety,
- * including the disclaimer of warranties.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 3. The name of the author may not be used to endorse or promote
- * products derived from this software without specific prior
- * written permission.
- *
- * ALTERNATIVELY, this product may be distributed under the terms of
- * the GNU Public License, in which case the provisions of the GPL are
- * required INSTEAD OF the above restrictions. (This clause is
- * necessary due to a potential bad interaction between the GPL 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 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.
- */
- #include "config.h"
- #include <errno.h>
- #include <pwd.h>
- #include <stdio.h>
- #include <stdlib.h>
- #include <string.h>
- #include <syslog.h>
- #include <ctype.h>
- #include <signal.h>
- #include <limits.h>
- #include <sys/types.h>
- #include <sys/stat.h>
- #include <fcntl.h>
- #include <unistd.h>
- #include <dirent.h>
- #include <security/pam_modules.h>
- #include <security/_pam_macros.h>
- #include <security/pam_modutil.h>
- #include <security/pam_ext.h>
- #include <selinux/selinux.h>
- #define MODULE "pam_sepermit"
- #define OPT_DELIM ":"
- struct lockfd {
- uid_t uid;
- int fd;
- int debug;
- };
- #define PROC_BASE "/proc"
- #define MAX_NAMES (int)(sizeof(unsigned long)*8)
- static int
- match_process_uid(pid_t pid, uid_t uid)
- {
- char buf[128];
- uid_t puid;
- FILE *f;
- int re = 0;
- snprintf (buf, sizeof buf, PROC_BASE "/%d/status", pid);
- if (!(f = fopen (buf, "r")))
- return 0;
- while (fgets(buf, sizeof buf, f)) {
- if (sscanf (buf, "Uid:\t%d", &puid)) {
- re = uid == puid;
- break;
- }
- }
- fclose(f);
- return re;
- }
- static int
- check_running (pam_handle_t *pamh, uid_t uid, int killall, int debug)
- {
- DIR *dir;
- struct dirent *de;
- pid_t *pid_table, pid, self;
- int i;
- int pids, max_pids;
- int running = 0;
- self = getpid();
- if (!(dir = opendir(PROC_BASE))) {
- pam_syslog(pamh, LOG_ERR, "Failed to open proc directory file %s:", PROC_BASE);
- return -1;
- }
- max_pids = 256;
- pid_table = malloc(max_pids * sizeof (pid_t));
- if (!pid_table) {
- (void)closedir(dir);
- pam_syslog(pamh, LOG_CRIT, "Memory allocation error");
- return -1;
- }
- pids = 0;
- while ((de = readdir (dir)) != NULL) {
- if (!(pid = (pid_t)atoi(de->d_name)) || pid == self)
- continue;
- if (pids == max_pids) {
- pid_t *npt;
- if (!(npt = realloc(pid_table, 2*pids*sizeof(pid_t)))) {
- free(pid_table);
- (void)closedir(dir);
- pam_syslog(pamh, LOG_CRIT, "Memory allocation error");
- return -1;
- }
- pid_table = npt;
- max_pids *= 2;
- }
- pid_table[pids++] = pid;
- }
- (void)closedir(dir);
- for (i = 0; i < pids; i++) {
- pid_t id;
- if (match_process_uid(pid_table[i], uid) == 0)
- continue;
- id = pid_table[i];
- if (killall) {
- if (debug)
- pam_syslog(pamh, LOG_NOTICE, "Attempting to kill %d", id);
- kill(id, SIGKILL);
- }
- running++;
- }
- free(pid_table);
- return running;
- }
- /*
- * This function reads the loginuid from the /proc system. It returns
- * (uid_t)-1 on failure.
- */
- static uid_t get_loginuid(pam_handle_t *pamh)
- {
- int fd, count;
- char loginuid[24];
- char *eptr;
- uid_t rv = (uid_t)-1;
- fd = open("/proc/self/loginuid", O_NOFOLLOW|O_RDONLY);
- if (fd < 0) {
- if (errno != ENOENT) {
- pam_syslog(pamh, LOG_ERR,
- "Cannot open /proc/self/loginuid: %m");
- }
- return rv;
- }
- if ((count = pam_modutil_read(fd, loginuid, sizeof(loginuid)-1)) < 1) {
- close(fd);
- return rv;
- }
- loginuid[count] = '\0';
- close(fd);
- errno = 0;
- rv = strtoul(loginuid, &eptr, 10);
- if (errno != 0 || eptr == loginuid)
- rv = (uid_t) -1;
- return rv;
- }
- static void
- sepermit_unlock(pam_handle_t *pamh, void *plockfd, int error_status UNUSED)
- {
- struct lockfd *lockfd = plockfd;
- struct flock fl;
- memset(&fl, 0, sizeof(fl));
- fl.l_type = F_UNLCK;
- fl.l_whence = SEEK_SET;
- if (lockfd->debug)
- pam_syslog(pamh, LOG_ERR, "Unlocking fd: %d uid: %d", lockfd->fd, lockfd->uid);
- /* Don't kill uid==0 */
- if (lockfd->uid)
- /* This is a DOS but it prevents an app from forking to prevent killing */
- while(check_running(pamh, lockfd->uid, 1, lockfd->debug) > 0)
- continue;
- (void)fcntl(lockfd->fd, F_SETLK, &fl);
- (void)close(lockfd->fd);
- free(lockfd);
- }
- static int
- sepermit_lock(pam_handle_t *pamh, const char *user, int debug)
- {
- char buf[PATH_MAX];
- struct flock fl;
- memset(&fl, 0, sizeof(fl));
- fl.l_type = F_WRLCK;
- fl.l_whence = SEEK_SET;
- struct passwd *pw = pam_modutil_getpwnam( pamh, user );
- if (!pw) {
- pam_syslog(pamh, LOG_NOTICE, "Unable to find uid for user %s",
- user);
- return -1;
- }
- if (check_running(pamh, pw->pw_uid, 0, debug) > 0) {
- pam_syslog(pamh, LOG_ERR, "User %s processes are running. Exclusive login not allowed", user);
- return -1;
- }
- snprintf(buf, sizeof(buf), "%s/%d.lock", SEPERMIT_LOCKDIR, pw->pw_uid);
- int fd = open(buf, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR);
- if (fd < 0) {
- pam_syslog(pamh, LOG_ERR, "Unable to open lock file %s/%d.lock", SEPERMIT_LOCKDIR, pw->pw_uid);
- return -1;
- }
- /* Need to close on exec */
- fcntl(fd, F_SETFD, FD_CLOEXEC);
- if (fcntl(fd, F_SETLK, &fl) == -1) {
- pam_syslog(pamh, LOG_ERR, "User %s with exclusive login already logged in", user);
- close(fd);
- return -1;
- }
- struct lockfd *lockfd=calloc(1, sizeof(struct lockfd));
- if (!lockfd) {
- close(fd);
- pam_syslog(pamh, LOG_CRIT, "Memory allocation error");
- return -1;
- }
- lockfd->uid = pw->pw_uid;
- lockfd->debug = debug;
- lockfd->fd=fd;
- pam_set_data(pamh, MODULE, lockfd, sepermit_unlock);
- return 0;
- }
- /* return 0 when matched, -1 when unmatched, pam error otherwise */
- static int
- sepermit_match(pam_handle_t *pamh, const char *cfgfile, const char *user,
- const char *seuser, int debug, int *sense)
- {
- FILE *f;
- char *line = NULL;
- char *start;
- size_t len = 0;
- int matched = 0;
- int exclusive = 0;
- int ignore = 0;
- f = fopen(cfgfile, "r");
- if (!f) {
- pam_syslog(pamh, LOG_ERR, "Failed to open config file %s: %m", cfgfile);
- return PAM_SERVICE_ERR;
- }
- while (!matched && getline(&line, &len, f) != -1) {
- size_t n;
- char *sptr;
- char *opt;
- if (line[0] == '#')
- continue;
- start = line;
- while (isspace(*start))
- ++start;
- n = strlen(start);
- while (n > 0 && isspace(start[n-1])) {
- --n;
- }
- if (n == 0)
- continue;
- start[n] = '\0';
- start = strtok_r(start, OPT_DELIM, &sptr);
- switch (start[0]) {
- case '@':
- ++start;
- if (debug)
- pam_syslog(pamh, LOG_NOTICE, "Matching user %s against group %s", user, start);
- if (pam_modutil_user_in_group_nam_nam(pamh, user, start)) {
- matched = 1;
- }
- break;
- case '%':
- if (seuser == NULL)
- break;
- ++start;
- if (debug)
- pam_syslog(pamh, LOG_NOTICE, "Matching seuser %s against seuser %s", seuser, start);
- if (strcmp(seuser, start) == 0) {
- matched = 1;
- }
- break;
- default:
- if (debug)
- pam_syslog(pamh, LOG_NOTICE, "Matching user %s against user %s", user, start);
- if (strcmp(user, start) == 0) {
- matched = 1;
- }
- }
- if (matched)
- while ((opt=strtok_r(NULL, OPT_DELIM, &sptr)) != NULL) {
- if (strcmp(opt, "exclusive") == 0)
- exclusive = 1;
- else if (strcmp(opt, "ignore") == 0)
- ignore = 1;
- else if (debug) {
- pam_syslog(pamh, LOG_NOTICE, "Unknown user option: %s", opt);
- }
- }
- }
- free(line);
- fclose(f);
- if (matched) {
- if (*sense == PAM_SUCCESS) {
- if (ignore)
- *sense = PAM_IGNORE;
- if (geteuid() == 0 && exclusive && get_loginuid(pamh) == (uid_t)-1)
- if (sepermit_lock(pamh, user, debug) < 0)
- *sense = PAM_AUTH_ERR;
- }
- return 0;
- }
- else
- return -1;
- }
- int
- pam_sm_authenticate(pam_handle_t *pamh, int flags UNUSED,
- int argc, const char **argv)
- {
- int i;
- int rv;
- int debug = 0;
- int sense = PAM_AUTH_ERR;
- const char *user = NULL;
- char *seuser = NULL;
- char *level = NULL;
- const char *cfgfile = SEPERMIT_CONF_FILE;
- /* Parse arguments. */
- for (i = 0; i < argc; i++) {
- if (strcmp(argv[i], "debug") == 0) {
- debug = 1;
- }
- if (strcmp(argv[i], "conf=") == 0) {
- cfgfile = argv[i] + 5;
- }
- }
- if (debug)
- pam_syslog(pamh, LOG_NOTICE, "Parsing config file: %s", cfgfile);
- if (pam_get_user(pamh, &user, NULL) != PAM_SUCCESS || *user == '\0') {
- pam_syslog(pamh, LOG_NOTICE, "cannot determine user name");
- return PAM_USER_UNKNOWN;
- }
- if (is_selinux_enabled() > 0) {
- if (security_getenforce() == 1) {
- if (debug)
- pam_syslog(pamh, LOG_NOTICE, "Enforcing mode, access will be allowed on match");
- sense = PAM_SUCCESS;
- }
- }
- if (getseuserbyname(user, &seuser, &level) != 0) {
- seuser = NULL;
- level = NULL;
- pam_syslog(pamh, LOG_ERR, "getseuserbyname failed: %m");
- }
- if (debug && sense != PAM_SUCCESS)
- pam_syslog(pamh, LOG_NOTICE, "Access will not be allowed on match");
- rv = sepermit_match(pamh, cfgfile, user, seuser, debug, &sense);
- if (debug)
- pam_syslog(pamh, LOG_NOTICE, "sepermit_match returned: %d", rv);
- free(seuser);
- free(level);
- switch (rv) {
- case -1:
- return PAM_IGNORE;
- case 0:
- return sense;
- }
- return rv;
- }
- int
- pam_sm_setcred (pam_handle_t *pamh UNUSED, int flags UNUSED,
- int argc UNUSED, const char **argv UNUSED)
- {
- return PAM_IGNORE;
- }
- int
- pam_sm_acct_mgmt(pam_handle_t *pamh, int flags,
- int argc, const char **argv)
- {
- return pam_sm_authenticate(pamh, flags, argc, argv);
- }
|