]> granicus.if.org Git - linux-pam/blob - modules/pam_succeed_if/pam_succeed_if.c
Relevant BUGIDs:
[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 #define _GNU_SOURCE
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 <security/pam_modules.h>
56 #include <security/_pam_modutil.h>
57
58 #define MODULE "pam_succeed_if"
59
60 static void
61 log_error(int priority, const char *fmt, ...)
62 {
63         va_list va;
64         char *fmt2;
65         fmt2 = malloc(strlen(fmt) + strlen(MODULE) + 3);
66         va_start(va, fmt);
67         if (fmt2 == NULL) {
68                 vsyslog(LOG_AUTHPRIV | priority, fmt, va);
69         } else {
70                 snprintf(fmt2, strlen(fmt) + strlen(MODULE) + 3,
71                          "%s: %s", MODULE, fmt);
72                 vsyslog(LOG_AUTHPRIV | priority, fmt2, va);
73                 free(fmt2);
74         }
75         va_end(va);
76 }
77
78 /* Basically, run cmp(atol(left), atol(right)), returning PAM_SUCCESS if
79  * the function returns non-zero, PAM_AUTH_ERR if it returns zero, and
80  * PAM_SYSTEM_ERR if the arguments can't be parsed as numbers. */
81 static int
82 evaluate_num(const char *left, const char *right, int (*cmp)(int, int))
83 {
84         long l, r;
85         char *p;
86         int ret = PAM_SUCCESS;
87
88         errno = 0;
89         l = strtol(left, &p, 0);
90         if ((p == NULL) || (*p != '\0') || errno) {
91                 log_error(LOG_INFO, "\"%s\" is not a number", left);
92                 ret = PAM_SERVICE_ERR;
93         }
94
95         r = strtol(right, &p, 0);
96         if ((p == NULL) || (*p != '\0') || errno) {
97                 log_error(LOG_INFO, "\"%s\" is not a number", right);
98                 ret = PAM_SERVICE_ERR;
99         }
100
101         if (ret != PAM_SUCCESS) {
102                 return ret;
103         }
104                 
105         return cmp(l, r) ? PAM_SUCCESS : PAM_AUTH_ERR;
106 }
107
108 /* Simple numeric comparison callbacks. */
109 static int
110 eq(int i, int j)
111 {
112         return i == j;
113 }
114 static int
115 ne(int i, int j)
116 {
117         return i != j;
118 }
119 static int
120 lt(int i, int j)
121 {
122         return i < j;
123 }
124 static int
125 le(int i, int j)
126 {
127         return lt(i, j) || eq(i, j);
128 }
129 static int
130 gt(int i, int j)
131 {
132         return i > j;
133 }
134 static int
135 ge(int i, int j)
136 {
137         return gt(i, j) || eq(i, j);
138 }
139
140 /* Test for numeric equality. */
141 static int
142 evaluate_eqn(const char *left, const char *right)
143 {
144         return evaluate_num(left, right, eq);
145 }
146 /* Test for string equality. */
147 static int
148 evaluate_eqs(const char *left, const char *right)
149 {
150         return (strcmp(left, right) == 0) ? PAM_SUCCESS : PAM_AUTH_ERR;
151 }
152 /* Test for numeric inequality. */
153 static int
154 evaluate_nen(const char *left, const char *right)
155 {
156         return evaluate_num(left, right, ne);
157 }
158 /* Test for string inequality. */
159 static int
160 evaluate_nes(const char *left, const char *right)
161 {
162         return (strcmp(left, right) != 0) ? PAM_SUCCESS : PAM_AUTH_ERR;
163 }
164 /* Test for numeric less-than-ness(?) */
165 static int
166 evaluate_lt(const char *left, const char *right)
167 {
168         return evaluate_num(left, right, lt);
169 }
170 /* Test for numeric less-than-or-equal-ness(?) */
171 static int
172 evaluate_le(const char *left, const char *right)
173 {
174         return evaluate_num(left, right, le);
175 }
176 /* Test for numeric greater-than-ness(?) */
177 static int
178 evaluate_gt(const char *left, const char *right)
179 {
180         return evaluate_num(left, right, gt);
181 }
182 /* Test for numeric greater-than-or-equal-ness(?) */
183 static int
184 evaluate_ge(const char *left, const char *right)
185 {
186         return evaluate_num(left, right, ge);
187 }
188 /* Check for file glob match. */
189 static int
190 evaluate_glob(const char *left, const char *right)
191 {
192         return (fnmatch(right, left, 0) == 0) ? PAM_SUCCESS : PAM_AUTH_ERR;
193 }
194 /* Check for file glob mismatch. */
195 static int
196 evaluate_noglob(const char *left, const char *right)
197 {
198         return (fnmatch(right, left, 0) != 0) ? PAM_SUCCESS : PAM_AUTH_ERR;
199 }
200 /* Return PAM_SUCCESS if the user is in the group. */
201 static int
202 evaluate_ingroup(pam_handle_t *pamh, const char *user, const char *group)
203 {
204         int ret;
205         ret = _pammodutil_user_in_group_nam_nam(pamh, user, group);
206         switch (ret) {
207         case 1:
208                 return PAM_SUCCESS;
209                 break;
210         default:
211                 break;
212         }
213         return PAM_AUTH_ERR;
214 }
215 /* Return PAM_SUCCESS if the user is NOT in the group. */
216 static int
217 evaluate_notingroup(pam_handle_t *pamh, const char *user, const char *group)
218 {
219         int ret;
220         ret = _pammodutil_user_in_group_nam_nam(pamh, user, group);
221         switch (ret) {
222         case 0:
223                 return PAM_SUCCESS;
224                 break;
225         default:
226                 break;
227         }
228         return PAM_AUTH_ERR;
229 }
230
231 /* Match a triple. */
232 static int
233 evaluate(pam_handle_t *pamh, int debug,
234          const char *left, const char *qual, const char *right,
235          struct passwd *pwd)
236 {
237         char buf[LINE_MAX] = "";
238         const char *attribute = left;
239         /* Figure out what we're evaluating here, and convert it to a string.*/
240         if ((strcasecmp(left, "login") == 0) ||
241             (strcasecmp(left, "name") == 0) ||
242             (strcasecmp(left, "user") == 0)) {
243                 snprintf(buf, sizeof(buf), "%s", pwd->pw_name);
244                 left = buf;
245         }
246         if (strcasecmp(left, "uid") == 0) {
247                 snprintf(buf, sizeof(buf), "%lu", (unsigned long) pwd->pw_uid);
248                 left = buf;
249         }
250         if (strcasecmp(left, "gid") == 0) {
251                 snprintf(buf, sizeof(buf), "%lu", (unsigned long) pwd->pw_gid);
252                 left = buf;
253         }
254         if (strcasecmp(left, "shell") == 0) {
255                 snprintf(buf, sizeof(buf), "%s", pwd->pw_shell);
256                 left = buf;
257         }
258         if ((strcasecmp(left, "home") == 0) ||
259             (strcasecmp(left, "dir") == 0) ||
260             (strcasecmp(left, "homedir") == 0)) {
261                 snprintf(buf, sizeof(buf), "%s", pwd->pw_dir);
262                 left = buf;
263         }
264         /* If we have no idea what's going on, return an error. */
265         if (left != buf) {
266                 log_error(LOG_CRIT, "unknown attribute \"%s\"", left);
267                 return PAM_SERVICE_ERR;
268         }
269         if (debug) {
270                 log_error(LOG_DEBUG, "'%s' resolves to '%s'", attribute, left);
271         }
272
273         /* Attribute value < some threshold. */
274         if ((strcasecmp(qual, "<") == 0) ||
275             (strcasecmp(qual, "lt") == 0)) {
276                 return evaluate_lt(left, right);
277         }
278         /* Attribute value <= some threshold. */
279         if ((strcasecmp(qual, "<=") == 0) ||
280             (strcasecmp(qual, "le") == 0)) {
281                 return evaluate_le(left, right);
282         }
283         /* Attribute value > some threshold. */
284         if ((strcasecmp(qual, ">") == 0) ||
285             (strcasecmp(qual, "gt") == 0)) {
286                 return evaluate_gt(left, right);
287         }
288         /* Attribute value >= some threshold. */
289         if ((strcasecmp(qual, ">=") == 0) ||
290             (strcasecmp(qual, "ge") == 0)) {
291                 return evaluate_ge(left, right);
292         }
293         /* Attribute value == some threshold. */
294         if (strcasecmp(qual, "eq") == 0) {
295                 return evaluate_eqn(left, right);
296         }
297         /* Attribute value = some string. */
298         if (strcasecmp(qual, "=") == 0) {
299                 return evaluate_eqs(left, right);
300         }
301         /* Attribute value != some threshold. */
302         if (strcasecmp(qual, "ne") == 0) {
303                 return evaluate_nen(left, right);
304         }
305         /* Attribute value != some string. */
306         if (strcasecmp(qual, "!=") == 0) {
307                 return evaluate_nes(left, right);
308         }
309         /* Attribute value matches some pattern. */
310         if ((strcasecmp(qual, "=~") == 0) ||
311             (strcasecmp(qual, "glob") == 0)) {
312                 return evaluate_glob(left, right);
313         }
314         if ((strcasecmp(qual, "!~") == 0) ||
315             (strcasecmp(qual, "noglob") == 0)) {
316                 return evaluate_noglob(left, right);
317         }
318         /* User is in this group. */
319         if (strcasecmp(qual, "ingroup") == 0) {
320                 return evaluate_ingroup(pamh, pwd->pw_name, right);
321         }
322         /* User is not in this group. */
323         if (strcasecmp(qual, "notingroup") == 0) {
324                 return evaluate_notingroup(pamh, pwd->pw_name, right);
325         }
326         /* Fail closed. */
327         return PAM_SERVICE_ERR;
328 }
329
330 int
331 pam_sm_authenticate(pam_handle_t *pamh, int flags, int argc, const char **argv)
332 {
333         const char *prompt;
334         const char *user;
335         struct passwd *pwd;
336         int ret, i, count, use_uid, debug;
337         const char *left, *right, *qual;
338         int quiet_fail, quiet_succ;
339
340         /* Get the user prompt. */
341         ret = pam_get_item(pamh, PAM_USER_PROMPT, (const void**) &prompt);
342         if ((ret != PAM_SUCCESS) || (prompt == NULL) || (strlen(prompt) == 0)) {
343                 prompt = "login: ";
344         }
345
346         quiet_fail = 0;
347         quiet_succ = 0;
348         for (use_uid = 0, debug = 0, i = 0; i < argc; i++) {
349                 if (strcmp(argv[i], "debug") == 0) {
350                         debug++;
351                 }
352                 if (strcmp(argv[i], "use_uid") == 0) {
353                         use_uid++;
354                 }
355                 if (strcmp(argv[i], "quiet") == 0) {
356                         quiet_fail++;
357                         quiet_succ++;
358                 }
359                 if (strcmp(argv[i], "quiet_fail") == 0) {
360                         quiet_fail++;
361                 }
362                 if (strcmp(argv[i], "quiet_success") == 0) {
363                         quiet_succ++;
364                 }
365         }
366
367         if (use_uid) {
368                 /* Get information about the user. */
369                 pwd = _pammodutil_getpwuid(pamh, getuid());
370                 if (pwd == NULL) {
371                         log_error(LOG_CRIT,
372                                   "error retrieving information about user %ld",
373                                   (long)getuid());
374                         return PAM_SERVICE_ERR;
375                 }
376         } else {
377                 /* Get the user's name. */
378                 ret = pam_get_user(pamh, &user, prompt);
379                 if ((ret != PAM_SUCCESS) || (user == NULL)) {
380                         log_error(LOG_CRIT, "error retrieving user name: %s",
381                                   pam_strerror(pamh, ret));
382                         return ret;
383                 }
384
385                 /* Get information about the user. */
386                 pwd = _pammodutil_getpwnam(pamh, user);
387                 if (pwd == NULL) {
388                         log_error(LOG_CRIT,
389                                   "error retrieving information about user %s",
390                                   user);
391                         return PAM_SERVICE_ERR;
392                 }
393         }
394
395         /* Walk the argument list. */
396         i = count = 0;
397         left = qual = right = NULL;
398         while (i <= argc) {
399                 if ((left != NULL) && (qual != NULL) && (right != NULL)) {
400                         ret = evaluate(pamh, debug,
401                                        left, qual, right,
402                                        pwd);
403                         if (ret != PAM_SUCCESS) {
404                                 if(!quiet_fail)
405                                         log_error(LOG_INFO,
406                                                   "requirement \"%s %s %s\" "
407                                                   "not met by user \"%s\"",
408                                                   left, qual, right, user);
409                                 break;
410                         }
411                         else
412                                 if(!quiet_succ)
413                                         log_error(LOG_INFO,
414                                                   "requirement \"%s %s %s\" "
415                                                   "was met by user \"%s\"",
416                                                   left, qual, right, user);
417                         left = qual = right = NULL;
418                 }
419                 if ((i < argc) && (strcmp(argv[i], "debug") == 0)) {
420                         i++;
421                         continue;
422                 }
423                 if ((i < argc) && (strcmp(argv[i], "use_uid") == 0)) {
424                         i++;
425                         continue;
426                 }
427                 if ((i < argc) && (strcmp(argv[i], "quiet") == 0)) {
428                         i++;
429                         continue;
430                 }
431                 if ((i < argc) && (strcmp(argv[i], "quiet_fail") == 0)) {
432                         i++;
433                         continue;
434                 }
435                 if ((i < argc) && (strcmp(argv[i], "quiet_success") == 0)) {
436                         i++;
437                         continue;
438                 }
439                 if ((i < argc) && (left == NULL)) {
440                         left = argv[i++];
441                         count++;
442                         continue;
443                 }
444                 if ((i < argc) && (qual == NULL)) {
445                         qual = argv[i++];
446                         count++;
447                         continue;
448                 }
449                 if ((i < argc) && (right == NULL)) {
450                         right = argv[i++];
451                         count++;
452                         continue;
453                 }
454                 i++;
455         }
456
457         return ret;
458 }
459
460 int
461 pam_sm_setcred(pam_handle_t *pamh, int flags, int argc, const char **argv)
462 {
463         return PAM_SUCCESS;
464 }
465
466 int
467 pam_sm_acct_mgmt(pam_handle_t *pamh, int flags, int argc, const char **argv)
468 {
469         return pam_sm_authenticate(pamh, flags, argc, argv);
470 }