From 1e50936923080d9f919e00de48733212cc2c4336 Mon Sep 17 00:00:00 2001 From: Thorsten Kukuk Date: Fri, 24 Sep 2004 11:42:39 +0000 Subject: Relevant BUGIDs: Purpose of commit: Commit summary: --------------- new feature: Add pam_succeed_if Module --- modules/pam_succeed_if/Makefile | 16 ++ modules/pam_succeed_if/README | 68 +++++ modules/pam_succeed_if/pam_succeed_if.8 | 30 ++ modules/pam_succeed_if/pam_succeed_if.c | 468 ++++++++++++++++++++++++++++++++ 4 files changed, 582 insertions(+) create mode 100644 modules/pam_succeed_if/Makefile create mode 100644 modules/pam_succeed_if/README create mode 100644 modules/pam_succeed_if/pam_succeed_if.8 create mode 100644 modules/pam_succeed_if/pam_succeed_if.c (limited to 'modules') diff --git a/modules/pam_succeed_if/Makefile b/modules/pam_succeed_if/Makefile new file mode 100644 index 00000000..51e18c81 --- /dev/null +++ b/modules/pam_succeed_if/Makefile @@ -0,0 +1,16 @@ +# +# $Id$ +# +# This Makefile controls a build process of $(TITLE) module for +# Linux-PAM. You should not modify this Makefile (unless you know +# what you are doing!). +# +# Created by Andrew Morgan 2000/08/27 +# + +include ../../Make.Rules + +TITLE=pam_succeed_if +MAN8=$(TITLE).8 + +include ../Simple.Rules diff --git a/modules/pam_succeed_if/README b/modules/pam_succeed_if/README new file mode 100644 index 00000000..fdb278ef --- /dev/null +++ b/modules/pam_succeed_if/README @@ -0,0 +1,68 @@ +pam_succeed_if: + Succeed or fail based on account characteristics. + + pam_succeed_if.so is designed to succeed or fail authentication based + on characteristics of the account belonging to the user being + authenticated. + + The module can be given one or more conditions as module arguments, and + authentication will succeed only if all of the conditions are met. + + Conditions are expressed in the form + + ATTRIBUTE OPERATOR VALUE + + Recognized attributes: + + LOGIN - The user's login name. + UID - The user's UID. + GID - The user's primary GID. + SHELL - The user's shell. + HOME - The user's home directory. + + Recognized operators: + + < - Arithmetic less-than. + <= - Arithmetic less-than-or-equal-to. + > - Arithmetic greater-than. + >= - Arithmetic greater-than-or-equal-to. + eq - Arithmetic equality. + = - String equality. + ne - Arithmetic inequality. + != - String inequality. + =~ - Wildcard match. + !~ - Wildcard mismatch. + ingroup - Group membership check. [*] + notingroup - Group non-membership check. [*] + + * The "ingroup" and "notingroup" operators should only be + used with the USER attribute. + + Examples: + + Deny authentication to all users except those in the wheel + group, before even asking for a password: + auth requisite pam_succeed_if.so user ingroup wheel + + Assume all users with UID less than 500 ("system users") have + valid accounts. + account sufficient pam_succeed_if.so uid < 500 + + Deny login to all nologin users. + auth requisite pam_succeed_if.so shell !~ nologin + +RECOGNIZED ARGUMENTS: + debug write debugging messages to syslog + use_uid perform checks on the account of the user under whose + UID the application is running instead of the user + being authenticated + quiet don't log failure or success to syslog + quiet_fail don't log failure to syslog + quiet_success don't log success to syslog + + +MODULE SERVICES PROVIDED: + authentication, account management + +AUTHOR: + Nalin Dahyabhai diff --git a/modules/pam_succeed_if/pam_succeed_if.8 b/modules/pam_succeed_if/pam_succeed_if.8 new file mode 100644 index 00000000..2dec43ac --- /dev/null +++ b/modules/pam_succeed_if/pam_succeed_if.8 @@ -0,0 +1,30 @@ +.\" Copyright 2003 Red Hat, Inc. +.\" Written by Nalin Dahyabhai +.TH pam_succeed_if 8 2003/6/30 "Red Hat Linux" "System Administrator's Manual" + +.SH NAME +pam_succeed_if \- succeed or fail based on account characteristics + +.SH SYNOPSIS +.B account sufficient pam_succeed_if.so uid < 500 + +.SH DESCRIPTION +pam_succeed_if.so is designed to succeed or fail authentication based on +characteristics of the account belonging to the user being authenticated. + +The module can be given one or more conditions as module arguments, and +authentication will succeed only if all of the conditions are met. + +.SH ARGUMENTS +.IP debug +Turns on debugging messages sent to syslog. +.IP use_uid +Evaluate conditions using the account of the user whose UID the application +is running under instead of the user being authenticated. + +.SH BUGS +Let's hope not, but if you find any, please report them via the "Bug Track" +link at http://bugzilla.redhat.com/bugzilla/ + +.SH AUTHOR +Nalin Dahyabhai diff --git a/modules/pam_succeed_if/pam_succeed_if.c b/modules/pam_succeed_if/pam_succeed_if.c new file mode 100644 index 00000000..82491b6e --- /dev/null +++ b/modules/pam_succeed_if/pam_succeed_if.c @@ -0,0 +1,468 @@ +/****************************************************************************** + * A simple user-attribute based module for PAM. + * + * Copyright (c) 2003 Red Hat, Inc. + * Written by Nalin Dahyabhai + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define MODULE "pam_succeed_if" + +static void +log_error(int priority, const char *fmt, ...) +{ + va_list va; + char *fmt2; + fmt2 = malloc(strlen(fmt) + strlen(MODULE) + 3); + va_start(va, fmt); + if (fmt2 == NULL) { + vsyslog(LOG_AUTHPRIV | priority, fmt, va); + } else { + snprintf(fmt2, strlen(fmt) + strlen(MODULE) + 3, + "%s: %s", MODULE, fmt); + vsyslog(LOG_AUTHPRIV | priority, fmt2, va); + free(fmt2); + } + va_end(va); +} + +/* Basically, run cmp(atol(left), atol(right)), returning PAM_SUCCESS if + * the function returns non-zero, PAM_AUTH_ERR if it returns zero, and + * PAM_SYSTEM_ERR if the arguments can't be parsed as numbers. */ +static int +evaluate_num(const char *left, const char *right, int (*cmp)(int, int)) +{ + long l, r; + char *p; + int ret = PAM_SUCCESS; + + errno = 0; + l = strtol(left, &p, 0); + if ((p == NULL) || (*p != '\0') || errno) { + log_error(LOG_INFO, "\"%s\" is not a number", left); + ret = PAM_SERVICE_ERR; + } + + r = strtol(right, &p, 0); + if ((p == NULL) || (*p != '\0') || errno) { + log_error(LOG_INFO, "\"%s\" is not a number", right); + ret = PAM_SERVICE_ERR; + } + + if (ret != PAM_SUCCESS) { + return ret; + } + + return cmp(l, r) ? PAM_SUCCESS : PAM_AUTH_ERR; +} + +/* Simple numeric comparison callbacks. */ +static int +eq(int i, int j) +{ + return i == j; +} +static int +ne(int i, int j) +{ + return i != j; +} +static int +lt(int i, int j) +{ + return i < j; +} +static int +le(int i, int j) +{ + return lt(i, j) || eq(i, j); +} +static int +gt(int i, int j) +{ + return i > j; +} +static int +ge(int i, int j) +{ + return gt(i, j) || eq(i, j); +} + +/* Test for numeric equality. */ +static int +evaluate_eqn(const char *left, const char *right) +{ + return evaluate_num(left, right, eq); +} +/* Test for string equality. */ +static int +evaluate_eqs(const char *left, const char *right) +{ + return (strcmp(left, right) == 0) ? PAM_SUCCESS : PAM_AUTH_ERR; +} +/* Test for numeric inequality. */ +static int +evaluate_nen(const char *left, const char *right) +{ + return evaluate_num(left, right, ne); +} +/* Test for string inequality. */ +static int +evaluate_nes(const char *left, const char *right) +{ + return (strcmp(left, right) != 0) ? PAM_SUCCESS : PAM_AUTH_ERR; +} +/* Test for numeric less-than-ness(?) */ +static int +evaluate_lt(const char *left, const char *right) +{ + return evaluate_num(left, right, lt); +} +/* Test for numeric less-than-or-equal-ness(?) */ +static int +evaluate_le(const char *left, const char *right) +{ + return evaluate_num(left, right, le); +} +/* Test for numeric greater-than-ness(?) */ +static int +evaluate_gt(const char *left, const char *right) +{ + return evaluate_num(left, right, gt); +} +/* Test for numeric greater-than-or-equal-ness(?) */ +static int +evaluate_ge(const char *left, const char *right) +{ + return evaluate_num(left, right, ge); +} +/* Check for file glob match. */ +static int +evaluate_glob(const char *left, const char *right) +{ + return (fnmatch(right, left, 0) == 0) ? PAM_SUCCESS : PAM_AUTH_ERR; +} +/* Check for file glob mismatch. */ +static int +evaluate_noglob(const char *left, const char *right) +{ + return (fnmatch(right, left, 0) != 0) ? PAM_SUCCESS : PAM_AUTH_ERR; +} +/* Return PAM_SUCCESS if the user is in the group. */ +static int +evaluate_ingroup(pam_handle_t *pamh, const char *user, const char *group) +{ + int ret; + ret = _pammodutil_user_in_group_nam_nam(pamh, user, group); + switch (ret) { + case 1: + return PAM_SUCCESS; + break; + default: + break; + } + return PAM_AUTH_ERR; +} +/* Return PAM_SUCCESS if the user is NOT in the group. */ +static int +evaluate_notingroup(pam_handle_t *pamh, const char *user, const char *group) +{ + int ret; + ret = _pammodutil_user_in_group_nam_nam(pamh, user, group); + switch (ret) { + case 0: + return PAM_SUCCESS; + break; + default: + break; + } + return PAM_AUTH_ERR; +} + +/* Match a triple. */ +static int +evaluate(pam_handle_t *pamh, int debug, + const char *left, const char *qual, const char *right, + struct passwd *pwd) +{ + char buf[LINE_MAX] = ""; + const char *attribute = left; + /* Figure out what we're evaluating here, and convert it to a string.*/ + if ((strcasecmp(left, "login") == 0) || + (strcasecmp(left, "name") == 0) || + (strcasecmp(left, "user") == 0)) { + snprintf(buf, sizeof(buf), "%s", pwd->pw_name); + left = buf; + } + if (strcasecmp(left, "uid") == 0) { + snprintf(buf, sizeof(buf), "%lu", (unsigned long) pwd->pw_uid); + left = buf; + } + if (strcasecmp(left, "gid") == 0) { + snprintf(buf, sizeof(buf), "%lu", (unsigned long) pwd->pw_gid); + left = buf; + } + if (strcasecmp(left, "shell") == 0) { + snprintf(buf, sizeof(buf), "%s", pwd->pw_shell); + left = buf; + } + if ((strcasecmp(left, "home") == 0) || + (strcasecmp(left, "dir") == 0) || + (strcasecmp(left, "homedir") == 0)) { + snprintf(buf, sizeof(buf), "%s", pwd->pw_dir); + left = buf; + } + /* If we have no idea what's going on, return an error. */ + if (left != buf) { + log_error(LOG_CRIT, "unknown attribute \"%s\"", left); + return PAM_SERVICE_ERR; + } + if (debug) { + log_error(LOG_DEBUG, "'%s' resolves to '%s'", attribute, left); + } + + /* Attribute value < some threshold. */ + if ((strcasecmp(qual, "<") == 0) || + (strcasecmp(qual, "lt") == 0)) { + return evaluate_lt(left, right); + } + /* Attribute value <= some threshold. */ + if ((strcasecmp(qual, "<=") == 0) || + (strcasecmp(qual, "le") == 0)) { + return evaluate_le(left, right); + } + /* Attribute value > some threshold. */ + if ((strcasecmp(qual, ">") == 0) || + (strcasecmp(qual, "gt") == 0)) { + return evaluate_gt(left, right); + } + /* Attribute value >= some threshold. */ + if ((strcasecmp(qual, ">=") == 0) || + (strcasecmp(qual, "ge") == 0)) { + return evaluate_ge(left, right); + } + /* Attribute value == some threshold. */ + if (strcasecmp(qual, "eq") == 0) { + return evaluate_eqn(left, right); + } + /* Attribute value = some string. */ + if (strcasecmp(qual, "=") == 0) { + return evaluate_eqs(left, right); + } + /* Attribute value != some threshold. */ + if (strcasecmp(qual, "ne") == 0) { + return evaluate_nen(left, right); + } + /* Attribute value != some string. */ + if (strcasecmp(qual, "!=") == 0) { + return evaluate_nes(left, right); + } + /* Attribute value matches some pattern. */ + if ((strcasecmp(qual, "=~") == 0) || + (strcasecmp(qual, "glob") == 0)) { + return evaluate_glob(left, right); + } + if ((strcasecmp(qual, "!~") == 0) || + (strcasecmp(qual, "noglob") == 0)) { + return evaluate_noglob(left, right); + } + /* User is in this group. */ + if (strcasecmp(qual, "ingroup") == 0) { + return evaluate_ingroup(pamh, pwd->pw_name, right); + } + /* User is not in this group. */ + if (strcasecmp(qual, "notingroup") == 0) { + return evaluate_notingroup(pamh, pwd->pw_name, right); + } + /* Fail closed. */ + return PAM_SERVICE_ERR; +} + +int +pam_sm_authenticate(pam_handle_t *pamh, int flags, int argc, const char **argv) +{ + const char *prompt; + const char *user; + struct passwd *pwd; + int ret, i, count, use_uid, debug; + const char *left, *right, *qual; + int quiet_fail, quiet_succ; + + /* Get the user prompt. */ + ret = pam_get_item(pamh, PAM_USER_PROMPT, (const void**) &prompt); + if ((ret != PAM_SUCCESS) || (prompt == NULL) || (strlen(prompt) == 0)) { + prompt = "login: "; + } + + quiet_fail = 0; + quiet_succ = 0; + for (use_uid = 0, debug = 0, i = 0; i < argc; i++) { + if (strcmp(argv[i], "debug") == 0) { + debug++; + } + if (strcmp(argv[i], "use_uid") == 0) { + use_uid++; + } + if (strcmp(argv[i], "quiet") == 0) { + quiet_fail++; + quiet_succ++; + } + if (strcmp(argv[i], "quiet_fail") == 0) { + quiet_fail++; + } + if (strcmp(argv[i], "quiet_success") == 0) { + quiet_succ++; + } + } + + if (use_uid) { + /* Get information about the user. */ + pwd = _pammodutil_getpwuid(pamh, getuid()); + if (pwd == NULL) { + log_error(LOG_CRIT, + "error retrieving information about user %ld", + (long)getuid()); + return PAM_SERVICE_ERR; + } + } else { + /* Get the user's name. */ + ret = pam_get_user(pamh, &user, prompt); + if ((ret != PAM_SUCCESS) || (user == NULL)) { + log_error(LOG_CRIT, "error retrieving user name: %s", + pam_strerror(pamh, ret)); + return ret; + } + + /* Get information about the user. */ + pwd = _pammodutil_getpwnam(pamh, user); + if (pwd == NULL) { + log_error(LOG_CRIT, + "error retrieving information about user %s", + user); + return PAM_SERVICE_ERR; + } + } + + /* Walk the argument list. */ + i = count = 0; + left = qual = right = NULL; + while (i <= argc) { + if ((left != NULL) && (qual != NULL) && (right != NULL)) { + ret = evaluate(pamh, debug, + left, qual, right, + pwd); + if (ret != PAM_SUCCESS) { + if(!quiet_fail) + log_error(LOG_INFO, + "requirement \"%s %s %s\" " + "not met by user \"%s\"", + left, qual, right, user); + break; + } + else + if(!quiet_succ) + log_error(LOG_INFO, + "requirement \"%s %s %s\" " + "was met by user \"%s\"", + left, qual, right, user); + left = qual = right = NULL; + } + if ((i < argc) && (strcmp(argv[i], "debug") == 0)) { + i++; + continue; + } + if ((i < argc) && (strcmp(argv[i], "use_uid") == 0)) { + i++; + continue; + } + if ((i < argc) && (strcmp(argv[i], "quiet") == 0)) { + i++; + continue; + } + if ((i < argc) && (strcmp(argv[i], "quiet_fail") == 0)) { + i++; + continue; + } + if ((i < argc) && (strcmp(argv[i], "quiet_success") == 0)) { + i++; + continue; + } + if ((i < argc) && (left == NULL)) { + left = argv[i++]; + count++; + continue; + } + if ((i < argc) && (qual == NULL)) { + qual = argv[i++]; + count++; + continue; + } + if ((i < argc) && (right == NULL)) { + right = argv[i++]; + count++; + continue; + } + i++; + } + + return ret; +} + +int +pam_sm_setcred(pam_handle_t *pamh, int flags, int argc, const char **argv) +{ + return PAM_SUCCESS; +} + +int +pam_sm_acct_mgmt(pam_handle_t *pamh, int flags, int argc, const char **argv) +{ + return pam_sm_authenticate(pamh, flags, argc, argv); +} -- cgit v1.2.3