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 if ((p=strstr(right, left)) == NULL)
196 if (p == right || *(p-1) == ':') { /* ':' is a list separator */
198 if (*p == '\0' || *p == ':') {
204 /* Check for list mismatch. */
206 evaluate_notinlist(const char *left, const char *right)
208 return evaluate_inlist(left, right) != PAM_SUCCESS ? PAM_SUCCESS : PAM_AUTH_ERR;
210 /* Return PAM_SUCCESS if the user is in the group. */
212 evaluate_ingroup(pam_handle_t *pamh, const char *user, const char *group)
214 if (pam_modutil_user_in_group_nam_nam(pamh, user, group) == 1)
218 /* Return PAM_SUCCESS if the user is NOT in the group. */
220 evaluate_notingroup(pam_handle_t *pamh, const char *user, const char *group)
222 if (pam_modutil_user_in_group_nam_nam(pamh, user, group) == 0)
226 /* Return PAM_SUCCESS if the (host,user) is in the netgroup. */
228 evaluate_innetgr(const char *host, const char *user, const char *group)
230 if (innetgr(group, host, user, NULL) == 1)
234 /* Return PAM_SUCCESS if the (host,user) is NOT in the netgroup. */
236 evaluate_notinnetgr(const char *host, const char *user, const char *group)
238 if (innetgr(group, host, user, NULL) == 0)
243 /* Match a triple. */
245 evaluate(pam_handle_t *pamh, int debug,
246 const char *left, const char *qual, const char *right,
249 char buf[LINE_MAX] = "";
250 const char *attribute = left;
251 /* Figure out what we're evaluating here, and convert it to a string.*/
252 if ((strcasecmp(left, "login") == 0) ||
253 (strcasecmp(left, "name") == 0) ||
254 (strcasecmp(left, "user") == 0)) {
255 snprintf(buf, sizeof(buf), "%s", pwd->pw_name);
258 if (strcasecmp(left, "uid") == 0) {
259 snprintf(buf, sizeof(buf), "%lu", (unsigned long) pwd->pw_uid);
262 if (strcasecmp(left, "gid") == 0) {
263 snprintf(buf, sizeof(buf), "%lu", (unsigned long) pwd->pw_gid);
266 if (strcasecmp(left, "shell") == 0) {
267 snprintf(buf, sizeof(buf), "%s", pwd->pw_shell);
270 if ((strcasecmp(left, "home") == 0) ||
271 (strcasecmp(left, "dir") == 0) ||
272 (strcasecmp(left, "homedir") == 0)) {
273 snprintf(buf, sizeof(buf), "%s", pwd->pw_dir);
276 if (strcasecmp(left, "service") == 0) {
278 if (pam_get_item(pamh, PAM_SERVICE, &svc) != PAM_SUCCESS)
280 snprintf(buf, sizeof(buf), "%s", (const char *)svc);
283 /* If we have no idea what's going on, return an error. */
285 pam_syslog(pamh, LOG_CRIT, "unknown attribute \"%s\"", left);
286 return PAM_SERVICE_ERR;
289 pam_syslog(pamh, LOG_DEBUG, "'%s' resolves to '%s'",
293 /* Attribute value < some threshold. */
294 if ((strcasecmp(qual, "<") == 0) ||
295 (strcasecmp(qual, "lt") == 0)) {
296 return evaluate_lt(pamh, left, right);
298 /* Attribute value <= some threshold. */
299 if ((strcasecmp(qual, "<=") == 0) ||
300 (strcasecmp(qual, "le") == 0)) {
301 return evaluate_le(pamh, left, right);
303 /* Attribute value > some threshold. */
304 if ((strcasecmp(qual, ">") == 0) ||
305 (strcasecmp(qual, "gt") == 0)) {
306 return evaluate_gt(pamh, left, right);
308 /* Attribute value >= some threshold. */
309 if ((strcasecmp(qual, ">=") == 0) ||
310 (strcasecmp(qual, "ge") == 0)) {
311 return evaluate_ge(pamh, left, right);
313 /* Attribute value == some threshold. */
314 if (strcasecmp(qual, "eq") == 0) {
315 return evaluate_eqn(pamh, left, right);
317 /* Attribute value = some string. */
318 if (strcasecmp(qual, "=") == 0) {
319 return evaluate_eqs(left, right);
321 /* Attribute value != some threshold. */
322 if (strcasecmp(qual, "ne") == 0) {
323 return evaluate_nen(pamh, left, right);
325 /* Attribute value != some string. */
326 if (strcasecmp(qual, "!=") == 0) {
327 return evaluate_nes(left, right);
329 /* Attribute value matches some pattern. */
330 if ((strcasecmp(qual, "=~") == 0) ||
331 (strcasecmp(qual, "glob") == 0)) {
332 return evaluate_glob(left, right);
334 if ((strcasecmp(qual, "!~") == 0) ||
335 (strcasecmp(qual, "noglob") == 0)) {
336 return evaluate_noglob(left, right);
338 /* Attribute value matches item in list. */
339 if (strcasecmp(qual, "in") == 0) {
340 return evaluate_inlist(left, right);
342 if (strcasecmp(qual, "notin") == 0) {
343 return evaluate_notinlist(left, right);
345 /* User is in this group. */
346 if (strcasecmp(qual, "ingroup") == 0) {
347 return evaluate_ingroup(pamh, pwd->pw_name, right);
349 /* User is not in this group. */
350 if (strcasecmp(qual, "notingroup") == 0) {
351 return evaluate_notingroup(pamh, pwd->pw_name, right);
353 /* (Rhost, user) is in this netgroup. */
354 if (strcasecmp(qual, "innetgr") == 0) {
356 if (pam_get_item(pamh, PAM_RHOST, &rhost) != PAM_SUCCESS)
358 return evaluate_innetgr(rhost, pwd->pw_name, right);
360 /* (Rhost, user) is not in this group. */
361 if (strcasecmp(qual, "notinnetgr") == 0) {
363 if (pam_get_item(pamh, PAM_RHOST, &rhost) != PAM_SUCCESS)
365 return evaluate_notinnetgr(rhost, pwd->pw_name, right);
368 return PAM_SERVICE_ERR;
372 pam_sm_authenticate (pam_handle_t *pamh, int flags UNUSED,
373 int argc, const char **argv)
378 int ret, i, count, use_uid, debug;
379 const char *left, *right, *qual;
380 int quiet_fail, quiet_succ;
382 /* Get the user prompt. */
383 ret = pam_get_item(pamh, PAM_USER_PROMPT, &prompt);
384 if ((ret != PAM_SUCCESS) || (prompt == NULL) || (strlen(prompt) == 0)) {
390 for (use_uid = 0, debug = 0, i = 0; i < argc; i++) {
391 if (strcmp(argv[i], "debug") == 0) {
394 if (strcmp(argv[i], "use_uid") == 0) {
397 if (strcmp(argv[i], "quiet") == 0) {
401 if (strcmp(argv[i], "quiet_fail") == 0) {
404 if (strcmp(argv[i], "quiet_success") == 0) {
410 /* Get information about the user. */
411 pwd = pam_modutil_getpwuid(pamh, getuid());
413 pam_syslog(pamh, LOG_CRIT,
414 "error retrieving information about user %lu",
415 (unsigned long)getuid());
416 return PAM_USER_UNKNOWN;
420 /* Get the user's name. */
421 ret = pam_get_user(pamh, &user, prompt);
422 if ((ret != PAM_SUCCESS) || (user == NULL)) {
423 pam_syslog(pamh, LOG_CRIT,
424 "error retrieving user name: %s",
425 pam_strerror(pamh, ret));
429 /* Get information about the user. */
430 pwd = pam_modutil_getpwnam(pamh, user);
432 pam_syslog(pamh, LOG_CRIT,
433 "error retrieving information about user %s",
435 return PAM_USER_UNKNOWN;
439 /* Walk the argument list. */
441 left = qual = right = NULL;
443 if ((left != NULL) && (qual != NULL) && (right != NULL)) {
444 ret = evaluate(pamh, debug,
447 if (ret != PAM_SUCCESS) {
449 pam_syslog(pamh, LOG_INFO,
450 "requirement \"%s %s %s\" "
451 "not met by user \"%s\"",
452 left, qual, right, user);
457 pam_syslog(pamh, LOG_INFO,
458 "requirement \"%s %s %s\" "
459 "was met by user \"%s\"",
460 left, qual, right, user);
461 left = qual = right = NULL;
463 if ((i < argc) && (strcmp(argv[i], "debug") == 0)) {
467 if ((i < argc) && (strcmp(argv[i], "use_uid") == 0)) {
471 if ((i < argc) && (strcmp(argv[i], "quiet") == 0)) {
475 if ((i < argc) && (strcmp(argv[i], "quiet_fail") == 0)) {
479 if ((i < argc) && (strcmp(argv[i], "quiet_success") == 0)) {
483 if ((i < argc) && (left == NULL)) {
488 if ((i < argc) && (qual == NULL)) {
493 if ((i < argc) && (right == NULL)) {
505 pam_sm_setcred(pam_handle_t *pamh UNUSED, int flags UNUSED,
506 int argc UNUSED, const char **argv UNUSED)
512 pam_sm_acct_mgmt(pam_handle_t *pamh, int flags, int argc, const char **argv)
514 return pam_sm_authenticate(pamh, flags, argc, argv);
518 pam_sm_open_session(pam_handle_t *pamh, int flags, int argc, const char **argv)
520 return pam_sm_authenticate(pamh, flags, argc, argv);
524 pam_sm_close_session(pam_handle_t *pamh, int flags, int argc, const char **argv)
526 return pam_sm_authenticate(pamh, flags, argc, argv);
530 pam_sm_chauthtok(pam_handle_t *pamh, int flags, int argc, const char **argv)
532 return pam_sm_authenticate(pamh, flags, argc, argv);
535 /* static module data */
537 struct pam_module _pam_succeed_if_modstruct = {
543 pam_sm_close_session,