pam_faillock.c 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764
  1. /*
  2. * Copyright (c) 2010, 2017, 2019 Tomas Mraz <tmraz@redhat.com>
  3. * Copyright (c) 2010, 2017, 2019 Red Hat, Inc.
  4. *
  5. * Redistribution and use in source and binary forms, with or without
  6. * modification, are permitted provided that the following conditions
  7. * are met:
  8. * 1. Redistributions of source code must retain the above copyright
  9. * notice, and the entire permission notice in its entirety,
  10. * including the disclaimer of warranties.
  11. * 2. Redistributions in binary form must reproduce the above copyright
  12. * notice, this list of conditions and the following disclaimer in the
  13. * documentation and/or other materials provided with the distribution.
  14. * 3. The name of the author may not be used to endorse or promote
  15. * products derived from this software without specific prior
  16. * written permission.
  17. *
  18. * ALTERNATIVELY, this product may be distributed under the terms of
  19. * the GNU Public License, in which case the provisions of the GPL are
  20. * required INSTEAD OF the above restrictions. (This clause is
  21. * necessary due to a potential bad interaction between the GPL and
  22. * the restrictions contained in a BSD-style copyright.)
  23. *
  24. * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
  25. * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
  26. * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  27. * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
  28. * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
  29. * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
  30. * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  31. * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
  32. * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
  33. * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
  34. * OF THE POSSIBILITY OF SUCH DAMAGE.
  35. */
  36. #include "config.h"
  37. #include <stdio.h>
  38. #include <string.h>
  39. #include <unistd.h>
  40. #include <stdint.h>
  41. #include <stdlib.h>
  42. #include <errno.h>
  43. #include <time.h>
  44. #include <pwd.h>
  45. #include <syslog.h>
  46. #include <ctype.h>
  47. #ifdef HAVE_LIBAUDIT
  48. #include <libaudit.h>
  49. #endif
  50. #include <security/pam_modules.h>
  51. #include <security/pam_modutil.h>
  52. #include <security/pam_ext.h>
  53. #include "pam_inline.h"
  54. #include "faillock.h"
  55. #define FAILLOCK_ACTION_PREAUTH 0
  56. #define FAILLOCK_ACTION_AUTHSUCC 1
  57. #define FAILLOCK_ACTION_AUTHFAIL 2
  58. #define FAILLOCK_FLAG_DENY_ROOT 0x1
  59. #define FAILLOCK_FLAG_AUDIT 0x2
  60. #define FAILLOCK_FLAG_SILENT 0x4
  61. #define FAILLOCK_FLAG_NO_LOG_INFO 0x8
  62. #define FAILLOCK_FLAG_UNLOCKED 0x10
  63. #define FAILLOCK_FLAG_LOCAL_ONLY 0x20
  64. #define FAILLOCK_FLAG_NO_DELAY 0x40
  65. #define MAX_TIME_INTERVAL 604800 /* 7 days */
  66. #define FAILLOCK_CONF_MAX_LINELEN 1023
  67. static const char default_faillock_conf[] = FAILLOCK_DEFAULT_CONF;
  68. struct options {
  69. unsigned int action;
  70. unsigned int flags;
  71. unsigned short deny;
  72. unsigned int fail_interval;
  73. unsigned int unlock_time;
  74. unsigned int root_unlock_time;
  75. char *dir;
  76. const char *user;
  77. char *admin_group;
  78. int failures;
  79. uint64_t latest_time;
  80. uid_t uid;
  81. int is_admin;
  82. uint64_t now;
  83. int fatal_error;
  84. };
  85. static int read_config_file(
  86. pam_handle_t *pamh,
  87. struct options *opts,
  88. const char *cfgfile
  89. );
  90. static void set_conf_opt(
  91. pam_handle_t *pamh,
  92. struct options *opts,
  93. const char *name,
  94. const char *value
  95. );
  96. static int
  97. args_parse(pam_handle_t *pamh, int argc, const char **argv,
  98. int flags, struct options *opts)
  99. {
  100. int i;
  101. int config_arg_index = -1;
  102. int rv;
  103. const char *conf = default_faillock_conf;
  104. memset(opts, 0, sizeof(*opts));
  105. opts->dir = strdup(FAILLOCK_DEFAULT_TALLYDIR);
  106. opts->deny = 3;
  107. opts->fail_interval = 900;
  108. opts->unlock_time = 600;
  109. opts->root_unlock_time = MAX_TIME_INTERVAL+1;
  110. for (i = 0; i < argc; ++i) {
  111. const char *str = pam_str_skip_prefix(argv[i], "conf=");
  112. if (str != NULL) {
  113. conf = str;
  114. config_arg_index = i;
  115. }
  116. }
  117. if ((rv = read_config_file(pamh, opts, conf)) != PAM_SUCCESS) {
  118. pam_syslog(pamh, LOG_ERR,
  119. "Configuration file missing or broken");
  120. return rv;
  121. }
  122. for (i = 0; i < argc; ++i) {
  123. if (i == config_arg_index) {
  124. continue;
  125. }
  126. else if (strcmp(argv[i], "preauth") == 0) {
  127. opts->action = FAILLOCK_ACTION_PREAUTH;
  128. }
  129. else if (strcmp(argv[i], "authfail") == 0) {
  130. opts->action = FAILLOCK_ACTION_AUTHFAIL;
  131. }
  132. else if (strcmp(argv[i], "authsucc") == 0) {
  133. opts->action = FAILLOCK_ACTION_AUTHSUCC;
  134. }
  135. else {
  136. char buf[FAILLOCK_CONF_MAX_LINELEN + 1];
  137. char *val;
  138. strncpy(buf, argv[i], sizeof(buf) - 1);
  139. buf[sizeof(buf) - 1] = '\0';
  140. val = strchr(buf, '=');
  141. if (val != NULL) {
  142. *val = '\0';
  143. ++val;
  144. }
  145. else {
  146. val = buf + sizeof(buf) - 1;
  147. }
  148. set_conf_opt(pamh, opts, buf, val);
  149. }
  150. }
  151. if (opts->root_unlock_time == MAX_TIME_INTERVAL+1)
  152. opts->root_unlock_time = opts->unlock_time;
  153. if (flags & PAM_SILENT)
  154. opts->flags |= FAILLOCK_FLAG_SILENT;
  155. if (opts->dir == NULL) {
  156. pam_syslog(pamh, LOG_CRIT, "Error allocating memory: %m");
  157. opts->fatal_error = 1;
  158. }
  159. if (opts->fatal_error)
  160. return PAM_BUF_ERR;
  161. return PAM_SUCCESS;
  162. }
  163. /* parse a single configuration file */
  164. static int
  165. read_config_file(pam_handle_t *pamh, struct options *opts, const char *cfgfile)
  166. {
  167. FILE *f;
  168. char linebuf[FAILLOCK_CONF_MAX_LINELEN+1];
  169. f = fopen(cfgfile, "r");
  170. if (f == NULL) {
  171. /* ignore non-existent default config file */
  172. if (errno == ENOENT && cfgfile == default_faillock_conf)
  173. return PAM_SUCCESS;
  174. return PAM_SERVICE_ERR;
  175. }
  176. while (fgets(linebuf, sizeof(linebuf), f) != NULL) {
  177. size_t len;
  178. char *ptr;
  179. char *name;
  180. int eq;
  181. len = strlen(linebuf);
  182. /* len cannot be 0 unless there is a bug in fgets */
  183. if (len && linebuf[len - 1] != '\n' && !feof(f)) {
  184. (void) fclose(f);
  185. return PAM_SERVICE_ERR;
  186. }
  187. if ((ptr=strchr(linebuf, '#')) != NULL) {
  188. *ptr = '\0';
  189. } else {
  190. ptr = linebuf + len;
  191. }
  192. /* drop terminating whitespace including the \n */
  193. while (ptr > linebuf) {
  194. if (!isspace(*(ptr-1))) {
  195. *ptr = '\0';
  196. break;
  197. }
  198. --ptr;
  199. }
  200. /* skip initial whitespace */
  201. for (ptr = linebuf; isspace(*ptr); ptr++);
  202. if (*ptr == '\0')
  203. continue;
  204. /* grab the key name */
  205. eq = 0;
  206. name = ptr;
  207. while (*ptr != '\0') {
  208. if (isspace(*ptr) || *ptr == '=') {
  209. eq = *ptr == '=';
  210. *ptr = '\0';
  211. ++ptr;
  212. break;
  213. }
  214. ++ptr;
  215. }
  216. /* grab the key value */
  217. while (*ptr != '\0') {
  218. if (*ptr != '=' || eq) {
  219. if (!isspace(*ptr)) {
  220. break;
  221. }
  222. } else {
  223. eq = 1;
  224. }
  225. ++ptr;
  226. }
  227. /* set the key:value pair on opts */
  228. set_conf_opt(pamh, opts, name, ptr);
  229. }
  230. (void)fclose(f);
  231. return PAM_SUCCESS;
  232. }
  233. static void
  234. set_conf_opt(pam_handle_t *pamh, struct options *opts, const char *name, const char *value)
  235. {
  236. if (strcmp(name, "dir") == 0) {
  237. if (value[0] != '/') {
  238. pam_syslog(pamh, LOG_ERR,
  239. "Tally directory is not absolute path (%s); keeping default", value);
  240. } else {
  241. free(opts->dir);
  242. opts->dir = strdup(value);
  243. }
  244. }
  245. else if (strcmp(name, "deny") == 0) {
  246. if (sscanf(value, "%hu", &opts->deny) != 1) {
  247. pam_syslog(pamh, LOG_ERR,
  248. "Bad number supplied for deny argument");
  249. }
  250. }
  251. else if (strcmp(name, "fail_interval") == 0) {
  252. unsigned int temp;
  253. if (sscanf(value, "%u", &temp) != 1 ||
  254. temp > MAX_TIME_INTERVAL) {
  255. pam_syslog(pamh, LOG_ERR,
  256. "Bad number supplied for fail_interval argument");
  257. } else {
  258. opts->fail_interval = temp;
  259. }
  260. }
  261. else if (strcmp(name, "unlock_time") == 0) {
  262. unsigned int temp;
  263. if (strcmp(value, "never") == 0) {
  264. opts->unlock_time = 0;
  265. }
  266. else if (sscanf(value, "%u", &temp) != 1 ||
  267. temp > MAX_TIME_INTERVAL) {
  268. pam_syslog(pamh, LOG_ERR,
  269. "Bad number supplied for unlock_time argument");
  270. }
  271. else {
  272. opts->unlock_time = temp;
  273. }
  274. }
  275. else if (strcmp(name, "root_unlock_time") == 0) {
  276. unsigned int temp;
  277. if (strcmp(value, "never") == 0) {
  278. opts->root_unlock_time = 0;
  279. }
  280. else if (sscanf(value, "%u", &temp) != 1 ||
  281. temp > MAX_TIME_INTERVAL) {
  282. pam_syslog(pamh, LOG_ERR,
  283. "Bad number supplied for root_unlock_time argument");
  284. } else {
  285. opts->root_unlock_time = temp;
  286. }
  287. }
  288. else if (strcmp(name, "admin_group") == 0) {
  289. free(opts->admin_group);
  290. opts->admin_group = strdup(value);
  291. if (opts->admin_group == NULL) {
  292. opts->fatal_error = 1;
  293. pam_syslog(pamh, LOG_CRIT, "Error allocating memory: %m");
  294. }
  295. }
  296. else if (strcmp(name, "even_deny_root") == 0) {
  297. opts->flags |= FAILLOCK_FLAG_DENY_ROOT;
  298. }
  299. else if (strcmp(name, "audit") == 0) {
  300. opts->flags |= FAILLOCK_FLAG_AUDIT;
  301. }
  302. else if (strcmp(name, "silent") == 0) {
  303. opts->flags |= FAILLOCK_FLAG_SILENT;
  304. }
  305. else if (strcmp(name, "no_log_info") == 0) {
  306. opts->flags |= FAILLOCK_FLAG_NO_LOG_INFO;
  307. }
  308. else if (strcmp(name, "local_users_only") == 0) {
  309. opts->flags |= FAILLOCK_FLAG_LOCAL_ONLY;
  310. }
  311. else if (strcmp(name, "nodelay") == 0) {
  312. opts->flags |= FAILLOCK_FLAG_NO_DELAY;
  313. }
  314. else {
  315. pam_syslog(pamh, LOG_ERR, "Unknown option: %s", name);
  316. }
  317. }
  318. static int
  319. check_local_user (pam_handle_t *pamh, const char *user)
  320. {
  321. return pam_modutil_check_user_in_passwd(pamh, user, NULL) == PAM_SUCCESS;
  322. }
  323. static int
  324. get_pam_user(pam_handle_t *pamh, struct options *opts)
  325. {
  326. const char *user;
  327. int rv;
  328. struct passwd *pwd;
  329. if ((rv=pam_get_user(pamh, &user, NULL)) != PAM_SUCCESS) {
  330. return rv == PAM_CONV_AGAIN ? PAM_INCOMPLETE : rv;
  331. }
  332. if (*user == '\0') {
  333. return PAM_IGNORE;
  334. }
  335. if ((pwd=pam_modutil_getpwnam(pamh, user)) == NULL) {
  336. if (opts->flags & FAILLOCK_FLAG_AUDIT) {
  337. pam_syslog(pamh, LOG_NOTICE, "User unknown: %s", user);
  338. }
  339. else {
  340. pam_syslog(pamh, LOG_NOTICE, "User unknown");
  341. }
  342. return PAM_IGNORE;
  343. }
  344. opts->user = user;
  345. opts->uid = pwd->pw_uid;
  346. if (pwd->pw_uid == 0) {
  347. opts->is_admin = 1;
  348. return PAM_SUCCESS;
  349. }
  350. if (opts->admin_group && *opts->admin_group) {
  351. opts->is_admin = pam_modutil_user_in_group_uid_nam(pamh,
  352. pwd->pw_uid, opts->admin_group);
  353. }
  354. return PAM_SUCCESS;
  355. }
  356. static int
  357. check_tally(pam_handle_t *pamh, struct options *opts, struct tally_data *tallies, int *fd)
  358. {
  359. int tfd;
  360. unsigned int i;
  361. uint64_t latest_time;
  362. int failures;
  363. opts->now = time(NULL);
  364. tfd = open_tally(opts->dir, opts->user, opts->uid, 0);
  365. *fd = tfd;
  366. if (tfd == -1) {
  367. if (errno == EACCES || errno == ENOENT) {
  368. return PAM_SUCCESS;
  369. }
  370. pam_syslog(pamh, LOG_ERR, "Error opening the tally file for %s: %m", opts->user);
  371. return PAM_SYSTEM_ERR;
  372. }
  373. if (read_tally(tfd, tallies) != 0) {
  374. pam_syslog(pamh, LOG_ERR, "Error reading the tally file for %s: %m", opts->user);
  375. return PAM_SYSTEM_ERR;
  376. }
  377. if (opts->is_admin && !(opts->flags & FAILLOCK_FLAG_DENY_ROOT)) {
  378. return PAM_SUCCESS;
  379. }
  380. latest_time = 0;
  381. for (i = 0; i < tallies->count; i++) {
  382. if ((tallies->records[i].status & TALLY_STATUS_VALID) &&
  383. tallies->records[i].time > latest_time)
  384. latest_time = tallies->records[i].time;
  385. }
  386. opts->latest_time = latest_time;
  387. failures = 0;
  388. for (i = 0; i < tallies->count; i++) {
  389. if ((tallies->records[i].status & TALLY_STATUS_VALID) &&
  390. latest_time - tallies->records[i].time < opts->fail_interval) {
  391. ++failures;
  392. }
  393. }
  394. opts->failures = failures;
  395. if (opts->deny && failures >= opts->deny) {
  396. if ((!opts->is_admin && opts->unlock_time && latest_time + opts->unlock_time < opts->now) ||
  397. (opts->is_admin && opts->root_unlock_time && latest_time + opts->root_unlock_time < opts->now)) {
  398. #ifdef HAVE_LIBAUDIT
  399. if (opts->action != FAILLOCK_ACTION_PREAUTH) { /* do not audit in preauth */
  400. char buf[64];
  401. int audit_fd;
  402. const void *rhost = NULL, *tty = NULL;
  403. audit_fd = audit_open();
  404. /* If there is an error & audit support is in the kernel report error */
  405. if ((audit_fd < 0) && !(errno == EINVAL || errno == EPROTONOSUPPORT ||
  406. errno == EAFNOSUPPORT))
  407. return PAM_SYSTEM_ERR;
  408. (void)pam_get_item(pamh, PAM_TTY, &tty);
  409. (void)pam_get_item(pamh, PAM_RHOST, &rhost);
  410. snprintf(buf, sizeof(buf), "pam_faillock uid=%u ", opts->uid);
  411. audit_log_user_message(audit_fd, AUDIT_RESP_ACCT_UNLOCK_TIMED, buf,
  412. rhost, NULL, tty, 1);
  413. }
  414. #endif
  415. opts->flags |= FAILLOCK_FLAG_UNLOCKED;
  416. return PAM_SUCCESS;
  417. }
  418. return PAM_AUTH_ERR;
  419. }
  420. return PAM_SUCCESS;
  421. }
  422. static void
  423. reset_tally(pam_handle_t *pamh, struct options *opts, int *fd)
  424. {
  425. int rv;
  426. if (*fd == -1) {
  427. *fd = open_tally(opts->dir, opts->user, opts->uid, 1);
  428. }
  429. else {
  430. while ((rv=ftruncate(*fd, 0)) == -1 && errno == EINTR);
  431. if (rv == -1) {
  432. pam_syslog(pamh, LOG_ERR, "Error clearing the tally file for %s: %m", opts->user);
  433. }
  434. }
  435. }
  436. static int
  437. write_tally(pam_handle_t *pamh, struct options *opts, struct tally_data *tallies, int *fd)
  438. {
  439. struct tally *records;
  440. unsigned int i;
  441. int failures;
  442. unsigned int oldest;
  443. uint64_t oldtime;
  444. const void *source = NULL;
  445. if (*fd == -1) {
  446. *fd = open_tally(opts->dir, opts->user, opts->uid, 1);
  447. }
  448. if (*fd == -1) {
  449. if (errno == EACCES) {
  450. return PAM_SUCCESS;
  451. }
  452. pam_syslog(pamh, LOG_ERR, "Error opening the tally file for %s: %m", opts->user);
  453. return PAM_SYSTEM_ERR;
  454. }
  455. oldtime = 0;
  456. oldest = 0;
  457. failures = 0;
  458. for (i = 0; i < tallies->count; ++i) {
  459. if (oldtime == 0 || tallies->records[i].time < oldtime) {
  460. oldtime = tallies->records[i].time;
  461. oldest = i;
  462. }
  463. if (opts->flags & FAILLOCK_FLAG_UNLOCKED ||
  464. opts->now - tallies->records[i].time >= opts->fail_interval ) {
  465. tallies->records[i].status &= ~TALLY_STATUS_VALID;
  466. } else {
  467. ++failures;
  468. }
  469. }
  470. if (oldest >= tallies->count || (tallies->records[oldest].status & TALLY_STATUS_VALID)) {
  471. oldest = tallies->count;
  472. if ((records=realloc(tallies->records, (oldest+1) * sizeof (*tallies->records))) == NULL) {
  473. pam_syslog(pamh, LOG_CRIT, "Error allocating memory for tally records: %m");
  474. return PAM_BUF_ERR;
  475. }
  476. ++tallies->count;
  477. tallies->records = records;
  478. }
  479. memset(&tallies->records[oldest], 0, sizeof (*tallies->records));
  480. tallies->records[oldest].status = TALLY_STATUS_VALID;
  481. if (pam_get_item(pamh, PAM_RHOST, &source) != PAM_SUCCESS || source == NULL) {
  482. if (pam_get_item(pamh, PAM_TTY, &source) != PAM_SUCCESS || source == NULL) {
  483. if (pam_get_item(pamh, PAM_SERVICE, &source) != PAM_SUCCESS || source == NULL) {
  484. source = "";
  485. }
  486. }
  487. else {
  488. tallies->records[oldest].status |= TALLY_STATUS_TTY;
  489. }
  490. }
  491. else {
  492. tallies->records[oldest].status |= TALLY_STATUS_RHOST;
  493. }
  494. strncpy(tallies->records[oldest].source, source, sizeof(tallies->records[oldest].source));
  495. /* source does not have to be null terminated */
  496. tallies->records[oldest].time = opts->now;
  497. ++failures;
  498. if (opts->deny && failures == opts->deny) {
  499. #ifdef HAVE_LIBAUDIT
  500. char buf[64];
  501. int audit_fd;
  502. audit_fd = audit_open();
  503. /* If there is an error & audit support is in the kernel report error */
  504. if ((audit_fd < 0) && !(errno == EINVAL || errno == EPROTONOSUPPORT ||
  505. errno == EAFNOSUPPORT))
  506. return PAM_SYSTEM_ERR;
  507. snprintf(buf, sizeof(buf), "pam_faillock uid=%u ", opts->uid);
  508. audit_log_user_message(audit_fd, AUDIT_ANOM_LOGIN_FAILURES, buf,
  509. NULL, NULL, NULL, 1);
  510. if (!opts->is_admin || (opts->flags & FAILLOCK_FLAG_DENY_ROOT)) {
  511. audit_log_user_message(audit_fd, AUDIT_RESP_ACCT_LOCK, buf,
  512. NULL, NULL, NULL, 1);
  513. }
  514. close(audit_fd);
  515. #endif
  516. if (!(opts->flags & FAILLOCK_FLAG_NO_LOG_INFO)) {
  517. pam_syslog(pamh, LOG_INFO, "Consecutive login failures for user %s account temporarily locked",
  518. opts->user);
  519. }
  520. }
  521. if (update_tally(*fd, tallies) == 0)
  522. return PAM_SUCCESS;
  523. return PAM_SYSTEM_ERR;
  524. }
  525. static void
  526. faillock_message(pam_handle_t *pamh, struct options *opts)
  527. {
  528. int64_t left;
  529. if (!(opts->flags & FAILLOCK_FLAG_SILENT)) {
  530. if (opts->is_admin) {
  531. left = opts->latest_time + opts->root_unlock_time - opts->now;
  532. }
  533. else {
  534. left = opts->latest_time + opts->unlock_time - opts->now;
  535. }
  536. pam_info(pamh, _("The account is locked due to %u failed logins."),
  537. (unsigned int)opts->failures);
  538. if (left > 0) {
  539. left = (left + 59)/60; /* minutes */
  540. #if defined HAVE_DNGETTEXT && defined ENABLE_NLS
  541. pam_info(
  542. pamh,
  543. dngettext(PACKAGE,
  544. "(%d minute left to unlock)",
  545. "(%d minutes left to unlock)",
  546. (int)left),
  547. (int)left);
  548. #else
  549. if (left == 1)
  550. pam_info(pamh, _("(%d minute left to unlock)"), (int)left);
  551. else
  552. /* TRANSLATORS: only used if dngettext is not supported. */
  553. pam_info(pamh, _("(%d minutes left to unlock)"), (int)left);
  554. #endif
  555. }
  556. }
  557. }
  558. static void
  559. tally_cleanup(struct tally_data *tallies, int fd)
  560. {
  561. if (fd != -1) {
  562. close(fd);
  563. }
  564. free(tallies->records);
  565. }
  566. static void
  567. opts_cleanup(struct options *opts)
  568. {
  569. free(opts->dir);
  570. free(opts->admin_group);
  571. }
  572. /*---------------------------------------------------------------------*/
  573. int
  574. pam_sm_authenticate(pam_handle_t *pamh, int flags,
  575. int argc, const char **argv)
  576. {
  577. struct options opts;
  578. int rv, fd = -1;
  579. struct tally_data tallies;
  580. memset(&tallies, 0, sizeof(tallies));
  581. rv = args_parse(pamh, argc, argv, flags, &opts);
  582. if (rv != PAM_SUCCESS)
  583. goto err;
  584. if (!(opts.flags & FAILLOCK_FLAG_NO_DELAY)) {
  585. pam_fail_delay(pamh, 2000000); /* 2 sec delay on failure */
  586. }
  587. if ((rv=get_pam_user(pamh, &opts)) != PAM_SUCCESS) {
  588. goto err;
  589. }
  590. if (!(opts.flags & FAILLOCK_FLAG_LOCAL_ONLY) ||
  591. check_local_user (pamh, opts.user) != 0) {
  592. switch (opts.action) {
  593. case FAILLOCK_ACTION_PREAUTH:
  594. rv = check_tally(pamh, &opts, &tallies, &fd);
  595. if (rv == PAM_AUTH_ERR && !(opts.flags & FAILLOCK_FLAG_SILENT)) {
  596. faillock_message(pamh, &opts);
  597. }
  598. break;
  599. case FAILLOCK_ACTION_AUTHSUCC:
  600. rv = check_tally(pamh, &opts, &tallies, &fd);
  601. if (rv == PAM_SUCCESS) {
  602. reset_tally(pamh, &opts, &fd);
  603. }
  604. break;
  605. case FAILLOCK_ACTION_AUTHFAIL:
  606. rv = check_tally(pamh, &opts, &tallies, &fd);
  607. if (rv == PAM_SUCCESS) {
  608. rv = PAM_IGNORE; /* this return value should be ignored */
  609. write_tally(pamh, &opts, &tallies, &fd);
  610. }
  611. break;
  612. }
  613. }
  614. tally_cleanup(&tallies, fd);
  615. err:
  616. opts_cleanup(&opts);
  617. return rv;
  618. }
  619. /*---------------------------------------------------------------------*/
  620. int
  621. pam_sm_setcred(pam_handle_t *pamh UNUSED, int flags UNUSED,
  622. int argc UNUSED, const char **argv UNUSED)
  623. {
  624. return PAM_SUCCESS;
  625. }
  626. /*---------------------------------------------------------------------*/
  627. int
  628. pam_sm_acct_mgmt(pam_handle_t *pamh, int flags,
  629. int argc, const char **argv)
  630. {
  631. struct options opts;
  632. int rv, fd = -1;
  633. struct tally_data tallies;
  634. memset(&tallies, 0, sizeof(tallies));
  635. rv = args_parse(pamh, argc, argv, flags, &opts);
  636. if (rv != PAM_SUCCESS)
  637. goto err;
  638. opts.action = FAILLOCK_ACTION_AUTHSUCC;
  639. if ((rv=get_pam_user(pamh, &opts)) != PAM_SUCCESS) {
  640. goto err;
  641. }
  642. if (!(opts.flags & FAILLOCK_FLAG_LOCAL_ONLY) ||
  643. check_local_user (pamh, opts.user) != 0) {
  644. check_tally(pamh, &opts, &tallies, &fd); /* for auditing */
  645. reset_tally(pamh, &opts, &fd);
  646. }
  647. tally_cleanup(&tallies, fd);
  648. err:
  649. opts_cleanup(&opts);
  650. return rv;
  651. }
  652. /*-----------------------------------------------------------------------*/