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>
58 #define PAM_SM_ACCOUNT
59 #define PAM_SM_SESSION
60 #define PAM_SM_PASSWORD
62 #include <security/pam_modules.h>
63 #include <security/pam_modutil.h>
64 #include <security/pam_ext.h>
66 /* Basically, run cmp(atol(left), atol(right)), returning PAM_SUCCESS if
67 * the function returns non-zero, PAM_AUTH_ERR if it returns zero, and
68 * PAM_SERVICE_ERR if the arguments can't be parsed as numbers. */
70 evaluate_num(const pam_handle_t *pamh, const char *left,
71 const char *right, int (*cmp)(long long, long long))
75 int ret = PAM_SUCCESS;
78 l = strtoll(left, &p, 0);
79 if ((p == NULL) || (*p != '\0') || errno) {
80 pam_syslog(pamh, LOG_INFO, "\"%s\" is not a number", left);
81 ret = PAM_SERVICE_ERR;
84 r = strtoll(right, &p, 0);
85 if ((p == NULL) || (*p != '\0') || errno) {
86 pam_syslog(pamh, LOG_INFO, "\"%s\" is not a number", right);
87 ret = PAM_SERVICE_ERR;
90 if (ret != PAM_SUCCESS) {
94 return cmp(l, r) ? PAM_SUCCESS : PAM_AUTH_ERR;
97 /* Simple numeric comparison callbacks. */
99 eq(long long i, long long j)
104 ne(long long i, long long j)
109 lt(long long i, long long j)
114 le(long long i, long long j)
116 return lt(i, j) || eq(i, j);
119 gt(long long i, long long j)
124 ge(long long i, long long j)
126 return gt(i, j) || eq(i, j);
129 /* Test for numeric equality. */
131 evaluate_eqn(const pam_handle_t *pamh, const char *left, const char *right)
133 return evaluate_num(pamh, left, right, eq);
135 /* Test for string equality. */
137 evaluate_eqs(const char *left, const char *right)
139 return (strcmp(left, right) == 0) ? PAM_SUCCESS : PAM_AUTH_ERR;
141 /* Test for numeric inequality. */
143 evaluate_nen(const pam_handle_t *pamh, const char *left, const char *right)
145 return evaluate_num(pamh, left, right, ne);
147 /* Test for string inequality. */
149 evaluate_nes(const char *left, const char *right)
151 return (strcmp(left, right) != 0) ? PAM_SUCCESS : PAM_AUTH_ERR;
153 /* Test for numeric less-than-ness(?) */
155 evaluate_lt(const pam_handle_t *pamh, const char *left, const char *right)
157 return evaluate_num(pamh, left, right, lt);
159 /* Test for numeric less-than-or-equal-ness(?) */
161 evaluate_le(const pam_handle_t *pamh, const char *left, const char *right)
163 return evaluate_num(pamh, left, right, le);
165 /* Test for numeric greater-than-ness(?) */
167 evaluate_gt(const pam_handle_t *pamh, const char *left, const char *right)
169 return evaluate_num(pamh, left, right, gt);
171 /* Test for numeric greater-than-or-equal-ness(?) */
173 evaluate_ge(const pam_handle_t *pamh, const char *left, const char *right)
175 return evaluate_num(pamh, left, right, ge);
177 /* Check for file glob match. */
179 evaluate_glob(const char *left, const char *right)
181 return (fnmatch(right, left, 0) == 0) ? PAM_SUCCESS : PAM_AUTH_ERR;
183 /* Check for file glob mismatch. */
185 evaluate_noglob(const char *left, const char *right)
187 return (fnmatch(right, left, 0) != 0) ? PAM_SUCCESS : PAM_AUTH_ERR;
189 /* Check for list match. */
191 evaluate_inlist(const char *left, const char *right)
194 /* Don't care about left containing ':'. */
195 while ((p=strstr(right, left)) != NULL) {
196 if (p == right || *(p-1) == ':') { /* ':' is a list separator */
198 if (*p == '\0' || *p == ':') {
202 right = strchr(p, ':');
210 /* Check for list mismatch. */
212 evaluate_notinlist(const char *left, const char *right)
214 return evaluate_inlist(left, right) != PAM_SUCCESS ? PAM_SUCCESS : PAM_AUTH_ERR;
216 /* Return PAM_SUCCESS if the user is in the group. */
218 evaluate_ingroup(pam_handle_t *pamh, const char *user, const char *group)
220 if (pam_modutil_user_in_group_nam_nam(pamh, user, group) == 1)
224 /* Return PAM_SUCCESS if the user is NOT in the group. */
226 evaluate_notingroup(pam_handle_t *pamh, const char *user, const char *group)
228 if (pam_modutil_user_in_group_nam_nam(pamh, user, group) == 0)
232 /* Return PAM_SUCCESS if the (host,user) is in the netgroup. */
234 evaluate_innetgr(const pam_handle_t* pamh, const char *host, const char *user, const char *group)
237 if (innetgr(group, host, user, NULL) == 1)
240 pam_syslog (pamh, LOG_ERR, "pam_succeed_if does not have netgroup support");
245 /* Return PAM_SUCCESS if the (host,user) is NOT in the netgroup. */
247 evaluate_notinnetgr(const pam_handle_t* pamh, const char *host, const char *user, const char *group)
250 if (innetgr(group, host, user, NULL) == 0)
253 pam_syslog (pamh, LOG_ERR, "pam_succeed_if does not have netgroup support");
258 /* Match a triple. */
260 evaluate(pam_handle_t *pamh, int debug,
261 const char *left, const char *qual, const char *right,
262 struct passwd **pwd, const char *user)
264 char buf[LINE_MAX] = "";
265 const char *attribute = left;
266 /* Figure out what we're evaluating here, and convert it to a string.*/
267 if ((strcasecmp(left, "login") == 0) ||
268 (strcasecmp(left, "name") == 0) ||
269 (strcasecmp(left, "user") == 0)) {
270 snprintf(buf, sizeof(buf), "%s", user);
273 /* Get information about the user if needed. */
274 if ((*pwd == NULL) &&
275 ((strcasecmp(left, "uid") == 0) ||
276 (strcasecmp(left, "gid") == 0) ||
277 (strcasecmp(left, "shell") == 0) ||
278 (strcasecmp(left, "home") == 0) ||
279 (strcasecmp(left, "dir") == 0) ||
280 (strcasecmp(left, "homedir") == 0))) {
281 *pwd = pam_modutil_getpwnam(pamh, user);
283 return PAM_USER_UNKNOWN;
286 if (strcasecmp(left, "uid") == 0) {
287 snprintf(buf, sizeof(buf), "%lu", (unsigned long) (*pwd)->pw_uid);
290 if (strcasecmp(left, "gid") == 0) {
291 snprintf(buf, sizeof(buf), "%lu", (unsigned long) (*pwd)->pw_gid);
294 if (strcasecmp(left, "shell") == 0) {
295 snprintf(buf, sizeof(buf), "%s", (*pwd)->pw_shell);
298 if ((strcasecmp(left, "home") == 0) ||
299 (strcasecmp(left, "dir") == 0) ||
300 (strcasecmp(left, "homedir") == 0)) {
301 snprintf(buf, sizeof(buf), "%s", (*pwd)->pw_dir);
304 if (strcasecmp(left, "service") == 0) {
306 if (pam_get_item(pamh, PAM_SERVICE, &svc) != PAM_SUCCESS ||
309 snprintf(buf, sizeof(buf), "%s", (const char *)svc);
312 if (strcasecmp(left, "ruser") == 0) {
314 if (pam_get_item(pamh, PAM_RUSER, &ruser) != PAM_SUCCESS ||
317 snprintf(buf, sizeof(buf), "%s", (const char *)ruser);
321 if (strcasecmp(left, "rhost") == 0) {
323 if (pam_get_item(pamh, PAM_RHOST, &rhost) != PAM_SUCCESS ||
326 snprintf(buf, sizeof(buf), "%s", (const char *)rhost);
329 if (strcasecmp(left, "tty") == 0) {
331 if (pam_get_item(pamh, PAM_TTY, &tty) != PAM_SUCCESS ||
334 snprintf(buf, sizeof(buf), "%s", (const char *)tty);
337 /* If we have no idea what's going on, return an error. */
339 pam_syslog(pamh, LOG_ERR, "unknown attribute \"%s\"", left);
340 return PAM_SERVICE_ERR;
343 pam_syslog(pamh, LOG_DEBUG, "'%s' resolves to '%s'",
347 /* Attribute value < some threshold. */
348 if ((strcasecmp(qual, "<") == 0) ||
349 (strcasecmp(qual, "lt") == 0)) {
350 return evaluate_lt(pamh, left, right);
352 /* Attribute value <= some threshold. */
353 if ((strcasecmp(qual, "<=") == 0) ||
354 (strcasecmp(qual, "le") == 0)) {
355 return evaluate_le(pamh, left, right);
357 /* Attribute value > some threshold. */
358 if ((strcasecmp(qual, ">") == 0) ||
359 (strcasecmp(qual, "gt") == 0)) {
360 return evaluate_gt(pamh, left, right);
362 /* Attribute value >= some threshold. */
363 if ((strcasecmp(qual, ">=") == 0) ||
364 (strcasecmp(qual, "ge") == 0)) {
365 return evaluate_ge(pamh, left, right);
367 /* Attribute value == some threshold. */
368 if (strcasecmp(qual, "eq") == 0) {
369 return evaluate_eqn(pamh, left, right);
371 /* Attribute value = some string. */
372 if (strcasecmp(qual, "=") == 0) {
373 return evaluate_eqs(left, right);
375 /* Attribute value != some threshold. */
376 if (strcasecmp(qual, "ne") == 0) {
377 return evaluate_nen(pamh, left, right);
379 /* Attribute value != some string. */
380 if (strcasecmp(qual, "!=") == 0) {
381 return evaluate_nes(left, right);
383 /* Attribute value matches some pattern. */
384 if ((strcasecmp(qual, "=~") == 0) ||
385 (strcasecmp(qual, "glob") == 0)) {
386 return evaluate_glob(left, right);
388 if ((strcasecmp(qual, "!~") == 0) ||
389 (strcasecmp(qual, "noglob") == 0)) {
390 return evaluate_noglob(left, right);
392 /* Attribute value matches item in list. */
393 if (strcasecmp(qual, "in") == 0) {
394 return evaluate_inlist(left, right);
396 if (strcasecmp(qual, "notin") == 0) {
397 return evaluate_notinlist(left, right);
399 /* User is in this group. */
400 if (strcasecmp(qual, "ingroup") == 0) {
401 return evaluate_ingroup(pamh, user, right);
403 /* User is not in this group. */
404 if (strcasecmp(qual, "notingroup") == 0) {
405 return evaluate_notingroup(pamh, user, right);
407 /* (Rhost, user) is in this netgroup. */
408 if (strcasecmp(qual, "innetgr") == 0) {
410 if (pam_get_item(pamh, PAM_RHOST, &rhost) != PAM_SUCCESS)
412 return evaluate_innetgr(pamh, rhost, user, right);
414 /* (Rhost, user) is not in this group. */
415 if (strcasecmp(qual, "notinnetgr") == 0) {
417 if (pam_get_item(pamh, PAM_RHOST, &rhost) != PAM_SUCCESS)
419 return evaluate_notinnetgr(pamh, rhost, user, right);
422 return PAM_SERVICE_ERR;
426 pam_sm_authenticate (pam_handle_t *pamh, int flags UNUSED,
427 int argc, const char **argv)
431 struct passwd *pwd = NULL;
432 int ret, i, count, use_uid, debug;
433 const char *left, *right, *qual;
434 int quiet_fail, quiet_succ, audit;
436 /* Get the user prompt. */
437 ret = pam_get_item(pamh, PAM_USER_PROMPT, &prompt);
438 if ((ret != PAM_SUCCESS) || (prompt == NULL) || (strlen(prompt) == 0)) {
445 for (use_uid = 0, debug = 0, i = 0; i < argc; i++) {
446 if (strcmp(argv[i], "debug") == 0) {
449 if (strcmp(argv[i], "use_uid") == 0) {
452 if (strcmp(argv[i], "quiet") == 0) {
456 if (strcmp(argv[i], "quiet_fail") == 0) {
459 if (strcmp(argv[i], "quiet_success") == 0) {
462 if (strcmp(argv[i], "audit") == 0) {
468 /* Get information about the user. */
469 pwd = pam_modutil_getpwuid(pamh, getuid());
471 pam_syslog(pamh, LOG_ERR,
472 "error retrieving information about user %lu",
473 (unsigned long)getuid());
474 return PAM_USER_UNKNOWN;
478 /* Get the user's name. */
479 ret = pam_get_user(pamh, &user, prompt);
480 if ((ret != PAM_SUCCESS) || (user == NULL)) {
481 pam_syslog(pamh, LOG_ERR,
482 "error retrieving user name: %s",
483 pam_strerror(pamh, ret));
487 /* Postpone requesting password data until it is needed */
490 /* Walk the argument list. */
492 left = qual = right = NULL;
493 for (i = 0; i < argc; i++) {
494 if (strcmp(argv[i], "debug") == 0) {
497 if (strcmp(argv[i], "use_uid") == 0) {
500 if (strcmp(argv[i], "quiet") == 0) {
503 if (strcmp(argv[i], "quiet_fail") == 0) {
506 if (strcmp(argv[i], "quiet_success") == 0) {
509 if (strcmp(argv[i], "audit") == 0) {
526 ret = evaluate(pamh, debug,
529 if (ret == PAM_USER_UNKNOWN && audit)
530 pam_syslog(pamh, LOG_NOTICE,
531 "error retrieving information about user %s",
533 if (ret != PAM_SUCCESS) {
534 if(!quiet_fail && ret != PAM_USER_UNKNOWN)
535 pam_syslog(pamh, LOG_INFO,
536 "requirement \"%s %s %s\" "
537 "not met by user \"%s\"",
538 left, qual, right, user);
539 left = qual = right = NULL;
544 pam_syslog(pamh, LOG_INFO,
545 "requirement \"%s %s %s\" "
546 "was met by user \"%s\"",
547 left, qual, right, user);
548 left = qual = right = NULL;
553 if (left || qual || right) {
554 ret = PAM_SERVICE_ERR;
555 pam_syslog(pamh, LOG_ERR,
556 "incomplete condition detected");
557 } else if (count == 0) {
558 pam_syslog(pamh, LOG_INFO,
559 "no condition detected; module succeeded");
566 pam_sm_setcred(pam_handle_t *pamh UNUSED, int flags UNUSED,
567 int argc UNUSED, const char **argv UNUSED)
573 pam_sm_acct_mgmt(pam_handle_t *pamh, int flags, int argc, const char **argv)
575 return pam_sm_authenticate(pamh, flags, argc, argv);
579 pam_sm_open_session(pam_handle_t *pamh, int flags, int argc, const char **argv)
581 return pam_sm_authenticate(pamh, flags, argc, argv);
585 pam_sm_close_session(pam_handle_t *pamh, int flags, int argc, const char **argv)
587 return pam_sm_authenticate(pamh, flags, argc, argv);
591 pam_sm_chauthtok(pam_handle_t *pamh, int flags, int argc, const char **argv)
593 return pam_sm_authenticate(pamh, flags, argc, argv);