7 /* By Tim Baverstock <warwick@mmm.co.uk>, Multi Media Machine Ltd.
10 * Stuff stolen from pam_rootok and pam_listfile
12 * Changes by Tomas Mraz <tmraz@redhat.com> 5 January 2005, 26 January 2006
13 * Audit option added for Tomas patch by Sebastien Tricaud <toady@gscore.org> 13 January 2005
14 * Portions Copyright 2006, Red Hat, Inc.
15 * Portions Copyright 1989 - 1993, Julianne Frances Haugh
16 * All rights reserved.
18 * Redistribution and use in source and binary forms, with or without
19 * modification, are permitted provided that the following conditions
21 * 1. Redistributions of source code must retain the above copyright
22 * notice, this list of conditions and the following disclaimer.
23 * 2. Redistributions in binary form must reproduce the above copyright
24 * notice, this list of conditions and the following disclaimer in the
25 * documentation and/or other materials provided with the distribution.
26 * 3. Neither the name of Julianne F. Haugh nor the names of its contributors
27 * may be used to endorse or promote products derived from this software
28 * without specific prior written permission.
30 * THIS SOFTWARE IS PROVIDED BY JULIE HAUGH AND CONTRIBUTORS ``AS IS'' AND
31 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
32 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
33 * ARE DISCLAIMED. IN NO EVENT SHALL JULIE HAUGH OR CONTRIBUTORS BE LIABLE
34 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
35 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
36 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
37 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
38 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
39 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
45 #if defined(MAIN) && defined(MEMORY_DEBUG)
47 #endif /* defined(MAIN) && defined(MEMORY_DEBUG) */
63 #include <sys/types.h>
65 #include <sys/param.h>
81 * here, we make a definition for the externally accessible function
82 * in this file (this definition is required for static a module
83 * but strongly encouraged generally) it is used to instruct the
84 * modules include file to define the function prototypes.
89 #define PAM_SM_ACCOUNT
90 /* #define PAM_SM_SESSION */
91 /* #define PAM_SM_PASSWORD */
93 #include <security/pam_ext.h>
95 #include <security/pam_modutil.h>
96 #include <security/pam_modules.h>
98 /*---------------------------------------------------------------------*/
100 #define DEFAULT_LOGFILE "/var/log/tallylog"
101 #define MODULE_NAME "pam_tally2"
103 #define tally_t uint16_t
104 #define TALLY_HI ((tally_t)~0L)
106 struct tally_options {
107 const char *filename;
111 long root_unlock_time;
115 #define PHASE_UNKNOWN 0
117 #define PHASE_ACCOUNT 2
118 #define PHASE_SESSION 3
120 #define OPT_MAGIC_ROOT 01
121 #define OPT_FAIL_ON_ERROR 02
122 #define OPT_DENY_ROOT 04
123 #define OPT_QUIET 040
124 #define OPT_AUDIT 0100
125 #define OPT_NOLOGNOTICE 0400
126 #define OPT_SERIALIZE 01000
127 #define OPT_DEBUG 02000
129 #define MAX_LOCK_WAITING_TIME 10
131 /*---------------------------------------------------------------------*/
133 /* some syslogging */
136 #define pam_syslog tally_log
138 tally_log (const pam_handle_t *pamh UNUSED, int priority UNUSED,
139 const char *fmt, ...)
144 fprintf(stderr, "%s: ", MODULE_NAME);
145 vfprintf(stderr, fmt, args);
146 fprintf(stderr,"\n");
150 #define pam_modutil_getpwnam(pamh, user) getpwnam(user)
153 /*---------------------------------------------------------------------*/
155 /* --- Support function: parse arguments --- */
160 log_phase_no_auth(pam_handle_t *pamh, int phase, const char *argv)
162 if ( phase != PHASE_AUTH ) {
163 pam_syslog(pamh, LOG_ERR,
164 "option %s allowed in auth phase only", argv);
169 tally_parse_args(pam_handle_t *pamh, struct tally_options *opts,
170 int phase, int argc, const char **argv)
172 memset(opts, 0, sizeof(*opts));
173 opts->filename = DEFAULT_LOGFILE;
174 opts->ctrl = OPT_FAIL_ON_ERROR;
175 opts->root_unlock_time = -1;
177 for ( ; argc-- > 0; ++argv ) {
179 if ( ! strncmp( *argv, "file=", 5 ) ) {
180 const char *from = *argv + 5;
182 pam_syslog(pamh, LOG_ERR,
183 "filename not /rooted; %s", *argv);
186 opts->filename = from;
188 else if ( ! strcmp( *argv, "onerr=fail" ) ) {
189 opts->ctrl |= OPT_FAIL_ON_ERROR;
191 else if ( ! strcmp( *argv, "onerr=succeed" ) ) {
192 opts->ctrl &= ~OPT_FAIL_ON_ERROR;
194 else if ( ! strcmp( *argv, "magic_root" ) ) {
195 opts->ctrl |= OPT_MAGIC_ROOT;
197 else if ( ! strcmp( *argv, "serialize" ) ) {
198 opts->ctrl |= OPT_SERIALIZE;
200 else if ( ! strcmp( *argv, "debug" ) ) {
201 opts->ctrl |= OPT_DEBUG;
203 else if ( ! strcmp( *argv, "even_deny_root_account" ) ||
204 ! strcmp( *argv, "even_deny_root" ) ) {
205 log_phase_no_auth(pamh, phase, *argv);
206 opts->ctrl |= OPT_DENY_ROOT;
208 else if ( ! strncmp( *argv, "deny=", 5 ) ) {
209 log_phase_no_auth(pamh, phase, *argv);
210 if ( sscanf((*argv)+5,"%hu",&opts->deny) != 1 ) {
211 pam_syslog(pamh, LOG_ERR, "bad number supplied: %s", *argv);
215 else if ( ! strncmp( *argv, "lock_time=", 10 ) ) {
216 log_phase_no_auth(pamh, phase, *argv);
217 if ( sscanf((*argv)+10,"%ld",&opts->lock_time) != 1 ) {
218 pam_syslog(pamh, LOG_ERR, "bad number supplied: %s", *argv);
222 else if ( ! strncmp( *argv, "unlock_time=", 12 ) ) {
223 log_phase_no_auth(pamh, phase, *argv);
224 if ( sscanf((*argv)+12,"%ld",&opts->unlock_time) != 1 ) {
225 pam_syslog(pamh, LOG_ERR, "bad number supplied: %s", *argv);
229 else if ( ! strncmp( *argv, "root_unlock_time=", 17 ) ) {
230 log_phase_no_auth(pamh, phase, *argv);
231 if ( sscanf((*argv)+17,"%ld",&opts->root_unlock_time) != 1 ) {
232 pam_syslog(pamh, LOG_ERR, "bad number supplied: %s", *argv);
235 opts->ctrl |= OPT_DENY_ROOT; /* even_deny_root implied */
237 else if ( ! strcmp( *argv, "quiet" ) ||
238 ! strcmp ( *argv, "silent")) {
239 opts->ctrl |= OPT_QUIET;
241 else if ( ! strcmp ( *argv, "no_log_info") ) {
242 opts->ctrl |= OPT_NOLOGNOTICE;
244 else if ( ! strcmp ( *argv, "audit") ) {
245 opts->ctrl |= OPT_AUDIT;
248 pam_syslog(pamh, LOG_ERR, "unknown option: %s", *argv);
252 if (opts->root_unlock_time == -1)
253 opts->root_unlock_time = opts->unlock_time;
258 #endif /* #ifndef MAIN */
260 /*---------------------------------------------------------------------*/
262 /* --- Support function: get uid (and optionally username) from PAM or
266 static char *cline_user=0; /* cline_user is used in the administration prog */
270 pam_get_uid(pam_handle_t *pamh, uid_t *uid, const char **userp, struct tally_options *opts)
272 const char *user = NULL;
278 if ((pam_get_user( pamh, &user, NULL )) != PAM_SUCCESS) {
283 if ( !user || !*user ) {
284 pam_syslog(pamh, LOG_ERR, "pam_get_uid; user?");
288 if ( ! ( pw = pam_modutil_getpwnam( pamh, user ) ) ) {
289 opts->ctrl & OPT_AUDIT ?
290 pam_syslog(pamh, LOG_ERR, "pam_get_uid; no such user %s", user) :
291 pam_syslog(pamh, LOG_ERR, "pam_get_uid; no such user");
292 return PAM_USER_UNKNOWN;
295 if ( uid ) *uid = pw->pw_uid;
296 if ( userp ) *userp = user;
300 /*---------------------------------------------------------------------*/
302 /* --- Support functions: set/get tally data --- */
312 _cleanup(pam_handle_t *pamh UNUSED, void *void_data, int error_status UNUSED)
314 struct tally_data *data = void_data;
315 if (data->tfile != -1)
321 tally_set_data( pam_handle_t *pamh, time_t oldtime, int tfile )
323 struct tally_data *data;
325 if ( (data=malloc(sizeof(*data))) != NULL ) {
326 data->time = oldtime;
328 pam_set_data(pamh, MODULE_NAME, (void *)data, _cleanup);
333 tally_get_data( pam_handle_t *pamh, time_t *oldtime, int *tfile )
336 const void *void_data;
337 const struct tally_data *data;
339 rv = pam_get_data(pamh, MODULE_NAME, &void_data);
340 if ( rv == PAM_SUCCESS && void_data != NULL && oldtime != NULL ) {
342 *oldtime = data->time;
343 *tfile = data->tfile;
351 #endif /* #ifndef MAIN */
353 /*---------------------------------------------------------------------*/
355 /* --- Support function: open/create tallyfile and return tally for uid --- */
357 /* If on entry tallyfile doesn't exist, creation is attempted. */
360 alarm_handler(int sig UNUSED)
361 { /* we just need to ignore it */
365 get_tally(pam_handle_t *pamh, uid_t uid, const char *filename,
366 int *tfile, struct tallylog *tally, unsigned int ctrl)
368 struct stat fileinfo;
370 void *void_tally = tally;
378 lstat_ret = lstat(filename, &fileinfo);
380 *tfile=open(filename, O_APPEND|O_CREAT, S_IRUSR|S_IWUSR);
381 /* Create file, or append-open in pathological case. */
384 if (errno == EACCES) {
385 return PAM_IGNORE; /* called with insufficient access rights */
388 pam_syslog(pamh, LOG_ALERT, "Couldn't create %s: %m", filename);
391 lstat_ret = fstat(*tfile, &fileinfo);
398 pam_syslog(pamh, LOG_ALERT, "Couldn't stat %s", filename);
402 if ((fileinfo.st_mode & S_IWOTH) || !S_ISREG(fileinfo.st_mode)) {
403 /* If the file is world writable or is not a
404 normal file, return error */
405 pam_syslog(pamh, LOG_ALERT,
406 "%s is either world writable or not a normal file",
411 if ((*tfile = open(filename, O_RDWR)) == -1) {
413 if (errno == EACCES) /* called with insufficient access rights */
416 pam_syslog(pamh, LOG_ALERT, "Error opening %s for update: %m", filename);
422 if (lseek(*tfile, (off_t)uid*(off_t)sizeof(*tally), SEEK_SET) == (off_t)-1) {
423 pam_syslog(pamh, LOG_ALERT, "lseek failed for %s: %m", filename);
431 if (!preopened && (ctrl & OPT_SERIALIZE)) {
432 /* this code is not thread safe as it uses fcntl locks and alarm()
433 so never use serialize with multithreaded services */
434 struct sigaction newsa, oldsa;
435 unsigned int oldalarm;
438 memset(&newsa, '\0', sizeof(newsa));
439 newsa.sa_handler = alarm_handler;
440 sigaction(SIGALRM, &newsa, &oldsa);
441 oldalarm = alarm(MAX_LOCK_WAITING_TIME);
443 rv = lockf(*tfile, F_LOCK, sizeof(*tally));
444 /* lock failure is not fatal, we attempt to read the tally anyway */
446 /* reinstate the eventual old alarm handler */
447 if (rv == -1 && errno == EINTR) {
448 if (oldalarm > MAX_LOCK_WAITING_TIME) {
449 oldalarm -= MAX_LOCK_WAITING_TIME;
450 } else if (oldalarm > 0) {
454 sigaction(SIGALRM, &oldsa, NULL);
458 if (pam_modutil_read(*tfile, void_tally, sizeof(*tally)) != sizeof(*tally)) {
459 memset(tally, 0, sizeof(*tally));
462 tally->fail_line[sizeof(tally->fail_line)-1] = '\0';
467 /*---------------------------------------------------------------------*/
469 /* --- Support function: update tallyfile with tally!=TALLY_HI --- */
472 set_tally(pam_handle_t *pamh, uid_t uid,
473 const char *filename, int *tfile, struct tallylog *tally)
475 void *void_tally = tally;
476 if (tally->fail_cnt != TALLY_HI) {
477 if (lseek(*tfile, (off_t)uid * sizeof(*tally), SEEK_SET) == (off_t)-1) {
478 pam_syslog(pamh, LOG_ALERT, "lseek failed for %s: %m", filename);
481 if (pam_modutil_write(*tfile, void_tally, sizeof(*tally)) != sizeof(*tally)) {
482 pam_syslog(pamh, LOG_ALERT, "update (write) failed for %s: %m", filename);
488 pam_syslog(pamh, LOG_ALERT, "update (fsync) failed for %s: %m", filename);
494 /*---------------------------------------------------------------------*/
496 /* --- PAM bits --- */
500 #define RETURN_ERROR(i) return ((opts->ctrl & OPT_FAIL_ON_ERROR)?(i):(PAM_SUCCESS))
502 /*---------------------------------------------------------------------*/
505 tally_check (tally_t oldcnt, time_t oldtime, pam_handle_t *pamh, uid_t uid,
506 const char *user, struct tally_options *opts,
507 struct tallylog *tally)
509 int rv = PAM_SUCCESS;
510 int loglevel = LOG_DEBUG;
514 const void *rhost = NULL, *tty = NULL;
517 if ((opts->ctrl & OPT_MAGIC_ROOT) && getuid() == 0) {
520 /* magic_root skips tally check */
522 audit_fd = audit_open();
523 /* If there is an error & audit support is in the kernel report error */
524 if ((audit_fd < 0) && !(errno == EINVAL || errno == EPROTONOSUPPORT ||
525 errno == EAFNOSUPPORT))
526 return PAM_SYSTEM_ERR;
527 (void)pam_get_item(pamh, PAM_TTY, &tty);
528 (void)pam_get_item(pamh, PAM_RHOST, &rhost);
530 if (opts->deny != 0 && /* deny==0 means no deny */
531 tally->fail_cnt > opts->deny && /* tally>deny means exceeded */
532 ((opts->ctrl & OPT_DENY_ROOT) || uid)) { /* even_deny stops uid check */
534 if (tally->fail_cnt == opts->deny+1) {
535 /* First say that max number was hit. */
536 snprintf(buf, sizeof(buf), "pam_tally2 uid=%u ", uid);
537 audit_log_user_message(audit_fd, AUDIT_ANOM_LOGIN_FAILURES, buf,
538 rhost, NULL, tty, 1);
542 /* Unlock time check */
543 if (opts->unlock_time && oldtime) {
544 if (opts->unlock_time + oldtime <= time(NULL)) {
545 /* ignore deny check after unlock_time elapsed */
547 snprintf(buf, sizeof(buf), "pam_tally2 uid=%u ", uid);
548 audit_log_user_message(audit_fd, AUDIT_RESP_ACCT_UNLOCK_TIMED, buf,
549 rhost, NULL, tty, 1);
556 /* Root unlock time check */
557 if (opts->root_unlock_time && oldtime) {
558 if (opts->root_unlock_time + oldtime <= time(NULL)) {
559 /* ignore deny check after unlock_time elapsed */
561 snprintf(buf, sizeof(buf), "pam_tally2 uid=%u ", uid);
562 audit_log_user_message(audit_fd, AUDIT_RESP_ACCT_UNLOCK_TIMED, buf,
563 rhost, NULL, tty, 1);
572 if (tally->fail_cnt == opts->deny+1) {
573 /* First say that max number was hit. */
574 audit_log_user_message(audit_fd, AUDIT_RESP_ACCT_LOCK, buf,
575 rhost, NULL, tty, 1);
579 if (!(opts->ctrl & OPT_QUIET)) {
580 pam_info(pamh, _("Account locked due to %u failed logins"),
581 (unsigned int)tally->fail_cnt);
583 loglevel = LOG_NOTICE;
584 rv = PAM_AUTH_ERR; /* Only unconditional failure */
588 /* Lock time check */
589 if (opts->lock_time && oldtime) {
590 if (opts->lock_time + oldtime > time(NULL)) {
591 /* don't increase fail_cnt or update fail_time when
593 tally->fail_cnt = oldcnt;
594 tally->fail_time = oldtime;
596 if (!(opts->ctrl & OPT_QUIET)) {
597 pam_info(pamh, _("Account temporary locked (%ld seconds left)"),
598 oldtime+opts->lock_time-time(NULL));
600 if (!(opts->ctrl & OPT_NOLOGNOTICE)) {
601 pam_syslog(pamh, LOG_NOTICE,
602 "user %s (%lu) has time limit [%lds left]"
603 " since last failure.",
604 user, (unsigned long)uid,
605 oldtime+opts->lock_time-time(NULL));
613 if (!(opts->ctrl & OPT_NOLOGNOTICE) && (loglevel != LOG_DEBUG || opts->ctrl & OPT_DEBUG)) {
614 pam_syslog(pamh, loglevel,
615 "user %s (%lu) tally %hu, deny %hu",
616 user, (unsigned long)uid, tally->fail_cnt, opts->deny);
619 if (audit_fd != -1) {
626 /* --- tally bump function: bump tally for uid by (signed) inc --- */
629 tally_bump (int inc, time_t *oldtime, pam_handle_t *pamh,
630 uid_t uid, const char *user, struct tally_options *opts, int *tfile)
632 struct tallylog tally;
634 const void *remote_host = NULL;
637 tally.fail_cnt = 0; /* !TALLY_HI --> Log opened for update */
639 i = get_tally(pamh, uid, opts->filename, tfile, &tally, opts->ctrl);
640 if (i != PAM_SUCCESS) {
648 /* to remember old fail time (for locktime) */
650 *oldtime = (time_t)tally.fail_time;
653 tally.fail_time = time(NULL);
655 (void) pam_get_item(pamh, PAM_RHOST, &remote_host);
657 (void) pam_get_item(pamh, PAM_TTY, &remote_host);
659 remote_host = "unknown";
663 strncpy(tally.fail_line, remote_host,
664 sizeof(tally.fail_line)-1);
665 tally.fail_line[sizeof(tally.fail_line)-1] = 0;
667 oldcnt = tally.fail_cnt;
669 if (!(opts->ctrl & OPT_MAGIC_ROOT) || getuid()) {
670 /* magic_root doesn't change tally */
671 tally.fail_cnt += inc;
673 if (tally.fail_cnt == TALLY_HI) { /* Overflow *and* underflow. :) */
674 tally.fail_cnt -= inc;
675 pam_syslog(pamh, LOG_ALERT, "Tally %sflowed for user %s",
676 (inc<0)?"under":"over",user);
680 rv = tally_check(oldcnt, *oldtime, pamh, uid, user, opts, &tally);
682 i = set_tally(pamh, uid, opts->filename, tfile, &tally);
683 if (i != PAM_SUCCESS) {
688 if (rv == PAM_SUCCESS)
691 } else if (!(opts->ctrl & OPT_SERIALIZE)) {
700 tally_reset (pam_handle_t *pamh, uid_t uid, struct tally_options *opts, int old_tfile)
702 struct tallylog tally;
703 int tfile = old_tfile;
706 /* resets only if not magic root */
708 if ((opts->ctrl & OPT_MAGIC_ROOT) && getuid() == 0) {
712 tally.fail_cnt = 0; /* !TALLY_HI --> Log opened for update */
714 i=get_tally(pamh, uid, opts->filename, &tfile, &tally, opts->ctrl);
715 if (i != PAM_SUCCESS) {
716 if (tfile != old_tfile) /* the descriptor is not owned by pam data */
721 memset(&tally, 0, sizeof(tally));
723 i=set_tally(pamh, uid, opts->filename, &tfile, &tally);
724 if (i != PAM_SUCCESS) {
725 if (tfile != old_tfile) /* the descriptor is not owned by pam data */
730 if (tfile != old_tfile)
736 /*---------------------------------------------------------------------*/
738 /* --- authentication management functions (only) --- */
741 pam_sm_authenticate(pam_handle_t *pamh, int flags UNUSED,
742 int argc, const char **argv)
749 options, *opts = &options;
755 rv = tally_parse_args(pamh, opts, PHASE_AUTH, argc, argv);
756 if (rv != PAM_SUCCESS)
759 if (flags & PAM_SILENT)
760 opts->ctrl |= OPT_QUIET;
762 rv = pam_get_uid(pamh, &uid, &user, opts);
763 if (rv != PAM_SUCCESS)
766 rv = tally_bump(1, &oldtime, pamh, uid, user, opts, &tfile);
768 tally_set_data(pamh, oldtime, tfile);
774 pam_sm_setcred(pam_handle_t *pamh, int flags UNUSED,
775 int argc, const char **argv)
782 options, *opts = &options;
788 rv = tally_parse_args(pamh, opts, PHASE_AUTH, argc, argv);
789 if ( rv != PAM_SUCCESS )
792 rv = pam_get_uid(pamh, &uid, &user, opts);
793 if ( rv != PAM_SUCCESS )
796 if ( tally_get_data(pamh, &oldtime, &tfile) != 0 )
800 rv = tally_reset(pamh, uid, opts, tfile);
802 pam_set_data(pamh, MODULE_NAME, NULL, NULL);
807 /*---------------------------------------------------------------------*/
809 /* --- authentication management functions (only) --- */
811 /* To reset failcount of user on successfull login */
814 pam_sm_acct_mgmt(pam_handle_t *pamh, int flags UNUSED,
815 int argc, const char **argv)
822 options, *opts = &options;
828 rv = tally_parse_args(pamh, opts, PHASE_ACCOUNT, argc, argv);
829 if ( rv != PAM_SUCCESS )
832 rv = pam_get_uid(pamh, &uid, &user, opts);
833 if ( rv != PAM_SUCCESS )
836 if ( tally_get_data(pamh, &oldtime, &tfile) != 0 )
840 rv = tally_reset(pamh, uid, opts, tfile);
842 pam_set_data(pamh, MODULE_NAME, NULL, NULL);
847 /*-----------------------------------------------------------------------*/
851 /* static module data */
853 struct pam_module _pam_tally2_modstruct = {
862 #ifdef PAM_SM_ACCOUNT
872 #endif /* #ifdef PAM_STATIC */
874 /*-----------------------------------------------------------------------*/
876 #else /* #ifndef MAIN */
878 static const char *cline_filename = DEFAULT_LOGFILE;
879 static tally_t cline_reset = TALLY_HI; /* Default is `interrogate only' */
880 static int cline_quiet = 0;
883 * Not going to link with pamlib just for these.. :)
890 case PAM_AUTH_ERR: return _("Authentication error");
891 case PAM_SERVICE_ERR: return _("Service error");
892 case PAM_USER_UNKNOWN: return _("Unknown user");
893 default: return _("Unknown error");
898 getopts( char **argv )
900 const char *pname = *argv;
901 for ( ; *argv ; (void)(*argv && ++argv) ) {
902 if ( !strcmp (*argv,"--file") ) cline_filename=*++argv;
903 else if ( !strcmp(*argv,"-f") ) cline_filename=*++argv;
904 else if ( !strncmp(*argv,"--file=",7) ) cline_filename=*argv+7;
905 else if ( !strcmp (*argv,"--user") ) cline_user=*++argv;
906 else if ( !strcmp (*argv,"-u") ) cline_user=*++argv;
907 else if ( !strncmp(*argv,"--user=",7) ) cline_user=*argv+7;
908 else if ( !strcmp (*argv,"--reset") ) cline_reset=0;
909 else if ( !strcmp (*argv,"-r") ) cline_reset=0;
910 else if ( !strncmp(*argv,"--reset=",8)) {
911 if ( sscanf(*argv+8,"%hu",&cline_reset) != 1 )
912 fprintf(stderr,_("%s: Bad number given to --reset=\n"),pname), exit(0);
914 else if ( !strcmp (*argv,"--quiet") ) cline_quiet=1;
916 fprintf(stderr,_("%s: Unrecognised option %s\n"),pname,*argv);
924 print_one(const struct tallylog *tally, uid_t uid)
930 struct passwd *pwent;
931 const char *username = "[NONAME]";
934 pwent = getpwuid(uid);
935 fail_time = tally->fail_time;
936 tm = localtime(&fail_time);
937 strftime (ptime, sizeof (ptime), "%D %H:%M:%S", tm);
940 username = pwent->pw_name;
943 printf (_("Login Failures Latest failure From\n"));
946 printf ("%-15.15s %5hu ", username, tally->fail_cnt);
947 if (tally->fail_time) {
948 printf ("%-17.17s %s", cp, tally->fail_line);
954 main( int argc UNUSED, char **argv )
956 struct tallylog tally;
958 if ( ! getopts( argv+1 ) ) {
959 printf(_("%s: [-f rooted-filename] [--file rooted-filename]\n"
960 " [-u username] [--user username]\n"
961 " [-r] [--reset[=n]] [--quiet]\n"),
969 * Major difference between individual user and all users:
970 * --user just handles one user, just like PAM.
971 * without --user it handles all users, sniffing cline_filename for nonzeros
977 struct tally_options opts;
980 memset(&opts, 0, sizeof(opts));
981 opts.ctrl = OPT_AUDIT;
982 i=pam_get_uid(NULL, &uid, NULL, &opts);
983 if ( i != PAM_SUCCESS ) {
984 fprintf(stderr,"%s: %s\n",*argv,pam_errors(i));
988 i=get_tally(NULL, uid, cline_filename, &tfile, &tally, 0);
989 if ( i != PAM_SUCCESS ) {
992 fprintf(stderr, "%s: %s\n", *argv, pam_errors(i));
997 print_one(&tally, uid);
999 if (cline_reset != TALLY_HI) {
1000 #ifdef HAVE_LIBAUDIT
1002 int audit_fd = audit_open();
1003 snprintf(buf, sizeof(buf), "pam_tally2 uid=%u reset=%hu", uid, cline_reset);
1004 audit_log_user_message(audit_fd, AUDIT_USER_ACCT,
1005 buf, NULL, NULL, ttyname(STDIN_FILENO), 1);
1009 if (cline_reset == 0) {
1010 memset(&tally, 0, sizeof(tally));
1012 tally.fail_cnt = cline_reset;
1014 i=set_tally(NULL, uid, cline_filename, &tfile, &tally);
1016 if (i != PAM_SUCCESS) {
1017 fprintf(stderr,"%s: %s\n",*argv,pam_errors(i));
1024 else /* !cline_user (ie, operate on all users) */ {
1025 FILE *tfile=fopen(cline_filename, "r");
1027 if (!tfile && cline_reset != 0) {
1032 for ( ; tfile && !feof(tfile); uid++ ) {
1033 if ( !fread(&tally, sizeof(tally), 1, tfile)
1034 || !tally.fail_cnt ) {
1037 print_one(&tally, uid);
1041 if ( cline_reset!=0 && cline_reset!=TALLY_HI ) {
1042 fprintf(stderr,_("%s: Can't reset all users to non-zero\n"),*argv);
1044 else if ( !cline_reset ) {
1045 #ifdef HAVE_LIBAUDIT
1047 int audit_fd = audit_open();
1048 snprintf(buf, sizeof(buf), "pam_tally2 uid=all reset=0");
1049 audit_log_user_message(audit_fd, AUDIT_USER_ACCT,
1050 buf, NULL, NULL, ttyname(STDIN_FILENO), 1);
1054 tfile=fopen(cline_filename, "w");
1055 if ( !tfile ) perror(*argv), exit(0);
1063 #endif /* #ifndef MAIN */