pam_dispatch.c 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447
  1. /* pam_dispatch.c - handles module function dispatch */
  2. /*
  3. * Copyright (c) 1998, 2005 Andrew G. Morgan <morgan@kernel.org>
  4. *
  5. */
  6. #include "pam_private.h"
  7. #include <stdlib.h>
  8. #include <stdio.h>
  9. /*
  10. * this is the return code we return when a function pointer is NULL
  11. * or, the handler structure indicates a broken module config line
  12. */
  13. #define PAM_MUST_FAIL_CODE PAM_PERM_DENIED
  14. /* impression codes - this gives some sense to the logical choices */
  15. #define _PAM_UNDEF 0
  16. #define _PAM_POSITIVE +1
  17. #define _PAM_NEGATIVE -1
  18. /* frozen chain required codes */
  19. #define _PAM_PLEASE_FREEZE 0
  20. #define _PAM_MAY_BE_FROZEN 1
  21. #define _PAM_MUST_BE_FROZEN 2
  22. /*
  23. * walk a stack of modules. Interpret the administrator's instructions
  24. * when combining the return code of each module.
  25. */
  26. static int _pam_dispatch_aux(pam_handle_t *pamh, int flags, struct handler *h,
  27. _pam_boolean resumed, int use_cached_chain)
  28. {
  29. int depth, impression, status, skip_depth, prev_level, stack_level;
  30. struct _pam_substack_state *substates = NULL;
  31. IF_NO_PAMH("_pam_dispatch_aux", pamh, PAM_SYSTEM_ERR);
  32. if (h == NULL) {
  33. const void *service=NULL;
  34. (void) pam_get_item(pamh, PAM_SERVICE, &service);
  35. pam_syslog(pamh, LOG_ERR, "no modules loaded for `%s' service",
  36. service ? (const char *)service:"<unknown>" );
  37. service = NULL;
  38. return PAM_MUST_FAIL_CODE;
  39. }
  40. /* if we are recalling this module stack because a former call did
  41. not complete, we restore the state of play from pamh. */
  42. if (resumed) {
  43. skip_depth = pamh->former.depth;
  44. status = pamh->former.status;
  45. impression = pamh->former.impression;
  46. substates = pamh->former.substates;
  47. /* forget all that */
  48. pamh->former.impression = _PAM_UNDEF;
  49. pamh->former.status = PAM_MUST_FAIL_CODE;
  50. pamh->former.depth = 0;
  51. pamh->former.substates = NULL;
  52. } else {
  53. skip_depth = 0;
  54. substates = malloc(PAM_SUBSTACK_MAX_LEVEL * sizeof(*substates));
  55. if (substates == NULL) {
  56. pam_syslog(pamh, LOG_CRIT,
  57. "_pam_dispatch_aux: no memory for substack states");
  58. return PAM_BUF_ERR;
  59. }
  60. substates[0].impression = impression = _PAM_UNDEF;
  61. substates[0].status = status = PAM_MUST_FAIL_CODE;
  62. }
  63. prev_level = 0;
  64. /* Loop through module logic stack */
  65. for (depth=0 ; h != NULL ; prev_level = stack_level, h = h->next, ++depth) {
  66. int retval, cached_retval, action;
  67. stack_level = h->stack_level;
  68. /* skip leading modules if they have already returned */
  69. if (depth < skip_depth) {
  70. continue;
  71. }
  72. /* remember state if we are entering a substack */
  73. if (prev_level < stack_level) {
  74. substates[stack_level].impression = impression;
  75. substates[stack_level].status = status;
  76. }
  77. /* attempt to call the module */
  78. if (h->handler_type == PAM_HT_MUST_FAIL) {
  79. D(("module poorly listed in PAM config; forcing failure"));
  80. retval = PAM_MUST_FAIL_CODE;
  81. } else if (h->handler_type == PAM_HT_SUBSTACK) {
  82. D(("skipping substack handler"));
  83. continue;
  84. } else if (h->func == NULL) {
  85. D(("module function is not defined, indicating failure"));
  86. retval = PAM_MODULE_UNKNOWN;
  87. } else {
  88. D(("passing control to module..."));
  89. pamh->mod_name=h->mod_name;
  90. pamh->mod_argc = h->argc;
  91. pamh->mod_argv = h->argv;
  92. retval = h->func(pamh, flags, h->argc, h->argv);
  93. pamh->mod_name=NULL;
  94. pamh->mod_argc = 0;
  95. pamh->mod_argv = NULL;
  96. D(("module returned: %s", pam_strerror(pamh, retval)));
  97. }
  98. /*
  99. * PAM_INCOMPLETE return is special. It indicates that the
  100. * module wants to wait for the application before continuing.
  101. * In order to return this, the module will have saved its
  102. * state so it can resume from an equivalent position when it
  103. * is called next time. (This was added as of 0.65)
  104. */
  105. if (retval == PAM_INCOMPLETE) {
  106. pamh->former.impression = impression;
  107. pamh->former.status = status;
  108. pamh->former.depth = depth;
  109. pamh->former.substates = substates;
  110. D(("module %d returned PAM_INCOMPLETE", depth));
  111. return retval;
  112. }
  113. /*
  114. * use_cached_chain is how we ensure that the setcred and
  115. * close_session modules are called in the same order as they did
  116. * when they were invoked as auth/open_session. This feature was
  117. * added in 0.75 to make the behavior of pam_setcred sane.
  118. */
  119. if (use_cached_chain != _PAM_PLEASE_FREEZE) {
  120. /* a former stack execution should have frozen the chain */
  121. cached_retval = *(h->cached_retval_p);
  122. if (cached_retval == _PAM_INVALID_RETVAL) {
  123. /* This may be a problem condition. It implies that
  124. the application is running setcred, close_session,
  125. chauthtok(2nd) without having first run
  126. authenticate, open_session, chauthtok(1st)
  127. [respectively]. */
  128. D(("use_cached_chain is set to [%d],"
  129. " but cached_retval == _PAM_INVALID_RETVAL",
  130. use_cached_chain));
  131. /* In the case of close_session and setcred there is a
  132. backward compatibility reason for allowing this, in
  133. the chauthtok case we have encountered a bug in
  134. libpam! */
  135. if (use_cached_chain == _PAM_MAY_BE_FROZEN) {
  136. /* (not ideal) force non-frozen stack control. */
  137. cached_retval = retval;
  138. } else {
  139. D(("BUG in libpam -"
  140. " chain is required to be frozen but isn't"));
  141. /* cached_retval is already _PAM_INVALID_RETVAL */
  142. }
  143. }
  144. } else {
  145. /* this stack execution is defining the frozen chain */
  146. cached_retval = h->cached_retval = retval;
  147. }
  148. /* verify that the return value is a valid one */
  149. if ((cached_retval < PAM_SUCCESS)
  150. || (cached_retval >= _PAM_RETURN_VALUES)) {
  151. retval = PAM_MUST_FAIL_CODE;
  152. action = _PAM_ACTION_BAD;
  153. } else {
  154. /* We treat the current retval with some respect. It may
  155. (for example, in the case of setcred) have a value that
  156. needs to be propagated to the user. We want to use the
  157. cached_retval to determine the modules to be executed
  158. in the stacked chain, but we want to treat each
  159. non-ignored module in the cached chain as now being
  160. 'required'. We only need to treat the,
  161. _PAM_ACTION_IGNORE, _PAM_ACTION_IS_JUMP and
  162. _PAM_ACTION_RESET actions specially. */
  163. action = h->actions[cached_retval];
  164. }
  165. D(("use_cached_chain=%d action=%d cached_retval=%d retval=%d",
  166. use_cached_chain, action, cached_retval, retval));
  167. /* decide what to do */
  168. switch (action) {
  169. case _PAM_ACTION_RESET:
  170. impression = substates[stack_level].impression;
  171. status = substates[stack_level].status;
  172. break;
  173. case _PAM_ACTION_OK:
  174. case _PAM_ACTION_DONE:
  175. if ( impression == _PAM_UNDEF
  176. || (impression == _PAM_POSITIVE && status == PAM_SUCCESS) ) {
  177. /* in case of using cached chain
  178. we could get here with PAM_IGNORE - don't return it */
  179. if ( retval != PAM_IGNORE || cached_retval == retval ) {
  180. impression = _PAM_POSITIVE;
  181. status = retval;
  182. }
  183. }
  184. if ( impression == _PAM_POSITIVE ) {
  185. if ( retval == PAM_SUCCESS ) {
  186. h->grantor = 1;
  187. }
  188. if ( action == _PAM_ACTION_DONE ) {
  189. goto decision_made;
  190. }
  191. }
  192. break;
  193. case _PAM_ACTION_BAD:
  194. case _PAM_ACTION_DIE:
  195. #ifdef PAM_FAIL_NOW_ON
  196. if ( cached_retval == PAM_ABORT ) {
  197. impression = _PAM_NEGATIVE;
  198. status = PAM_PERM_DENIED;
  199. goto decision_made;
  200. }
  201. #endif /* PAM_FAIL_NOW_ON */
  202. if ( impression != _PAM_NEGATIVE ) {
  203. impression = _PAM_NEGATIVE;
  204. /* Don't return with PAM_IGNORE as status */
  205. if ( retval == PAM_IGNORE )
  206. status = PAM_MUST_FAIL_CODE;
  207. else
  208. status = retval;
  209. }
  210. if ( action == _PAM_ACTION_DIE ) {
  211. goto decision_made;
  212. }
  213. break;
  214. case _PAM_ACTION_IGNORE:
  215. break;
  216. /* if we get here, we expect action is a positive number --
  217. this is what the ...JUMP macro checks. */
  218. default:
  219. if ( _PAM_ACTION_IS_JUMP(action) ) {
  220. /* If we are evaluating a cached chain, we treat this
  221. module as required (aka _PAM_ACTION_OK) as well as
  222. executing the jump. */
  223. if (use_cached_chain) {
  224. if (impression == _PAM_UNDEF
  225. || (impression == _PAM_POSITIVE
  226. && status == PAM_SUCCESS) ) {
  227. if ( retval != PAM_IGNORE || cached_retval == retval ) {
  228. if ( impression == _PAM_UNDEF && retval == PAM_SUCCESS ) {
  229. h->grantor = 1;
  230. }
  231. impression = _PAM_POSITIVE;
  232. status = retval;
  233. }
  234. }
  235. }
  236. /* this means that we need to skip #action stacked modules */
  237. while (h->next != NULL && h->next->stack_level >= stack_level && action > 0) {
  238. do {
  239. h = h->next;
  240. ++depth;
  241. } while (h->next != NULL && h->next->stack_level > stack_level);
  242. --action;
  243. }
  244. /* note if we try to skip too many modules action is
  245. still non-zero and we snag the next if. */
  246. }
  247. /* this case is a syntax error: we can't succeed */
  248. if (action) {
  249. pam_syslog(pamh, LOG_ERR, "bad jump in stack");
  250. impression = _PAM_NEGATIVE;
  251. status = PAM_MUST_FAIL_CODE;
  252. }
  253. }
  254. continue;
  255. decision_made: /* by getting here we have made a decision */
  256. while (h->next != NULL && h->next->stack_level >= stack_level) {
  257. h = h->next;
  258. ++depth;
  259. }
  260. }
  261. /* Sanity check */
  262. if ( status == PAM_SUCCESS && impression != _PAM_POSITIVE ) {
  263. D(("caught on sanity check -- this is probably a config error!"));
  264. status = PAM_MUST_FAIL_CODE;
  265. }
  266. free(substates);
  267. /* We have made a decision about the modules executed */
  268. return status;
  269. }
  270. static void _pam_clear_grantors(struct handler *h)
  271. {
  272. for (; h != NULL; h = h->next) {
  273. h->grantor = 0;
  274. }
  275. }
  276. /*
  277. * This function translates the module dispatch request into a pointer
  278. * to the stack of modules that will actually be run. the
  279. * _pam_dispatch_aux() function (above) is responsible for walking the
  280. * module stack.
  281. */
  282. int _pam_dispatch(pam_handle_t *pamh, int flags, int choice)
  283. {
  284. struct handler *h = NULL;
  285. int retval = PAM_SYSTEM_ERR, use_cached_chain;
  286. _pam_boolean resumed;
  287. IF_NO_PAMH("_pam_dispatch", pamh, PAM_SYSTEM_ERR);
  288. if (__PAM_FROM_MODULE(pamh)) {
  289. D(("called from a module!?"));
  290. goto end;
  291. }
  292. /* Load all modules, resolve all symbols */
  293. if ((retval = _pam_init_handlers(pamh)) != PAM_SUCCESS) {
  294. pam_syslog(pamh, LOG_ERR, "unable to dispatch function");
  295. goto end;
  296. }
  297. use_cached_chain = _PAM_PLEASE_FREEZE;
  298. switch (choice) {
  299. case PAM_AUTHENTICATE:
  300. h = pamh->handlers.conf.authenticate;
  301. break;
  302. case PAM_SETCRED:
  303. h = pamh->handlers.conf.setcred;
  304. use_cached_chain = _PAM_MAY_BE_FROZEN;
  305. break;
  306. case PAM_ACCOUNT:
  307. h = pamh->handlers.conf.acct_mgmt;
  308. break;
  309. case PAM_OPEN_SESSION:
  310. h = pamh->handlers.conf.open_session;
  311. break;
  312. case PAM_CLOSE_SESSION:
  313. h = pamh->handlers.conf.close_session;
  314. use_cached_chain = _PAM_MAY_BE_FROZEN;
  315. break;
  316. case PAM_CHAUTHTOK:
  317. h = pamh->handlers.conf.chauthtok;
  318. break;
  319. default:
  320. pam_syslog(pamh, LOG_ERR, "undefined fn choice; %d", choice);
  321. retval = PAM_ABORT;
  322. goto end;
  323. }
  324. if (h == NULL) { /* there was no handlers.conf... entry; will use
  325. * handlers.other... */
  326. switch (choice) {
  327. case PAM_AUTHENTICATE:
  328. h = pamh->handlers.other.authenticate;
  329. break;
  330. case PAM_SETCRED:
  331. h = pamh->handlers.other.setcred;
  332. break;
  333. case PAM_ACCOUNT:
  334. h = pamh->handlers.other.acct_mgmt;
  335. break;
  336. case PAM_OPEN_SESSION:
  337. h = pamh->handlers.other.open_session;
  338. break;
  339. case PAM_CLOSE_SESSION:
  340. h = pamh->handlers.other.close_session;
  341. break;
  342. case PAM_CHAUTHTOK:
  343. h = pamh->handlers.other.chauthtok;
  344. break;
  345. }
  346. }
  347. /* Did a module return an "incomplete state" last time? */
  348. if (pamh->former.choice != PAM_NOT_STACKED) {
  349. if (pamh->former.choice != choice) {
  350. pam_syslog(pamh, LOG_ERR,
  351. "application failed to re-exec stack [%d:%d]",
  352. pamh->former.choice, choice);
  353. retval = PAM_ABORT;
  354. goto end;
  355. }
  356. resumed = PAM_TRUE;
  357. } else {
  358. resumed = PAM_FALSE;
  359. _pam_clear_grantors(h);
  360. }
  361. __PAM_TO_MODULE(pamh);
  362. /* call the list of module functions */
  363. pamh->choice = choice;
  364. retval = _pam_dispatch_aux(pamh, flags, h, resumed, use_cached_chain);
  365. __PAM_TO_APP(pamh);
  366. /* Should we recall where to resume next time? */
  367. if (retval == PAM_INCOMPLETE) {
  368. D(("module [%d] returned PAM_INCOMPLETE"));
  369. pamh->former.choice = choice;
  370. } else {
  371. pamh->former.choice = PAM_NOT_STACKED;
  372. }
  373. end:
  374. #ifdef HAVE_LIBAUDIT
  375. if (choice != PAM_CHAUTHTOK || flags & PAM_UPDATE_AUTHTOK || retval != PAM_SUCCESS) {
  376. retval = _pam_auditlog(pamh, choice, retval, flags, h);
  377. }
  378. #endif
  379. return retval;
  380. }