]> granicus.if.org Git - linux-pam/blob - modules/pam_succeed_if/pam_succeed_if.c
pam_succeed_if: Request user data only when needed
[linux-pam] / modules / pam_succeed_if / pam_succeed_if.c
1 /******************************************************************************
2  * A simple user-attribute based module for PAM.
3  *
4  * Copyright (c) 2003 Red Hat, Inc.
5  * Written by Nalin Dahyabhai <nalin@redhat.com>
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
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
18  *    written permission.
19  *
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.)
25  *
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.
37  *
38  */
39
40 #include "config.h"
41
42 #include <sys/types.h>
43 #include <errno.h>
44 #include <fcntl.h>
45 #include <fnmatch.h>
46 #include <limits.h>
47 #include <stdarg.h>
48 #include <stdio.h>
49 #include <stdlib.h>
50 #include <string.h>
51 #include <syslog.h>
52 #include <unistd.h>
53 #include <pwd.h>
54 #include <grp.h>
55 #include <netdb.h>
56
57 #define PAM_SM_AUTH
58 #define PAM_SM_ACCOUNT
59 #define PAM_SM_SESSION
60 #define PAM_SM_PASSWORD
61
62 #include <security/pam_modules.h>
63 #include <security/pam_modutil.h>
64 #include <security/pam_ext.h>
65
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. */
69 static int
70 evaluate_num(const pam_handle_t *pamh, const char *left,
71              const char *right, int (*cmp)(long long, long long))
72 {
73         long long l, r;
74         char *p;
75         int ret = PAM_SUCCESS;
76
77         errno = 0;
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;
82         }
83
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;
88         }
89
90         if (ret != PAM_SUCCESS) {
91                 return ret;
92         }
93
94         return cmp(l, r) ? PAM_SUCCESS : PAM_AUTH_ERR;
95 }
96
97 /* Simple numeric comparison callbacks. */
98 static int
99 eq(long long i, long long j)
100 {
101         return i == j;
102 }
103 static int
104 ne(long long i, long long j)
105 {
106         return i != j;
107 }
108 static int
109 lt(long long i, long long j)
110 {
111         return i < j;
112 }
113 static int
114 le(long long i, long long j)
115 {
116         return lt(i, j) || eq(i, j);
117 }
118 static int
119 gt(long long i, long long j)
120 {
121         return i > j;
122 }
123 static int
124 ge(long long i, long long j)
125 {
126         return gt(i, j) || eq(i, j);
127 }
128
129 /* Test for numeric equality. */
130 static int
131 evaluate_eqn(const pam_handle_t *pamh, const char *left, const char *right)
132 {
133         return evaluate_num(pamh, left, right, eq);
134 }
135 /* Test for string equality. */
136 static int
137 evaluate_eqs(const char *left, const char *right)
138 {
139         return (strcmp(left, right) == 0) ? PAM_SUCCESS : PAM_AUTH_ERR;
140 }
141 /* Test for numeric inequality. */
142 static int
143 evaluate_nen(const pam_handle_t *pamh, const char *left, const char *right)
144 {
145         return evaluate_num(pamh, left, right, ne);
146 }
147 /* Test for string inequality. */
148 static int
149 evaluate_nes(const char *left, const char *right)
150 {
151         return (strcmp(left, right) != 0) ? PAM_SUCCESS : PAM_AUTH_ERR;
152 }
153 /* Test for numeric less-than-ness(?) */
154 static int
155 evaluate_lt(const pam_handle_t *pamh, const char *left, const char *right)
156 {
157         return evaluate_num(pamh, left, right, lt);
158 }
159 /* Test for numeric less-than-or-equal-ness(?) */
160 static int
161 evaluate_le(const pam_handle_t *pamh, const char *left, const char *right)
162 {
163         return evaluate_num(pamh, left, right, le);
164 }
165 /* Test for numeric greater-than-ness(?) */
166 static int
167 evaluate_gt(const pam_handle_t *pamh, const char *left, const char *right)
168 {
169         return evaluate_num(pamh, left, right, gt);
170 }
171 /* Test for numeric greater-than-or-equal-ness(?) */
172 static int
173 evaluate_ge(const pam_handle_t *pamh, const char *left, const char *right)
174 {
175         return evaluate_num(pamh, left, right, ge);
176 }
177 /* Check for file glob match. */
178 static int
179 evaluate_glob(const char *left, const char *right)
180 {
181         return (fnmatch(right, left, 0) == 0) ? PAM_SUCCESS : PAM_AUTH_ERR;
182 }
183 /* Check for file glob mismatch. */
184 static int
185 evaluate_noglob(const char *left, const char *right)
186 {
187         return (fnmatch(right, left, 0) != 0) ? PAM_SUCCESS : PAM_AUTH_ERR;
188 }
189 /* Check for list match. */
190 static int
191 evaluate_inlist(const char *left, const char *right)
192 {
193         char *p;
194         /* Don't care about left containing ':'. */
195         while ((p=strstr(right, left)) != NULL) {
196                 if (p == right || *(p-1) == ':') { /* ':' is a list separator */
197                         p += strlen(left);
198                         if (*p == '\0' || *p == ':') {
199                                 return PAM_SUCCESS;
200                         }
201                 }
202                 right = strchr(p, ':');
203                 if (right == NULL)
204                         break;
205                 else
206                         ++right;
207         }
208         return PAM_AUTH_ERR;
209 }
210 /* Check for list mismatch. */
211 static int
212 evaluate_notinlist(const char *left, const char *right)
213 {
214         return evaluate_inlist(left, right) != PAM_SUCCESS ? PAM_SUCCESS : PAM_AUTH_ERR;
215 }
216 /* Return PAM_SUCCESS if the user is in the group. */
217 static int
218 evaluate_ingroup(pam_handle_t *pamh, const char *user, const char *group)
219 {
220         if (pam_modutil_user_in_group_nam_nam(pamh, user, group) == 1)
221                 return PAM_SUCCESS;
222         return PAM_AUTH_ERR;
223 }
224 /* Return PAM_SUCCESS if the user is NOT in the group. */
225 static int
226 evaluate_notingroup(pam_handle_t *pamh, const char *user, const char *group)
227 {
228         if (pam_modutil_user_in_group_nam_nam(pamh, user, group) == 0)
229                 return PAM_SUCCESS;
230         return PAM_AUTH_ERR;
231 }
232 /* Return PAM_SUCCESS if the (host,user) is in the netgroup. */
233 static int
234 evaluate_innetgr(const pam_handle_t* pamh, const char *host, const char *user, const char *group)
235 {
236 #ifdef HAVE_INNETGR
237         if (innetgr(group, host, user, NULL) == 1)
238                 return PAM_SUCCESS;
239 #else
240         pam_syslog (pamh, LOG_ERR, "pam_succeed_if does not have netgroup support");
241 #endif
242
243         return PAM_AUTH_ERR;
244 }
245 /* Return PAM_SUCCESS if the (host,user) is NOT in the netgroup. */
246 static int
247 evaluate_notinnetgr(const pam_handle_t* pamh, const char *host, const char *user, const char *group)
248 {
249 #ifdef HAVE_INNETGR
250         if (innetgr(group, host, user, NULL) == 0)
251                 return PAM_SUCCESS;
252 #else
253         pam_syslog (pamh, LOG_ERR, "pam_succeed_if does not have netgroup support");
254 #endif
255         return PAM_AUTH_ERR;
256 }
257
258 /* Match a triple. */
259 static int
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)
263 {
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);
271                 left = buf;
272         }
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);
282                 if (*pwd == NULL) {
283                         return PAM_USER_UNKNOWN;
284                 }
285         }
286         if (strcasecmp(left, "uid") == 0) {
287                 snprintf(buf, sizeof(buf), "%lu", (unsigned long) (*pwd)->pw_uid);
288                 left = buf;
289         }
290         if (strcasecmp(left, "gid") == 0) {
291                 snprintf(buf, sizeof(buf), "%lu", (unsigned long) (*pwd)->pw_gid);
292                 left = buf;
293         }
294         if (strcasecmp(left, "shell") == 0) {
295                 snprintf(buf, sizeof(buf), "%s", (*pwd)->pw_shell);
296                 left = buf;
297         }
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);
302                 left = buf;
303         }
304         if (strcasecmp(left, "service") == 0) {
305                 const void *svc;
306                 if (pam_get_item(pamh, PAM_SERVICE, &svc) != PAM_SUCCESS ||
307                         svc == NULL)
308                         svc = "";
309                 snprintf(buf, sizeof(buf), "%s", (const char *)svc);
310                 left = buf;
311         }
312         if (strcasecmp(left, "ruser") == 0) {
313                 const void *ruser;
314                 if (pam_get_item(pamh, PAM_RUSER, &ruser) != PAM_SUCCESS ||
315                         ruser == NULL)
316                         ruser = "";
317                 snprintf(buf, sizeof(buf), "%s", (const char *)ruser);
318                 left = buf;
319                 user = buf;
320         }
321         if (strcasecmp(left, "rhost") == 0) {
322                 const void *rhost;
323                 if (pam_get_item(pamh, PAM_RHOST, &rhost) != PAM_SUCCESS ||
324                         rhost == NULL)
325                         rhost = "";
326                 snprintf(buf, sizeof(buf), "%s", (const char *)rhost);
327                 left = buf;
328         }
329         if (strcasecmp(left, "tty") == 0) {
330                 const void *tty;
331                 if (pam_get_item(pamh, PAM_TTY, &tty) != PAM_SUCCESS ||
332                         tty == NULL)
333                         tty = "";
334                 snprintf(buf, sizeof(buf), "%s", (const char *)tty);
335                 left = buf;
336         }
337         /* If we have no idea what's going on, return an error. */
338         if (left != buf) {
339                 pam_syslog(pamh, LOG_ERR, "unknown attribute \"%s\"", left);
340                 return PAM_SERVICE_ERR;
341         }
342         if (debug) {
343                 pam_syslog(pamh, LOG_DEBUG, "'%s' resolves to '%s'",
344                            attribute, left);
345         }
346
347         /* Attribute value < some threshold. */
348         if ((strcasecmp(qual, "<") == 0) ||
349             (strcasecmp(qual, "lt") == 0)) {
350                 return evaluate_lt(pamh, left, right);
351         }
352         /* Attribute value <= some threshold. */
353         if ((strcasecmp(qual, "<=") == 0) ||
354             (strcasecmp(qual, "le") == 0)) {
355                 return evaluate_le(pamh, left, right);
356         }
357         /* Attribute value > some threshold. */
358         if ((strcasecmp(qual, ">") == 0) ||
359             (strcasecmp(qual, "gt") == 0)) {
360                 return evaluate_gt(pamh, left, right);
361         }
362         /* Attribute value >= some threshold. */
363         if ((strcasecmp(qual, ">=") == 0) ||
364             (strcasecmp(qual, "ge") == 0)) {
365                 return evaluate_ge(pamh, left, right);
366         }
367         /* Attribute value == some threshold. */
368         if (strcasecmp(qual, "eq") == 0) {
369                 return evaluate_eqn(pamh, left, right);
370         }
371         /* Attribute value = some string. */
372         if (strcasecmp(qual, "=") == 0) {
373                 return evaluate_eqs(left, right);
374         }
375         /* Attribute value != some threshold. */
376         if (strcasecmp(qual, "ne") == 0) {
377                 return evaluate_nen(pamh, left, right);
378         }
379         /* Attribute value != some string. */
380         if (strcasecmp(qual, "!=") == 0) {
381                 return evaluate_nes(left, right);
382         }
383         /* Attribute value matches some pattern. */
384         if ((strcasecmp(qual, "=~") == 0) ||
385             (strcasecmp(qual, "glob") == 0)) {
386                 return evaluate_glob(left, right);
387         }
388         if ((strcasecmp(qual, "!~") == 0) ||
389             (strcasecmp(qual, "noglob") == 0)) {
390                 return evaluate_noglob(left, right);
391         }
392         /* Attribute value matches item in list. */
393         if (strcasecmp(qual, "in") == 0) {
394                 return evaluate_inlist(left, right);
395         }
396         if (strcasecmp(qual, "notin") == 0) {
397                 return evaluate_notinlist(left, right);
398         }
399         /* User is in this group. */
400         if (strcasecmp(qual, "ingroup") == 0) {
401                 return evaluate_ingroup(pamh, user, right);
402         }
403         /* User is not in this group. */
404         if (strcasecmp(qual, "notingroup") == 0) {
405                 return evaluate_notingroup(pamh, user, right);
406         }
407         /* (Rhost, user) is in this netgroup. */
408         if (strcasecmp(qual, "innetgr") == 0) {
409                 const void *rhost;
410                 if (pam_get_item(pamh, PAM_RHOST, &rhost) != PAM_SUCCESS)
411                         rhost = NULL;
412                 return evaluate_innetgr(pamh, rhost, user, right);
413         }
414         /* (Rhost, user) is not in this group. */
415         if (strcasecmp(qual, "notinnetgr") == 0) {
416                 const void *rhost;
417                 if (pam_get_item(pamh, PAM_RHOST, &rhost) != PAM_SUCCESS)
418                         rhost = NULL;
419                 return evaluate_notinnetgr(pamh, rhost, user, right);
420         }
421         /* Fail closed. */
422         return PAM_SERVICE_ERR;
423 }
424
425 int
426 pam_sm_authenticate (pam_handle_t *pamh, int flags UNUSED,
427                      int argc, const char **argv)
428 {
429         const void *prompt;
430         const char *user;
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;
435
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)) {
439                 prompt = "login: ";
440         }
441
442         quiet_fail = 0;
443         quiet_succ = 0;
444         audit = 0;
445         for (use_uid = 0, debug = 0, i = 0; i < argc; i++) {
446                 if (strcmp(argv[i], "debug") == 0) {
447                         debug++;
448                 }
449                 if (strcmp(argv[i], "use_uid") == 0) {
450                         use_uid++;
451                 }
452                 if (strcmp(argv[i], "quiet") == 0) {
453                         quiet_fail++;
454                         quiet_succ++;
455                 }
456                 if (strcmp(argv[i], "quiet_fail") == 0) {
457                         quiet_fail++;
458                 }
459                 if (strcmp(argv[i], "quiet_success") == 0) {
460                         quiet_succ++;
461                 }
462                 if (strcmp(argv[i], "audit") == 0) {
463                         audit++;
464                 }
465         }
466
467         if (use_uid) {
468                 /* Get information about the user. */
469                 pwd = pam_modutil_getpwuid(pamh, getuid());
470                 if (pwd == NULL) {
471                         pam_syslog(pamh, LOG_ERR,
472                                    "error retrieving information about user %lu",
473                                    (unsigned long)getuid());
474                         return PAM_USER_UNKNOWN;
475                 }
476                 user = pwd->pw_name;
477         } else {
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));
484                         return ret;
485                 }
486
487                 /* Postpone requesting password data until it is needed */
488         }
489
490         /* Walk the argument list. */
491         count = 0;
492         left = qual = right = NULL;
493         for (i = 0; i < argc; i++) {
494                 if (strcmp(argv[i], "debug") == 0) {
495                         continue;
496                 }
497                 if (strcmp(argv[i], "use_uid") == 0) {
498                         continue;
499                 }
500                 if (strcmp(argv[i], "quiet") == 0) {
501                         continue;
502                 }
503                 if (strcmp(argv[i], "quiet_fail") == 0) {
504                         continue;
505                 }
506                 if (strcmp(argv[i], "quiet_success") == 0) {
507                         continue;
508                 }
509                 if (strcmp(argv[i], "audit") == 0) {
510                         continue;
511                 }
512                 if (left == NULL) {
513                         left = argv[i];
514                         continue;
515                 }
516                 if (qual == NULL) {
517                         qual = argv[i];
518                         continue;
519                 }
520                 if (right == NULL) {
521                         right = argv[i];
522                         if (right == NULL)
523                                 continue;
524
525                         count++;
526                         ret = evaluate(pamh, debug,
527                                        left, qual, right,
528                                        &pwd, user);
529                         if (ret == PAM_USER_UNKNOWN && audit)
530                                 pam_syslog(pamh, LOG_NOTICE,
531                                            "error retrieving information about user %s",
532                                            user);
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;
540                                 break;
541                         }
542                         else
543                                 if(!quiet_succ)
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;
549                         continue;
550                 }
551         }
552
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");
560         }
561
562         return ret;
563 }
564
565 int
566 pam_sm_setcred(pam_handle_t *pamh UNUSED, int flags UNUSED,
567                int argc UNUSED, const char **argv UNUSED)
568 {
569         return PAM_IGNORE;
570 }
571
572 int
573 pam_sm_acct_mgmt(pam_handle_t *pamh, int flags, int argc, const char **argv)
574 {
575         return pam_sm_authenticate(pamh, flags, argc, argv);
576 }
577
578 int
579 pam_sm_open_session(pam_handle_t *pamh, int flags, int argc, const char **argv)
580 {
581         return pam_sm_authenticate(pamh, flags, argc, argv);
582 }
583
584 int
585 pam_sm_close_session(pam_handle_t *pamh, int flags, int argc, const char **argv)
586 {
587         return pam_sm_authenticate(pamh, flags, argc, argv);
588 }
589
590 int
591 pam_sm_chauthtok(pam_handle_t *pamh, int flags, int argc, const char **argv)
592 {
593         return pam_sm_authenticate(pamh, flags, argc, argv);
594 }