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