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)(int, int))
75 int ret = PAM_SUCCESS;
78 l = strtol(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 = strtol(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. */
116 return lt(i, j) || eq(i, 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 char *host, const char *user, const char *group)
236 if (innetgr(group, host, user, NULL) == 1)
240 /* Return PAM_SUCCESS if the (host,user) is NOT in the netgroup. */
242 evaluate_notinnetgr(const char *host, const char *user, const char *group)
244 if (innetgr(group, host, user, NULL) == 0)
249 /* Match a triple. */
251 evaluate(pam_handle_t *pamh, int debug,
252 const char *left, const char *qual, const char *right,
253 struct passwd *pwd, const char *user)
255 char buf[LINE_MAX] = "";
256 const char *attribute = left;
257 /* Figure out what we're evaluating here, and convert it to a string.*/
258 if ((strcasecmp(left, "login") == 0) ||
259 (strcasecmp(left, "name") == 0) ||
260 (strcasecmp(left, "user") == 0)) {
261 snprintf(buf, sizeof(buf), "%s", user);
264 if (strcasecmp(left, "uid") == 0) {
265 snprintf(buf, sizeof(buf), "%lu", (unsigned long) pwd->pw_uid);
268 if (strcasecmp(left, "gid") == 0) {
269 snprintf(buf, sizeof(buf), "%lu", (unsigned long) pwd->pw_gid);
272 if (strcasecmp(left, "shell") == 0) {
273 snprintf(buf, sizeof(buf), "%s", pwd->pw_shell);
276 if ((strcasecmp(left, "home") == 0) ||
277 (strcasecmp(left, "dir") == 0) ||
278 (strcasecmp(left, "homedir") == 0)) {
279 snprintf(buf, sizeof(buf), "%s", pwd->pw_dir);
282 if (strcasecmp(left, "service") == 0) {
284 if (pam_get_item(pamh, PAM_SERVICE, &svc) != PAM_SUCCESS ||
287 snprintf(buf, sizeof(buf), "%s", (const char *)svc);
290 if (strcasecmp(left, "ruser") == 0) {
292 if (pam_get_item(pamh, PAM_RUSER, &ruser) != PAM_SUCCESS ||
295 snprintf(buf, sizeof(buf), "%s", (const char *)ruser);
299 if (strcasecmp(left, "rhost") == 0) {
301 if (pam_get_item(pamh, PAM_SERVICE, &rhost) != PAM_SUCCESS ||
304 snprintf(buf, sizeof(buf), "%s", (const char *)rhost);
307 if (strcasecmp(left, "tty") == 0) {
309 if (pam_get_item(pamh, PAM_SERVICE, &tty) != PAM_SUCCESS ||
312 snprintf(buf, sizeof(buf), "%s", (const char *)tty);
315 /* If we have no idea what's going on, return an error. */
317 pam_syslog(pamh, LOG_CRIT, "unknown attribute \"%s\"", left);
318 return PAM_SERVICE_ERR;
321 pam_syslog(pamh, LOG_DEBUG, "'%s' resolves to '%s'",
325 /* Attribute value < some threshold. */
326 if ((strcasecmp(qual, "<") == 0) ||
327 (strcasecmp(qual, "lt") == 0)) {
328 return evaluate_lt(pamh, left, right);
330 /* Attribute value <= some threshold. */
331 if ((strcasecmp(qual, "<=") == 0) ||
332 (strcasecmp(qual, "le") == 0)) {
333 return evaluate_le(pamh, left, right);
335 /* Attribute value > some threshold. */
336 if ((strcasecmp(qual, ">") == 0) ||
337 (strcasecmp(qual, "gt") == 0)) {
338 return evaluate_gt(pamh, left, right);
340 /* Attribute value >= some threshold. */
341 if ((strcasecmp(qual, ">=") == 0) ||
342 (strcasecmp(qual, "ge") == 0)) {
343 return evaluate_ge(pamh, left, right);
345 /* Attribute value == some threshold. */
346 if (strcasecmp(qual, "eq") == 0) {
347 return evaluate_eqn(pamh, left, right);
349 /* Attribute value = some string. */
350 if (strcasecmp(qual, "=") == 0) {
351 return evaluate_eqs(left, right);
353 /* Attribute value != some threshold. */
354 if (strcasecmp(qual, "ne") == 0) {
355 return evaluate_nen(pamh, left, right);
357 /* Attribute value != some string. */
358 if (strcasecmp(qual, "!=") == 0) {
359 return evaluate_nes(left, right);
361 /* Attribute value matches some pattern. */
362 if ((strcasecmp(qual, "=~") == 0) ||
363 (strcasecmp(qual, "glob") == 0)) {
364 return evaluate_glob(left, right);
366 if ((strcasecmp(qual, "!~") == 0) ||
367 (strcasecmp(qual, "noglob") == 0)) {
368 return evaluate_noglob(left, right);
370 /* Attribute value matches item in list. */
371 if (strcasecmp(qual, "in") == 0) {
372 return evaluate_inlist(left, right);
374 if (strcasecmp(qual, "notin") == 0) {
375 return evaluate_notinlist(left, right);
377 /* User is in this group. */
378 if (strcasecmp(qual, "ingroup") == 0) {
379 return evaluate_ingroup(pamh, user, right);
381 /* User is not in this group. */
382 if (strcasecmp(qual, "notingroup") == 0) {
383 return evaluate_notingroup(pamh, user, right);
385 /* (Rhost, user) is in this netgroup. */
386 if (strcasecmp(qual, "innetgr") == 0) {
388 if (pam_get_item(pamh, PAM_RHOST, &rhost) != PAM_SUCCESS)
390 return evaluate_innetgr(rhost, user, right);
392 /* (Rhost, user) is not in this group. */
393 if (strcasecmp(qual, "notinnetgr") == 0) {
395 if (pam_get_item(pamh, PAM_RHOST, &rhost) != PAM_SUCCESS)
397 return evaluate_notinnetgr(rhost, user, right);
400 return PAM_SERVICE_ERR;
404 pam_sm_authenticate (pam_handle_t *pamh, int flags UNUSED,
405 int argc, const char **argv)
410 int ret, i, count, use_uid, debug;
411 const char *left, *right, *qual;
412 int quiet_fail, quiet_succ, audit;
414 /* Get the user prompt. */
415 ret = pam_get_item(pamh, PAM_USER_PROMPT, &prompt);
416 if ((ret != PAM_SUCCESS) || (prompt == NULL) || (strlen(prompt) == 0)) {
423 for (use_uid = 0, debug = 0, i = 0; i < argc; i++) {
424 if (strcmp(argv[i], "debug") == 0) {
427 if (strcmp(argv[i], "use_uid") == 0) {
430 if (strcmp(argv[i], "quiet") == 0) {
434 if (strcmp(argv[i], "quiet_fail") == 0) {
437 if (strcmp(argv[i], "quiet_success") == 0) {
440 if (strcmp(argv[i], "audit") == 0) {
446 /* Get information about the user. */
447 pwd = pam_modutil_getpwuid(pamh, getuid());
449 pam_syslog(pamh, LOG_CRIT,
450 "error retrieving information about user %lu",
451 (unsigned long)getuid());
452 return PAM_USER_UNKNOWN;
456 /* Get the user's name. */
457 ret = pam_get_user(pamh, &user, prompt);
458 if ((ret != PAM_SUCCESS) || (user == NULL)) {
459 pam_syslog(pamh, LOG_CRIT,
460 "error retrieving user name: %s",
461 pam_strerror(pamh, ret));
465 /* Get information about the user. */
466 pwd = pam_modutil_getpwnam(pamh, user);
469 pam_syslog(pamh, LOG_NOTICE,
470 "error retrieving information about user %s",
472 return PAM_USER_UNKNOWN;
476 /* Walk the argument list. */
478 left = qual = right = NULL;
479 for (i = 0; i < argc; i++) {
480 if (strcmp(argv[i], "debug") == 0) {
483 if (strcmp(argv[i], "use_uid") == 0) {
486 if (strcmp(argv[i], "quiet") == 0) {
489 if (strcmp(argv[i], "quiet_fail") == 0) {
492 if (strcmp(argv[i], "quiet_success") == 0) {
495 if (strcmp(argv[i], "audit") == 0) {
512 ret = evaluate(pamh, debug,
515 if (ret != PAM_SUCCESS) {
517 pam_syslog(pamh, LOG_INFO,
518 "requirement \"%s %s %s\" "
519 "not met by user \"%s\"",
520 left, qual, right, user);
521 left = qual = right = NULL;
526 pam_syslog(pamh, LOG_INFO,
527 "requirement \"%s %s %s\" "
528 "was met by user \"%s\"",
529 left, qual, right, user);
530 left = qual = right = NULL;
535 if (left || qual || right) {
536 ret = PAM_SERVICE_ERR;
537 pam_syslog(pamh, LOG_CRIT,
538 "incomplete condition detected");
539 } else if (count == 0) {
540 pam_syslog(pamh, LOG_INFO,
541 "no condition detected; module succeeded");
548 pam_sm_setcred(pam_handle_t *pamh UNUSED, int flags UNUSED,
549 int argc UNUSED, const char **argv UNUSED)
555 pam_sm_acct_mgmt(pam_handle_t *pamh, int flags, int argc, const char **argv)
557 return pam_sm_authenticate(pamh, flags, argc, argv);
561 pam_sm_open_session(pam_handle_t *pamh, int flags, int argc, const char **argv)
563 return pam_sm_authenticate(pamh, flags, argc, argv);
567 pam_sm_close_session(pam_handle_t *pamh, int flags, int argc, const char **argv)
569 return pam_sm_authenticate(pamh, flags, argc, argv);
573 pam_sm_chauthtok(pam_handle_t *pamh, int flags, int argc, const char **argv)
575 return pam_sm_authenticate(pamh, flags, argc, argv);
578 /* static module data */
580 struct pam_module _pam_succeed_if_modstruct = {
586 pam_sm_close_session,