Initial import from FreeBSD RELENG_4:
[games.git] / contrib / libpam / libpam / pam_dispatch.c
1 /* pam_dispatch.c - handles module function dispatch */
2
3 /*
4  * $Id: pam_dispatch.c,v 1.8 1997/01/04 20:04:09 morgan Exp morgan $
5  * $FreeBSD: src/contrib/libpam/libpam/pam_dispatch.c,v 1.1.1.1.6.2 2001/06/11 15:28:12 markm Exp $
6  *
7  * last modified by AGM
8  */
9
10 #include <stdlib.h>
11 #include <stdio.h>
12
13 #include "pam_private.h"
14
15 /*
16  * this is the return code we return when a function pointer is NULL
17  * or, the handler structure indicates a broken module config line
18  */
19 #define PAM_MUST_FAIL_CODE        PAM_PERM_DENIED
20
21 /* impression codes - this gives some sense to the logical choices */
22 #define _PAM_UNDEF     0
23 #define _PAM_POSITIVE +1
24 #define _PAM_NEGATIVE -1
25
26 /*
27  * walk a stack of modules.  Interpret the administrator's instructions
28  * when combining the return code of each module.
29  */
30
31 static int _pam_dispatch_aux(pam_handle_t *pamh, int flags, struct handler *h,
32                              _pam_boolean resumed)
33 {
34     int depth, impression, status, skip_depth;
35
36     IF_NO_PAMH("_pam_dispatch_aux", pamh, PAM_SYSTEM_ERR);
37
38     if (h == NULL) {
39         const char *service=NULL;
40
41         (void) pam_get_item(pamh, PAM_SERVICE, (const void **)&service);
42         pam_system_log(pamh, NULL, LOG_ERR,
43                        "no modules loaded for `%s' service",
44                        service ? service:"<unknown>" );
45         service = NULL;
46         return PAM_MUST_FAIL_CODE;
47     }
48
49     /* if we are recalling this module stack because a former call did
50        not complete, we restore the state of play from pamh. */
51     if (resumed) {
52         skip_depth = pamh->former.depth;
53         status = pamh->former.status;
54         impression = pamh->former.impression;
55         /* forget all that */
56         pamh->former.impression = _PAM_UNDEF;
57         pamh->former.status = PAM_MUST_FAIL_CODE;
58         pamh->former.depth = 0;
59     } else {
60         skip_depth = 0;
61         impression = _PAM_UNDEF;
62         status = PAM_MUST_FAIL_CODE;
63     }
64
65     /* Loop through module logic stack */
66     for (depth=0 ; h != NULL ; h = h->next, ++depth) {
67         int retval, action;
68
69         /* skip leading modules if they have already returned */
70         if (depth < skip_depth) {
71             continue;
72         }
73
74         /* attempt to call the module */
75         if (h->func == NULL) {
76             D(("module function is not defined, indicating failure"));
77             retval = PAM_MODULE_UNKNOWN;
78         } else {
79             D(("passing control to module..."));
80             retval = h->func(pamh, flags, h->argc, h->argv);
81             D(("module returned: %s", pam_strerror(pamh, retval)));
82             if (h->must_fail) {
83                 D(("module poorly listed in pam.conf; forcing failure"));
84                 retval = PAM_MUST_FAIL_CODE;
85             }
86         }
87
88         /*
89          * PAM_INCOMPLETE return is special.  It indicates that the
90          * module wants to wait for the application before continuing.
91          * In order to return this, the module will have saved its
92          * state so it can resume from an equivalent position when it
93          * is called next time.  (This was added as of 0.65)
94          */
95         if (retval == PAM_INCOMPLETE) {
96             pamh->former.impression = impression;
97             pamh->former.status = status;
98             pamh->former.depth = depth;
99
100             D(("module %d returned PAM_INCOMPLETE", depth));
101             return retval;
102         }
103
104         /* verify that the return value is a valid one */
105         if (retval < PAM_SUCCESS || retval >= _PAM_RETURN_VALUES) {
106             retval = PAM_MUST_FAIL_CODE;
107             action = _PAM_ACTION_BAD;
108         } else {
109             action = h->actions[retval];
110         }
111
112         /* decide what to do */
113         switch (action) {
114         case _PAM_ACTION_RESET:
115             impression = _PAM_UNDEF;
116             status = PAM_MUST_FAIL_CODE;
117             break;
118
119         case _PAM_ACTION_OK:
120         case _PAM_ACTION_DONE:
121             if ( impression == _PAM_UNDEF
122                  || (impression == _PAM_POSITIVE && status == PAM_SUCCESS) ) {
123                 impression = _PAM_POSITIVE;
124                 status = retval;
125             }
126             if ( impression == _PAM_POSITIVE && action == _PAM_ACTION_DONE ) {
127                 goto decision_made;
128             }
129             break;
130
131         case _PAM_ACTION_BAD:
132         case _PAM_ACTION_DIE:
133 #ifdef PAM_FAIL_NOW_ON
134             if ( retval == PAM_ABORT ) {
135                 impression = _PAM_NEGATIVE;
136                 status = PAM_PERM_DENIED;
137                 goto decision_made;
138             }
139 #endif /* PAM_FAIL_NOW_ON */
140             if ( impression != _PAM_NEGATIVE ) {
141                 impression = _PAM_NEGATIVE;
142                 status = retval;
143             }
144             if ( action == _PAM_ACTION_DIE ) {
145                 goto decision_made;
146             }
147             break;
148
149         case _PAM_ACTION_IGNORE:
150             break;
151
152         /* if we get here, we expect action is a positive number --
153            this is what the ...JUMP macro checks. */
154
155         default:
156             if ( _PAM_ACTION_IS_JUMP(action) ) {
157                 /* this means that we need to skip #action stacked modules */
158                 do {
159                     h = h->next;
160                 } while ( --action > 0 && h != NULL );
161
162                 /* note if we try to skip too many modules action is
163                    still non-zero and we snag the next if. */
164             }
165
166             /* this case is a syntax error: we can't succeed */
167             if (action) {
168                 D(("action syntax error"));
169                 impression = _PAM_NEGATIVE;
170                 status = PAM_MUST_FAIL_CODE;
171             }
172         }
173     }
174
175 decision_made:     /* by getting  here we have made a decision */
176
177     /* Sanity check */
178     if ( status == PAM_SUCCESS && impression != _PAM_POSITIVE ) {
179         D(("caught on sanity check -- this is probably a config error!"));
180         status = PAM_MUST_FAIL_CODE;
181     }
182
183     /* We have made a decision about the modules executed */
184     return status;
185 }
186
187 /*
188  * This function translates the module dispatch request into a pointer
189  * to the stack of modules that will actually be run.  the
190  * _pam_dispatch_aux() function (above) is responsible for walking the
191  * module stack.
192  */
193
194 int _pam_dispatch(pam_handle_t *pamh, int flags, int choice)
195 {
196     struct handler *h = NULL;
197     int retval;
198     _pam_boolean resumed;
199
200     IF_NO_PAMH("_pam_dispatch",pamh,PAM_SYSTEM_ERR);
201
202     /* Load all modules, resolve all symbols */
203
204     if ((retval = _pam_init_handlers(pamh)) != PAM_SUCCESS) {
205         pam_system_log(pamh, NULL, LOG_ERR, "unable to dispatch function");
206         return retval;
207     }
208
209     switch (choice) {
210     case PAM_AUTHENTICATE:
211         h = pamh->handlers.conf.authenticate;
212         break;
213     case PAM_SETCRED:
214         h = pamh->handlers.conf.setcred;
215         break;
216     case PAM_ACCOUNT:
217         h = pamh->handlers.conf.acct_mgmt;
218         break;
219     case PAM_OPEN_SESSION:
220         h = pamh->handlers.conf.open_session;
221         break;
222     case PAM_CLOSE_SESSION:
223         h = pamh->handlers.conf.close_session;
224         break;
225     case PAM_CHAUTHTOK:
226         h = pamh->handlers.conf.chauthtok;
227         break;
228     default:
229         pam_system_log(pamh, NULL, LOG_ERR, "undefined fn choice; %d", choice);
230         return PAM_ABORT;
231     }
232
233     if (h == NULL) {     /* there was no handlers.conf... entry; will use
234                           * handlers.other... */
235         switch (choice) {
236         case PAM_AUTHENTICATE:
237             h = pamh->handlers.other.authenticate;
238             break;
239         case PAM_SETCRED:
240             h = pamh->handlers.other.setcred;
241             break;
242         case PAM_ACCOUNT:
243             h = pamh->handlers.other.acct_mgmt;
244             break;
245         case PAM_OPEN_SESSION:
246             h = pamh->handlers.other.open_session;
247             break;
248         case PAM_CLOSE_SESSION:
249             h = pamh->handlers.other.close_session;
250             break;
251         case PAM_CHAUTHTOK:
252             h = pamh->handlers.other.chauthtok;
253             break;
254         }
255     }
256
257     /* Did a module return an "incomplete state" last time? */
258     if (pamh->former.choice != PAM_NOT_STACKED) {
259         if (pamh->former.choice != choice) {
260             pam_system_log(pamh, NULL, LOG_ERR,
261                            "application failed to re-exec stack [%d:%d]",
262                            pamh->former.choice, choice);
263             return PAM_ABORT;
264         }
265         resumed = PAM_TRUE;
266     } else {
267         resumed = PAM_FALSE;
268     }
269
270     /* call the list of module functions */
271     retval = _pam_dispatch_aux(pamh, flags, h, resumed);
272     resumed = PAM_FALSE;
273
274     /* Should we recall where to resume next time? */
275     if (retval == PAM_INCOMPLETE) {
276         D(("module [%d] returned PAM_INCOMPLETE"));
277         pamh->former.choice = choice;
278     } else {
279         pamh->former.choice = PAM_NOT_STACKED;
280     }
281
282     return retval;
283 }
284
285 /*
286  * $Log: pam_dispatch.c,v $
287  */