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 $
6 * $DragonFly: src/contrib/libpam/libpam/Attic/pam_dispatch.c,v 1.2 2003/06/17 04:24:03 dillon Exp $
14 #include "pam_private.h"
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
20 #define PAM_MUST_FAIL_CODE PAM_PERM_DENIED
22 /* impression codes - this gives some sense to the logical choices */
24 #define _PAM_POSITIVE +1
25 #define _PAM_NEGATIVE -1
28 * walk a stack of modules. Interpret the administrator's instructions
29 * when combining the return code of each module.
32 static int _pam_dispatch_aux(pam_handle_t *pamh, int flags, struct handler *h,
35 int depth, impression, status, skip_depth;
37 IF_NO_PAMH("_pam_dispatch_aux", pamh, PAM_SYSTEM_ERR);
40 const char *service=NULL;
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>" );
47 return PAM_MUST_FAIL_CODE;
50 /* if we are recalling this module stack because a former call did
51 not complete, we restore the state of play from pamh. */
53 skip_depth = pamh->former.depth;
54 status = pamh->former.status;
55 impression = pamh->former.impression;
57 pamh->former.impression = _PAM_UNDEF;
58 pamh->former.status = PAM_MUST_FAIL_CODE;
59 pamh->former.depth = 0;
62 impression = _PAM_UNDEF;
63 status = PAM_MUST_FAIL_CODE;
66 /* Loop through module logic stack */
67 for (depth=0 ; h != NULL ; h = h->next, ++depth) {
70 /* skip leading modules if they have already returned */
71 if (depth < skip_depth) {
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;
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)));
84 D(("module poorly listed in pam.conf; forcing failure"));
85 retval = PAM_MUST_FAIL_CODE;
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)
96 if (retval == PAM_INCOMPLETE) {
97 pamh->former.impression = impression;
98 pamh->former.status = status;
99 pamh->former.depth = depth;
101 D(("module %d returned PAM_INCOMPLETE", depth));
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;
110 action = h->actions[retval];
113 /* decide what to do */
115 case _PAM_ACTION_RESET:
116 impression = _PAM_UNDEF;
117 status = PAM_MUST_FAIL_CODE;
121 case _PAM_ACTION_DONE:
122 if ( impression == _PAM_UNDEF
123 || (impression == _PAM_POSITIVE && status == PAM_SUCCESS) ) {
124 impression = _PAM_POSITIVE;
127 if ( impression == _PAM_POSITIVE && action == _PAM_ACTION_DONE ) {
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;
140 #endif /* PAM_FAIL_NOW_ON */
141 if ( impression != _PAM_NEGATIVE ) {
142 impression = _PAM_NEGATIVE;
145 if ( action == _PAM_ACTION_DIE ) {
150 case _PAM_ACTION_IGNORE:
153 /* if we get here, we expect action is a positive number --
154 this is what the ...JUMP macro checks. */
157 if ( _PAM_ACTION_IS_JUMP(action) ) {
158 /* this means that we need to skip #action stacked modules */
161 } while ( --action > 0 && h != NULL );
163 /* note if we try to skip too many modules action is
164 still non-zero and we snag the next if. */
167 /* this case is a syntax error: we can't succeed */
169 D(("action syntax error"));
170 impression = _PAM_NEGATIVE;
171 status = PAM_MUST_FAIL_CODE;
176 decision_made: /* by getting here we have made a decision */
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;
184 /* We have made a decision about the modules executed */
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
195 int _pam_dispatch(pam_handle_t *pamh, int flags, int choice)
197 struct handler *h = NULL;
199 _pam_boolean resumed;
201 IF_NO_PAMH("_pam_dispatch",pamh,PAM_SYSTEM_ERR);
203 /* Load all modules, resolve all symbols */
205 if ((retval = _pam_init_handlers(pamh)) != PAM_SUCCESS) {
206 pam_system_log(pamh, NULL, LOG_ERR, "unable to dispatch function");
211 case PAM_AUTHENTICATE:
212 h = pamh->handlers.conf.authenticate;
215 h = pamh->handlers.conf.setcred;
218 h = pamh->handlers.conf.acct_mgmt;
220 case PAM_OPEN_SESSION:
221 h = pamh->handlers.conf.open_session;
223 case PAM_CLOSE_SESSION:
224 h = pamh->handlers.conf.close_session;
227 h = pamh->handlers.conf.chauthtok;
230 pam_system_log(pamh, NULL, LOG_ERR, "undefined fn choice; %d", choice);
234 if (h == NULL) { /* there was no handlers.conf... entry; will use
235 * handlers.other... */
237 case PAM_AUTHENTICATE:
238 h = pamh->handlers.other.authenticate;
241 h = pamh->handlers.other.setcred;
244 h = pamh->handlers.other.acct_mgmt;
246 case PAM_OPEN_SESSION:
247 h = pamh->handlers.other.open_session;
249 case PAM_CLOSE_SESSION:
250 h = pamh->handlers.other.close_session;
253 h = pamh->handlers.other.chauthtok;
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);
271 /* call the list of module functions */
272 retval = _pam_dispatch_aux(pamh, flags, h, resumed);
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;
280 pamh->former.choice = PAM_NOT_STACKED;
287 * $Log: pam_dispatch.c,v $