/modules/pam_tally2/pam_tally2.c

https://github.com/wichert/linux-pam · C · 1060 lines · 793 code · 160 blank · 107 comment · 241 complexity · dbc938e022abe52a01a7abf6601bb026 MD5 · raw file

  1. /*
  2. * pam_tally2.c
  3. *
  4. */
  5. /* By Tim Baverstock <warwick@mmm.co.uk>, Multi Media Machine Ltd.
  6. * 5 March 1997
  7. *
  8. * Stuff stolen from pam_rootok and pam_listfile
  9. *
  10. * Changes by Tomas Mraz <tmraz@redhat.com> 5 January 2005, 26 January 2006
  11. * Audit option added for Tomas patch by Sebastien Tricaud <toady@gscore.org> 13 January 2005
  12. * Portions Copyright 2006, Red Hat, Inc.
  13. * Portions Copyright 1989 - 1993, Julianne Frances Haugh
  14. * All rights reserved.
  15. *
  16. * Redistribution and use in source and binary forms, with or without
  17. * modification, are permitted provided that the following conditions
  18. * are met:
  19. * 1. Redistributions of source code must retain the above copyright
  20. * notice, this list of conditions and the following disclaimer.
  21. * 2. Redistributions in binary form must reproduce the above copyright
  22. * notice, this list of conditions and the following disclaimer in the
  23. * documentation and/or other materials provided with the distribution.
  24. * 3. Neither the name of Julianne F. Haugh nor the names of its contributors
  25. * may be used to endorse or promote products derived from this software
  26. * without specific prior written permission.
  27. *
  28. * THIS SOFTWARE IS PROVIDED BY JULIE HAUGH AND CONTRIBUTORS ``AS IS'' AND
  29. * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  30. * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  31. * ARE DISCLAIMED. IN NO EVENT SHALL JULIE HAUGH OR CONTRIBUTORS BE LIABLE
  32. * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  33. * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
  34. * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  35. * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  36. * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
  37. * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  38. * SUCH DAMAGE.
  39. */
  40. #include "config.h"
  41. #if defined(MAIN) && defined(MEMORY_DEBUG)
  42. # undef exit
  43. #endif /* defined(MAIN) && defined(MEMORY_DEBUG) */
  44. #include <stdio.h>
  45. #include <string.h>
  46. #include <unistd.h>
  47. #include <stdarg.h>
  48. #include <stdlib.h>
  49. #include <syslog.h>
  50. #include <pwd.h>
  51. #include <time.h>
  52. #include <stdint.h>
  53. #include <errno.h>
  54. #ifdef HAVE_LIBAUDIT
  55. #include <libaudit.h>
  56. #endif
  57. #include <sys/types.h>
  58. #include <sys/stat.h>
  59. #include <sys/param.h>
  60. #include <fcntl.h>
  61. #include <unistd.h>
  62. #include <signal.h>
  63. #include "tallylog.h"
  64. #ifndef TRUE
  65. #define TRUE 1L
  66. #define FALSE 0L
  67. #endif
  68. #ifndef HAVE_FSEEKO
  69. #define fseeko fseek
  70. #endif
  71. /*
  72. * here, we make a definition for the externally accessible function
  73. * in this file (this definition is required for static a module
  74. * but strongly encouraged generally) it is used to instruct the
  75. * modules include file to define the function prototypes.
  76. */
  77. #ifndef MAIN
  78. #define PAM_SM_AUTH
  79. #define PAM_SM_ACCOUNT
  80. /* #define PAM_SM_SESSION */
  81. /* #define PAM_SM_PASSWORD */
  82. #include <security/pam_ext.h>
  83. #endif
  84. #include <security/pam_modutil.h>
  85. #include <security/pam_modules.h>
  86. /*---------------------------------------------------------------------*/
  87. #define DEFAULT_LOGFILE "/var/log/tallylog"
  88. #define MODULE_NAME "pam_tally2"
  89. #define tally_t uint16_t
  90. #define TALLY_HI ((tally_t)~0L)
  91. struct tally_options {
  92. const char *filename;
  93. tally_t deny;
  94. long lock_time;
  95. long unlock_time;
  96. long root_unlock_time;
  97. unsigned int ctrl;
  98. };
  99. #define PHASE_UNKNOWN 0
  100. #define PHASE_AUTH 1
  101. #define PHASE_ACCOUNT 2
  102. #define PHASE_SESSION 3
  103. #define OPT_MAGIC_ROOT 01
  104. #define OPT_FAIL_ON_ERROR 02
  105. #define OPT_DENY_ROOT 04
  106. #define OPT_QUIET 040
  107. #define OPT_AUDIT 0100
  108. #define OPT_NOLOGNOTICE 0400
  109. #define OPT_SERIALIZE 01000
  110. #define MAX_LOCK_WAITING_TIME 10
  111. /*---------------------------------------------------------------------*/
  112. /* some syslogging */
  113. #ifdef MAIN
  114. #define pam_syslog tally_log
  115. static void
  116. tally_log (const pam_handle_t *pamh UNUSED, int priority UNUSED,
  117. const char *fmt, ...)
  118. {
  119. va_list args;
  120. va_start(args, fmt);
  121. fprintf(stderr, "%s: ", MODULE_NAME);
  122. vfprintf(stderr, fmt, args);
  123. fprintf(stderr,"\n");
  124. va_end(args);
  125. }
  126. #define pam_modutil_getpwnam(pamh, user) getpwnam(user)
  127. #endif
  128. /*---------------------------------------------------------------------*/
  129. /* --- Support function: parse arguments --- */
  130. #ifndef MAIN
  131. static void
  132. log_phase_no_auth(pam_handle_t *pamh, int phase, const char *argv)
  133. {
  134. if ( phase != PHASE_AUTH ) {
  135. pam_syslog(pamh, LOG_ERR,
  136. "option %s allowed in auth phase only", argv);
  137. }
  138. }
  139. static int
  140. tally_parse_args(pam_handle_t *pamh, struct tally_options *opts,
  141. int phase, int argc, const char **argv)
  142. {
  143. memset(opts, 0, sizeof(*opts));
  144. opts->filename = DEFAULT_LOGFILE;
  145. opts->ctrl = OPT_FAIL_ON_ERROR;
  146. opts->root_unlock_time = -1;
  147. for ( ; argc-- > 0; ++argv ) {
  148. if ( ! strncmp( *argv, "file=", 5 ) ) {
  149. const char *from = *argv + 5;
  150. if ( *from!='/' ) {
  151. pam_syslog(pamh, LOG_ERR,
  152. "filename not /rooted; %s", *argv);
  153. return PAM_AUTH_ERR;
  154. }
  155. opts->filename = from;
  156. }
  157. else if ( ! strcmp( *argv, "onerr=fail" ) ) {
  158. opts->ctrl |= OPT_FAIL_ON_ERROR;
  159. }
  160. else if ( ! strcmp( *argv, "onerr=succeed" ) ) {
  161. opts->ctrl &= ~OPT_FAIL_ON_ERROR;
  162. }
  163. else if ( ! strcmp( *argv, "magic_root" ) ) {
  164. opts->ctrl |= OPT_MAGIC_ROOT;
  165. }
  166. else if ( ! strcmp( *argv, "serialize" ) ) {
  167. opts->ctrl |= OPT_SERIALIZE;
  168. }
  169. else if ( ! strcmp( *argv, "even_deny_root_account" ) ||
  170. ! strcmp( *argv, "even_deny_root" ) ) {
  171. log_phase_no_auth(pamh, phase, *argv);
  172. opts->ctrl |= OPT_DENY_ROOT;
  173. }
  174. else if ( ! strncmp( *argv, "deny=", 5 ) ) {
  175. log_phase_no_auth(pamh, phase, *argv);
  176. if ( sscanf((*argv)+5,"%hu",&opts->deny) != 1 ) {
  177. pam_syslog(pamh, LOG_ERR, "bad number supplied: %s", *argv);
  178. return PAM_AUTH_ERR;
  179. }
  180. }
  181. else if ( ! strncmp( *argv, "lock_time=", 10 ) ) {
  182. log_phase_no_auth(pamh, phase, *argv);
  183. if ( sscanf((*argv)+10,"%ld",&opts->lock_time) != 1 ) {
  184. pam_syslog(pamh, LOG_ERR, "bad number supplied: %s", *argv);
  185. return PAM_AUTH_ERR;
  186. }
  187. }
  188. else if ( ! strncmp( *argv, "unlock_time=", 12 ) ) {
  189. log_phase_no_auth(pamh, phase, *argv);
  190. if ( sscanf((*argv)+12,"%ld",&opts->unlock_time) != 1 ) {
  191. pam_syslog(pamh, LOG_ERR, "bad number supplied: %s", *argv);
  192. return PAM_AUTH_ERR;
  193. }
  194. }
  195. else if ( ! strncmp( *argv, "root_unlock_time=", 17 ) ) {
  196. log_phase_no_auth(pamh, phase, *argv);
  197. if ( sscanf((*argv)+17,"%ld",&opts->root_unlock_time) != 1 ) {
  198. pam_syslog(pamh, LOG_ERR, "bad number supplied: %s", *argv);
  199. return PAM_AUTH_ERR;
  200. }
  201. opts->ctrl |= OPT_DENY_ROOT; /* even_deny_root implied */
  202. }
  203. else if ( ! strcmp( *argv, "quiet" ) ||
  204. ! strcmp ( *argv, "silent")) {
  205. opts->ctrl |= OPT_QUIET;
  206. }
  207. else if ( ! strcmp ( *argv, "no_log_info") ) {
  208. opts->ctrl |= OPT_NOLOGNOTICE;
  209. }
  210. else if ( ! strcmp ( *argv, "audit") ) {
  211. opts->ctrl |= OPT_AUDIT;
  212. }
  213. else {
  214. pam_syslog(pamh, LOG_ERR, "unknown option: %s", *argv);
  215. }
  216. }
  217. if (opts->root_unlock_time == -1)
  218. opts->root_unlock_time = opts->unlock_time;
  219. return PAM_SUCCESS;
  220. }
  221. #endif /* #ifndef MAIN */
  222. /*---------------------------------------------------------------------*/
  223. /* --- Support function: get uid (and optionally username) from PAM or
  224. cline_user --- */
  225. #ifdef MAIN
  226. static char *cline_user=0; /* cline_user is used in the administration prog */
  227. #endif
  228. static int
  229. pam_get_uid(pam_handle_t *pamh, uid_t *uid, const char **userp, struct tally_options *opts)
  230. {
  231. const char *user = NULL;
  232. struct passwd *pw;
  233. #ifdef MAIN
  234. user = cline_user;
  235. #else
  236. if ((pam_get_user( pamh, &user, NULL )) != PAM_SUCCESS) {
  237. user = NULL;
  238. }
  239. #endif
  240. if ( !user || !*user ) {
  241. pam_syslog(pamh, LOG_ERR, "pam_get_uid; user?");
  242. return PAM_AUTH_ERR;
  243. }
  244. if ( ! ( pw = pam_modutil_getpwnam( pamh, user ) ) ) {
  245. opts->ctrl & OPT_AUDIT ?
  246. pam_syslog(pamh, LOG_ERR, "pam_get_uid; no such user %s", user) :
  247. pam_syslog(pamh, LOG_ERR, "pam_get_uid; no such user");
  248. return PAM_USER_UNKNOWN;
  249. }
  250. if ( uid ) *uid = pw->pw_uid;
  251. if ( userp ) *userp = user;
  252. return PAM_SUCCESS;
  253. }
  254. /*---------------------------------------------------------------------*/
  255. /* --- Support functions: set/get tally data --- */
  256. #ifndef MAIN
  257. struct tally_data {
  258. time_t time;
  259. int tfile;
  260. };
  261. static void
  262. _cleanup(pam_handle_t *pamh UNUSED, void *void_data, int error_status UNUSED)
  263. {
  264. struct tally_data *data = void_data;
  265. if (data->tfile != -1)
  266. close(data->tfile);
  267. free(data);
  268. }
  269. static void
  270. tally_set_data( pam_handle_t *pamh, time_t oldtime, int tfile )
  271. {
  272. struct tally_data *data;
  273. if ( (data=malloc(sizeof(*data))) != NULL ) {
  274. data->time = oldtime;
  275. data->tfile = tfile;
  276. pam_set_data(pamh, MODULE_NAME, (void *)data, _cleanup);
  277. }
  278. }
  279. static int
  280. tally_get_data( pam_handle_t *pamh, time_t *oldtime, int *tfile )
  281. {
  282. int rv;
  283. const void *void_data;
  284. const struct tally_data *data;
  285. rv = pam_get_data(pamh, MODULE_NAME, &void_data);
  286. if ( rv == PAM_SUCCESS && void_data != NULL && oldtime != NULL ) {
  287. data = void_data;
  288. *oldtime = data->time;
  289. *tfile = data->tfile;
  290. }
  291. else {
  292. rv = -1;
  293. *oldtime = 0;
  294. }
  295. return rv;
  296. }
  297. #endif /* #ifndef MAIN */
  298. /*---------------------------------------------------------------------*/
  299. /* --- Support function: open/create tallyfile and return tally for uid --- */
  300. /* If on entry tallyfile doesn't exist, creation is attempted. */
  301. static void
  302. alarm_handler(int sig UNUSED)
  303. { /* we just need to ignore it */
  304. }
  305. static int
  306. get_tally(pam_handle_t *pamh, uid_t uid, const char *filename,
  307. int *tfile, struct tallylog *tally, unsigned int ctrl)
  308. {
  309. struct stat fileinfo;
  310. int lstat_ret;
  311. void *void_tally = tally;
  312. int preopened = 0;
  313. if (*tfile != -1) {
  314. preopened = 1;
  315. goto skip_open;
  316. }
  317. lstat_ret = lstat(filename, &fileinfo);
  318. if (lstat_ret) {
  319. *tfile=open(filename, O_APPEND|O_CREAT, S_IRUSR|S_IWUSR);
  320. /* Create file, or append-open in pathological case. */
  321. if (*tfile == -1) {
  322. #ifndef MAIN
  323. if (errno == EACCES) {
  324. return PAM_IGNORE; /* called with insufficient access rights */
  325. }
  326. #endif
  327. pam_syslog(pamh, LOG_ALERT, "Couldn't create %s: %m", filename);
  328. return PAM_AUTH_ERR;
  329. }
  330. lstat_ret = fstat(*tfile, &fileinfo);
  331. close(*tfile);
  332. }
  333. *tfile = -1;
  334. if ( lstat_ret ) {
  335. pam_syslog(pamh, LOG_ALERT, "Couldn't stat %s", filename);
  336. return PAM_AUTH_ERR;
  337. }
  338. if ((fileinfo.st_mode & S_IWOTH) || !S_ISREG(fileinfo.st_mode)) {
  339. /* If the file is world writable or is not a
  340. normal file, return error */
  341. pam_syslog(pamh, LOG_ALERT,
  342. "%s is either world writable or not a normal file",
  343. filename);
  344. return PAM_AUTH_ERR;
  345. }
  346. if ((*tfile = open(filename, O_RDWR)) == -1) {
  347. #ifndef MAIN
  348. if (errno == EACCES) /* called with insufficient access rights */
  349. return PAM_IGNORE;
  350. #endif
  351. pam_syslog(pamh, LOG_ALERT, "Error opening %s for update: %m", filename);
  352. return PAM_AUTH_ERR;
  353. }
  354. skip_open:
  355. if (lseek(*tfile, (off_t)uid*(off_t)sizeof(*tally), SEEK_SET) == (off_t)-1) {
  356. pam_syslog(pamh, LOG_ALERT, "lseek failed for %s: %m", filename);
  357. if (!preopened) {
  358. close(*tfile);
  359. *tfile = -1;
  360. }
  361. return PAM_AUTH_ERR;
  362. }
  363. if (!preopened && (ctrl & OPT_SERIALIZE)) {
  364. /* this code is not thread safe as it uses fcntl locks and alarm()
  365. so never use serialize with multithreaded services */
  366. struct sigaction newsa, oldsa;
  367. unsigned int oldalarm;
  368. int rv;
  369. memset(&newsa, '\0', sizeof(newsa));
  370. newsa.sa_handler = alarm_handler;
  371. sigaction(SIGALRM, &newsa, &oldsa);
  372. oldalarm = alarm(MAX_LOCK_WAITING_TIME);
  373. rv = lockf(*tfile, F_LOCK, sizeof(*tally));
  374. /* lock failure is not fatal, we attempt to read the tally anyway */
  375. /* reinstate the eventual old alarm handler */
  376. if (rv == -1 && errno == EINTR) {
  377. if (oldalarm > MAX_LOCK_WAITING_TIME) {
  378. oldalarm -= MAX_LOCK_WAITING_TIME;
  379. } else if (oldalarm > 0) {
  380. oldalarm = 1;
  381. }
  382. }
  383. sigaction(SIGALRM, &oldsa, NULL);
  384. alarm(oldalarm);
  385. }
  386. if (fileinfo.st_size < (off_t)(uid+1)*(off_t)sizeof(*tally)) {
  387. memset(tally, 0, sizeof(*tally));
  388. } else if (pam_modutil_read(*tfile, void_tally, sizeof(*tally)) != sizeof(*tally)) {
  389. memset(tally, 0, sizeof(*tally));
  390. /* Shouldn't happen */
  391. }
  392. tally->fail_line[sizeof(tally->fail_line)-1] = '\0';
  393. return PAM_SUCCESS;
  394. }
  395. /*---------------------------------------------------------------------*/
  396. /* --- Support function: update tallyfile with tally!=TALLY_HI --- */
  397. static int
  398. set_tally(pam_handle_t *pamh, uid_t uid,
  399. const char *filename, int *tfile, struct tallylog *tally)
  400. {
  401. void *void_tally = tally;
  402. if (tally->fail_cnt != TALLY_HI) {
  403. if (lseek(*tfile, (off_t)uid * sizeof(*tally), SEEK_SET) == (off_t)-1) {
  404. pam_syslog(pamh, LOG_ALERT, "lseek failed for %s: %m", filename);
  405. return PAM_AUTH_ERR;
  406. }
  407. if (pam_modutil_write(*tfile, void_tally, sizeof(*tally)) != sizeof(*tally)) {
  408. pam_syslog(pamh, LOG_ALERT, "update (write) failed for %s: %m", filename);
  409. return PAM_AUTH_ERR;
  410. }
  411. }
  412. if (fsync(*tfile)) {
  413. pam_syslog(pamh, LOG_ALERT, "update (fsync) failed for %s: %m", filename);
  414. return PAM_AUTH_ERR;
  415. }
  416. return PAM_SUCCESS;
  417. }
  418. /*---------------------------------------------------------------------*/
  419. /* --- PAM bits --- */
  420. #ifndef MAIN
  421. #define RETURN_ERROR(i) return ((opts->ctrl & OPT_FAIL_ON_ERROR)?(i):(PAM_SUCCESS))
  422. /*---------------------------------------------------------------------*/
  423. static int
  424. tally_check (tally_t oldcnt, time_t oldtime, pam_handle_t *pamh, uid_t uid,
  425. const char *user, struct tally_options *opts,
  426. struct tallylog *tally)
  427. {
  428. int rv = PAM_SUCCESS;
  429. #ifdef HAVE_LIBAUDIT
  430. char buf[64];
  431. int audit_fd = -1;
  432. const void *rhost = NULL, *tty = NULL;
  433. #endif
  434. if ((opts->ctrl & OPT_MAGIC_ROOT) && getuid() == 0) {
  435. return PAM_SUCCESS;
  436. }
  437. /* magic_root skips tally check */
  438. #ifdef HAVE_LIBAUDIT
  439. audit_fd = audit_open();
  440. /* If there is an error & audit support is in the kernel report error */
  441. if ((audit_fd < 0) && !(errno == EINVAL || errno == EPROTONOSUPPORT ||
  442. errno == EAFNOSUPPORT))
  443. return PAM_SYSTEM_ERR;
  444. (void)pam_get_item(pamh, PAM_TTY, &tty);
  445. (void)pam_get_item(pamh, PAM_RHOST, &rhost);
  446. #endif
  447. if (opts->deny != 0 && /* deny==0 means no deny */
  448. tally->fail_cnt > opts->deny && /* tally>deny means exceeded */
  449. ((opts->ctrl & OPT_DENY_ROOT) || uid)) { /* even_deny stops uid check */
  450. #ifdef HAVE_LIBAUDIT
  451. if (tally->fail_cnt == opts->deny+1) {
  452. /* First say that max number was hit. */
  453. snprintf(buf, sizeof(buf), "pam_tally2 uid=%u ", uid);
  454. audit_log_user_message(audit_fd, AUDIT_ANOM_LOGIN_FAILURES, buf,
  455. rhost, NULL, tty, 1);
  456. }
  457. #endif
  458. if (uid) {
  459. /* Unlock time check */
  460. if (opts->unlock_time && oldtime) {
  461. if (opts->unlock_time + oldtime <= time(NULL)) {
  462. /* ignore deny check after unlock_time elapsed */
  463. #ifdef HAVE_LIBAUDIT
  464. snprintf(buf, sizeof(buf), "pam_tally2 uid=%u ", uid);
  465. audit_log_user_message(audit_fd, AUDIT_RESP_ACCT_UNLOCK_TIMED, buf,
  466. rhost, NULL, tty, 1);
  467. #endif
  468. rv = PAM_SUCCESS;
  469. goto cleanup;
  470. }
  471. }
  472. } else {
  473. /* Root unlock time check */
  474. if (opts->root_unlock_time && oldtime) {
  475. if (opts->root_unlock_time + oldtime <= time(NULL)) {
  476. /* ignore deny check after unlock_time elapsed */
  477. #ifdef HAVE_LIBAUDIT
  478. snprintf(buf, sizeof(buf), "pam_tally2 uid=%u ", uid);
  479. audit_log_user_message(audit_fd, AUDIT_RESP_ACCT_UNLOCK_TIMED, buf,
  480. rhost, NULL, tty, 1);
  481. #endif
  482. rv = PAM_SUCCESS;
  483. goto cleanup;
  484. }
  485. }
  486. }
  487. #ifdef HAVE_LIBAUDIT
  488. if (tally->fail_cnt == opts->deny+1) {
  489. /* First say that max number was hit. */
  490. audit_log_user_message(audit_fd, AUDIT_RESP_ACCT_LOCK, buf,
  491. rhost, NULL, tty, 1);
  492. }
  493. #endif
  494. if (!(opts->ctrl & OPT_QUIET)) {
  495. pam_info(pamh, _("Account locked due to %u failed logins"),
  496. (unsigned int)tally->fail_cnt);
  497. }
  498. if (!(opts->ctrl & OPT_NOLOGNOTICE)) {
  499. pam_syslog(pamh, LOG_NOTICE,
  500. "user %s (%lu) tally %hu, deny %hu",
  501. user, (unsigned long)uid, tally->fail_cnt, opts->deny);
  502. }
  503. rv = PAM_AUTH_ERR; /* Only unconditional failure */
  504. goto cleanup;
  505. }
  506. /* Lock time check */
  507. if (opts->lock_time && oldtime) {
  508. if (opts->lock_time + oldtime > time(NULL)) {
  509. /* don't increase fail_cnt or update fail_time when
  510. lock_time applies */
  511. tally->fail_cnt = oldcnt;
  512. tally->fail_time = oldtime;
  513. if (!(opts->ctrl & OPT_QUIET)) {
  514. pam_info(pamh, _("Account temporary locked (%ld seconds left)"),
  515. oldtime+opts->lock_time-time(NULL));
  516. }
  517. if (!(opts->ctrl & OPT_NOLOGNOTICE)) {
  518. pam_syslog(pamh, LOG_NOTICE,
  519. "user %s (%lu) has time limit [%lds left]"
  520. " since last failure.",
  521. user, (unsigned long)uid,
  522. oldtime+opts->lock_time-time(NULL));
  523. }
  524. rv = PAM_AUTH_ERR;
  525. goto cleanup;
  526. }
  527. }
  528. cleanup:
  529. #ifdef HAVE_LIBAUDIT
  530. if (audit_fd != -1) {
  531. close(audit_fd);
  532. }
  533. #endif
  534. return rv;
  535. }
  536. /* --- tally bump function: bump tally for uid by (signed) inc --- */
  537. static int
  538. tally_bump (int inc, time_t *oldtime, pam_handle_t *pamh,
  539. uid_t uid, const char *user, struct tally_options *opts, int *tfile)
  540. {
  541. struct tallylog tally;
  542. tally_t oldcnt;
  543. const void *remote_host = NULL;
  544. int i, rv;
  545. tally.fail_cnt = 0; /* !TALLY_HI --> Log opened for update */
  546. i = get_tally(pamh, uid, opts->filename, tfile, &tally, opts->ctrl);
  547. if (i != PAM_SUCCESS) {
  548. if (*tfile != -1) {
  549. close(*tfile);
  550. *tfile = -1;
  551. }
  552. RETURN_ERROR(i);
  553. }
  554. /* to remember old fail time (for locktime) */
  555. if (oldtime) {
  556. *oldtime = (time_t)tally.fail_time;
  557. }
  558. tally.fail_time = time(NULL);
  559. (void) pam_get_item(pamh, PAM_RHOST, &remote_host);
  560. if (!remote_host) {
  561. (void) pam_get_item(pamh, PAM_TTY, &remote_host);
  562. if (!remote_host) {
  563. remote_host = "unknown";
  564. }
  565. }
  566. strncpy(tally.fail_line, remote_host,
  567. sizeof(tally.fail_line)-1);
  568. tally.fail_line[sizeof(tally.fail_line)-1] = 0;
  569. oldcnt = tally.fail_cnt;
  570. if (!(opts->ctrl & OPT_MAGIC_ROOT) || getuid()) {
  571. /* magic_root doesn't change tally */
  572. tally.fail_cnt += inc;
  573. if (tally.fail_cnt == TALLY_HI) { /* Overflow *and* underflow. :) */
  574. tally.fail_cnt -= inc;
  575. pam_syslog(pamh, LOG_ALERT, "Tally %sflowed for user %s",
  576. (inc<0)?"under":"over",user);
  577. }
  578. }
  579. rv = tally_check(oldcnt, *oldtime, pamh, uid, user, opts, &tally);
  580. i = set_tally(pamh, uid, opts->filename, tfile, &tally);
  581. if (i != PAM_SUCCESS) {
  582. if (*tfile != -1) {
  583. close(*tfile);
  584. *tfile = -1;
  585. }
  586. if (rv == PAM_SUCCESS)
  587. RETURN_ERROR( i );
  588. /* fallthrough */
  589. } else if (!(opts->ctrl & OPT_SERIALIZE)) {
  590. close(*tfile);
  591. *tfile = -1;
  592. }
  593. return rv;
  594. }
  595. static int
  596. tally_reset (pam_handle_t *pamh, uid_t uid, struct tally_options *opts, int old_tfile)
  597. {
  598. struct tallylog tally;
  599. int tfile = old_tfile;
  600. int i;
  601. /* resets only if not magic root */
  602. if ((opts->ctrl & OPT_MAGIC_ROOT) && getuid() == 0) {
  603. return PAM_SUCCESS;
  604. }
  605. tally.fail_cnt = 0; /* !TALLY_HI --> Log opened for update */
  606. i=get_tally(pamh, uid, opts->filename, &tfile, &tally, opts->ctrl);
  607. if (i != PAM_SUCCESS) {
  608. if (tfile != old_tfile) /* the descriptor is not owned by pam data */
  609. close(tfile);
  610. RETURN_ERROR(i);
  611. }
  612. memset(&tally, 0, sizeof(tally));
  613. i=set_tally(pamh, uid, opts->filename, &tfile, &tally);
  614. if (i != PAM_SUCCESS) {
  615. if (tfile != old_tfile) /* the descriptor is not owned by pam data */
  616. close(tfile);
  617. RETURN_ERROR(i);
  618. }
  619. if (tfile != old_tfile)
  620. close(tfile);
  621. return PAM_SUCCESS;
  622. }
  623. /*---------------------------------------------------------------------*/
  624. /* --- authentication management functions (only) --- */
  625. PAM_EXTERN int
  626. pam_sm_authenticate(pam_handle_t *pamh, int flags UNUSED,
  627. int argc, const char **argv)
  628. {
  629. int
  630. rv, tfile = -1;
  631. time_t
  632. oldtime = 0;
  633. struct tally_options
  634. options, *opts = &options;
  635. uid_t
  636. uid;
  637. const char
  638. *user;
  639. rv = tally_parse_args(pamh, opts, PHASE_AUTH, argc, argv);
  640. if (rv != PAM_SUCCESS)
  641. RETURN_ERROR(rv);
  642. if (flags & PAM_SILENT)
  643. opts->ctrl |= OPT_QUIET;
  644. rv = pam_get_uid(pamh, &uid, &user, opts);
  645. if (rv != PAM_SUCCESS)
  646. RETURN_ERROR(rv);
  647. rv = tally_bump(1, &oldtime, pamh, uid, user, opts, &tfile);
  648. tally_set_data(pamh, oldtime, tfile);
  649. return rv;
  650. }
  651. PAM_EXTERN int
  652. pam_sm_setcred(pam_handle_t *pamh, int flags UNUSED,
  653. int argc, const char **argv)
  654. {
  655. int
  656. rv, tfile = -1;
  657. time_t
  658. oldtime = 0;
  659. struct tally_options
  660. options, *opts = &options;
  661. uid_t
  662. uid;
  663. const char
  664. *user;
  665. rv = tally_parse_args(pamh, opts, PHASE_AUTH, argc, argv);
  666. if ( rv != PAM_SUCCESS )
  667. RETURN_ERROR( rv );
  668. rv = pam_get_uid(pamh, &uid, &user, opts);
  669. if ( rv != PAM_SUCCESS )
  670. RETURN_ERROR( rv );
  671. if ( tally_get_data(pamh, &oldtime, &tfile) != 0 )
  672. /* no data found */
  673. return PAM_SUCCESS;
  674. rv = tally_reset(pamh, uid, opts, tfile);
  675. pam_set_data(pamh, MODULE_NAME, NULL, NULL);
  676. return rv;
  677. }
  678. /*---------------------------------------------------------------------*/
  679. /* --- authentication management functions (only) --- */
  680. /* To reset failcount of user on successfull login */
  681. PAM_EXTERN int
  682. pam_sm_acct_mgmt(pam_handle_t *pamh, int flags UNUSED,
  683. int argc, const char **argv)
  684. {
  685. int
  686. rv, tfile = -1;
  687. time_t
  688. oldtime = 0;
  689. struct tally_options
  690. options, *opts = &options;
  691. uid_t
  692. uid;
  693. const char
  694. *user;
  695. rv = tally_parse_args(pamh, opts, PHASE_ACCOUNT, argc, argv);
  696. if ( rv != PAM_SUCCESS )
  697. RETURN_ERROR( rv );
  698. rv = pam_get_uid(pamh, &uid, &user, opts);
  699. if ( rv != PAM_SUCCESS )
  700. RETURN_ERROR( rv );
  701. if ( tally_get_data(pamh, &oldtime, &tfile) != 0 )
  702. /* no data found */
  703. return PAM_SUCCESS;
  704. rv = tally_reset(pamh, uid, opts, tfile);
  705. pam_set_data(pamh, MODULE_NAME, NULL, NULL);
  706. return rv;
  707. }
  708. /*-----------------------------------------------------------------------*/
  709. #ifdef PAM_STATIC
  710. /* static module data */
  711. struct pam_module _pam_tally2_modstruct = {
  712. MODULE_NAME,
  713. #ifdef PAM_SM_AUTH
  714. pam_sm_authenticate,
  715. pam_sm_setcred,
  716. #else
  717. NULL,
  718. NULL,
  719. #endif
  720. #ifdef PAM_SM_ACCOUNT
  721. pam_sm_acct_mgmt,
  722. #else
  723. NULL,
  724. #endif
  725. NULL,
  726. NULL,
  727. NULL,
  728. };
  729. #endif /* #ifdef PAM_STATIC */
  730. /*-----------------------------------------------------------------------*/
  731. #else /* #ifndef MAIN */
  732. static const char *cline_filename = DEFAULT_LOGFILE;
  733. static tally_t cline_reset = TALLY_HI; /* Default is `interrogate only' */
  734. static int cline_quiet = 0;
  735. /*
  736. * Not going to link with pamlib just for these.. :)
  737. */
  738. static const char *
  739. pam_errors( int i )
  740. {
  741. switch (i) {
  742. case PAM_AUTH_ERR: return _("Authentication error");
  743. case PAM_SERVICE_ERR: return _("Service error");
  744. case PAM_USER_UNKNOWN: return _("Unknown user");
  745. default: return _("Unknown error");
  746. }
  747. }
  748. static int
  749. getopts( char **argv )
  750. {
  751. const char *pname = *argv;
  752. for ( ; *argv ; (void)(*argv && ++argv) ) {
  753. if ( !strcmp (*argv,"--file") ) cline_filename=*++argv;
  754. else if ( !strcmp(*argv,"-f") ) cline_filename=*++argv;
  755. else if ( !strncmp(*argv,"--file=",7) ) cline_filename=*argv+7;
  756. else if ( !strcmp (*argv,"--user") ) cline_user=*++argv;
  757. else if ( !strcmp (*argv,"-u") ) cline_user=*++argv;
  758. else if ( !strncmp(*argv,"--user=",7) ) cline_user=*argv+7;
  759. else if ( !strcmp (*argv,"--reset") ) cline_reset=0;
  760. else if ( !strcmp (*argv,"-r") ) cline_reset=0;
  761. else if ( !strncmp(*argv,"--reset=",8)) {
  762. if ( sscanf(*argv+8,"%hu",&cline_reset) != 1 )
  763. fprintf(stderr,_("%s: Bad number given to --reset=\n"),pname), exit(0);
  764. }
  765. else if ( !strcmp (*argv,"--quiet") ) cline_quiet=1;
  766. else {
  767. fprintf(stderr,_("%s: Unrecognised option %s\n"),pname,*argv);
  768. return FALSE;
  769. }
  770. }
  771. return TRUE;
  772. }
  773. static void
  774. print_one(const struct tallylog *tally, uid_t uid)
  775. {
  776. static int once;
  777. char *cp;
  778. time_t fail_time;
  779. struct tm *tm;
  780. struct passwd *pwent;
  781. const char *username = "[NONAME]";
  782. char ptime[80];
  783. pwent = getpwuid(uid);
  784. fail_time = tally->fail_time;
  785. tm = localtime(&fail_time);
  786. strftime (ptime, sizeof (ptime), "%D %H:%M:%S", tm);
  787. cp = ptime;
  788. if (pwent) {
  789. username = pwent->pw_name;
  790. }
  791. if (!once) {
  792. printf (_("Login Failures Latest failure From\n"));
  793. once++;
  794. }
  795. printf ("%-15.15s %5hu ", username, tally->fail_cnt);
  796. if (tally->fail_time) {
  797. printf ("%-17.17s %s", cp, tally->fail_line);
  798. }
  799. putchar ('\n');
  800. }
  801. int
  802. main( int argc UNUSED, char **argv )
  803. {
  804. struct tallylog tally;
  805. if ( ! getopts( argv+1 ) ) {
  806. printf(_("%s: [-f rooted-filename] [--file rooted-filename]\n"
  807. " [-u username] [--user username]\n"
  808. " [-r] [--reset[=n]] [--quiet]\n"),
  809. *argv);
  810. exit(2);
  811. }
  812. umask(077);
  813. /*
  814. * Major difference between individual user and all users:
  815. * --user just handles one user, just like PAM.
  816. * without --user it handles all users, sniffing cline_filename for nonzeros
  817. */
  818. if ( cline_user ) {
  819. uid_t uid;
  820. int tfile = -1;
  821. struct tally_options opts;
  822. int i;
  823. memset(&opts, 0, sizeof(opts));
  824. opts.ctrl = OPT_AUDIT;
  825. i=pam_get_uid(NULL, &uid, NULL, &opts);
  826. if ( i != PAM_SUCCESS ) {
  827. fprintf(stderr,"%s: %s\n",*argv,pam_errors(i));
  828. exit(1);
  829. }
  830. i=get_tally(NULL, uid, cline_filename, &tfile, &tally, 0);
  831. if ( i != PAM_SUCCESS ) {
  832. if (tfile != -1)
  833. close(tfile);
  834. fprintf(stderr, "%s: %s\n", *argv, pam_errors(i));
  835. exit(1);
  836. }
  837. if ( !cline_quiet )
  838. print_one(&tally, uid);
  839. if (cline_reset != TALLY_HI) {
  840. #ifdef HAVE_LIBAUDIT
  841. char buf[64];
  842. int audit_fd = audit_open();
  843. snprintf(buf, sizeof(buf), "pam_tally2 uid=%u reset=%hu", uid, cline_reset);
  844. audit_log_user_message(audit_fd, AUDIT_USER_ACCT,
  845. buf, NULL, NULL, ttyname(STDIN_FILENO), 1);
  846. if (audit_fd >=0)
  847. close(audit_fd);
  848. #endif
  849. if (cline_reset == 0) {
  850. memset(&tally, 0, sizeof(tally));
  851. } else {
  852. tally.fail_cnt = cline_reset;
  853. }
  854. i=set_tally(NULL, uid, cline_filename, &tfile, &tally);
  855. close(tfile);
  856. if (i != PAM_SUCCESS) {
  857. fprintf(stderr,"%s: %s\n",*argv,pam_errors(i));
  858. exit(1);
  859. }
  860. } else {
  861. close(tfile);
  862. }
  863. }
  864. else /* !cline_user (ie, operate on all users) */ {
  865. FILE *tfile=fopen(cline_filename, "r");
  866. uid_t uid=0;
  867. if (!tfile && cline_reset != 0) {
  868. perror(*argv);
  869. exit(1);
  870. }
  871. for ( ; tfile && !feof(tfile); uid++ ) {
  872. if ( !fread(&tally, sizeof(tally), 1, tfile)
  873. || !tally.fail_cnt ) {
  874. continue;
  875. }
  876. print_one(&tally, uid);
  877. }
  878. if (tfile)
  879. fclose(tfile);
  880. if ( cline_reset!=0 && cline_reset!=TALLY_HI ) {
  881. fprintf(stderr,_("%s: Can't reset all users to non-zero\n"),*argv);
  882. }
  883. else if ( !cline_reset ) {
  884. #ifdef HAVE_LIBAUDIT
  885. char buf[64];
  886. int audit_fd = audit_open();
  887. snprintf(buf, sizeof(buf), "pam_tally2 uid=all reset=0");
  888. audit_log_user_message(audit_fd, AUDIT_USER_ACCT,
  889. buf, NULL, NULL, ttyname(STDIN_FILENO), 1);
  890. if (audit_fd >=0)
  891. close(audit_fd);
  892. #endif
  893. tfile=fopen(cline_filename, "w");
  894. if ( !tfile ) perror(*argv), exit(0);
  895. fclose(tfile);
  896. }
  897. }
  898. return 0;
  899. }
  900. #endif /* #ifndef MAIN */