]> granicus.if.org Git - linux-pam/blob - modules/pam_listfile/pam_listfile.c
Relevant BUGIDs:
[linux-pam] / modules / pam_listfile / pam_listfile.c
1 /*
2  * by Elliot Lee <sopwith@redhat.com>, Red Hat Software. July 25, 1996.
3  * log refused access error christopher mccrory <chrismcc@netus.com> 1998/7/11
4  *
5  * This code began life as the pam_rootok module.
6  */
7
8 #include "config.h"
9
10 #include <stdio.h>
11 #include <stdlib.h>
12 #include <sys/types.h>
13 #include <sys/stat.h>
14 #include <unistd.h>
15 #include <syslog.h>
16 #include <stdarg.h>
17 #include <string.h>
18 #include <pwd.h>
19 #include <grp.h>
20
21 #ifdef DEBUG
22 #include <assert.h>
23 #endif
24
25 /*
26  * here, we make a definition for the externally accessible function
27  * in this file (this definition is required for static a module
28  * but strongly encouraged generally) it is used to instruct the
29  * modules include file to define the function prototypes.
30  */
31
32 #define PAM_SM_AUTH
33 #define PAM_SM_ACCOUNT
34 #define PAM_SM_PASSWORD
35 #define PAM_SM_SESSION
36
37 #include <security/pam_modules.h>
38 #include <security/_pam_macros.h>
39 #include <security/pam_modutil.h>
40 #include <security/pam_ext.h>
41
42 /* checks if a user is on a list of members */
43 static int is_on_list(char * const *list, const char *member)
44 {
45     while (*list) {
46         if (strcmp(*list, member) == 0)
47             return 1;
48         list++;
49     }
50     return 0;
51 }
52
53 /* --- authentication management functions (only) --- */
54
55 /* Extended Items that are not directly available via pam_get_item() */
56 #define EI_GROUP (1 << 0)
57 #define EI_SHELL (1 << 1)
58
59 /* Constants for apply= parameter */
60 #define APPLY_TYPE_NULL         0
61 #define APPLY_TYPE_NONE         1
62 #define APPLY_TYPE_USER         2
63 #define APPLY_TYPE_GROUP        3
64
65 #define LESSER(a, b) ((a) < (b) ? (a) : (b))
66
67 PAM_EXTERN int
68 pam_sm_authenticate (pam_handle_t *pamh, int flags UNUSED,
69                      int argc, const char **argv)
70 {
71     int retval, i, citem=0, extitem=0, onerr=PAM_SERVICE_ERR, sense=2;
72     const void *void_citemp;
73     const char *citemp;
74     char *ifname=NULL;
75     char aline[256];
76     char mybuf[256],myval[256];
77     struct stat fileinfo;
78     FILE *inf;
79     char apply_val[256];
80     int apply_type;
81
82     /* Stuff for "extended" items */
83     struct passwd *userinfo;
84     struct group *grpinfo;
85     char *itemlist[256]; /* Maximum of 256 items */
86
87     apply_type=APPLY_TYPE_NULL;
88     memset(apply_val,0,sizeof(apply_val));
89
90     for(i=0; i < argc; i++) {
91         {
92             const char *junk;
93
94             memset(mybuf,'\0',sizeof(mybuf));
95             memset(myval,'\0',sizeof(mybuf));
96             junk = strchr(argv[i], '=');
97             if((junk == NULL) || (junk - argv[i]) >= (int) sizeof(mybuf)) {
98                 pam_syslog(pamh,LOG_ERR, "Bad option: \"%s\"",
99                          argv[i]);
100                 continue;
101             }
102             strncpy(mybuf, argv[i],
103                     LESSER(junk - argv[i], (int)sizeof(mybuf) - 1));
104             strncpy(myval, junk + 1, sizeof(myval) - 1);
105         }
106         if(!strcmp(mybuf,"onerr"))
107             if(!strcmp(myval,"succeed"))
108                 onerr = PAM_SUCCESS;
109             else if(!strcmp(myval,"fail"))
110                 onerr = PAM_SERVICE_ERR;
111             else {
112                 if (ifname) free (ifname);
113                 return PAM_SERVICE_ERR;
114             }
115         else if(!strcmp(mybuf,"sense"))
116             if(!strcmp(myval,"allow"))
117                 sense=0;
118             else if(!strcmp(myval,"deny"))
119                 sense=1;
120             else {
121                 if (ifname) free (ifname);
122                 return onerr;
123             }
124         else if(!strcmp(mybuf,"file")) {
125             if (ifname) free (ifname);
126             ifname = (char *)malloc(strlen(myval)+1);
127             if (!ifname)
128                 return PAM_BUF_ERR;
129             strcpy(ifname,myval);
130         } else if(!strcmp(mybuf,"item"))
131             if(!strcmp(myval,"user"))
132                 citem = PAM_USER;
133             else if(!strcmp(myval,"tty"))
134                 citem = PAM_TTY;
135             else if(!strcmp(myval,"rhost"))
136                 citem = PAM_RHOST;
137             else if(!strcmp(myval,"ruser"))
138                 citem = PAM_RUSER;
139             else { /* These items are related to the user, but are not
140                       directly gettable with pam_get_item */
141                 citem = PAM_USER;
142                 if(!strcmp(myval,"group"))
143                     extitem = EI_GROUP;
144                 else if(!strcmp(myval,"shell"))
145                     extitem = EI_SHELL;
146                 else
147                     citem = 0;
148             } else if(!strcmp(mybuf,"apply")) {
149                 apply_type=APPLY_TYPE_NONE;
150                 memset(apply_val,'\0',sizeof(apply_val));
151                 if (myval[0]=='@') {
152                     apply_type=APPLY_TYPE_GROUP;
153                     strncpy(apply_val,myval+1,sizeof(apply_val)-1);
154                 } else {
155                     apply_type=APPLY_TYPE_USER;
156                     strncpy(apply_val,myval,sizeof(apply_val)-1);
157                 }
158             } else {
159                 free(ifname);
160                 pam_syslog(pamh,LOG_ERR, "Unknown option: %s",mybuf);
161                 return onerr;
162             }
163     }
164
165     if(!citem) {
166         pam_syslog(pamh,LOG_ERR,
167                   "Unknown item or item not specified");
168         free(ifname);
169         return onerr;
170     } else if(!ifname) {
171         pam_syslog(pamh,LOG_ERR, "List filename not specified");
172         return onerr;
173     } else if(sense == 2) {
174         pam_syslog(pamh,LOG_ERR,
175                   "Unknown sense or sense not specified");
176         free(ifname);
177         return onerr;
178     } else if(
179               (apply_type==APPLY_TYPE_NONE) ||
180               ((apply_type!=APPLY_TYPE_NULL) && (*apply_val=='\0'))
181               ) {
182         pam_syslog(pamh,LOG_ERR,
183                   "Invalid usage for apply= parameter");
184         free (ifname);
185         return onerr;
186     }
187
188     /* Check if it makes sense to use the apply= parameter */
189     if (apply_type != APPLY_TYPE_NULL) {
190         if((citem==PAM_USER) || (citem==PAM_RUSER)) {
191             pam_syslog(pamh,LOG_WARNING,
192                       "Non-sense use for apply= parameter");
193             apply_type=APPLY_TYPE_NULL;
194         }
195         if(extitem && (extitem==EI_GROUP)) {
196             pam_syslog(pamh,LOG_WARNING,
197                       "Non-sense use for apply= parameter");
198             apply_type=APPLY_TYPE_NULL;
199         }
200     }
201
202     /* Short-circuit - test if this session apply for this user */
203     {
204         const char *user_name;
205         int rval;
206
207         rval=pam_get_user(pamh,&user_name,NULL);
208         if((rval==PAM_SUCCESS) && user_name && user_name[0]) {
209             /* Got it ? Valid ? */
210             if(apply_type==APPLY_TYPE_USER) {
211                 if(strcmp(user_name, apply_val)) {
212                     /* Does not apply to this user */
213 #ifdef DEBUG
214                     pam_syslog(pamh,LOG_DEBUG,
215                               "don't apply: apply=%s, user=%s",
216                              apply_val,user_name);
217 #endif /* DEBUG */
218                     free(ifname);
219                     return PAM_IGNORE;
220                 }
221             } else if(apply_type==APPLY_TYPE_GROUP) {
222                 if(!pam_modutil_user_in_group_nam_nam(pamh,user_name,apply_val)) {
223                     /* Not a member of apply= group */
224 #ifdef DEBUG
225                     pam_syslog(pamh,LOG_DEBUG,
226
227                              "don't apply: %s not a member of group %s",
228                              user_name,apply_val);
229 #endif /* DEBUG */
230                     free(ifname);
231                     return PAM_IGNORE;
232                 }
233             }
234         }
235     }
236
237     retval = pam_get_item(pamh,citem,&void_citemp);
238     citemp = void_citemp;
239     if(retval != PAM_SUCCESS) {
240         return onerr;
241     }
242     if((citem == PAM_USER) && !citemp) {
243         retval = pam_get_user(pamh,&citemp,NULL);
244         if (retval != PAM_SUCCESS || !citemp) {
245             free(ifname);
246             return PAM_SERVICE_ERR;
247         }
248     }
249     if((citem == PAM_TTY) && citemp) {
250         /* Normalize the TTY name. */
251         if(strncmp(citemp, "/dev/", 5) == 0) {
252             citemp += 5;
253         }
254     }
255
256     if(!citemp || (strlen(citemp) == 0)) {
257         free(ifname);
258         /* The item was NULL - we are sure not to match */
259         return sense?PAM_SUCCESS:PAM_AUTH_ERR;
260     }
261
262     if(extitem) {
263         switch(extitem) {
264             case EI_GROUP:
265                 userinfo = pam_modutil_getpwnam(pamh, citemp);
266                 if (userinfo == NULL) {
267                     pam_syslog(pamh,LOG_ERR, "getpwnam(%s) failed",
268                              citemp);
269                     free(ifname);
270                     return onerr;
271                 }
272                 grpinfo = pam_modutil_getgrgid(pamh, userinfo->pw_gid);
273                 if (grpinfo == NULL) {
274                     pam_syslog(pamh,LOG_ERR, "getgrgid(%d) failed",
275                              (int)userinfo->pw_gid);
276                     free(ifname);
277                     return onerr;
278                 }
279                 itemlist[0] = x_strdup(grpinfo->gr_name);
280                 setgrent();
281                 for (i=1; (i < (int)(sizeof(itemlist)/sizeof(itemlist[0])-1)) &&
282                          (grpinfo = getgrent()); ) {
283                     if (is_on_list(grpinfo->gr_mem,citemp)) {
284                         itemlist[i++] = x_strdup(grpinfo->gr_name);
285                     }
286                 }
287                 endgrent();
288                 itemlist[i] = NULL;
289                 break;
290             case EI_SHELL:
291                 /* Assume that we have already gotten PAM_USER in
292                    pam_get_item() - a valid assumption since citem
293                    gets set to PAM_USER in the extitem switch */
294                 userinfo = pam_modutil_getpwnam(pamh, citemp);
295                 if (userinfo == NULL) {
296                     pam_syslog(pamh,LOG_ERR, "getpwnam(%s) failed",
297                              citemp);
298                     free(ifname);
299                     return onerr;
300                 }
301                 citemp = userinfo->pw_shell;
302                 break;
303             default:
304                 pam_syslog(pamh,LOG_ERR,
305
306                          "Internal weirdness, unknown extended item %d",
307                          extitem);
308                 free(ifname);
309                 return onerr;
310         }
311     }
312 #ifdef DEBUG
313     pam_syslog(pamh,LOG_INFO,
314
315              "Got file = %s, item = %d, value = %s, sense = %d",
316              ifname, citem, citemp, sense);
317 #endif
318     if(lstat(ifname,&fileinfo)) {
319         pam_syslog(pamh,LOG_ERR, "Couldn't open %s",ifname);
320         free(ifname);
321         return onerr;
322     }
323
324     if((fileinfo.st_mode & S_IWOTH)
325        || !S_ISREG(fileinfo.st_mode)) {
326         /* If the file is world writable or is not a
327            normal file, return error */
328         pam_syslog(pamh,LOG_ERR,
329                  "%s is either world writable or not a normal file",
330                  ifname);
331         free(ifname);
332         return PAM_AUTH_ERR;
333     }
334
335     inf = fopen(ifname,"r");
336     if(inf == NULL) { /* Check that we opened it successfully */
337         if (onerr == PAM_SERVICE_ERR) {
338             /* Only report if it's an error... */
339             pam_syslog(pamh,LOG_ERR,  "Error opening %s", ifname);
340         }
341         free(ifname);
342         return onerr;
343     }
344     /* There should be no more errors from here on */
345     retval=PAM_AUTH_ERR;
346     /* This loop assumes that PAM_SUCCESS == 0
347        and PAM_AUTH_ERR != 0 */
348 #ifdef DEBUG
349     assert(PAM_SUCCESS == 0);
350     assert(PAM_AUTH_ERR != 0);
351 #endif
352     if(extitem == EI_GROUP) {
353         while((fgets(aline,sizeof(aline),inf) != NULL)
354               && retval) {
355             if(strlen(aline) == 0)
356                 continue;
357             if(aline[strlen(aline) - 1] == '\n')
358                 aline[strlen(aline) - 1] = '\0';
359             for(i=0;itemlist[i];)
360                 /* If any of the items match, strcmp() == 0, and we get out
361                    of this loop */
362                 retval = (strcmp(aline,itemlist[i++]) && retval);
363         }
364         for(i=0;itemlist[i];)
365             free(itemlist[i++]);
366     } else {
367         while((fgets(aline,sizeof(aline),inf) != NULL)
368               && retval) {
369             char *a = aline;
370             if(strlen(aline) == 0)
371                 continue;
372             if(aline[strlen(aline) - 1] == '\n')
373                 aline[strlen(aline) - 1] = '\0';
374             if(strlen(aline) == 0)
375                 continue;
376             if(aline[strlen(aline) - 1] == '\r')
377                 aline[strlen(aline) - 1] = '\0';
378             if(citem == PAM_TTY)
379                 if(strncmp(a, "/dev/", 5) == 0)
380                     a += 5;
381             retval = strcmp(a,citemp);
382         }
383     }
384     fclose(inf);
385     free(ifname);
386     if ((sense && retval) || (!sense && !retval)) {
387 #ifdef DEBUG
388         pam_syslog(pamh,LOG_INFO,
389                  "Returning PAM_SUCCESS, retval = %d", retval);
390 #endif
391         return PAM_SUCCESS;
392     }
393     else {
394         const void *service;
395         const char *user_name;
396 #ifdef DEBUG
397         pam_syslog(pamh,LOG_INFO,
398                  "Returning PAM_AUTH_ERR, retval = %d", retval);
399 #endif
400         (void) pam_get_item(pamh, PAM_SERVICE, &service);
401         (void) pam_get_user(pamh, &user_name, NULL);
402         pam_syslog (pamh, LOG_ALERT, "Refused user %s for service %s",
403                     user_name, (const char *)service);
404         return PAM_AUTH_ERR;
405     }
406 }
407
408 PAM_EXTERN int
409 pam_sm_setcred (pam_handle_t *pamh UNUSED, int flags UNUSED,
410                 int argc UNUSED, const char **argv UNUSED)
411 {
412     return PAM_SUCCESS;
413 }
414
415 PAM_EXTERN int
416 pam_sm_acct_mgmt (pam_handle_t *pamh, int flags,
417                   int argc, const char **argv)
418 {
419     return pam_sm_authenticate(pamh, flags, argc, argv);
420 }
421
422 PAM_EXTERN int
423 pam_sm_open_session (pam_handle_t *pamh, int flags,
424                      int argc, const char **argv)
425 {
426     return pam_sm_authenticate(pamh, flags, argc, argv);
427 }
428
429 PAM_EXTERN int
430 pam_sm_close_session (pam_handle_t *pamh, int flags,
431                       int argc, const char **argv)
432 {
433     return pam_sm_authenticate(pamh, flags, argc, argv);
434 }
435
436 PAM_EXTERN int
437 pam_sm_chauthtok (pam_handle_t *pamh, int flags,
438                   int argc, const char **argv)
439 {
440     return pam_sm_authenticate(pamh, flags, argc, argv);
441 }
442
443 #ifdef PAM_STATIC
444
445 /* static module data */
446
447 struct pam_module _pam_listfile_modstruct = {
448     "pam_listfile",
449     pam_sm_authenticate,
450     pam_sm_setcred,
451     pam_sm_acct_mgmt,
452     pam_sm_open_session,
453     pam_sm_close_session,
454     pam_sm_chauthtok,
455 };
456
457 #endif /* PAM_STATIC */
458
459 /* end of module definition */