1 /******************************************************************************
2 * A simple user-attribute based module for PAM.
4 * Copyright (c) 2003 Red Hat, Inc.
5 * Written by Nalin Dahyabhai <nalin@redhat.com>
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, and the entire permission notice in its entirety,
12 * including the disclaimer of warranties.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
16 * 3. The name of the author may not be used to endorse or promote
17 * products derived from this software without specific prior
20 * ALTERNATIVELY, this product may be distributed under the terms of
21 * the GNU Public License, in which case the provisions of the GPL are
22 * required INSTEAD OF the above restrictions. (This clause is
23 * necessary due to a potential bad interaction between the GPL and
24 * the restrictions contained in a BSD-style copyright.)
26 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
27 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
28 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
29 * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
30 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
31 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
32 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
33 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
34 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
35 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
36 * OF THE POSSIBILITY OF SUCH DAMAGE.
42 #include <sys/types.h>
55 #include <security/pam_modules.h>
56 #include <security/_pam_modutil.h>
58 #define MODULE "pam_succeed_if"
61 log_error(int priority, const char *fmt, ...)
65 fmt2 = malloc(strlen(fmt) + strlen(MODULE) + 3);
68 vsyslog(LOG_AUTHPRIV | priority, fmt, va);
70 snprintf(fmt2, strlen(fmt) + strlen(MODULE) + 3,
71 "%s: %s", MODULE, fmt);
72 vsyslog(LOG_AUTHPRIV | priority, fmt2, va);
78 /* Basically, run cmp(atol(left), atol(right)), returning PAM_SUCCESS if
79 * the function returns non-zero, PAM_AUTH_ERR if it returns zero, and
80 * PAM_SYSTEM_ERR if the arguments can't be parsed as numbers. */
82 evaluate_num(const char *left, const char *right, int (*cmp)(int, int))
86 int ret = PAM_SUCCESS;
89 l = strtol(left, &p, 0);
90 if ((p == NULL) || (*p != '\0') || errno) {
91 log_error(LOG_INFO, "\"%s\" is not a number", left);
92 ret = PAM_SERVICE_ERR;
95 r = strtol(right, &p, 0);
96 if ((p == NULL) || (*p != '\0') || errno) {
97 log_error(LOG_INFO, "\"%s\" is not a number", right);
98 ret = PAM_SERVICE_ERR;
101 if (ret != PAM_SUCCESS) {
105 return cmp(l, r) ? PAM_SUCCESS : PAM_AUTH_ERR;
108 /* Simple numeric comparison callbacks. */
127 return lt(i, j) || eq(i, j);
137 return gt(i, j) || eq(i, j);
140 /* Test for numeric equality. */
142 evaluate_eqn(const char *left, const char *right)
144 return evaluate_num(left, right, eq);
146 /* Test for string equality. */
148 evaluate_eqs(const char *left, const char *right)
150 return (strcmp(left, right) == 0) ? PAM_SUCCESS : PAM_AUTH_ERR;
152 /* Test for numeric inequality. */
154 evaluate_nen(const char *left, const char *right)
156 return evaluate_num(left, right, ne);
158 /* Test for string inequality. */
160 evaluate_nes(const char *left, const char *right)
162 return (strcmp(left, right) != 0) ? PAM_SUCCESS : PAM_AUTH_ERR;
164 /* Test for numeric less-than-ness(?) */
166 evaluate_lt(const char *left, const char *right)
168 return evaluate_num(left, right, lt);
170 /* Test for numeric less-than-or-equal-ness(?) */
172 evaluate_le(const char *left, const char *right)
174 return evaluate_num(left, right, le);
176 /* Test for numeric greater-than-ness(?) */
178 evaluate_gt(const char *left, const char *right)
180 return evaluate_num(left, right, gt);
182 /* Test for numeric greater-than-or-equal-ness(?) */
184 evaluate_ge(const char *left, const char *right)
186 return evaluate_num(left, right, ge);
188 /* Check for file glob match. */
190 evaluate_glob(const char *left, const char *right)
192 return (fnmatch(right, left, 0) == 0) ? PAM_SUCCESS : PAM_AUTH_ERR;
194 /* Check for file glob mismatch. */
196 evaluate_noglob(const char *left, const char *right)
198 return (fnmatch(right, left, 0) != 0) ? PAM_SUCCESS : PAM_AUTH_ERR;
200 /* Return PAM_SUCCESS if the user is in the group. */
202 evaluate_ingroup(pam_handle_t *pamh, const char *user, const char *group)
205 ret = _pammodutil_user_in_group_nam_nam(pamh, user, group);
215 /* Return PAM_SUCCESS if the user is NOT in the group. */
217 evaluate_notingroup(pam_handle_t *pamh, const char *user, const char *group)
220 ret = _pammodutil_user_in_group_nam_nam(pamh, user, group);
231 /* Match a triple. */
233 evaluate(pam_handle_t *pamh, int debug,
234 const char *left, const char *qual, const char *right,
237 char buf[LINE_MAX] = "";
238 const char *attribute = left;
239 /* Figure out what we're evaluating here, and convert it to a string.*/
240 if ((strcasecmp(left, "login") == 0) ||
241 (strcasecmp(left, "name") == 0) ||
242 (strcasecmp(left, "user") == 0)) {
243 snprintf(buf, sizeof(buf), "%s", pwd->pw_name);
246 if (strcasecmp(left, "uid") == 0) {
247 snprintf(buf, sizeof(buf), "%lu", (unsigned long) pwd->pw_uid);
250 if (strcasecmp(left, "gid") == 0) {
251 snprintf(buf, sizeof(buf), "%lu", (unsigned long) pwd->pw_gid);
254 if (strcasecmp(left, "shell") == 0) {
255 snprintf(buf, sizeof(buf), "%s", pwd->pw_shell);
258 if ((strcasecmp(left, "home") == 0) ||
259 (strcasecmp(left, "dir") == 0) ||
260 (strcasecmp(left, "homedir") == 0)) {
261 snprintf(buf, sizeof(buf), "%s", pwd->pw_dir);
264 /* If we have no idea what's going on, return an error. */
266 log_error(LOG_CRIT, "unknown attribute \"%s\"", left);
267 return PAM_SERVICE_ERR;
270 log_error(LOG_DEBUG, "'%s' resolves to '%s'", attribute, left);
273 /* Attribute value < some threshold. */
274 if ((strcasecmp(qual, "<") == 0) ||
275 (strcasecmp(qual, "lt") == 0)) {
276 return evaluate_lt(left, right);
278 /* Attribute value <= some threshold. */
279 if ((strcasecmp(qual, "<=") == 0) ||
280 (strcasecmp(qual, "le") == 0)) {
281 return evaluate_le(left, right);
283 /* Attribute value > some threshold. */
284 if ((strcasecmp(qual, ">") == 0) ||
285 (strcasecmp(qual, "gt") == 0)) {
286 return evaluate_gt(left, right);
288 /* Attribute value >= some threshold. */
289 if ((strcasecmp(qual, ">=") == 0) ||
290 (strcasecmp(qual, "ge") == 0)) {
291 return evaluate_ge(left, right);
293 /* Attribute value == some threshold. */
294 if (strcasecmp(qual, "eq") == 0) {
295 return evaluate_eqn(left, right);
297 /* Attribute value = some string. */
298 if (strcasecmp(qual, "=") == 0) {
299 return evaluate_eqs(left, right);
301 /* Attribute value != some threshold. */
302 if (strcasecmp(qual, "ne") == 0) {
303 return evaluate_nen(left, right);
305 /* Attribute value != some string. */
306 if (strcasecmp(qual, "!=") == 0) {
307 return evaluate_nes(left, right);
309 /* Attribute value matches some pattern. */
310 if ((strcasecmp(qual, "=~") == 0) ||
311 (strcasecmp(qual, "glob") == 0)) {
312 return evaluate_glob(left, right);
314 if ((strcasecmp(qual, "!~") == 0) ||
315 (strcasecmp(qual, "noglob") == 0)) {
316 return evaluate_noglob(left, right);
318 /* User is in this group. */
319 if (strcasecmp(qual, "ingroup") == 0) {
320 return evaluate_ingroup(pamh, pwd->pw_name, right);
322 /* User is not in this group. */
323 if (strcasecmp(qual, "notingroup") == 0) {
324 return evaluate_notingroup(pamh, pwd->pw_name, right);
327 return PAM_SERVICE_ERR;
331 pam_sm_authenticate(pam_handle_t *pamh, int flags, int argc, const char **argv)
336 int ret, i, count, use_uid, debug;
337 const char *left, *right, *qual;
338 int quiet_fail, quiet_succ;
340 /* Get the user prompt. */
341 ret = pam_get_item(pamh, PAM_USER_PROMPT, (const void**) &prompt);
342 if ((ret != PAM_SUCCESS) || (prompt == NULL) || (strlen(prompt) == 0)) {
348 for (use_uid = 0, debug = 0, i = 0; i < argc; i++) {
349 if (strcmp(argv[i], "debug") == 0) {
352 if (strcmp(argv[i], "use_uid") == 0) {
355 if (strcmp(argv[i], "quiet") == 0) {
359 if (strcmp(argv[i], "quiet_fail") == 0) {
362 if (strcmp(argv[i], "quiet_success") == 0) {
368 /* Get information about the user. */
369 pwd = _pammodutil_getpwuid(pamh, getuid());
372 "error retrieving information about user %ld",
374 return PAM_SERVICE_ERR;
377 /* Get the user's name. */
378 ret = pam_get_user(pamh, &user, prompt);
379 if ((ret != PAM_SUCCESS) || (user == NULL)) {
380 log_error(LOG_CRIT, "error retrieving user name: %s",
381 pam_strerror(pamh, ret));
385 /* Get information about the user. */
386 pwd = _pammodutil_getpwnam(pamh, user);
389 "error retrieving information about user %s",
391 return PAM_SERVICE_ERR;
395 /* Walk the argument list. */
397 left = qual = right = NULL;
399 if ((left != NULL) && (qual != NULL) && (right != NULL)) {
400 ret = evaluate(pamh, debug,
403 if (ret != PAM_SUCCESS) {
406 "requirement \"%s %s %s\" "
407 "not met by user \"%s\"",
408 left, qual, right, user);
414 "requirement \"%s %s %s\" "
415 "was met by user \"%s\"",
416 left, qual, right, user);
417 left = qual = right = NULL;
419 if ((i < argc) && (strcmp(argv[i], "debug") == 0)) {
423 if ((i < argc) && (strcmp(argv[i], "use_uid") == 0)) {
427 if ((i < argc) && (strcmp(argv[i], "quiet") == 0)) {
431 if ((i < argc) && (strcmp(argv[i], "quiet_fail") == 0)) {
435 if ((i < argc) && (strcmp(argv[i], "quiet_success") == 0)) {
439 if ((i < argc) && (left == NULL)) {
444 if ((i < argc) && (qual == NULL)) {
449 if ((i < argc) && (right == NULL)) {
461 pam_sm_setcred(pam_handle_t *pamh, int flags, int argc, const char **argv)
467 pam_sm_acct_mgmt(pam_handle_t *pamh, int flags, int argc, const char **argv)
469 return pam_sm_authenticate(pamh, flags, argc, argv);