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
128 #define MAX_LOCK_WAITING_TIME 10
130 /*---------------------------------------------------------------------*/
132 /* some syslogging */
135 #define pam_syslog tally_log
137 tally_log (const pam_handle_t *pamh UNUSED, int priority UNUSED,
138 const char *fmt, ...)
143 fprintf(stderr, "%s: ", MODULE_NAME);
144 vfprintf(stderr, fmt, args);
145 fprintf(stderr,"\n");
149 #define pam_modutil_getpwnam(pamh, user) getpwnam(user)
152 /*---------------------------------------------------------------------*/
154 /* --- Support function: parse arguments --- */
159 log_phase_no_auth(pam_handle_t *pamh, int phase, const char *argv)
161 if ( phase != PHASE_AUTH ) {
162 pam_syslog(pamh, LOG_ERR,
163 "option %s allowed in auth phase only", argv);
168 tally_parse_args(pam_handle_t *pamh, struct tally_options *opts,
169 int phase, int argc, const char **argv)
171 memset(opts, 0, sizeof(*opts));
172 opts->filename = DEFAULT_LOGFILE;
173 opts->ctrl = OPT_FAIL_ON_ERROR;
174 opts->root_unlock_time = -1;
176 for ( ; argc-- > 0; ++argv ) {
178 if ( ! strncmp( *argv, "file=", 5 ) ) {
179 const char *from = *argv + 5;
181 pam_syslog(pamh, LOG_ERR,
182 "filename not /rooted; %s", *argv);
185 opts->filename = from;
187 else if ( ! strcmp( *argv, "onerr=fail" ) ) {
188 opts->ctrl |= OPT_FAIL_ON_ERROR;
190 else if ( ! strcmp( *argv, "onerr=succeed" ) ) {
191 opts->ctrl &= ~OPT_FAIL_ON_ERROR;
193 else if ( ! strcmp( *argv, "magic_root" ) ) {
194 opts->ctrl |= OPT_MAGIC_ROOT;
196 else if ( ! strcmp( *argv, "serialize" ) ) {
197 opts->ctrl |= OPT_SERIALIZE;
199 else if ( ! strcmp( *argv, "even_deny_root_account" ) ||
200 ! strcmp( *argv, "even_deny_root" ) ) {
201 log_phase_no_auth(pamh, phase, *argv);
202 opts->ctrl |= OPT_DENY_ROOT;
204 else if ( ! strncmp( *argv, "deny=", 5 ) ) {
205 log_phase_no_auth(pamh, phase, *argv);
206 if ( sscanf((*argv)+5,"%hu",&opts->deny) != 1 ) {
207 pam_syslog(pamh, LOG_ERR, "bad number supplied: %s", *argv);
211 else if ( ! strncmp( *argv, "lock_time=", 10 ) ) {
212 log_phase_no_auth(pamh, phase, *argv);
213 if ( sscanf((*argv)+10,"%ld",&opts->lock_time) != 1 ) {
214 pam_syslog(pamh, LOG_ERR, "bad number supplied: %s", *argv);
218 else if ( ! strncmp( *argv, "unlock_time=", 12 ) ) {
219 log_phase_no_auth(pamh, phase, *argv);
220 if ( sscanf((*argv)+12,"%ld",&opts->unlock_time) != 1 ) {
221 pam_syslog(pamh, LOG_ERR, "bad number supplied: %s", *argv);
225 else if ( ! strncmp( *argv, "root_unlock_time=", 17 ) ) {
226 log_phase_no_auth(pamh, phase, *argv);
227 if ( sscanf((*argv)+17,"%ld",&opts->root_unlock_time) != 1 ) {
228 pam_syslog(pamh, LOG_ERR, "bad number supplied: %s", *argv);
231 opts->ctrl |= OPT_DENY_ROOT; /* even_deny_root implied */
233 else if ( ! strcmp( *argv, "quiet" ) ||
234 ! strcmp ( *argv, "silent")) {
235 opts->ctrl |= OPT_QUIET;
237 else if ( ! strcmp ( *argv, "no_log_info") ) {
238 opts->ctrl |= OPT_NOLOGNOTICE;
240 else if ( ! strcmp ( *argv, "audit") ) {
241 opts->ctrl |= OPT_AUDIT;
244 pam_syslog(pamh, LOG_ERR, "unknown option: %s", *argv);
248 if (opts->root_unlock_time == -1)
249 opts->root_unlock_time = opts->unlock_time;
254 #endif /* #ifndef MAIN */
256 /*---------------------------------------------------------------------*/
258 /* --- Support function: get uid (and optionally username) from PAM or
262 static char *cline_user=0; /* cline_user is used in the administration prog */
266 pam_get_uid(pam_handle_t *pamh, uid_t *uid, const char **userp, struct tally_options *opts)
268 const char *user = NULL;
274 if ((pam_get_user( pamh, &user, NULL )) != PAM_SUCCESS) {
279 if ( !user || !*user ) {
280 pam_syslog(pamh, LOG_ERR, "pam_get_uid; user?");
284 if ( ! ( pw = pam_modutil_getpwnam( pamh, user ) ) ) {
285 opts->ctrl & OPT_AUDIT ?
286 pam_syslog(pamh, LOG_ERR, "pam_get_uid; no such user %s", user) :
287 pam_syslog(pamh, LOG_ERR, "pam_get_uid; no such user");
288 return PAM_USER_UNKNOWN;
291 if ( uid ) *uid = pw->pw_uid;
292 if ( userp ) *userp = user;
296 /*---------------------------------------------------------------------*/
298 /* --- Support functions: set/get tally data --- */
308 _cleanup(pam_handle_t *pamh UNUSED, void *void_data, int error_status UNUSED)
310 struct tally_data *data = void_data;
311 if (data->tfile != -1)
317 tally_set_data( pam_handle_t *pamh, time_t oldtime, int tfile )
319 struct tally_data *data;
321 if ( (data=malloc(sizeof(*data))) != NULL ) {
322 data->time = oldtime;
324 pam_set_data(pamh, MODULE_NAME, (void *)data, _cleanup);
329 tally_get_data( pam_handle_t *pamh, time_t *oldtime, int *tfile )
332 const void *void_data;
333 const struct tally_data *data;
335 rv = pam_get_data(pamh, MODULE_NAME, &void_data);
336 if ( rv == PAM_SUCCESS && void_data != NULL && oldtime != NULL ) {
338 *oldtime = data->time;
339 *tfile = data->tfile;
347 #endif /* #ifndef MAIN */
349 /*---------------------------------------------------------------------*/
351 /* --- Support function: open/create tallyfile and return tally for uid --- */
353 /* If on entry tallyfile doesn't exist, creation is attempted. */
356 alarm_handler(int sig UNUSED)
357 { /* we just need to ignore it */
361 get_tally(pam_handle_t *pamh, uid_t uid, const char *filename,
362 int *tfile, struct tallylog *tally, unsigned int ctrl)
364 struct stat fileinfo;
366 void *void_tally = tally;
374 lstat_ret = lstat(filename, &fileinfo);
376 *tfile=open(filename, O_APPEND|O_CREAT, S_IRUSR|S_IWUSR);
377 /* Create file, or append-open in pathological case. */
380 if (errno == EACCES) {
381 return PAM_IGNORE; /* called with insufficient access rights */
384 pam_syslog(pamh, LOG_ALERT, "Couldn't create %s: %m", filename);
387 lstat_ret = fstat(*tfile, &fileinfo);
394 pam_syslog(pamh, LOG_ALERT, "Couldn't stat %s", filename);
398 if ((fileinfo.st_mode & S_IWOTH) || !S_ISREG(fileinfo.st_mode)) {
399 /* If the file is world writable or is not a
400 normal file, return error */
401 pam_syslog(pamh, LOG_ALERT,
402 "%s is either world writable or not a normal file",
407 if ((*tfile = open(filename, O_RDWR)) == -1) {
409 if (errno == EACCES) /* called with insufficient access rights */
412 pam_syslog(pamh, LOG_ALERT, "Error opening %s for update: %m", filename);
418 if (lseek(*tfile, (off_t)uid*(off_t)sizeof(*tally), SEEK_SET) == (off_t)-1) {
419 pam_syslog(pamh, LOG_ALERT, "lseek failed for %s: %m", filename);
427 if (!preopened && (ctrl & OPT_SERIALIZE)) {
428 /* this code is not thread safe as it uses fcntl locks and alarm()
429 so never use serialize with multithreaded services */
430 struct sigaction newsa, oldsa;
431 unsigned int oldalarm;
434 memset(&newsa, '\0', sizeof(newsa));
435 newsa.sa_handler = alarm_handler;
436 sigaction(SIGALRM, &newsa, &oldsa);
437 oldalarm = alarm(MAX_LOCK_WAITING_TIME);
439 rv = lockf(*tfile, F_LOCK, sizeof(*tally));
440 /* lock failure is not fatal, we attempt to read the tally anyway */
442 /* reinstate the eventual old alarm handler */
443 if (rv == -1 && errno == EINTR) {
444 if (oldalarm > MAX_LOCK_WAITING_TIME) {
445 oldalarm -= MAX_LOCK_WAITING_TIME;
446 } else if (oldalarm > 0) {
450 sigaction(SIGALRM, &oldsa, NULL);
454 if (fileinfo.st_size < (off_t)(uid+1)*(off_t)sizeof(*tally)) {
455 memset(tally, 0, sizeof(*tally));
456 } else if (pam_modutil_read(*tfile, void_tally, sizeof(*tally)) != sizeof(*tally)) {
457 memset(tally, 0, sizeof(*tally));
458 /* Shouldn't happen */
461 tally->fail_line[sizeof(tally->fail_line)-1] = '\0';
466 /*---------------------------------------------------------------------*/
468 /* --- Support function: update tallyfile with tally!=TALLY_HI --- */
471 set_tally(pam_handle_t *pamh, uid_t uid,
472 const char *filename, int *tfile, struct tallylog *tally)
474 void *void_tally = tally;
475 if (tally->fail_cnt != TALLY_HI) {
476 if (lseek(*tfile, (off_t)uid * sizeof(*tally), SEEK_SET) == (off_t)-1) {
477 pam_syslog(pamh, LOG_ALERT, "lseek failed for %s: %m", filename);
480 if (pam_modutil_write(*tfile, void_tally, sizeof(*tally)) != sizeof(*tally)) {
481 pam_syslog(pamh, LOG_ALERT, "update (write) failed for %s: %m", filename);
487 pam_syslog(pamh, LOG_ALERT, "update (fsync) failed for %s: %m", filename);
493 /*---------------------------------------------------------------------*/
495 /* --- PAM bits --- */
499 #define RETURN_ERROR(i) return ((opts->ctrl & OPT_FAIL_ON_ERROR)?(i):(PAM_SUCCESS))
501 /*---------------------------------------------------------------------*/
504 tally_check (tally_t oldcnt, time_t oldtime, pam_handle_t *pamh, uid_t uid,
505 const char *user, struct tally_options *opts,
506 struct tallylog *tally)
508 int rv = PAM_SUCCESS;
514 if ((opts->ctrl & OPT_MAGIC_ROOT) && getuid() == 0) {
517 /* magic_root skips tally check */
519 audit_fd = audit_open();
520 /* If there is an error & audit support is in the kernel report error */
521 if ((audit_fd < 0) && !(errno == EINVAL || errno == EPROTONOSUPPORT ||
522 errno == EAFNOSUPPORT))
523 return PAM_SYSTEM_ERR;
525 if (opts->deny != 0 && /* deny==0 means no deny */
526 tally->fail_cnt > opts->deny && /* tally>deny means exceeded */
527 ((opts->ctrl & OPT_DENY_ROOT) || uid)) { /* even_deny stops uid check */
529 if (tally->fail_cnt == opts->deny+1) {
530 /* First say that max number was hit. */
531 snprintf(buf, sizeof(buf), "pam_tally2 uid=%u ", uid);
532 audit_log_user_message(audit_fd, AUDIT_ANOM_LOGIN_FAILURES, buf,
533 NULL, NULL, NULL, 1);
537 /* Unlock time check */
538 if (opts->unlock_time && oldtime) {
539 if (opts->unlock_time + oldtime <= time(NULL)) {
540 /* ignore deny check after unlock_time elapsed */
542 snprintf(buf, sizeof(buf), "pam_tally2 uid=%u ", uid);
543 audit_log_user_message(audit_fd, AUDIT_RESP_ACCT_UNLOCK_TIMED, buf,
544 NULL, NULL, NULL, 1);
551 /* Root unlock time check */
552 if (opts->root_unlock_time && oldtime) {
553 if (opts->root_unlock_time + oldtime <= time(NULL)) {
554 /* ignore deny check after unlock_time elapsed */
556 snprintf(buf, sizeof(buf), "pam_tally2 uid=%u ", uid);
557 audit_log_user_message(audit_fd, AUDIT_RESP_ACCT_UNLOCK_TIMED, buf,
558 NULL, NULL, NULL, 1);
567 if (tally->fail_cnt == opts->deny+1) {
568 /* First say that max number was hit. */
569 audit_log_user_message(audit_fd, AUDIT_RESP_ACCT_LOCK, buf,
570 NULL, NULL, NULL, 1);
574 if (!(opts->ctrl & OPT_QUIET)) {
575 pam_info(pamh, _("Account locked due to %u failed logins"),
576 (unsigned int)tally->fail_cnt);
578 if (!(opts->ctrl & OPT_NOLOGNOTICE)) {
579 pam_syslog(pamh, LOG_NOTICE,
580 "user %s (%lu) tally %hu, deny %hu",
581 user, (unsigned long)uid, tally->fail_cnt, opts->deny);
583 rv = PAM_AUTH_ERR; /* Only unconditional failure */
587 /* Lock time check */
588 if (opts->lock_time && oldtime) {
589 if (opts->lock_time + oldtime > time(NULL)) {
590 /* don't increase fail_cnt or update fail_time when
592 tally->fail_cnt = oldcnt;
593 tally->fail_time = oldtime;
595 if (!(opts->ctrl & OPT_QUIET)) {
596 pam_info(pamh, _("Account temporary locked (%ld seconds left)"),
597 oldtime+opts->lock_time-time(NULL));
599 if (!(opts->ctrl & OPT_NOLOGNOTICE)) {
600 pam_syslog(pamh, LOG_NOTICE,
601 "user %s (%lu) has time limit [%lds left]"
602 " since last failure.",
603 user, (unsigned long)uid,
604 oldtime+opts->lock_time-time(NULL));
613 if (audit_fd != -1) {
620 /* --- tally bump function: bump tally for uid by (signed) inc --- */
623 tally_bump (int inc, time_t *oldtime, pam_handle_t *pamh,
624 uid_t uid, const char *user, struct tally_options *opts, int *tfile)
626 struct tallylog tally;
628 const void *remote_host = NULL;
631 tally.fail_cnt = 0; /* !TALLY_HI --> Log opened for update */
633 i = get_tally(pamh, uid, opts->filename, tfile, &tally, opts->ctrl);
634 if (i != PAM_SUCCESS) {
642 /* to remember old fail time (for locktime) */
644 *oldtime = (time_t)tally.fail_time;
647 tally.fail_time = time(NULL);
649 (void) pam_get_item(pamh, PAM_RHOST, &remote_host);
651 (void) pam_get_item(pamh, PAM_TTY, &remote_host);
653 remote_host = "unknown";
657 strncpy(tally.fail_line, remote_host,
658 sizeof(tally.fail_line)-1);
659 tally.fail_line[sizeof(tally.fail_line)-1] = 0;
661 oldcnt = tally.fail_cnt;
663 if (!(opts->ctrl & OPT_MAGIC_ROOT) || getuid()) {
664 /* magic_root doesn't change tally */
665 tally.fail_cnt += inc;
667 if (tally.fail_cnt == TALLY_HI) { /* Overflow *and* underflow. :) */
668 tally.fail_cnt -= inc;
669 pam_syslog(pamh, LOG_ALERT, "Tally %sflowed for user %s",
670 (inc<0)?"under":"over",user);
674 rv = tally_check(oldcnt, *oldtime, pamh, uid, user, opts, &tally);
676 i = set_tally(pamh, uid, opts->filename, tfile, &tally);
677 if (i != PAM_SUCCESS) {
682 if (rv == PAM_SUCCESS)
685 } else if (!(opts->ctrl & OPT_SERIALIZE)) {
694 tally_reset (pam_handle_t *pamh, uid_t uid, struct tally_options *opts, int old_tfile)
696 struct tallylog tally;
697 int tfile = old_tfile;
700 /* resets only if not magic root */
702 if ((opts->ctrl & OPT_MAGIC_ROOT) && getuid() == 0) {
706 tally.fail_cnt = 0; /* !TALLY_HI --> Log opened for update */
708 i=get_tally(pamh, uid, opts->filename, &tfile, &tally, opts->ctrl);
709 if (i != PAM_SUCCESS) {
710 if (tfile != old_tfile) /* the descriptor is not owned by pam data */
715 memset(&tally, 0, sizeof(tally));
717 i=set_tally(pamh, uid, opts->filename, &tfile, &tally);
718 if (i != PAM_SUCCESS) {
719 if (tfile != old_tfile) /* the descriptor is not owned by pam data */
724 if (tfile != old_tfile)
730 /*---------------------------------------------------------------------*/
732 /* --- authentication management functions (only) --- */
735 pam_sm_authenticate(pam_handle_t *pamh, int flags UNUSED,
736 int argc, const char **argv)
743 options, *opts = &options;
749 rv = tally_parse_args(pamh, opts, PHASE_AUTH, argc, argv);
750 if (rv != PAM_SUCCESS)
753 if (flags & PAM_SILENT)
754 opts->ctrl |= OPT_QUIET;
756 rv = pam_get_uid(pamh, &uid, &user, opts);
757 if (rv != PAM_SUCCESS)
760 rv = tally_bump(1, &oldtime, pamh, uid, user, opts, &tfile);
762 tally_set_data(pamh, oldtime, tfile);
768 pam_sm_setcred(pam_handle_t *pamh, int flags UNUSED,
769 int argc, const char **argv)
776 options, *opts = &options;
782 rv = tally_parse_args(pamh, opts, PHASE_AUTH, argc, argv);
783 if ( rv != PAM_SUCCESS )
786 rv = pam_get_uid(pamh, &uid, &user, opts);
787 if ( rv != PAM_SUCCESS )
790 if ( tally_get_data(pamh, &oldtime, &tfile) != 0 )
794 rv = tally_reset(pamh, uid, opts, tfile);
796 pam_set_data(pamh, MODULE_NAME, NULL, NULL);
801 /*---------------------------------------------------------------------*/
803 /* --- authentication management functions (only) --- */
805 /* To reset failcount of user on successfull login */
808 pam_sm_acct_mgmt(pam_handle_t *pamh, int flags UNUSED,
809 int argc, const char **argv)
816 options, *opts = &options;
822 rv = tally_parse_args(pamh, opts, PHASE_ACCOUNT, argc, argv);
823 if ( rv != PAM_SUCCESS )
826 rv = pam_get_uid(pamh, &uid, &user, opts);
827 if ( rv != PAM_SUCCESS )
830 if ( tally_get_data(pamh, &oldtime, &tfile) != 0 )
834 rv = tally_reset(pamh, uid, opts, tfile);
836 pam_set_data(pamh, MODULE_NAME, NULL, NULL);
841 /*-----------------------------------------------------------------------*/
845 /* static module data */
847 struct pam_module _pam_tally2_modstruct = {
856 #ifdef PAM_SM_ACCOUNT
866 #endif /* #ifdef PAM_STATIC */
868 /*-----------------------------------------------------------------------*/
870 #else /* #ifndef MAIN */
872 static const char *cline_filename = DEFAULT_LOGFILE;
873 static tally_t cline_reset = TALLY_HI; /* Default is `interrogate only' */
874 static int cline_quiet = 0;
877 * Not going to link with pamlib just for these.. :)
884 case PAM_AUTH_ERR: return _("Authentication error");
885 case PAM_SERVICE_ERR: return _("Service error");
886 case PAM_USER_UNKNOWN: return _("Unknown user");
887 default: return _("Unknown error");
892 getopts( char **argv )
894 const char *pname = *argv;
895 for ( ; *argv ; (void)(*argv && ++argv) ) {
896 if ( !strcmp (*argv,"--file") ) cline_filename=*++argv;
897 else if ( !strcmp(*argv,"-f") ) cline_filename=*++argv;
898 else if ( !strncmp(*argv,"--file=",7) ) cline_filename=*argv+7;
899 else if ( !strcmp (*argv,"--user") ) cline_user=*++argv;
900 else if ( !strcmp (*argv,"-u") ) cline_user=*++argv;
901 else if ( !strncmp(*argv,"--user=",7) ) cline_user=*argv+7;
902 else if ( !strcmp (*argv,"--reset") ) cline_reset=0;
903 else if ( !strcmp (*argv,"-r") ) cline_reset=0;
904 else if ( !strncmp(*argv,"--reset=",8)) {
905 if ( sscanf(*argv+8,"%hu",&cline_reset) != 1 )
906 fprintf(stderr,_("%s: Bad number given to --reset=\n"),pname), exit(0);
908 else if ( !strcmp (*argv,"--quiet") ) cline_quiet=1;
910 fprintf(stderr,_("%s: Unrecognised option %s\n"),pname,*argv);
918 print_one(const struct tallylog *tally, uid_t uid)
924 struct passwd *pwent;
925 const char *username = "[NONAME]";
928 pwent = getpwuid(uid);
929 fail_time = tally->fail_time;
930 tm = localtime(&fail_time);
931 strftime (ptime, sizeof (ptime), "%D %H:%M:%S", tm);
934 username = pwent->pw_name;
937 printf (_("Login Failures Latest failure From\n"));
940 printf ("%-15.15s %5hu ", username, tally->fail_cnt);
941 if (tally->fail_time) {
942 printf ("%-17.17s %s", cp, tally->fail_line);
948 main( int argc UNUSED, char **argv )
950 struct tallylog tally;
952 if ( ! getopts( argv+1 ) ) {
953 printf(_("%s: [-f rooted-filename] [--file rooted-filename]\n"
954 " [-u username] [--user username]\n"
955 " [-r] [--reset[=n]] [--quiet]\n"),
963 * Major difference between individual user and all users:
964 * --user just handles one user, just like PAM.
965 * without --user it handles all users, sniffing cline_filename for nonzeros
971 struct tally_options opts;
974 memset(&opts, 0, sizeof(opts));
975 opts.ctrl = OPT_AUDIT;
976 i=pam_get_uid(NULL, &uid, NULL, &opts);
977 if ( i != PAM_SUCCESS ) {
978 fprintf(stderr,"%s: %s\n",*argv,pam_errors(i));
982 i=get_tally(NULL, uid, cline_filename, &tfile, &tally, 0);
983 if ( i != PAM_SUCCESS ) {
986 fprintf(stderr, "%s: %s\n", *argv, pam_errors(i));
991 print_one(&tally, uid);
993 if (cline_reset != TALLY_HI) {
996 int audit_fd = audit_open();
997 snprintf(buf, sizeof(buf), "pam_tally2 uid=%u reset=%hu", uid, cline_reset);
998 audit_log_user_message(audit_fd, AUDIT_USER_ACCT,
999 buf, NULL, NULL, NULL, 1);
1003 if (cline_reset == 0) {
1004 memset(&tally, 0, sizeof(tally));
1006 tally.fail_cnt = cline_reset;
1008 i=set_tally(NULL, uid, cline_filename, &tfile, &tally);
1010 if (i != PAM_SUCCESS) {
1011 fprintf(stderr,"%s: %s\n",*argv,pam_errors(i));
1018 else /* !cline_user (ie, operate on all users) */ {
1019 FILE *tfile=fopen(cline_filename, "r");
1021 if (!tfile && cline_reset != 0) {
1026 for ( ; tfile && !feof(tfile); uid++ ) {
1027 if ( !fread(&tally, sizeof(tally), 1, tfile)
1028 || !tally.fail_cnt ) {
1031 print_one(&tally, uid);
1035 if ( cline_reset!=0 && cline_reset!=TALLY_HI ) {
1036 fprintf(stderr,_("%s: Can't reset all users to non-zero\n"),*argv);
1038 else if ( !cline_reset ) {
1039 #ifdef HAVE_LIBAUDIT
1041 int audit_fd = audit_open();
1042 snprintf(buf, sizeof(buf), "pam_tally2 uid=all reset=0");
1043 audit_log_user_message(audit_fd, AUDIT_USER_ACCT,
1044 buf, NULL, NULL, NULL, 1);
1048 tfile=fopen(cline_filename, "w");
1049 if ( !tfile ) perror(*argv), exit(0);
1057 #endif /* #ifndef MAIN */