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)
286 snprintf(buf, sizeof(buf), "%s", (const char *)svc);
289 /* If we have no idea what's going on, return an error. */
291 pam_syslog(pamh, LOG_CRIT, "unknown attribute \"%s\"", left);
292 return PAM_SERVICE_ERR;
295 pam_syslog(pamh, LOG_DEBUG, "'%s' resolves to '%s'",
299 /* Attribute value < some threshold. */
300 if ((strcasecmp(qual, "<") == 0) ||
301 (strcasecmp(qual, "lt") == 0)) {
302 return evaluate_lt(pamh, left, right);
304 /* Attribute value <= some threshold. */
305 if ((strcasecmp(qual, "<=") == 0) ||
306 (strcasecmp(qual, "le") == 0)) {
307 return evaluate_le(pamh, left, right);
309 /* Attribute value > some threshold. */
310 if ((strcasecmp(qual, ">") == 0) ||
311 (strcasecmp(qual, "gt") == 0)) {
312 return evaluate_gt(pamh, left, right);
314 /* Attribute value >= some threshold. */
315 if ((strcasecmp(qual, ">=") == 0) ||
316 (strcasecmp(qual, "ge") == 0)) {
317 return evaluate_ge(pamh, left, right);
319 /* Attribute value == some threshold. */
320 if (strcasecmp(qual, "eq") == 0) {
321 return evaluate_eqn(pamh, left, right);
323 /* Attribute value = some string. */
324 if (strcasecmp(qual, "=") == 0) {
325 return evaluate_eqs(left, right);
327 /* Attribute value != some threshold. */
328 if (strcasecmp(qual, "ne") == 0) {
329 return evaluate_nen(pamh, left, right);
331 /* Attribute value != some string. */
332 if (strcasecmp(qual, "!=") == 0) {
333 return evaluate_nes(left, right);
335 /* Attribute value matches some pattern. */
336 if ((strcasecmp(qual, "=~") == 0) ||
337 (strcasecmp(qual, "glob") == 0)) {
338 return evaluate_glob(left, right);
340 if ((strcasecmp(qual, "!~") == 0) ||
341 (strcasecmp(qual, "noglob") == 0)) {
342 return evaluate_noglob(left, right);
344 /* Attribute value matches item in list. */
345 if (strcasecmp(qual, "in") == 0) {
346 return evaluate_inlist(left, right);
348 if (strcasecmp(qual, "notin") == 0) {
349 return evaluate_notinlist(left, right);
351 /* User is in this group. */
352 if (strcasecmp(qual, "ingroup") == 0) {
353 return evaluate_ingroup(pamh, user, right);
355 /* User is not in this group. */
356 if (strcasecmp(qual, "notingroup") == 0) {
357 return evaluate_notingroup(pamh, user, right);
359 /* (Rhost, user) is in this netgroup. */
360 if (strcasecmp(qual, "innetgr") == 0) {
362 if (pam_get_item(pamh, PAM_RHOST, &rhost) != PAM_SUCCESS)
364 return evaluate_innetgr(rhost, user, right);
366 /* (Rhost, user) is not in this group. */
367 if (strcasecmp(qual, "notinnetgr") == 0) {
369 if (pam_get_item(pamh, PAM_RHOST, &rhost) != PAM_SUCCESS)
371 return evaluate_notinnetgr(rhost, user, right);
374 return PAM_SERVICE_ERR;
378 pam_sm_authenticate (pam_handle_t *pamh, int flags UNUSED,
379 int argc, const char **argv)
384 int ret, i, count, use_uid, debug;
385 const char *left, *right, *qual;
386 int quiet_fail, quiet_succ, audit;
388 /* Get the user prompt. */
389 ret = pam_get_item(pamh, PAM_USER_PROMPT, &prompt);
390 if ((ret != PAM_SUCCESS) || (prompt == NULL) || (strlen(prompt) == 0)) {
397 for (use_uid = 0, debug = 0, i = 0; i < argc; i++) {
398 if (strcmp(argv[i], "debug") == 0) {
401 if (strcmp(argv[i], "use_uid") == 0) {
404 if (strcmp(argv[i], "quiet") == 0) {
408 if (strcmp(argv[i], "quiet_fail") == 0) {
411 if (strcmp(argv[i], "quiet_success") == 0) {
414 if (strcmp(argv[i], "audit") == 0) {
420 /* Get information about the user. */
421 pwd = pam_modutil_getpwuid(pamh, getuid());
423 pam_syslog(pamh, LOG_CRIT,
424 "error retrieving information about user %lu",
425 (unsigned long)getuid());
426 return PAM_USER_UNKNOWN;
430 /* Get the user's name. */
431 ret = pam_get_user(pamh, &user, prompt);
432 if ((ret != PAM_SUCCESS) || (user == NULL)) {
433 pam_syslog(pamh, LOG_CRIT,
434 "error retrieving user name: %s",
435 pam_strerror(pamh, ret));
439 /* Get information about the user. */
440 pwd = pam_modutil_getpwnam(pamh, user);
443 pam_syslog(pamh, LOG_NOTICE,
444 "error retrieving information about user %s",
446 return PAM_USER_UNKNOWN;
450 /* Walk the argument list. */
452 left = qual = right = NULL;
453 for (i = 0; i < argc; i++) {
454 if (strcmp(argv[i], "debug") == 0) {
457 if (strcmp(argv[i], "use_uid") == 0) {
460 if (strcmp(argv[i], "quiet") == 0) {
463 if (strcmp(argv[i], "quiet_fail") == 0) {
466 if (strcmp(argv[i], "quiet_success") == 0) {
469 if (strcmp(argv[i], "audit") == 0) {
486 ret = evaluate(pamh, debug,
489 if (ret != PAM_SUCCESS) {
491 pam_syslog(pamh, LOG_INFO,
492 "requirement \"%s %s %s\" "
493 "not met by user \"%s\"",
494 left, qual, right, user);
495 left = qual = right = NULL;
500 pam_syslog(pamh, LOG_INFO,
501 "requirement \"%s %s %s\" "
502 "was met by user \"%s\"",
503 left, qual, right, user);
504 left = qual = right = NULL;
509 if (left || qual || right) {
510 ret = PAM_SERVICE_ERR;
511 pam_syslog(pamh, LOG_CRIT,
512 "incomplete condition detected");
513 } else if (count == 0) {
514 pam_syslog(pamh, LOG_INFO,
515 "no condition detected; module succeeded");
522 pam_sm_setcred(pam_handle_t *pamh UNUSED, int flags UNUSED,
523 int argc UNUSED, const char **argv UNUSED)
529 pam_sm_acct_mgmt(pam_handle_t *pamh, int flags, int argc, const char **argv)
531 return pam_sm_authenticate(pamh, flags, argc, argv);
535 pam_sm_open_session(pam_handle_t *pamh, int flags, int argc, const char **argv)
537 return pam_sm_authenticate(pamh, flags, argc, argv);
541 pam_sm_close_session(pam_handle_t *pamh, int flags, int argc, const char **argv)
543 return pam_sm_authenticate(pamh, flags, argc, argv);
547 pam_sm_chauthtok(pam_handle_t *pamh, int flags, int argc, const char **argv)
549 return pam_sm_authenticate(pamh, flags, argc, argv);
552 /* static module data */
554 struct pam_module _pam_succeed_if_modstruct = {
560 pam_sm_close_session,