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