pam_succeed_if.c 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618
  1. /******************************************************************************
  2. * A simple user-attribute based module for PAM.
  3. *
  4. * Copyright (c) 2003 Red Hat, Inc.
  5. * Written by Nalin Dahyabhai <nalin@redhat.com>
  6. *
  7. * Redistribution and use in source and binary forms, with or without
  8. * modification, are permitted provided that the following conditions
  9. * are met:
  10. * 1. Redistributions of source code must retain the above copyright
  11. * notice, and the entire permission notice in its entirety,
  12. * including the disclaimer of warranties.
  13. * 2. Redistributions in binary form must reproduce the above copyright
  14. * notice, this list of conditions and the following disclaimer in the
  15. * documentation and/or other materials provided with the distribution.
  16. * 3. The name of the author may not be used to endorse or promote
  17. * products derived from this software without specific prior
  18. * written permission.
  19. *
  20. * ALTERNATIVELY, this product may be distributed under the terms of
  21. * the GNU Public License, in which case the provisions of the GPL are
  22. * required INSTEAD OF the above restrictions. (This clause is
  23. * necessary due to a potential bad interaction between the GPL and
  24. * the restrictions contained in a BSD-style copyright.)
  25. *
  26. * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
  27. * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
  28. * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  29. * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
  30. * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
  31. * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
  32. * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  33. * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
  34. * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
  35. * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
  36. * OF THE POSSIBILITY OF SUCH DAMAGE.
  37. */
  38. #include "config.h"
  39. #include <sys/types.h>
  40. #include <errno.h>
  41. #include <fcntl.h>
  42. #include <fnmatch.h>
  43. #include <limits.h>
  44. #include <stdarg.h>
  45. #include <stdio.h>
  46. #include <stdlib.h>
  47. #include <string.h>
  48. #include <syslog.h>
  49. #include <unistd.h>
  50. #include <pwd.h>
  51. #include <grp.h>
  52. #include <netdb.h>
  53. #include <security/pam_modules.h>
  54. #include <security/pam_modutil.h>
  55. #include <security/pam_ext.h>
  56. /* Basically, run cmp(atol(left), atol(right)), returning PAM_SUCCESS if
  57. * the function returns non-zero, PAM_AUTH_ERR if it returns zero, and
  58. * PAM_SERVICE_ERR if the arguments can't be parsed as numbers. */
  59. static int
  60. evaluate_num(const pam_handle_t *pamh, const char *left,
  61. const char *right, int (*cmp)(long long, long long))
  62. {
  63. long long l, r;
  64. char *p;
  65. int ret = PAM_SUCCESS;
  66. errno = 0;
  67. l = strtoll(left, &p, 0);
  68. if ((p == NULL) || (*p != '\0') || errno) {
  69. pam_syslog(pamh, LOG_INFO, "\"%s\" is not a number", left);
  70. ret = PAM_SERVICE_ERR;
  71. }
  72. r = strtoll(right, &p, 0);
  73. if ((p == NULL) || (*p != '\0') || errno) {
  74. pam_syslog(pamh, LOG_INFO, "\"%s\" is not a number", right);
  75. ret = PAM_SERVICE_ERR;
  76. }
  77. if (ret != PAM_SUCCESS) {
  78. return ret;
  79. }
  80. return cmp(l, r) ? PAM_SUCCESS : PAM_AUTH_ERR;
  81. }
  82. /* Simple numeric comparison callbacks. */
  83. static int
  84. eq(long long i, long long j)
  85. {
  86. return i == j;
  87. }
  88. static int
  89. ne(long long i, long long j)
  90. {
  91. return i != j;
  92. }
  93. static int
  94. lt(long long i, long long j)
  95. {
  96. return i < j;
  97. }
  98. static int
  99. le(long long i, long long j)
  100. {
  101. return lt(i, j) || eq(i, j);
  102. }
  103. static int
  104. gt(long long i, long long j)
  105. {
  106. return i > j;
  107. }
  108. static int
  109. ge(long long i, long long j)
  110. {
  111. return gt(i, j) || eq(i, j);
  112. }
  113. /* Test for numeric equality. */
  114. static int
  115. evaluate_eqn(const pam_handle_t *pamh, const char *left, const char *right)
  116. {
  117. return evaluate_num(pamh, left, right, eq);
  118. }
  119. /* Test for string equality. */
  120. static int
  121. evaluate_eqs(const char *left, const char *right)
  122. {
  123. return (strcmp(left, right) == 0) ? PAM_SUCCESS : PAM_AUTH_ERR;
  124. }
  125. /* Test for numeric inequality. */
  126. static int
  127. evaluate_nen(const pam_handle_t *pamh, const char *left, const char *right)
  128. {
  129. return evaluate_num(pamh, left, right, ne);
  130. }
  131. /* Test for string inequality. */
  132. static int
  133. evaluate_nes(const char *left, const char *right)
  134. {
  135. return (strcmp(left, right) != 0) ? PAM_SUCCESS : PAM_AUTH_ERR;
  136. }
  137. /* Test for numeric less-than-ness(?) */
  138. static int
  139. evaluate_lt(const pam_handle_t *pamh, const char *left, const char *right)
  140. {
  141. return evaluate_num(pamh, left, right, lt);
  142. }
  143. /* Test for numeric less-than-or-equal-ness(?) */
  144. static int
  145. evaluate_le(const pam_handle_t *pamh, const char *left, const char *right)
  146. {
  147. return evaluate_num(pamh, left, right, le);
  148. }
  149. /* Test for numeric greater-than-ness(?) */
  150. static int
  151. evaluate_gt(const pam_handle_t *pamh, const char *left, const char *right)
  152. {
  153. return evaluate_num(pamh, left, right, gt);
  154. }
  155. /* Test for numeric greater-than-or-equal-ness(?) */
  156. static int
  157. evaluate_ge(const pam_handle_t *pamh, const char *left, const char *right)
  158. {
  159. return evaluate_num(pamh, left, right, ge);
  160. }
  161. /* Check for file glob match. */
  162. static int
  163. evaluate_glob(const char *left, const char *right)
  164. {
  165. return (fnmatch(right, left, 0) == 0) ? PAM_SUCCESS : PAM_AUTH_ERR;
  166. }
  167. /* Check for file glob mismatch. */
  168. static int
  169. evaluate_noglob(const char *left, const char *right)
  170. {
  171. return (fnmatch(right, left, 0) != 0) ? PAM_SUCCESS : PAM_AUTH_ERR;
  172. }
  173. /* Check for list match. */
  174. static int
  175. evaluate_inlist(const char *left, const char *right)
  176. {
  177. char *p;
  178. /* Don't care about left containing ':'. */
  179. while ((p=strstr(right, left)) != NULL) {
  180. if (p == right || *(p-1) == ':') { /* ':' is a list separator */
  181. p += strlen(left);
  182. if (*p == '\0' || *p == ':') {
  183. return PAM_SUCCESS;
  184. }
  185. }
  186. right = strchr(p, ':');
  187. if (right == NULL)
  188. break;
  189. else
  190. ++right;
  191. }
  192. return PAM_AUTH_ERR;
  193. }
  194. /* Check for list mismatch. */
  195. static int
  196. evaluate_notinlist(const char *left, const char *right)
  197. {
  198. return evaluate_inlist(left, right) != PAM_SUCCESS ? PAM_SUCCESS : PAM_AUTH_ERR;
  199. }
  200. /* Return PAM_SUCCESS if the user is in the group. */
  201. static int
  202. evaluate_ingroup(pam_handle_t *pamh, const char *user, const char *grouplist)
  203. {
  204. char *ptr = NULL;
  205. static const char delim[] = ":";
  206. char const *grp = NULL;
  207. char *group = strdup(grouplist);
  208. if (group == NULL)
  209. return PAM_BUF_ERR;
  210. grp = strtok_r(group, delim, &ptr);
  211. while(grp != NULL) {
  212. if (pam_modutil_user_in_group_nam_nam(pamh, user, grp) == 1) {
  213. free(group);
  214. return PAM_SUCCESS;
  215. }
  216. grp = strtok_r(NULL, delim, &ptr);
  217. }
  218. free(group);
  219. return PAM_AUTH_ERR;
  220. }
  221. /* Return PAM_SUCCESS if the user is NOT in the group. */
  222. static int
  223. evaluate_notingroup(pam_handle_t *pamh, const char *user, const char *grouplist)
  224. {
  225. char *ptr = NULL;
  226. static const char delim[] = ":";
  227. char const *grp = NULL;
  228. char *group = strdup(grouplist);
  229. if (group == NULL)
  230. return PAM_BUF_ERR;
  231. grp = strtok_r(group, delim, &ptr);
  232. while(grp != NULL) {
  233. if (pam_modutil_user_in_group_nam_nam(pamh, user, grp) == 1) {
  234. free(group);
  235. return PAM_AUTH_ERR;
  236. }
  237. grp = strtok_r(NULL, delim, &ptr);
  238. }
  239. free(group);
  240. return PAM_SUCCESS;
  241. }
  242. #ifdef HAVE_INNETGR
  243. # define SOMETIMES_UNUSED UNUSED
  244. #else
  245. # define SOMETIMES_UNUSED
  246. #endif
  247. /* Return PAM_SUCCESS if the (host,user) is in the netgroup. */
  248. static int
  249. evaluate_innetgr(const pam_handle_t* pamh SOMETIMES_UNUSED, const char *host, const char *user, const char *group)
  250. {
  251. #ifdef HAVE_INNETGR
  252. if (innetgr(group, host, user, NULL) == 1)
  253. return PAM_SUCCESS;
  254. #else
  255. pam_syslog (pamh, LOG_ERR, "pam_succeed_if does not have netgroup support");
  256. #endif
  257. return PAM_AUTH_ERR;
  258. }
  259. /* Return PAM_SUCCESS if the (host,user) is NOT in the netgroup. */
  260. static int
  261. evaluate_notinnetgr(const pam_handle_t* pamh SOMETIMES_UNUSED, const char *host, const char *user, const char *group)
  262. {
  263. #ifdef HAVE_INNETGR
  264. if (innetgr(group, host, user, NULL) == 0)
  265. return PAM_SUCCESS;
  266. #else
  267. pam_syslog (pamh, LOG_ERR, "pam_succeed_if does not have netgroup support");
  268. #endif
  269. return PAM_AUTH_ERR;
  270. }
  271. /* Match a triple. */
  272. static int
  273. evaluate(pam_handle_t *pamh, int debug,
  274. const char *left, const char *qual, const char *right,
  275. struct passwd **pwd, const char *user)
  276. {
  277. char buf[LINE_MAX] = "";
  278. const char *attribute = left;
  279. /* Figure out what we're evaluating here, and convert it to a string.*/
  280. if ((strcasecmp(left, "login") == 0) ||
  281. (strcasecmp(left, "name") == 0) ||
  282. (strcasecmp(left, "user") == 0)) {
  283. snprintf(buf, sizeof(buf), "%s", user);
  284. left = buf;
  285. }
  286. /* Get information about the user if needed. */
  287. if ((*pwd == NULL) &&
  288. ((strcasecmp(left, "uid") == 0) ||
  289. (strcasecmp(left, "gid") == 0) ||
  290. (strcasecmp(left, "shell") == 0) ||
  291. (strcasecmp(left, "home") == 0) ||
  292. (strcasecmp(left, "dir") == 0) ||
  293. (strcasecmp(left, "homedir") == 0))) {
  294. *pwd = pam_modutil_getpwnam(pamh, user);
  295. if (*pwd == NULL) {
  296. return PAM_USER_UNKNOWN;
  297. }
  298. }
  299. if (strcasecmp(left, "uid") == 0) {
  300. snprintf(buf, sizeof(buf), "%lu", (unsigned long) (*pwd)->pw_uid);
  301. left = buf;
  302. }
  303. if (strcasecmp(left, "gid") == 0) {
  304. snprintf(buf, sizeof(buf), "%lu", (unsigned long) (*pwd)->pw_gid);
  305. left = buf;
  306. }
  307. if (strcasecmp(left, "shell") == 0) {
  308. snprintf(buf, sizeof(buf), "%s", (*pwd)->pw_shell);
  309. left = buf;
  310. }
  311. if ((strcasecmp(left, "home") == 0) ||
  312. (strcasecmp(left, "dir") == 0) ||
  313. (strcasecmp(left, "homedir") == 0)) {
  314. snprintf(buf, sizeof(buf), "%s", (*pwd)->pw_dir);
  315. left = buf;
  316. }
  317. if (strcasecmp(left, "service") == 0) {
  318. const void *svc;
  319. if (pam_get_item(pamh, PAM_SERVICE, &svc) != PAM_SUCCESS ||
  320. svc == NULL)
  321. svc = "";
  322. snprintf(buf, sizeof(buf), "%s", (const char *)svc);
  323. left = buf;
  324. }
  325. if (strcasecmp(left, "ruser") == 0) {
  326. const void *ruser;
  327. if (pam_get_item(pamh, PAM_RUSER, &ruser) != PAM_SUCCESS ||
  328. ruser == NULL)
  329. ruser = "";
  330. snprintf(buf, sizeof(buf), "%s", (const char *)ruser);
  331. left = buf;
  332. user = buf;
  333. }
  334. if (strcasecmp(left, "rhost") == 0) {
  335. const void *rhost;
  336. if (pam_get_item(pamh, PAM_RHOST, &rhost) != PAM_SUCCESS ||
  337. rhost == NULL)
  338. rhost = "";
  339. snprintf(buf, sizeof(buf), "%s", (const char *)rhost);
  340. left = buf;
  341. }
  342. if (strcasecmp(left, "tty") == 0) {
  343. const void *tty;
  344. if (pam_get_item(pamh, PAM_TTY, &tty) != PAM_SUCCESS ||
  345. tty == NULL)
  346. tty = "";
  347. snprintf(buf, sizeof(buf), "%s", (const char *)tty);
  348. left = buf;
  349. }
  350. /* If we have no idea what's going on, return an error. */
  351. if (left != buf) {
  352. pam_syslog(pamh, LOG_ERR, "unknown attribute \"%s\"", left);
  353. return PAM_SERVICE_ERR;
  354. }
  355. if (debug) {
  356. pam_syslog(pamh, LOG_DEBUG, "'%s' resolves to '%s'",
  357. attribute, left);
  358. }
  359. /* Attribute value < some threshold. */
  360. if ((strcasecmp(qual, "<") == 0) ||
  361. (strcasecmp(qual, "lt") == 0)) {
  362. return evaluate_lt(pamh, left, right);
  363. }
  364. /* Attribute value <= some threshold. */
  365. if ((strcasecmp(qual, "<=") == 0) ||
  366. (strcasecmp(qual, "le") == 0)) {
  367. return evaluate_le(pamh, left, right);
  368. }
  369. /* Attribute value > some threshold. */
  370. if ((strcasecmp(qual, ">") == 0) ||
  371. (strcasecmp(qual, "gt") == 0)) {
  372. return evaluate_gt(pamh, left, right);
  373. }
  374. /* Attribute value >= some threshold. */
  375. if ((strcasecmp(qual, ">=") == 0) ||
  376. (strcasecmp(qual, "ge") == 0)) {
  377. return evaluate_ge(pamh, left, right);
  378. }
  379. /* Attribute value == some threshold. */
  380. if (strcasecmp(qual, "eq") == 0) {
  381. return evaluate_eqn(pamh, left, right);
  382. }
  383. /* Attribute value = some string. */
  384. if (strcasecmp(qual, "=") == 0) {
  385. return evaluate_eqs(left, right);
  386. }
  387. /* Attribute value != some threshold. */
  388. if (strcasecmp(qual, "ne") == 0) {
  389. return evaluate_nen(pamh, left, right);
  390. }
  391. /* Attribute value != some string. */
  392. if (strcasecmp(qual, "!=") == 0) {
  393. return evaluate_nes(left, right);
  394. }
  395. /* Attribute value matches some pattern. */
  396. if ((strcasecmp(qual, "=~") == 0) ||
  397. (strcasecmp(qual, "glob") == 0)) {
  398. return evaluate_glob(left, right);
  399. }
  400. if ((strcasecmp(qual, "!~") == 0) ||
  401. (strcasecmp(qual, "noglob") == 0)) {
  402. return evaluate_noglob(left, right);
  403. }
  404. /* Attribute value matches item in list. */
  405. if (strcasecmp(qual, "in") == 0) {
  406. return evaluate_inlist(left, right);
  407. }
  408. if (strcasecmp(qual, "notin") == 0) {
  409. return evaluate_notinlist(left, right);
  410. }
  411. /* User is in this group(s). */
  412. if (strcasecmp(qual, "ingroup") == 0) {
  413. return evaluate_ingroup(pamh, user, right);
  414. }
  415. /* User is not in this group(s). */
  416. if (strcasecmp(qual, "notingroup") == 0) {
  417. return evaluate_notingroup(pamh, user, right);
  418. }
  419. /* (Rhost, user) is in this netgroup. */
  420. if (strcasecmp(qual, "innetgr") == 0) {
  421. const void *rhost;
  422. if (pam_get_item(pamh, PAM_RHOST, &rhost) != PAM_SUCCESS)
  423. rhost = NULL;
  424. return evaluate_innetgr(pamh, rhost, user, right);
  425. }
  426. /* (Rhost, user) is not in this group. */
  427. if (strcasecmp(qual, "notinnetgr") == 0) {
  428. const void *rhost;
  429. if (pam_get_item(pamh, PAM_RHOST, &rhost) != PAM_SUCCESS)
  430. rhost = NULL;
  431. return evaluate_notinnetgr(pamh, rhost, user, right);
  432. }
  433. /* Fail closed. */
  434. return PAM_SERVICE_ERR;
  435. }
  436. int
  437. pam_sm_authenticate (pam_handle_t *pamh, int flags UNUSED,
  438. int argc, const char **argv)
  439. {
  440. const char *user;
  441. struct passwd *pwd = NULL;
  442. int ret, i, count, use_uid, debug;
  443. const char *left, *right, *qual;
  444. int quiet_fail, quiet_succ, audit;
  445. quiet_fail = 0;
  446. quiet_succ = 0;
  447. audit = 0;
  448. for (use_uid = 0, debug = 0, i = 0; i < argc; i++) {
  449. if (strcmp(argv[i], "debug") == 0) {
  450. debug++;
  451. }
  452. if (strcmp(argv[i], "use_uid") == 0) {
  453. use_uid++;
  454. }
  455. if (strcmp(argv[i], "quiet") == 0) {
  456. quiet_fail++;
  457. quiet_succ++;
  458. }
  459. if (strcmp(argv[i], "quiet_fail") == 0) {
  460. quiet_fail++;
  461. }
  462. if (strcmp(argv[i], "quiet_success") == 0) {
  463. quiet_succ++;
  464. }
  465. if (strcmp(argv[i], "audit") == 0) {
  466. audit++;
  467. }
  468. }
  469. if (use_uid) {
  470. /* Get information about the user. */
  471. pwd = pam_modutil_getpwuid(pamh, getuid());
  472. if (pwd == NULL) {
  473. pam_syslog(pamh, LOG_ERR,
  474. "error retrieving information about user %lu",
  475. (unsigned long)getuid());
  476. return PAM_USER_UNKNOWN;
  477. }
  478. user = pwd->pw_name;
  479. } else {
  480. /* Get the user's name. */
  481. ret = pam_get_user(pamh, &user, NULL);
  482. if (ret != PAM_SUCCESS) {
  483. pam_syslog(pamh, LOG_NOTICE,
  484. "cannot determine user name: %s",
  485. pam_strerror(pamh, ret));
  486. return ret;
  487. }
  488. /* Postpone requesting password data until it is needed */
  489. }
  490. /* Walk the argument list. */
  491. count = 0;
  492. left = qual = right = NULL;
  493. for (i = 0; i < argc; i++) {
  494. if (strcmp(argv[i], "debug") == 0) {
  495. continue;
  496. }
  497. if (strcmp(argv[i], "use_uid") == 0) {
  498. continue;
  499. }
  500. if (strcmp(argv[i], "quiet") == 0) {
  501. continue;
  502. }
  503. if (strcmp(argv[i], "quiet_fail") == 0) {
  504. continue;
  505. }
  506. if (strcmp(argv[i], "quiet_success") == 0) {
  507. continue;
  508. }
  509. if (strcmp(argv[i], "audit") == 0) {
  510. continue;
  511. }
  512. if (left == NULL) {
  513. left = argv[i];
  514. continue;
  515. }
  516. if (qual == NULL) {
  517. qual = argv[i];
  518. continue;
  519. }
  520. if (right == NULL) {
  521. right = argv[i];
  522. if (right == NULL)
  523. continue;
  524. count++;
  525. ret = evaluate(pamh, debug,
  526. left, qual, right,
  527. &pwd, user);
  528. if (ret == PAM_USER_UNKNOWN && audit)
  529. pam_syslog(pamh, LOG_NOTICE,
  530. "error retrieving information about user %s",
  531. user);
  532. if (ret != PAM_SUCCESS) {
  533. if(!quiet_fail && ret != PAM_USER_UNKNOWN)
  534. pam_syslog(pamh, LOG_INFO,
  535. "requirement \"%s %s %s\" "
  536. "not met by user \"%s\"",
  537. left, qual, right, user);
  538. left = qual = right = NULL;
  539. break;
  540. }
  541. else
  542. if(!quiet_succ)
  543. pam_syslog(pamh, LOG_INFO,
  544. "requirement \"%s %s %s\" "
  545. "was met by user \"%s\"",
  546. left, qual, right, user);
  547. left = qual = right = NULL;
  548. continue;
  549. }
  550. }
  551. if (left || qual || right) {
  552. ret = PAM_SERVICE_ERR;
  553. pam_syslog(pamh, LOG_ERR,
  554. "incomplete condition detected");
  555. } else if (count == 0) {
  556. pam_syslog(pamh, LOG_INFO,
  557. "no condition detected; module succeeded");
  558. }
  559. return ret;
  560. }
  561. int
  562. pam_sm_setcred(pam_handle_t *pamh UNUSED, int flags UNUSED,
  563. int argc UNUSED, const char **argv UNUSED)
  564. {
  565. return PAM_IGNORE;
  566. }
  567. int
  568. pam_sm_acct_mgmt(pam_handle_t *pamh, int flags, int argc, const char **argv)
  569. {
  570. return pam_sm_authenticate(pamh, flags, argc, argv);
  571. }
  572. int
  573. pam_sm_open_session(pam_handle_t *pamh, int flags, int argc, const char **argv)
  574. {
  575. return pam_sm_authenticate(pamh, flags, argc, argv);
  576. }
  577. int
  578. pam_sm_close_session(pam_handle_t *pamh, int flags, int argc, const char **argv)
  579. {
  580. return pam_sm_authenticate(pamh, flags, argc, argv);
  581. }
  582. int
  583. pam_sm_chauthtok(pam_handle_t *pamh, int flags, int argc, const char **argv)
  584. {
  585. return pam_sm_authenticate(pamh, flags, argc, argv);
  586. }