1 /* pam_dispatch.c - handles module function dispatch */
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 $
13 #include "pam_private.h"
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
19 #define PAM_MUST_FAIL_CODE PAM_PERM_DENIED
21 /* impression codes - this gives some sense to the logical choices */
23 #define _PAM_POSITIVE +1
24 #define _PAM_NEGATIVE -1
27 * walk a stack of modules. Interpret the administrator's instructions
28 * when combining the return code of each module.
31 static int _pam_dispatch_aux(pam_handle_t *pamh, int flags, struct handler *h,
34 int depth, impression, status, skip_depth;
36 IF_NO_PAMH("_pam_dispatch_aux", pamh, PAM_SYSTEM_ERR);
39 const char *service=NULL;
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>" );
46 return PAM_MUST_FAIL_CODE;
49 /* if we are recalling this module stack because a former call did
50 not complete, we restore the state of play from pamh. */
52 skip_depth = pamh->former.depth;
53 status = pamh->former.status;
54 impression = pamh->former.impression;
56 pamh->former.impression = _PAM_UNDEF;
57 pamh->former.status = PAM_MUST_FAIL_CODE;
58 pamh->former.depth = 0;
61 impression = _PAM_UNDEF;
62 status = PAM_MUST_FAIL_CODE;
65 /* Loop through module logic stack */
66 for (depth=0 ; h != NULL ; h = h->next, ++depth) {
69 /* skip leading modules if they have already returned */
70 if (depth < skip_depth) {
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;
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)));
83 D(("module poorly listed in pam.conf; forcing failure"));
84 retval = PAM_MUST_FAIL_CODE;
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)
95 if (retval == PAM_INCOMPLETE) {
96 pamh->former.impression = impression;
97 pamh->former.status = status;
98 pamh->former.depth = depth;
100 D(("module %d returned PAM_INCOMPLETE", depth));
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;
109 action = h->actions[retval];
112 /* decide what to do */
114 case _PAM_ACTION_RESET:
115 impression = _PAM_UNDEF;
116 status = PAM_MUST_FAIL_CODE;
120 case _PAM_ACTION_DONE:
121 if ( impression == _PAM_UNDEF
122 || (impression == _PAM_POSITIVE && status == PAM_SUCCESS) ) {
123 impression = _PAM_POSITIVE;
126 if ( impression == _PAM_POSITIVE && action == _PAM_ACTION_DONE ) {
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;
139 #endif /* PAM_FAIL_NOW_ON */
140 if ( impression != _PAM_NEGATIVE ) {
141 impression = _PAM_NEGATIVE;
144 if ( action == _PAM_ACTION_DIE ) {
149 case _PAM_ACTION_IGNORE:
152 /* if we get here, we expect action is a positive number --
153 this is what the ...JUMP macro checks. */
156 if ( _PAM_ACTION_IS_JUMP(action) ) {
157 /* this means that we need to skip #action stacked modules */
160 } while ( --action > 0 && h != NULL );
162 /* note if we try to skip too many modules action is
163 still non-zero and we snag the next if. */
166 /* this case is a syntax error: we can't succeed */
168 D(("action syntax error"));
169 impression = _PAM_NEGATIVE;
170 status = PAM_MUST_FAIL_CODE;
175 decision_made: /* by getting here we have made a decision */
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;
183 /* We have made a decision about the modules executed */
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
194 int _pam_dispatch(pam_handle_t *pamh, int flags, int choice)
196 struct handler *h = NULL;
198 _pam_boolean resumed;
200 IF_NO_PAMH("_pam_dispatch",pamh,PAM_SYSTEM_ERR);
202 /* Load all modules, resolve all symbols */
204 if ((retval = _pam_init_handlers(pamh)) != PAM_SUCCESS) {
205 pam_system_log(pamh, NULL, LOG_ERR, "unable to dispatch function");
210 case PAM_AUTHENTICATE:
211 h = pamh->handlers.conf.authenticate;
214 h = pamh->handlers.conf.setcred;
217 h = pamh->handlers.conf.acct_mgmt;
219 case PAM_OPEN_SESSION:
220 h = pamh->handlers.conf.open_session;
222 case PAM_CLOSE_SESSION:
223 h = pamh->handlers.conf.close_session;
226 h = pamh->handlers.conf.chauthtok;
229 pam_system_log(pamh, NULL, LOG_ERR, "undefined fn choice; %d", choice);
233 if (h == NULL) { /* there was no handlers.conf... entry; will use
234 * handlers.other... */
236 case PAM_AUTHENTICATE:
237 h = pamh->handlers.other.authenticate;
240 h = pamh->handlers.other.setcred;
243 h = pamh->handlers.other.acct_mgmt;
245 case PAM_OPEN_SESSION:
246 h = pamh->handlers.other.open_session;
248 case PAM_CLOSE_SESSION:
249 h = pamh->handlers.other.close_session;
252 h = pamh->handlers.other.chauthtok;
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);
270 /* call the list of module functions */
271 retval = _pam_dispatch_aux(pamh, flags, h, resumed);
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;
279 pamh->former.choice = PAM_NOT_STACKED;
286 * $Log: pam_dispatch.c,v $