Import Linux-PAM.
[profile/ivi/pam.git] / 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
128 #define MAX_LOCK_WAITING_TIME 10
129
130 /*---------------------------------------------------------------------*/
131
132 /* some syslogging */
133
134 #ifdef MAIN
135 #define pam_syslog tally_log
136 static void
137 tally_log (const pam_handle_t *pamh UNUSED, int priority UNUSED,
138             const char *fmt, ...)
139 {
140         va_list args;
141
142         va_start(args, fmt);
143         fprintf(stderr, "%s: ", MODULE_NAME);
144         vfprintf(stderr, fmt, args);
145         fprintf(stderr,"\n");
146         va_end(args);
147 }
148
149 #define pam_modutil_getpwnam(pamh, user) getpwnam(user)
150 #endif
151
152 /*---------------------------------------------------------------------*/
153
154 /* --- Support function: parse arguments --- */
155
156 #ifndef MAIN
157
158 static void
159 log_phase_no_auth(pam_handle_t *pamh, int phase, const char *argv)
160 {
161     if ( phase != PHASE_AUTH ) {
162         pam_syslog(pamh, LOG_ERR,
163                    "option %s allowed in auth phase only", argv);
164     }
165 }
166
167 static int
168 tally_parse_args(pam_handle_t *pamh, struct tally_options *opts,
169                     int phase, int argc, const char **argv)
170 {
171     memset(opts, 0, sizeof(*opts));
172     opts->filename = DEFAULT_LOGFILE;
173     opts->ctrl = OPT_FAIL_ON_ERROR;
174     opts->root_unlock_time = -1;
175
176     for ( ; argc-- > 0; ++argv ) {
177
178       if ( ! strncmp( *argv, "file=", 5 ) ) {
179         const char *from = *argv + 5;
180         if ( *from!='/' ) {
181           pam_syslog(pamh, LOG_ERR,
182                      "filename not /rooted; %s", *argv);
183           return PAM_AUTH_ERR;
184         }
185         opts->filename = from;
186       }
187       else if ( ! strcmp( *argv, "onerr=fail" ) ) {
188         opts->ctrl |= OPT_FAIL_ON_ERROR;
189       }
190       else if ( ! strcmp( *argv, "onerr=succeed" ) ) {
191         opts->ctrl &= ~OPT_FAIL_ON_ERROR;
192       }
193       else if ( ! strcmp( *argv, "magic_root" ) ) {
194         opts->ctrl |= OPT_MAGIC_ROOT;
195       }
196       else if ( ! strcmp( *argv, "serialize" ) ) {
197         opts->ctrl |= OPT_SERIALIZE;
198       }
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;
203       }
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);
208           return PAM_AUTH_ERR;
209         }
210       }
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);
215           return PAM_AUTH_ERR;
216         }
217       }
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);
222           return PAM_AUTH_ERR;
223         }
224       }
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);
229           return PAM_AUTH_ERR;
230         }
231         opts->ctrl |= OPT_DENY_ROOT; /* even_deny_root implied */
232       }
233       else if ( ! strcmp( *argv, "quiet" ) ||
234                 ! strcmp ( *argv, "silent")) {
235         opts->ctrl |= OPT_QUIET;
236       }
237       else if ( ! strcmp ( *argv, "no_log_info") ) {
238         opts->ctrl |= OPT_NOLOGNOTICE;
239       }
240       else if ( ! strcmp ( *argv, "audit") ) {
241         opts->ctrl |= OPT_AUDIT;
242       }
243       else {
244         pam_syslog(pamh, LOG_ERR, "unknown option: %s", *argv);
245       }
246     }
247
248     if (opts->root_unlock_time == -1)
249         opts->root_unlock_time = opts->unlock_time;
250
251     return PAM_SUCCESS;
252 }
253
254 #endif   /* #ifndef MAIN */
255
256 /*---------------------------------------------------------------------*/
257
258 /* --- Support function: get uid (and optionally username) from PAM or
259         cline_user --- */
260
261 #ifdef MAIN
262 static char *cline_user=0;  /* cline_user is used in the administration prog */
263 #endif
264
265 static int
266 pam_get_uid(pam_handle_t *pamh, uid_t *uid, const char **userp, struct tally_options *opts)
267 {
268     const char *user = NULL;
269     struct passwd *pw;
270
271 #ifdef MAIN
272     user = cline_user;
273 #else
274     if ((pam_get_user( pamh, &user, NULL )) != PAM_SUCCESS) {
275       user = NULL;
276     }
277 #endif
278
279     if ( !user || !*user ) {
280       pam_syslog(pamh, LOG_ERR, "pam_get_uid; user?");
281       return PAM_AUTH_ERR;
282     }
283
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;
289     }
290
291     if ( uid )   *uid   = pw->pw_uid;
292     if ( userp ) *userp = user;
293     return PAM_SUCCESS;
294 }
295
296 /*---------------------------------------------------------------------*/
297
298 /* --- Support functions: set/get tally data --- */
299
300 #ifndef MAIN
301
302 struct tally_data {
303     time_t time;
304     int    tfile;
305 };
306
307 static void
308 _cleanup(pam_handle_t *pamh UNUSED, void *void_data, int error_status UNUSED)
309 {
310     struct tally_data *data = void_data;
311     if (data->tfile != -1)
312         close(data->tfile);
313     free(data);
314 }
315
316 static void
317 tally_set_data( pam_handle_t *pamh, time_t oldtime, int tfile )
318 {
319     struct tally_data *data;
320
321     if ( (data=malloc(sizeof(*data))) != NULL ) {
322         data->time = oldtime;
323         data->tfile = tfile;
324         pam_set_data(pamh, MODULE_NAME, (void *)data, _cleanup);
325     }
326 }
327
328 static int
329 tally_get_data( pam_handle_t *pamh, time_t *oldtime, int *tfile )
330 {
331     int rv;
332     const void *void_data;
333     const struct tally_data *data;
334
335     rv = pam_get_data(pamh, MODULE_NAME, &void_data);
336     if ( rv == PAM_SUCCESS && void_data != NULL && oldtime != NULL ) {
337       data = void_data;
338       *oldtime = data->time;
339       *tfile = data->tfile;
340     }
341     else {
342       rv = -1;
343       *oldtime = 0;
344     }
345     return rv;
346 }
347 #endif   /* #ifndef MAIN */
348
349 /*---------------------------------------------------------------------*/
350
351 /* --- Support function: open/create tallyfile and return tally for uid --- */
352
353 /* If on entry tallyfile doesn't exist, creation is attempted. */
354
355 static void
356 alarm_handler(int sig UNUSED)
357 {   /* we just need to ignore it */
358 }
359
360 static int
361 get_tally(pam_handle_t *pamh, uid_t uid, const char *filename,
362         int *tfile, struct tallylog *tally, unsigned int ctrl)
363 {
364     struct stat fileinfo;
365     int lstat_ret;
366     void *void_tally = tally;
367     int preopened = 0;
368
369     if (*tfile != -1) {
370         preopened = 1;
371         goto skip_open;
372     }
373
374     lstat_ret = lstat(filename, &fileinfo);
375     if (lstat_ret) {
376       *tfile=open(filename, O_APPEND|O_CREAT, S_IRUSR|S_IWUSR);
377       /* Create file, or append-open in pathological case. */
378       if (*tfile == -1) {
379 #ifndef MAIN
380         if (errno == EACCES) {
381             return PAM_IGNORE; /* called with insufficient access rights */
382         }
383 #endif
384         pam_syslog(pamh, LOG_ALERT, "Couldn't create %s: %m", filename);
385         return PAM_AUTH_ERR;
386       }
387       lstat_ret = fstat(*tfile, &fileinfo);
388       close(*tfile);
389     }
390
391     *tfile = -1;
392
393     if ( lstat_ret ) {
394       pam_syslog(pamh, LOG_ALERT, "Couldn't stat %s", filename);
395       return PAM_AUTH_ERR;
396     }
397
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",
403                filename);
404       return PAM_AUTH_ERR;
405     }
406
407     if ((*tfile = open(filename, O_RDWR)) == -1) {
408 #ifndef MAIN
409       if (errno == EACCES) /* called with insufficient access rights */
410           return PAM_IGNORE;
411 #endif
412       pam_syslog(pamh, LOG_ALERT, "Error opening %s for update: %m", filename);
413
414       return PAM_AUTH_ERR;
415     }
416
417 skip_open:
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);
420         if (!preopened) {
421             close(*tfile);
422             *tfile = -1;
423         }
424         return PAM_AUTH_ERR;
425     }
426
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;
432         int rv;
433
434         memset(&newsa, '\0', sizeof(newsa));
435         newsa.sa_handler = alarm_handler;
436         sigaction(SIGALRM, &newsa, &oldsa);
437         oldalarm = alarm(MAX_LOCK_WAITING_TIME);
438
439         rv = lockf(*tfile, F_LOCK, sizeof(*tally));
440         /* lock failure is not fatal, we attempt to read the tally anyway */
441
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) {
447                 oldalarm = 1;
448             }
449         }
450         sigaction(SIGALRM, &oldsa, NULL);
451         alarm(oldalarm);
452     }
453
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 */
459     }
460
461     tally->fail_line[sizeof(tally->fail_line)-1] = '\0';
462
463     return PAM_SUCCESS;
464 }
465
466 /*---------------------------------------------------------------------*/
467
468 /* --- Support function: update tallyfile with tally!=TALLY_HI --- */
469
470 static int
471 set_tally(pam_handle_t *pamh, uid_t uid,
472           const char *filename, int *tfile, struct tallylog *tally)
473 {
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);
478                             return PAM_AUTH_ERR;
479         }
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);
482             return PAM_AUTH_ERR;
483         }
484     }
485
486     if (fsync(*tfile)) {
487       pam_syslog(pamh, LOG_ALERT, "update (fsync) failed for %s: %m", filename);
488       return PAM_AUTH_ERR;
489     }
490     return PAM_SUCCESS;
491 }
492
493 /*---------------------------------------------------------------------*/
494
495 /* --- PAM bits --- */
496
497 #ifndef MAIN
498
499 #define RETURN_ERROR(i) return ((opts->ctrl & OPT_FAIL_ON_ERROR)?(i):(PAM_SUCCESS))
500
501 /*---------------------------------------------------------------------*/
502
503 static int
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)
507 {
508     int rv = PAM_SUCCESS;
509 #ifdef HAVE_LIBAUDIT
510     char buf[64];
511     int audit_fd = -1;
512 #endif
513
514     if ((opts->ctrl & OPT_MAGIC_ROOT) && getuid() == 0) {
515       return PAM_SUCCESS;
516     }
517     /* magic_root skips tally check */
518 #ifdef HAVE_LIBAUDIT
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;
524 #endif
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    */
528 #ifdef HAVE_LIBAUDIT
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);
534         }
535 #endif
536         if (uid) {
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 */
541 #ifdef HAVE_LIBAUDIT
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);
545 #endif
546                     rv = PAM_SUCCESS;
547                     goto cleanup;
548                 }
549             }
550         } else {
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 */
555 #ifdef HAVE_LIBAUDIT
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);
559 #endif
560                     rv = PAM_SUCCESS;
561                     goto cleanup;
562                 }
563             }
564         }
565
566 #ifdef HAVE_LIBAUDIT
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);
571         }
572 #endif
573
574         if (!(opts->ctrl & OPT_QUIET)) {
575             pam_info(pamh, _("Account locked due to %u failed logins"),
576                     (unsigned int)tally->fail_cnt);
577         }
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);
582         }
583         rv = PAM_AUTH_ERR;                 /* Only unconditional failure   */
584         goto cleanup;
585     }
586
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
591                lock_time applies */
592             tally->fail_cnt = oldcnt;
593             tally->fail_time = oldtime;
594
595             if (!(opts->ctrl & OPT_QUIET)) {
596                 pam_info(pamh, _("Account temporary locked (%ld seconds left)"),
597                          oldtime+opts->lock_time-time(NULL));
598             }
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));
605             }
606             rv = PAM_AUTH_ERR;
607             goto cleanup;
608         }
609     }
610
611 cleanup:
612 #ifdef HAVE_LIBAUDIT
613     if (audit_fd != -1) {
614         close(audit_fd);
615     }
616 #endif
617     return rv;
618 }
619
620 /* --- tally bump function: bump tally for uid by (signed) inc --- */
621
622 static int
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)
625 {
626     struct tallylog tally;
627     tally_t oldcnt;
628     const void *remote_host = NULL;
629     int i, rv;
630
631     tally.fail_cnt = 0;  /* !TALLY_HI --> Log opened for update */
632
633     i = get_tally(pamh, uid, opts->filename, tfile, &tally, opts->ctrl);
634     if (i != PAM_SUCCESS) {
635         if (*tfile != -1) {
636             close(*tfile);
637             *tfile = -1;
638         }
639         RETURN_ERROR(i);
640     }
641
642     /* to remember old fail time (for locktime) */
643     if (oldtime) {
644         *oldtime = (time_t)tally.fail_time;
645     }
646
647     tally.fail_time = time(NULL);
648
649     (void) pam_get_item(pamh, PAM_RHOST, &remote_host);
650     if (!remote_host) {
651         (void) pam_get_item(pamh, PAM_TTY, &remote_host);
652         if (!remote_host) {
653             remote_host = "unknown";
654         }
655     }
656
657     strncpy(tally.fail_line, remote_host,
658                     sizeof(tally.fail_line)-1);
659     tally.fail_line[sizeof(tally.fail_line)-1] = 0;
660
661     oldcnt = tally.fail_cnt;
662
663     if (!(opts->ctrl & OPT_MAGIC_ROOT) || getuid()) {
664       /* magic_root doesn't change tally */
665       tally.fail_cnt += inc;
666
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);
671       }
672     }
673
674     rv = tally_check(oldcnt, *oldtime, pamh, uid, user, opts, &tally);
675
676     i = set_tally(pamh, uid, opts->filename, tfile, &tally);
677     if (i != PAM_SUCCESS) {
678         if (*tfile != -1) {
679             close(*tfile);
680             *tfile = -1;
681         }
682         if (rv == PAM_SUCCESS)
683             RETURN_ERROR( i );
684         /* fallthrough */
685     } else if (!(opts->ctrl & OPT_SERIALIZE)) {
686         close(*tfile);
687         *tfile = -1;
688     }
689
690     return rv;
691 }
692
693 static int
694 tally_reset (pam_handle_t *pamh, uid_t uid, struct tally_options *opts, int old_tfile)
695 {
696     struct tallylog tally;
697     int tfile = old_tfile;
698     int i;
699
700     /* resets only if not magic root */
701
702     if ((opts->ctrl & OPT_MAGIC_ROOT) && getuid() == 0) {
703         return PAM_SUCCESS;
704     }
705
706     tally.fail_cnt = 0;  /* !TALLY_HI --> Log opened for update */
707
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 */
711             close(tfile);
712         RETURN_ERROR(i);
713     }
714
715     memset(&tally, 0, sizeof(tally));
716
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 */
720             close(tfile);
721         RETURN_ERROR(i);
722     }
723
724     if (tfile != old_tfile)
725         close(tfile);
726
727     return PAM_SUCCESS;
728 }
729
730 /*---------------------------------------------------------------------*/
731
732 /* --- authentication management functions (only) --- */
733
734 PAM_EXTERN int
735 pam_sm_authenticate(pam_handle_t *pamh, int flags UNUSED,
736                     int argc, const char **argv)
737 {
738   int
739     rv, tfile = -1;
740   time_t
741     oldtime = 0;
742   struct tally_options
743     options, *opts = &options;
744   uid_t
745     uid;
746   const char
747     *user;
748
749   rv = tally_parse_args(pamh, opts, PHASE_AUTH, argc, argv);
750   if (rv != PAM_SUCCESS)
751       RETURN_ERROR(rv);
752
753   if (flags & PAM_SILENT)
754     opts->ctrl |= OPT_QUIET;
755
756   rv = pam_get_uid(pamh, &uid, &user, opts);
757   if (rv != PAM_SUCCESS)
758       RETURN_ERROR(rv);
759
760   rv = tally_bump(1, &oldtime, pamh, uid, user, opts, &tfile);
761
762   tally_set_data(pamh, oldtime, tfile);
763
764   return rv;
765 }
766
767 PAM_EXTERN int
768 pam_sm_setcred(pam_handle_t *pamh, int flags UNUSED,
769                int argc, const char **argv)
770 {
771   int
772     rv, tfile = -1;
773   time_t
774     oldtime = 0;
775   struct tally_options
776     options, *opts = &options;
777   uid_t
778     uid;
779   const char
780     *user;
781
782   rv = tally_parse_args(pamh, opts, PHASE_AUTH, argc, argv);
783   if ( rv != PAM_SUCCESS )
784       RETURN_ERROR( rv );
785
786   rv = pam_get_uid(pamh, &uid, &user, opts);
787   if ( rv != PAM_SUCCESS )
788       RETURN_ERROR( rv );
789
790   if ( tally_get_data(pamh, &oldtime, &tfile) != 0 )
791   /* no data found */
792       return PAM_SUCCESS;
793
794   rv = tally_reset(pamh, uid, opts, tfile);
795
796   pam_set_data(pamh, MODULE_NAME, NULL, NULL);
797
798   return rv;
799 }
800
801 /*---------------------------------------------------------------------*/
802
803 /* --- authentication management functions (only) --- */
804
805 /* To reset failcount of user on successfull login */
806
807 PAM_EXTERN int
808 pam_sm_acct_mgmt(pam_handle_t *pamh, int flags UNUSED,
809                  int argc, const char **argv)
810 {
811   int
812     rv, tfile = -1;
813   time_t
814     oldtime = 0;
815   struct tally_options
816     options, *opts = &options;
817   uid_t
818     uid;
819   const char
820     *user;
821
822   rv = tally_parse_args(pamh, opts, PHASE_ACCOUNT, argc, argv);
823   if ( rv != PAM_SUCCESS )
824       RETURN_ERROR( rv );
825
826   rv = pam_get_uid(pamh, &uid, &user, opts);
827   if ( rv != PAM_SUCCESS )
828       RETURN_ERROR( rv );
829
830   if ( tally_get_data(pamh, &oldtime, &tfile) != 0 )
831   /* no data found */
832       return PAM_SUCCESS;
833
834   rv = tally_reset(pamh, uid, opts, tfile);
835
836   pam_set_data(pamh, MODULE_NAME, NULL, NULL);
837
838   return rv;
839 }
840
841 /*-----------------------------------------------------------------------*/
842
843 #ifdef PAM_STATIC
844
845 /* static module data */
846
847 struct pam_module _pam_tally_modstruct = {
848      MODULE_NAME,
849 #ifdef PAM_SM_AUTH
850      pam_sm_authenticate,
851      pam_sm_setcred,
852 #else
853      NULL,
854      NULL,
855 #endif
856 #ifdef PAM_SM_ACCOUNT
857      pam_sm_acct_mgmt,
858 #else
859      NULL,
860 #endif
861      NULL,
862      NULL,
863      NULL,
864 };
865
866 #endif   /* #ifdef PAM_STATIC */
867
868 /*-----------------------------------------------------------------------*/
869
870 #else   /* #ifndef MAIN */
871
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;
875
876 /*
877  *  Not going to link with pamlib just for these.. :)
878  */
879
880 static const char *
881 pam_errors( int i )
882 {
883   switch (i) {
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");
888   }
889 }
890
891 static int
892 getopts( char **argv )
893 {
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);
907     }
908     else if ( !strcmp (*argv,"--quiet")   ) cline_quiet=1;
909     else {
910       fprintf(stderr,_("%s: Unrecognised option %s\n"),pname,*argv);
911       return FALSE;
912     }
913   }
914   return TRUE;
915 }
916
917 static void
918 print_one(const struct tallylog *tally, uid_t uid)
919 {
920    static int once;
921    char *cp;
922    time_t fail_time;
923    struct tm *tm;
924    struct passwd *pwent;
925    const char *username = "[NONAME]";
926    char ptime[80];
927
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);
932    cp = ptime;
933    if (pwent) {
934         username = pwent->pw_name;
935    }
936    if (!once) {
937         printf (_("Login           Failures Latest failure     From\n"));
938         once++;
939    }
940    printf ("%-15.15s %5hu    ", username, tally->fail_cnt);
941    if (tally->fail_time) {
942         printf ("%-17.17s  %s", cp, tally->fail_line);
943    }
944    putchar ('\n');
945 }
946
947 int
948 main( int argc UNUSED, char **argv )
949 {
950   struct tallylog tally;
951
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"),
956            *argv);
957     exit(2);
958   }
959
960   umask(077);
961
962   /*
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
966    */
967
968   if ( cline_user ) {
969     uid_t uid;
970     int tfile = -1;
971     struct tally_options opts;
972     int i;
973
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));
979       exit(1);
980     }
981
982     i=get_tally(NULL, uid, cline_filename, &tfile, &tally, 0);
983     if ( i != PAM_SUCCESS ) {
984       if (tfile != -1)
985           close(tfile);
986       fprintf(stderr, "%s: %s\n", *argv, pam_errors(i));
987       exit(1);
988     }
989
990     if ( !cline_quiet )
991       print_one(&tally, uid);
992
993     if (cline_reset != TALLY_HI) {
994 #ifdef HAVE_LIBAUDIT
995         char buf[64];
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);
1000         if (audit_fd >=0)
1001                 close(audit_fd);
1002 #endif
1003         if (cline_reset == 0) {
1004             memset(&tally, 0, sizeof(tally));
1005         } else {
1006             tally.fail_cnt = cline_reset;
1007         }
1008         i=set_tally(NULL, uid, cline_filename, &tfile, &tally);
1009         close(tfile);
1010         if (i != PAM_SUCCESS) {
1011             fprintf(stderr,"%s: %s\n",*argv,pam_errors(i));
1012             exit(1);
1013         }
1014     } else {
1015         close(tfile);
1016     }
1017   }
1018   else /* !cline_user (ie, operate on all users) */ {
1019     FILE *tfile=fopen(cline_filename, "r");
1020     uid_t uid=0;
1021     if (!tfile  && cline_reset != 0) {
1022         perror(*argv);
1023         exit(1);
1024     }
1025
1026     for ( ; tfile && !feof(tfile); uid++ ) {
1027       if ( !fread(&tally, sizeof(tally), 1, tfile)
1028            || !tally.fail_cnt ) {
1029          continue;
1030       }
1031       print_one(&tally, uid);
1032     }
1033     if (tfile)
1034       fclose(tfile);
1035     if ( cline_reset!=0 && cline_reset!=TALLY_HI ) {
1036       fprintf(stderr,_("%s: Can't reset all users to non-zero\n"),*argv);
1037     }
1038     else if ( !cline_reset ) {
1039 #ifdef HAVE_LIBAUDIT
1040       char buf[64];
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);
1045       if (audit_fd >=0)
1046               close(audit_fd);
1047 #endif
1048       tfile=fopen(cline_filename, "w");
1049       if ( !tfile ) perror(*argv), exit(0);
1050       fclose(tfile);
1051     }
1052   }
1053   return 0;
1054 }
1055
1056
1057 #endif   /* #ifndef MAIN */