]> granicus.if.org Git - linux-pam/blob - modules/pam_tally2/pam_tally2.c
pam_tally2: Optionally log the tally count when checking.
[linux-pam] / modules / pam_tally2 / pam_tally2.c
1 /*
2  * pam_tally2.c
3  *
4  */
5
6
7 /* By Tim Baverstock <warwick@mmm.co.uk>, Multi Media Machine Ltd.
8  * 5 March 1997
9  *
10  * Stuff stolen from pam_rootok and pam_listfile
11  *
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.
17  *
18  * Redistribution and use in source and binary forms, with or without
19  * modification, are permitted provided that the following conditions
20  * are met:
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.
29  *
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
40  * SUCH DAMAGE.
41  */
42
43 #include "config.h"
44
45 #if defined(MAIN) && defined(MEMORY_DEBUG)
46 # undef exit
47 #endif /* defined(MAIN) && defined(MEMORY_DEBUG) */
48
49 #include <stdio.h>
50 #include <string.h>
51 #include <unistd.h>
52 #include <stdarg.h>
53 #include <stdlib.h>
54 #include <syslog.h>
55 #include <pwd.h>
56 #include <time.h>
57 #include <stdint.h>
58 #include <errno.h>
59 #ifdef HAVE_LIBAUDIT
60 #include <libaudit.h>
61 #endif
62
63 #include <sys/types.h>
64 #include <sys/stat.h>
65 #include <sys/param.h>
66 #include <fcntl.h>
67 #include <unistd.h>
68 #include <signal.h>
69 #include "tallylog.h"
70
71 #ifndef TRUE
72 #define TRUE  1L
73 #define FALSE 0L
74 #endif
75
76 #ifndef HAVE_FSEEKO
77 #define fseeko fseek
78 #endif
79
80 /*
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.
85  */
86
87 #ifndef MAIN
88 #define PAM_SM_AUTH
89 #define PAM_SM_ACCOUNT
90 /* #define PAM_SM_SESSION  */
91 /* #define PAM_SM_PASSWORD */
92
93 #include <security/pam_ext.h>
94 #endif
95 #include <security/pam_modutil.h>
96 #include <security/pam_modules.h>
97
98 /*---------------------------------------------------------------------*/
99
100 #define DEFAULT_LOGFILE "/var/log/tallylog"
101 #define MODULE_NAME     "pam_tally2"
102
103 #define tally_t    uint16_t
104 #define TALLY_HI   ((tally_t)~0L)
105
106 struct tally_options {
107     const char *filename;
108     tally_t deny;
109     long lock_time;
110     long unlock_time;
111     long root_unlock_time;
112     unsigned int ctrl;
113 };
114
115 #define PHASE_UNKNOWN 0
116 #define PHASE_AUTH    1
117 #define PHASE_ACCOUNT 2
118 #define PHASE_SESSION 3
119
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
128
129 #define MAX_LOCK_WAITING_TIME 10
130
131 /*---------------------------------------------------------------------*/
132
133 /* some syslogging */
134
135 #ifdef MAIN
136 #define pam_syslog tally_log
137 static void
138 tally_log (const pam_handle_t *pamh UNUSED, int priority UNUSED,
139             const char *fmt, ...)
140 {
141         va_list args;
142
143         va_start(args, fmt);
144         fprintf(stderr, "%s: ", MODULE_NAME);
145         vfprintf(stderr, fmt, args);
146         fprintf(stderr,"\n");
147         va_end(args);
148 }
149
150 #define pam_modutil_getpwnam(pamh, user) getpwnam(user)
151 #endif
152
153 /*---------------------------------------------------------------------*/
154
155 /* --- Support function: parse arguments --- */
156
157 #ifndef MAIN
158
159 static void
160 log_phase_no_auth(pam_handle_t *pamh, int phase, const char *argv)
161 {
162     if ( phase != PHASE_AUTH ) {
163         pam_syslog(pamh, LOG_ERR,
164                    "option %s allowed in auth phase only", argv);
165     }
166 }
167
168 static int
169 tally_parse_args(pam_handle_t *pamh, struct tally_options *opts,
170                     int phase, int argc, const char **argv)
171 {
172     memset(opts, 0, sizeof(*opts));
173     opts->filename = DEFAULT_LOGFILE;
174     opts->ctrl = OPT_FAIL_ON_ERROR;
175     opts->root_unlock_time = -1;
176
177     for ( ; argc-- > 0; ++argv ) {
178
179       if ( ! strncmp( *argv, "file=", 5 ) ) {
180         const char *from = *argv + 5;
181         if ( *from!='/' ) {
182           pam_syslog(pamh, LOG_ERR,
183                      "filename not /rooted; %s", *argv);
184           return PAM_AUTH_ERR;
185         }
186         opts->filename = from;
187       }
188       else if ( ! strcmp( *argv, "onerr=fail" ) ) {
189         opts->ctrl |= OPT_FAIL_ON_ERROR;
190       }
191       else if ( ! strcmp( *argv, "onerr=succeed" ) ) {
192         opts->ctrl &= ~OPT_FAIL_ON_ERROR;
193       }
194       else if ( ! strcmp( *argv, "magic_root" ) ) {
195         opts->ctrl |= OPT_MAGIC_ROOT;
196       }
197       else if ( ! strcmp( *argv, "serialize" ) ) {
198         opts->ctrl |= OPT_SERIALIZE;
199       }
200       else if ( ! strcmp( *argv, "debug" ) ) {
201         opts->ctrl |= OPT_DEBUG;
202       }
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;
207       }
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);
212           return PAM_AUTH_ERR;
213         }
214       }
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);
219           return PAM_AUTH_ERR;
220         }
221       }
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);
226           return PAM_AUTH_ERR;
227         }
228       }
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);
233           return PAM_AUTH_ERR;
234         }
235         opts->ctrl |= OPT_DENY_ROOT; /* even_deny_root implied */
236       }
237       else if ( ! strcmp( *argv, "quiet" ) ||
238                 ! strcmp ( *argv, "silent")) {
239         opts->ctrl |= OPT_QUIET;
240       }
241       else if ( ! strcmp ( *argv, "no_log_info") ) {
242         opts->ctrl |= OPT_NOLOGNOTICE;
243       }
244       else if ( ! strcmp ( *argv, "audit") ) {
245         opts->ctrl |= OPT_AUDIT;
246       }
247       else {
248         pam_syslog(pamh, LOG_ERR, "unknown option: %s", *argv);
249       }
250     }
251
252     if (opts->root_unlock_time == -1)
253         opts->root_unlock_time = opts->unlock_time;
254
255     return PAM_SUCCESS;
256 }
257
258 #endif   /* #ifndef MAIN */
259
260 /*---------------------------------------------------------------------*/
261
262 /* --- Support function: get uid (and optionally username) from PAM or
263         cline_user --- */
264
265 #ifdef MAIN
266 static char *cline_user=0;  /* cline_user is used in the administration prog */
267 #endif
268
269 static int
270 pam_get_uid(pam_handle_t *pamh, uid_t *uid, const char **userp, struct tally_options *opts)
271 {
272     const char *user = NULL;
273     struct passwd *pw;
274
275 #ifdef MAIN
276     user = cline_user;
277 #else
278     if ((pam_get_user( pamh, &user, NULL )) != PAM_SUCCESS) {
279       user = NULL;
280     }
281 #endif
282
283     if ( !user || !*user ) {
284       pam_syslog(pamh, LOG_ERR, "pam_get_uid; user?");
285       return PAM_AUTH_ERR;
286     }
287
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;
293     }
294
295     if ( uid )   *uid   = pw->pw_uid;
296     if ( userp ) *userp = user;
297     return PAM_SUCCESS;
298 }
299
300 /*---------------------------------------------------------------------*/
301
302 /* --- Support functions: set/get tally data --- */
303
304 #ifndef MAIN
305
306 struct tally_data {
307     time_t time;
308     int    tfile;
309 };
310
311 static void
312 _cleanup(pam_handle_t *pamh UNUSED, void *void_data, int error_status UNUSED)
313 {
314     struct tally_data *data = void_data;
315     if (data->tfile != -1)
316         close(data->tfile);
317     free(data);
318 }
319
320 static void
321 tally_set_data( pam_handle_t *pamh, time_t oldtime, int tfile )
322 {
323     struct tally_data *data;
324
325     if ( (data=malloc(sizeof(*data))) != NULL ) {
326         data->time = oldtime;
327         data->tfile = tfile;
328         pam_set_data(pamh, MODULE_NAME, (void *)data, _cleanup);
329     }
330 }
331
332 static int
333 tally_get_data( pam_handle_t *pamh, time_t *oldtime, int *tfile )
334 {
335     int rv;
336     const void *void_data;
337     const struct tally_data *data;
338
339     rv = pam_get_data(pamh, MODULE_NAME, &void_data);
340     if ( rv == PAM_SUCCESS && void_data != NULL && oldtime != NULL ) {
341       data = void_data;
342       *oldtime = data->time;
343       *tfile = data->tfile;
344     }
345     else {
346       rv = -1;
347       *oldtime = 0;
348     }
349     return rv;
350 }
351 #endif   /* #ifndef MAIN */
352
353 /*---------------------------------------------------------------------*/
354
355 /* --- Support function: open/create tallyfile and return tally for uid --- */
356
357 /* If on entry tallyfile doesn't exist, creation is attempted. */
358
359 static void
360 alarm_handler(int sig UNUSED)
361 {   /* we just need to ignore it */
362 }
363
364 static int
365 get_tally(pam_handle_t *pamh, uid_t uid, const char *filename,
366         int *tfile, struct tallylog *tally, unsigned int ctrl)
367 {
368     struct stat fileinfo;
369     int lstat_ret;
370     void *void_tally = tally;
371     int preopened = 0;
372
373     if (*tfile != -1) {
374         preopened = 1;
375         goto skip_open;
376     }
377
378     lstat_ret = lstat(filename, &fileinfo);
379     if (lstat_ret) {
380       *tfile=open(filename, O_APPEND|O_CREAT, S_IRUSR|S_IWUSR);
381       /* Create file, or append-open in pathological case. */
382       if (*tfile == -1) {
383 #ifndef MAIN
384         if (errno == EACCES) {
385             return PAM_IGNORE; /* called with insufficient access rights */
386         }
387 #endif
388         pam_syslog(pamh, LOG_ALERT, "Couldn't create %s: %m", filename);
389         return PAM_AUTH_ERR;
390       }
391       lstat_ret = fstat(*tfile, &fileinfo);
392       close(*tfile);
393     }
394
395     *tfile = -1;
396
397     if ( lstat_ret ) {
398       pam_syslog(pamh, LOG_ALERT, "Couldn't stat %s", filename);
399       return PAM_AUTH_ERR;
400     }
401
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",
407                filename);
408       return PAM_AUTH_ERR;
409     }
410
411     if ((*tfile = open(filename, O_RDWR)) == -1) {
412 #ifndef MAIN
413       if (errno == EACCES) /* called with insufficient access rights */
414           return PAM_IGNORE;
415 #endif
416       pam_syslog(pamh, LOG_ALERT, "Error opening %s for update: %m", filename);
417
418       return PAM_AUTH_ERR;
419     }
420
421 skip_open:
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);
424         if (!preopened) {
425             close(*tfile);
426             *tfile = -1;
427         }
428         return PAM_AUTH_ERR;
429     }
430
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;
436         int rv;
437
438         memset(&newsa, '\0', sizeof(newsa));
439         newsa.sa_handler = alarm_handler;
440         sigaction(SIGALRM, &newsa, &oldsa);
441         oldalarm = alarm(MAX_LOCK_WAITING_TIME);
442
443         rv = lockf(*tfile, F_LOCK, sizeof(*tally));
444         /* lock failure is not fatal, we attempt to read the tally anyway */
445
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) {
451                 oldalarm = 1;
452             }
453         }
454         sigaction(SIGALRM, &oldsa, NULL);
455         alarm(oldalarm);
456     }
457
458     if (pam_modutil_read(*tfile, void_tally, sizeof(*tally)) != sizeof(*tally)) {
459         memset(tally, 0, sizeof(*tally));
460     }
461
462     tally->fail_line[sizeof(tally->fail_line)-1] = '\0';
463
464     return PAM_SUCCESS;
465 }
466
467 /*---------------------------------------------------------------------*/
468
469 /* --- Support function: update tallyfile with tally!=TALLY_HI --- */
470
471 static int
472 set_tally(pam_handle_t *pamh, uid_t uid,
473           const char *filename, int *tfile, struct tallylog *tally)
474 {
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);
479                             return PAM_AUTH_ERR;
480         }
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);
483             return PAM_AUTH_ERR;
484         }
485     }
486
487     if (fsync(*tfile)) {
488       pam_syslog(pamh, LOG_ALERT, "update (fsync) failed for %s: %m", filename);
489       return PAM_AUTH_ERR;
490     }
491     return PAM_SUCCESS;
492 }
493
494 /*---------------------------------------------------------------------*/
495
496 /* --- PAM bits --- */
497
498 #ifndef MAIN
499
500 #define RETURN_ERROR(i) return ((opts->ctrl & OPT_FAIL_ON_ERROR)?(i):(PAM_SUCCESS))
501
502 /*---------------------------------------------------------------------*/
503
504 static int
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)
508 {
509     int rv = PAM_SUCCESS;
510     int loglevel = LOG_DEBUG;
511 #ifdef HAVE_LIBAUDIT
512     char buf[64];
513     int audit_fd = -1;
514     const void *rhost = NULL, *tty = NULL;
515 #endif
516
517     if ((opts->ctrl & OPT_MAGIC_ROOT) && getuid() == 0) {
518       return PAM_SUCCESS;
519     }
520     /* magic_root skips tally check */
521 #ifdef HAVE_LIBAUDIT
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);
529 #endif
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    */
533 #ifdef HAVE_LIBAUDIT
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);
539         }
540 #endif
541         if (uid) {
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 */
546 #ifdef HAVE_LIBAUDIT
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);
550 #endif
551                     rv = PAM_SUCCESS;
552                     goto cleanup;
553                 }
554             }
555         } else {
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 */
560 #ifdef HAVE_LIBAUDIT
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);
564 #endif
565                     rv = PAM_SUCCESS;
566                     goto cleanup;
567                 }
568             }
569         }
570
571 #ifdef HAVE_LIBAUDIT
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);
576         }
577 #endif
578
579         if (!(opts->ctrl & OPT_QUIET)) {
580             pam_info(pamh, _("Account locked due to %u failed logins"),
581                     (unsigned int)tally->fail_cnt);
582         }
583         loglevel = LOG_NOTICE;
584         rv = PAM_AUTH_ERR;                 /* Only unconditional failure   */
585         goto cleanup;
586     }
587
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
592                lock_time applies */
593             tally->fail_cnt = oldcnt;
594             tally->fail_time = oldtime;
595
596             if (!(opts->ctrl & OPT_QUIET)) {
597                 pam_info(pamh, _("Account temporary locked (%ld seconds left)"),
598                          oldtime+opts->lock_time-time(NULL));
599             }
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));
606             }
607             rv = PAM_AUTH_ERR;
608             goto cleanup;
609         }
610     }
611
612 cleanup:
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);
617     }
618 #ifdef HAVE_LIBAUDIT
619     if (audit_fd != -1) {
620         close(audit_fd);
621     }
622 #endif
623     return rv;
624 }
625
626 /* --- tally bump function: bump tally for uid by (signed) inc --- */
627
628 static int
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)
631 {
632     struct tallylog tally;
633     tally_t oldcnt;
634     const void *remote_host = NULL;
635     int i, rv;
636
637     tally.fail_cnt = 0;  /* !TALLY_HI --> Log opened for update */
638
639     i = get_tally(pamh, uid, opts->filename, tfile, &tally, opts->ctrl);
640     if (i != PAM_SUCCESS) {
641         if (*tfile != -1) {
642             close(*tfile);
643             *tfile = -1;
644         }
645         RETURN_ERROR(i);
646     }
647
648     /* to remember old fail time (for locktime) */
649     if (oldtime) {
650         *oldtime = (time_t)tally.fail_time;
651     }
652
653     tally.fail_time = time(NULL);
654
655     (void) pam_get_item(pamh, PAM_RHOST, &remote_host);
656     if (!remote_host) {
657         (void) pam_get_item(pamh, PAM_TTY, &remote_host);
658         if (!remote_host) {
659             remote_host = "unknown";
660         }
661     }
662
663     strncpy(tally.fail_line, remote_host,
664                     sizeof(tally.fail_line)-1);
665     tally.fail_line[sizeof(tally.fail_line)-1] = 0;
666
667     oldcnt = tally.fail_cnt;
668
669     if (!(opts->ctrl & OPT_MAGIC_ROOT) || getuid()) {
670       /* magic_root doesn't change tally */
671       tally.fail_cnt += inc;
672
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);
677       }
678     }
679
680     rv = tally_check(oldcnt, *oldtime, pamh, uid, user, opts, &tally);
681
682     i = set_tally(pamh, uid, opts->filename, tfile, &tally);
683     if (i != PAM_SUCCESS) {
684         if (*tfile != -1) {
685             close(*tfile);
686             *tfile = -1;
687         }
688         if (rv == PAM_SUCCESS)
689             RETURN_ERROR( i );
690         /* fallthrough */
691     } else if (!(opts->ctrl & OPT_SERIALIZE)) {
692         close(*tfile);
693         *tfile = -1;
694     }
695
696     return rv;
697 }
698
699 static int
700 tally_reset (pam_handle_t *pamh, uid_t uid, struct tally_options *opts, int old_tfile)
701 {
702     struct tallylog tally;
703     int tfile = old_tfile;
704     int i;
705
706     /* resets only if not magic root */
707
708     if ((opts->ctrl & OPT_MAGIC_ROOT) && getuid() == 0) {
709         return PAM_SUCCESS;
710     }
711
712     tally.fail_cnt = 0;  /* !TALLY_HI --> Log opened for update */
713
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 */
717             close(tfile);
718         RETURN_ERROR(i);
719     }
720
721     memset(&tally, 0, sizeof(tally));
722
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 */
726             close(tfile);
727         RETURN_ERROR(i);
728     }
729
730     if (tfile != old_tfile)
731         close(tfile);
732
733     return PAM_SUCCESS;
734 }
735
736 /*---------------------------------------------------------------------*/
737
738 /* --- authentication management functions (only) --- */
739
740 PAM_EXTERN int
741 pam_sm_authenticate(pam_handle_t *pamh, int flags UNUSED,
742                     int argc, const char **argv)
743 {
744   int
745     rv, tfile = -1;
746   time_t
747     oldtime = 0;
748   struct tally_options
749     options, *opts = &options;
750   uid_t
751     uid;
752   const char
753     *user;
754
755   rv = tally_parse_args(pamh, opts, PHASE_AUTH, argc, argv);
756   if (rv != PAM_SUCCESS)
757       RETURN_ERROR(rv);
758
759   if (flags & PAM_SILENT)
760     opts->ctrl |= OPT_QUIET;
761
762   rv = pam_get_uid(pamh, &uid, &user, opts);
763   if (rv != PAM_SUCCESS)
764       RETURN_ERROR(rv);
765
766   rv = tally_bump(1, &oldtime, pamh, uid, user, opts, &tfile);
767
768   tally_set_data(pamh, oldtime, tfile);
769
770   return rv;
771 }
772
773 PAM_EXTERN int
774 pam_sm_setcred(pam_handle_t *pamh, int flags UNUSED,
775                int argc, const char **argv)
776 {
777   int
778     rv, tfile = -1;
779   time_t
780     oldtime = 0;
781   struct tally_options
782     options, *opts = &options;
783   uid_t
784     uid;
785   const char
786     *user;
787
788   rv = tally_parse_args(pamh, opts, PHASE_AUTH, argc, argv);
789   if ( rv != PAM_SUCCESS )
790       RETURN_ERROR( rv );
791
792   rv = pam_get_uid(pamh, &uid, &user, opts);
793   if ( rv != PAM_SUCCESS )
794       RETURN_ERROR( rv );
795
796   if ( tally_get_data(pamh, &oldtime, &tfile) != 0 )
797   /* no data found */
798       return PAM_SUCCESS;
799
800   rv = tally_reset(pamh, uid, opts, tfile);
801
802   pam_set_data(pamh, MODULE_NAME, NULL, NULL);
803
804   return rv;
805 }
806
807 /*---------------------------------------------------------------------*/
808
809 /* --- authentication management functions (only) --- */
810
811 /* To reset failcount of user on successfull login */
812
813 PAM_EXTERN int
814 pam_sm_acct_mgmt(pam_handle_t *pamh, int flags UNUSED,
815                  int argc, const char **argv)
816 {
817   int
818     rv, tfile = -1;
819   time_t
820     oldtime = 0;
821   struct tally_options
822     options, *opts = &options;
823   uid_t
824     uid;
825   const char
826     *user;
827
828   rv = tally_parse_args(pamh, opts, PHASE_ACCOUNT, argc, argv);
829   if ( rv != PAM_SUCCESS )
830       RETURN_ERROR( rv );
831
832   rv = pam_get_uid(pamh, &uid, &user, opts);
833   if ( rv != PAM_SUCCESS )
834       RETURN_ERROR( rv );
835
836   if ( tally_get_data(pamh, &oldtime, &tfile) != 0 )
837   /* no data found */
838       return PAM_SUCCESS;
839
840   rv = tally_reset(pamh, uid, opts, tfile);
841
842   pam_set_data(pamh, MODULE_NAME, NULL, NULL);
843
844   return rv;
845 }
846
847 /*-----------------------------------------------------------------------*/
848
849 #ifdef PAM_STATIC
850
851 /* static module data */
852
853 struct pam_module _pam_tally2_modstruct = {
854      MODULE_NAME,
855 #ifdef PAM_SM_AUTH
856      pam_sm_authenticate,
857      pam_sm_setcred,
858 #else
859      NULL,
860      NULL,
861 #endif
862 #ifdef PAM_SM_ACCOUNT
863      pam_sm_acct_mgmt,
864 #else
865      NULL,
866 #endif
867      NULL,
868      NULL,
869      NULL,
870 };
871
872 #endif   /* #ifdef PAM_STATIC */
873
874 /*-----------------------------------------------------------------------*/
875
876 #else   /* #ifndef MAIN */
877
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;
881
882 /*
883  *  Not going to link with pamlib just for these.. :)
884  */
885
886 static const char *
887 pam_errors( int i )
888 {
889   switch (i) {
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");
894   }
895 }
896
897 static int
898 getopts( char **argv )
899 {
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);
913     }
914     else if ( !strcmp (*argv,"--quiet")   ) cline_quiet=1;
915     else {
916       fprintf(stderr,_("%s: Unrecognised option %s\n"),pname,*argv);
917       return FALSE;
918     }
919   }
920   return TRUE;
921 }
922
923 static void
924 print_one(const struct tallylog *tally, uid_t uid)
925 {
926    static int once;
927    char *cp;
928    time_t fail_time;
929    struct tm *tm;
930    struct passwd *pwent;
931    const char *username = "[NONAME]";
932    char ptime[80];
933
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);
938    cp = ptime;
939    if (pwent) {
940         username = pwent->pw_name;
941    }
942    if (!once) {
943         printf (_("Login           Failures Latest failure     From\n"));
944         once++;
945    }
946    printf ("%-15.15s %5hu    ", username, tally->fail_cnt);
947    if (tally->fail_time) {
948         printf ("%-17.17s  %s", cp, tally->fail_line);
949    }
950    putchar ('\n');
951 }
952
953 int
954 main( int argc UNUSED, char **argv )
955 {
956   struct tallylog tally;
957
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"),
962            *argv);
963     exit(2);
964   }
965
966   umask(077);
967
968   /*
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
972    */
973
974   if ( cline_user ) {
975     uid_t uid;
976     int tfile = -1;
977     struct tally_options opts;
978     int i;
979
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));
985       exit(1);
986     }
987
988     i=get_tally(NULL, uid, cline_filename, &tfile, &tally, 0);
989     if ( i != PAM_SUCCESS ) {
990       if (tfile != -1)
991           close(tfile);
992       fprintf(stderr, "%s: %s\n", *argv, pam_errors(i));
993       exit(1);
994     }
995
996     if ( !cline_quiet )
997       print_one(&tally, uid);
998
999     if (cline_reset != TALLY_HI) {
1000 #ifdef HAVE_LIBAUDIT
1001         char buf[64];
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);
1006         if (audit_fd >=0)
1007                 close(audit_fd);
1008 #endif
1009         if (cline_reset == 0) {
1010             memset(&tally, 0, sizeof(tally));
1011         } else {
1012             tally.fail_cnt = cline_reset;
1013         }
1014         i=set_tally(NULL, uid, cline_filename, &tfile, &tally);
1015         close(tfile);
1016         if (i != PAM_SUCCESS) {
1017             fprintf(stderr,"%s: %s\n",*argv,pam_errors(i));
1018             exit(1);
1019         }
1020     } else {
1021         close(tfile);
1022     }
1023   }
1024   else /* !cline_user (ie, operate on all users) */ {
1025     FILE *tfile=fopen(cline_filename, "r");
1026     uid_t uid=0;
1027     if (!tfile  && cline_reset != 0) {
1028         perror(*argv);
1029         exit(1);
1030     }
1031
1032     for ( ; tfile && !feof(tfile); uid++ ) {
1033       if ( !fread(&tally, sizeof(tally), 1, tfile)
1034            || !tally.fail_cnt ) {
1035          continue;
1036       }
1037       print_one(&tally, uid);
1038     }
1039     if (tfile)
1040       fclose(tfile);
1041     if ( cline_reset!=0 && cline_reset!=TALLY_HI ) {
1042       fprintf(stderr,_("%s: Can't reset all users to non-zero\n"),*argv);
1043     }
1044     else if ( !cline_reset ) {
1045 #ifdef HAVE_LIBAUDIT
1046       char buf[64];
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);
1051       if (audit_fd >=0)
1052               close(audit_fd);
1053 #endif
1054       tfile=fopen(cline_filename, "w");
1055       if ( !tfile ) perror(*argv), exit(0);
1056       fclose(tfile);
1057     }
1058   }
1059   return 0;
1060 }
1061
1062
1063 #endif   /* #ifndef MAIN */