]> granicus.if.org Git - linux-pam/blob - modules/pam_tally/pam_tally.c
Fix whitespace issues
[linux-pam] / modules / pam_tally / pam_tally.c
1 /*
2  * pam_tally.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
13  * Audit option added for Tomas patch by
14  * Sebastien Tricaud <toady@gscore.org> 13 January 2005
15  */
16
17 #include "config.h"
18
19 #include <stdio.h>
20 #include <string.h>
21 #include <unistd.h>
22 #include <stdarg.h>
23 #include <stdlib.h>
24 #include <syslog.h>
25 #include <pwd.h>
26 #include <time.h>
27
28 #include <sys/types.h>
29 #include <sys/stat.h>
30 #include <sys/param.h>
31 #include "faillog.h"
32
33 /*
34  * here, we make a definition for the externally accessible function
35  * in this file (this definition is required for static a module
36  * but strongly encouraged generally) it is used to instruct the
37  * modules include file to define the function prototypes.
38  */
39
40 #ifndef MAIN
41 #define PAM_SM_AUTH
42 #define PAM_SM_ACCOUNT
43 /* #define PAM_SM_SESSION  */
44 /* #define PAM_SM_PASSWORD */
45
46 #include <security/pam_modutil.h>
47 #include <security/pam_ext.h>
48 #endif
49 #include <security/pam_modules.h>
50
51 #ifndef TRUE
52 #define TRUE  1L
53 #define FALSE 0L
54 #endif
55
56 #ifndef HAVE_FSEEKO
57 #define fseeko fseek
58 #endif
59
60 /*---------------------------------------------------------------------*/
61
62 #define DEFAULT_LOGFILE "/var/log/faillog"
63 #define MODULE_NAME     "pam_tally"
64
65 #define tally_t    unsigned short int
66 #define TALLY_FMT  "%hu"
67 #define TALLY_HI   ((tally_t)~0L)
68
69 #ifndef FILENAME_MAX
70 # define FILENAME_MAX MAXPATHLEN
71 #endif
72
73 struct fail_s {
74     struct faillog fs_faillog;
75 #ifndef MAIN
76     time_t fs_fail_time;
77 #endif /* ndef MAIN */
78 };
79
80 struct tally_options {
81     const char *filename;
82     tally_t deny;
83     long lock_time;
84     long unlock_time;
85     unsigned int ctrl;
86 };
87
88 #define PHASE_UNKNOWN 0
89 #define PHASE_AUTH    1
90 #define PHASE_ACCOUNT 2
91 #define PHASE_SESSION 3
92
93 #define OPT_MAGIC_ROOT                    01
94 #define OPT_FAIL_ON_ERROR                 02
95 #define OPT_DENY_ROOT                     04
96 #define OPT_PER_USER                     010
97 #define OPT_NO_LOCK_TIME                 020
98 #define OPT_NO_RESET                     040
99 #define OPT_AUDIT                       0100
100 #define OPT_SILENT                      0200
101 #define OPT_NOLOGNOTICE                 0400
102
103
104 /*---------------------------------------------------------------------*/
105
106 /* some syslogging */
107
108 #ifdef MAIN
109 #define pam_syslog tally_log
110 static void
111 tally_log (const pam_handle_t *pamh UNUSED, int priority UNUSED,
112             const char *fmt, ...)
113 {
114         va_list args;
115
116         va_start(args, fmt);
117         fprintf(stderr, "%s: ", MODULE_NAME);
118         vfprintf(stderr, fmt, args);
119         fprintf(stderr,"\n");
120         va_end(args);
121 }
122
123 #define pam_modutil_getpwnam(pamh,user) getpwnam(user)
124
125 #endif
126
127 /*---------------------------------------------------------------------*/
128
129 /* --- Support function: parse arguments --- */
130
131 #ifndef MAIN
132
133 static void
134 log_phase_no_auth(pam_handle_t *pamh, int phase, const char *argv)
135 {
136     if ( phase != PHASE_AUTH ) {
137         pam_syslog(pamh, LOG_ERR,
138                    "option %s allowed in auth phase only", argv);
139     }
140 }
141
142 static int
143 tally_parse_args(pam_handle_t *pamh, struct tally_options *opts,
144                     int phase, int argc, const char **argv)
145 {
146     memset(opts, 0, sizeof(*opts));
147     opts->filename = DEFAULT_LOGFILE;
148
149     for ( ; argc-- > 0; ++argv ) {
150
151       if ( ! strncmp( *argv, "file=", 5 ) ) {
152         const char *from = *argv + 5;
153         if ( *from!='/' || strlen(from)>FILENAME_MAX-1 ) {
154           pam_syslog(pamh, LOG_ERR,
155                      "filename not /rooted or too long; %s", *argv);
156           return PAM_AUTH_ERR;
157         }
158         opts->filename = from;
159       }
160       else if ( ! strcmp( *argv, "onerr=fail" ) ) {
161         opts->ctrl |= OPT_FAIL_ON_ERROR;
162       }
163       else if ( ! strcmp( *argv, "onerr=succeed" ) ) {
164         opts->ctrl &= ~OPT_FAIL_ON_ERROR;
165       }
166       else if ( ! strcmp( *argv, "magic_root" ) ) {
167         opts->ctrl |= OPT_MAGIC_ROOT;
168       }
169       else if ( ! strcmp( *argv, "even_deny_root_account" ) ) {
170         log_phase_no_auth(pamh, phase, *argv);
171         opts->ctrl |= OPT_DENY_ROOT;
172       }
173       else if ( ! strncmp( *argv, "deny=", 5 ) ) {
174         log_phase_no_auth(pamh, phase, *argv);
175         if ( sscanf((*argv)+5,TALLY_FMT,&opts->deny) != 1 ) {
176           pam_syslog(pamh, LOG_ERR, "bad number supplied: %s", *argv);
177           return PAM_AUTH_ERR;
178         }
179       }
180       else if ( ! strncmp( *argv, "lock_time=", 10 ) ) {
181         log_phase_no_auth(pamh, phase, *argv);
182         if ( sscanf((*argv)+10,"%ld",&opts->lock_time) != 1 ) {
183           pam_syslog(pamh, LOG_ERR, "bad number supplied: %s", *argv);
184           return PAM_AUTH_ERR;
185         }
186       }
187       else if ( ! strncmp( *argv, "unlock_time=", 12 ) ) {
188         log_phase_no_auth(pamh, phase, *argv);
189         if ( sscanf((*argv)+12,"%ld",&opts->unlock_time) != 1 ) {
190           pam_syslog(pamh, LOG_ERR, "bad number supplied: %s", *argv);
191           return PAM_AUTH_ERR;
192         }
193       }
194       else if ( ! strcmp( *argv, "per_user" ) )
195       {
196         log_phase_no_auth(pamh, phase, *argv);
197         opts->ctrl |= OPT_PER_USER;
198       }
199       else if ( ! strcmp( *argv, "no_lock_time") )
200       {
201         log_phase_no_auth(pamh, phase, *argv);
202         opts->ctrl |= OPT_NO_LOCK_TIME;
203       }
204       else if ( ! strcmp( *argv, "no_reset" ) ) {
205         opts->ctrl |= OPT_NO_RESET;
206       }
207       else if ( ! strcmp ( *argv, "audit") ) {
208         opts->ctrl |= OPT_AUDIT;
209       }
210       else if ( ! strcmp ( *argv, "silent") ) {
211         opts->ctrl |= OPT_SILENT;
212       }
213       else if ( ! strcmp ( *argv, "no_log_info") ) {
214         opts->ctrl |= OPT_NOLOGNOTICE;
215       }
216       else {
217         pam_syslog(pamh, LOG_ERR, "unknown option: %s", *argv);
218       }
219     }
220
221     return PAM_SUCCESS;
222 }
223
224 #endif   /* #ifndef MAIN */
225
226 /*---------------------------------------------------------------------*/
227
228 /* --- Support function: get uid (and optionally username) from PAM or
229         cline_user --- */
230
231 #ifdef MAIN
232 static char *cline_user=0;  /* cline_user is used in the administration prog */
233 #endif
234
235 static int
236 pam_get_uid(pam_handle_t *pamh, uid_t *uid, const char **userp, struct tally_options *opts)
237 {
238     const char *user = NULL;
239     struct passwd *pw;
240
241 #ifdef MAIN
242     user = cline_user;
243 #else
244     if ((pam_get_user( pamh, &user, NULL )) != PAM_SUCCESS) {
245       pam_syslog(pamh, LOG_ERR, "pam_get_user; user?");
246       return PAM_AUTH_ERR;
247     }
248 #endif
249
250     if ( !user || !*user ) {
251       pam_syslog(pamh, LOG_ERR, "pam_get_uid; user?");
252       return PAM_AUTH_ERR;
253     }
254
255     if ( ! ( pw = pam_modutil_getpwnam( pamh, user ) ) ) {
256       opts->ctrl & OPT_AUDIT ?
257               pam_syslog(pamh, LOG_ERR, "pam_get_uid; no such user %s", user) :
258               pam_syslog(pamh, LOG_ERR, "pam_get_uid; no such user");
259       return PAM_USER_UNKNOWN;
260     }
261
262     if ( uid )   *uid   = pw->pw_uid;
263     if ( userp ) *userp = user;
264     return PAM_SUCCESS;
265 }
266
267 /*---------------------------------------------------------------------*/
268
269 /* --- Support functions: set/get tally data --- */
270
271 #ifndef MAIN
272
273 static void
274 _cleanup(pam_handle_t *pamh UNUSED, void *data, int error_status UNUSED)
275 {
276     free(data);
277 }
278
279
280 static void
281 tally_set_data( pam_handle_t *pamh, time_t oldtime )
282 {
283     time_t *data;
284
285     if ( (data=malloc(sizeof(time_t))) != NULL ) {
286         *data = oldtime;
287         pam_set_data(pamh, MODULE_NAME, (void *)data, _cleanup);
288     }
289 }
290
291 static int
292 tally_get_data( pam_handle_t *pamh, time_t *oldtime )
293 {
294     int rv;
295     const void *data;
296
297     rv = pam_get_data(pamh, MODULE_NAME, &data);
298     if ( rv == PAM_SUCCESS && data != NULL && oldtime != NULL ) {
299       *oldtime = *(const time_t *)data;
300       pam_set_data(pamh, MODULE_NAME, NULL, NULL);
301     }
302     else {
303       rv = -1;
304       if (oldtime)
305         *oldtime = 0;
306     }
307     return rv;
308 }
309 #endif   /* #ifndef MAIN */
310
311 /*---------------------------------------------------------------------*/
312
313 /* --- Support function: open/create tallyfile and return tally for uid --- */
314
315 /* If on entry *tally==TALLY_HI, tallyfile is opened READONLY */
316 /* Otherwise, if on entry tallyfile doesn't exist, creation is attempted. */
317
318 static int
319 get_tally(pam_handle_t *pamh, tally_t *tally, uid_t uid,
320              const char *filename, FILE **TALLY, struct fail_s *fsp)
321 {
322     struct stat fileinfo;
323     int lstat_ret = lstat(filename,&fileinfo);
324
325     if ( lstat_ret && *tally!=TALLY_HI ) {
326       int oldmask = umask(077);
327       *TALLY=fopen(filename, "a");
328       /* Create file, or append-open in pathological case. */
329       umask(oldmask);
330       if ( !*TALLY ) {
331         pam_syslog(pamh, LOG_ALERT, "Couldn't create %s", filename);
332         return PAM_AUTH_ERR;
333       }
334       lstat_ret = fstat(fileno(*TALLY),&fileinfo);
335       fclose(*TALLY);
336     }
337
338     if ( lstat_ret ) {
339       pam_syslog(pamh, LOG_ALERT, "Couldn't stat %s", filename);
340       return PAM_AUTH_ERR;
341     }
342
343     if((fileinfo.st_mode & S_IWOTH) || !S_ISREG(fileinfo.st_mode)) {
344       /* If the file is world writable or is not a
345          normal file, return error */
346       pam_syslog(pamh, LOG_ALERT,
347                "%s is either world writable or not a normal file",
348                filename);
349       return PAM_AUTH_ERR;
350     }
351
352     if ( ! ( *TALLY = fopen(filename,(*tally!=TALLY_HI)?"r+":"r") ) ) {
353       pam_syslog(pamh, LOG_ALERT, "Error opening %s for %s", filename, *tally!=TALLY_HI?"update":"read");
354
355 /* Discovering why account service fails: e/uid are target user.
356  *
357  *      perror(MODULE_NAME);
358  *      fprintf(stderr,"uid %d euid %d\n",getuid(), geteuid());
359  */
360       return PAM_AUTH_ERR;
361     }
362
363     if ( fseeko( *TALLY, (off_t) uid * sizeof(struct faillog), SEEK_SET ) ) {
364           pam_syslog(pamh, LOG_ALERT, "fseek failed for %s", filename);
365           fclose(*TALLY);
366           return PAM_AUTH_ERR;
367     }
368
369     if ( (size_t)fileinfo.st_size <= uid * sizeof(struct faillog) ) {
370
371         memset(fsp, 0, sizeof(struct faillog));
372         *tally=0;
373         fsp->fs_faillog.fail_time = time(NULL);
374
375     } else if (( fread((char *) &fsp->fs_faillog,
376                        sizeof(struct faillog), 1, *TALLY) )==0 ) {
377
378         *tally=0; /* Assuming a gappy filesystem */
379
380     } else {
381
382         *tally = fsp->fs_faillog.fail_cnt;
383
384     }
385
386     return PAM_SUCCESS;
387 }
388
389 /*---------------------------------------------------------------------*/
390
391 /* --- Support function: update and close tallyfile with tally!=TALLY_HI --- */
392
393 static int
394 set_tally(pam_handle_t *pamh, tally_t tally, uid_t uid,
395              const char *filename, FILE **TALLY, struct fail_s *fsp)
396 {
397     int retval = PAM_SUCCESS;
398
399     if ( tally!=TALLY_HI ) {
400       if ( fseeko( *TALLY, (off_t) uid * sizeof(struct faillog), SEEK_SET ) ) {
401         pam_syslog(pamh, LOG_ALERT, "fseek failed for %s", filename);
402         retval = PAM_AUTH_ERR;
403       } else {
404         fsp->fs_faillog.fail_cnt = tally;
405         if (fwrite((char *) &fsp->fs_faillog,
406                    sizeof(struct faillog), 1, *TALLY)==0 ) {
407           pam_syslog(pamh, LOG_ALERT, "update (fwrite) failed for %s", filename);
408           retval = PAM_AUTH_ERR;
409         }
410       }
411     }
412
413     if ( fclose(*TALLY) ) {
414       pam_syslog(pamh, LOG_ALERT, "update (fclose) failed for %s", filename);
415       return PAM_AUTH_ERR;
416     }
417     *TALLY=NULL;
418     return retval;
419 }
420
421 /*---------------------------------------------------------------------*/
422
423 /* --- PAM bits --- */
424
425 #ifndef MAIN
426
427 #define RETURN_ERROR(i) return ((opts->ctrl & OPT_FAIL_ON_ERROR)?(i):(PAM_SUCCESS))
428
429 /*---------------------------------------------------------------------*/
430
431 /* --- tally bump function: bump tally for uid by (signed) inc --- */
432
433 static int
434 tally_bump (int inc, time_t *oldtime, pam_handle_t *pamh,
435             uid_t uid, const char *user, struct tally_options *opts)
436 {
437   tally_t
438     tally         = 0;  /* !TALLY_HI --> Log opened for update */
439
440     FILE
441       *TALLY = NULL;
442     const void
443       *remote_host = NULL,
444       *cur_tty = NULL;
445     struct fail_s fs, *fsp = &fs;
446     int i;
447
448     i=get_tally(pamh, &tally, uid, opts->filename, &TALLY, fsp);
449     if ( i != PAM_SUCCESS ) { RETURN_ERROR( i ); }
450
451     /* to remember old fail time (for locktime) */
452     fsp->fs_fail_time = fsp->fs_faillog.fail_time;
453     if ( inc > 0 ) {
454         if ( oldtime ) {
455             *oldtime = fsp->fs_faillog.fail_time;
456         }
457         fsp->fs_faillog.fail_time = time(NULL);
458     } else {
459         if ( oldtime ) {
460             fsp->fs_faillog.fail_time = *oldtime;
461         }
462     }
463     (void) pam_get_item(pamh, PAM_RHOST, &remote_host);
464     if (!remote_host) {
465
466         (void) pam_get_item(pamh, PAM_TTY, &cur_tty);
467         if (!cur_tty) {
468             strncpy(fsp->fs_faillog.fail_line, "unknown",
469                     sizeof(fsp->fs_faillog.fail_line) - 1);
470             fsp->fs_faillog.fail_line[sizeof(fsp->fs_faillog.fail_line)-1] = 0;
471         } else {
472             strncpy(fsp->fs_faillog.fail_line, cur_tty,
473                     sizeof(fsp->fs_faillog.fail_line)-1);
474             fsp->fs_faillog.fail_line[sizeof(fsp->fs_faillog.fail_line)-1] = 0;
475         }
476
477     } else {
478         strncpy(fsp->fs_faillog.fail_line, remote_host,
479                 (size_t)sizeof(fsp->fs_faillog.fail_line));
480         fsp->fs_faillog.fail_line[sizeof(fsp->fs_faillog.fail_line)-1] = 0;
481     }
482
483     if ( !(opts->ctrl & OPT_MAGIC_ROOT) || getuid() ) {   /* magic_root doesn't change tally */
484
485       tally+=inc;
486
487       if ( tally==TALLY_HI ) { /* Overflow *and* underflow. :) */
488         tally-=inc;
489         pam_syslog(pamh, LOG_ALERT, "Tally %sflowed for user %s",
490                  (inc<0)?"under":"over",user);
491       }
492     }
493
494     i=set_tally(pamh, tally, uid, opts->filename, &TALLY, fsp );
495     if ( i != PAM_SUCCESS ) { RETURN_ERROR( i ); }
496
497     return PAM_SUCCESS;
498 }
499
500 static int
501 tally_check (time_t oldtime, pam_handle_t *pamh, uid_t uid,
502                const char *user, struct tally_options *opts)
503 {
504   tally_t
505     deny          = opts->deny;
506   tally_t
507     tally         = TALLY_HI;
508   long
509     lock_time     = opts->lock_time;
510
511     struct fail_s fs, *fsp = &fs;
512     FILE *TALLY=0;
513     int i;
514
515     i=get_tally(pamh, &tally, uid, opts->filename, &TALLY, fsp);
516     if ( i != PAM_SUCCESS ) { RETURN_ERROR( i ); }
517
518     if ( TALLY != NULL ) {
519       fclose(TALLY);
520     }
521
522     if ( !(opts->ctrl & OPT_MAGIC_ROOT) || getuid() ) {       /* magic_root skips tally check */
523
524       /* To deny or not to deny; that is the question */
525
526       /* if there's .fail_max entry and per_user=TRUE then deny=.fail_max */
527
528       if ( (fsp->fs_faillog.fail_max) && (opts->ctrl & OPT_PER_USER) ) {
529           deny = fsp->fs_faillog.fail_max;
530       }
531       if ( (fsp->fs_faillog.fail_locktime) && (opts->ctrl & OPT_PER_USER) ) {
532           lock_time = fsp->fs_faillog.fail_locktime;
533       }
534       if (lock_time && oldtime
535           && !(opts->ctrl & OPT_NO_LOCK_TIME) )
536       {
537         if ( lock_time + oldtime > time(NULL) )
538         {
539           if (!(opts->ctrl & OPT_SILENT))
540                pam_info (pamh,
541                          _("Account temporary locked (%ld seconds left)"),
542                          oldtime+lock_time-time(NULL));
543
544           if (!(opts->ctrl & OPT_NOLOGNOTICE))
545                pam_syslog (pamh, LOG_NOTICE,
546                            "user %s (%lu) has time limit [%lds left]"
547                            " since last failure.",
548                            user, (unsigned long int) uid,
549                            oldtime+lock_time-time(NULL));
550                 return PAM_AUTH_ERR;
551         }
552       }
553       if (opts->unlock_time && oldtime)
554       {
555         if ( opts->unlock_time + oldtime <= time(NULL) )
556         {       /* ignore deny check after unlock_time elapsed */
557                 return PAM_SUCCESS;
558         }
559       }
560       if (
561         ( deny != 0 ) &&                     /* deny==0 means no deny        */
562         ( tally > deny ) &&                  /* tally>deny means exceeded    */
563         ( ((opts->ctrl & OPT_DENY_ROOT) || uid) )    /* even_deny stops uid check    */
564         ) {
565         if (!(opts->ctrl & OPT_SILENT))
566           pam_info (pamh, _("Account locked due to %u failed logins"),
567                     (unsigned int)tally);
568
569         if (!(opts->ctrl & OPT_NOLOGNOTICE))
570           pam_syslog(pamh, LOG_NOTICE,
571                      "user %s (%lu) tally "TALLY_FMT", deny "TALLY_FMT,
572                      user, (unsigned long int) uid, tally, deny);
573         return PAM_AUTH_ERR;                 /* Only unconditional failure   */
574       }
575     }
576
577     return PAM_SUCCESS;
578 }
579
580 static int
581 tally_reset (pam_handle_t *pamh, uid_t uid, struct tally_options *opts)
582 {
583   tally_t
584     tally         = 0;  /* !TALLY_HI --> Log opened for update */
585
586     struct fail_s fs, *fsp = &fs;
587     FILE *TALLY=0;
588     int i;
589
590     i=get_tally(pamh, &tally, uid, opts->filename, &TALLY, fsp);
591     if ( i != PAM_SUCCESS ) { RETURN_ERROR( i ); }
592
593       /* resets if not magic root
594        */
595
596     if ( (!(opts->ctrl & OPT_MAGIC_ROOT) || getuid())
597          && !(opts->ctrl & OPT_NO_RESET) )
598         { tally=0; }
599
600     if (tally == 0)
601     {
602         fsp->fs_faillog.fail_time = (time_t) 0;
603         strcpy(fsp->fs_faillog.fail_line, "");
604     }
605
606     i=set_tally(pamh, tally, uid, opts->filename, &TALLY, fsp);
607     if ( i != PAM_SUCCESS ) { RETURN_ERROR( i ); }
608
609     return PAM_SUCCESS;
610 }
611
612 /*---------------------------------------------------------------------*/
613
614 /* --- authentication management functions (only) --- */
615
616 #ifdef PAM_SM_AUTH
617
618 PAM_EXTERN int
619 pam_sm_authenticate(pam_handle_t *pamh, int flags,
620                     int argc, const char **argv)
621 {
622   int
623     rvcheck, rvbump;
624   time_t
625     oldtime = 0;
626   struct tally_options
627     options, *opts = &options;
628   uid_t
629     uid;
630   const char
631     *user;
632
633   rvcheck = tally_parse_args(pamh, opts, PHASE_AUTH, argc, argv);
634   if ( rvcheck != PAM_SUCCESS )
635       RETURN_ERROR( rvcheck );
636
637   if (flags & PAM_SILENT)
638     opts->ctrl |= OPT_SILENT;
639
640   rvcheck = pam_get_uid(pamh, &uid, &user, opts);
641   if ( rvcheck != PAM_SUCCESS )
642       RETURN_ERROR( rvcheck );
643
644   rvbump = tally_bump(1, &oldtime, pamh, uid, user, opts);
645   rvcheck = tally_check(oldtime, pamh, uid, user, opts);
646
647   tally_set_data(pamh, oldtime);
648
649   return rvcheck != PAM_SUCCESS ? rvcheck : rvbump;
650 }
651
652 PAM_EXTERN int
653 pam_sm_setcred(pam_handle_t *pamh, int flags,
654                int argc, const char **argv)
655 {
656   int
657     rv;
658   time_t
659     oldtime = 0;
660   struct tally_options
661     options, *opts = &options;
662   uid_t
663     uid;
664   const char
665     *user;
666
667   rv = tally_parse_args(pamh, opts, PHASE_AUTH, argc, argv);
668   if ( rv != PAM_SUCCESS )
669       RETURN_ERROR( rv );
670
671   if (flags & PAM_SILENT)
672     opts->ctrl |= OPT_SILENT;
673
674   rv = pam_get_uid(pamh, &uid, &user, opts);
675   if ( rv != PAM_SUCCESS )
676       RETURN_ERROR( rv );
677
678   if ( tally_get_data(pamh, &oldtime) != 0 )
679   /* no data found */
680       return PAM_SUCCESS;
681
682   if ( (rv=tally_bump(-1, &oldtime, pamh, uid, user, opts)) != PAM_SUCCESS )
683       return rv;
684   return tally_reset(pamh, uid, opts);
685 }
686
687 #endif
688
689 /*---------------------------------------------------------------------*/
690
691 /* --- authentication management functions (only) --- */
692
693 #ifdef PAM_SM_ACCOUNT
694
695 /* To reset failcount of user on successfull login */
696
697 PAM_EXTERN int
698 pam_sm_acct_mgmt(pam_handle_t *pamh, int flags,
699                  int argc, const char **argv)
700 {
701   int
702     rv;
703   time_t
704     oldtime = 0;
705   struct tally_options
706     options, *opts = &options;
707   uid_t
708     uid;
709   const char
710     *user;
711
712   rv = tally_parse_args(pamh, opts, PHASE_ACCOUNT, argc, argv);
713   if ( rv != PAM_SUCCESS )
714       RETURN_ERROR( rv );
715
716   if (flags & PAM_SILENT)
717     opts->ctrl |= OPT_SILENT;
718
719   rv = pam_get_uid(pamh, &uid, &user, opts);
720   if ( rv != PAM_SUCCESS )
721       RETURN_ERROR( rv );
722
723   if ( tally_get_data(pamh, &oldtime) != 0 )
724   /* no data found */
725       return PAM_SUCCESS;
726
727   if ( (rv=tally_bump(-1, &oldtime, pamh, uid, user, opts)) != PAM_SUCCESS )
728       return rv;
729   return tally_reset(pamh, uid, opts);
730 }
731
732 #endif  /* #ifdef PAM_SM_ACCOUNT */
733
734 /*-----------------------------------------------------------------------*/
735
736 #ifdef PAM_STATIC
737
738 /* static module data */
739
740 struct pam_module _pam_tally_modstruct = {
741      MODULE_NAME,
742 #ifdef PAM_SM_AUTH
743      pam_sm_authenticate,
744      pam_sm_setcred,
745 #else
746      NULL,
747      NULL,
748 #endif
749 #ifdef PAM_SM_ACCOUNT
750      pam_sm_acct_mgmt,
751 #else
752      NULL,
753 #endif
754      NULL,
755      NULL,
756      NULL,
757 };
758
759 #endif   /* #ifdef PAM_STATIC */
760
761 /*-----------------------------------------------------------------------*/
762
763 #else   /* #ifndef MAIN */
764
765 static const char *cline_filename = DEFAULT_LOGFILE;
766 static tally_t cline_reset = TALLY_HI; /* Default is `interrogate only' */
767 static int cline_quiet =  0;
768
769 /*
770  *  Not going to link with pamlib just for these.. :)
771  */
772
773 static const char *
774 pam_errors( int i )
775 {
776   switch (i) {
777   case PAM_AUTH_ERR:     return _("Authentication error");
778   case PAM_SERVICE_ERR:  return _("Service error");
779   case PAM_USER_UNKNOWN: return _("Unknown user");
780   default:               return _("Unknown error");
781   }
782 }
783
784 static int
785 getopts( char **argv )
786 {
787   const char *pname = *argv;
788   for ( ; *argv ; (void)(*argv && ++argv) ) {
789     if      ( !strcmp (*argv,"--file")    ) cline_filename=*++argv;
790     else if ( !strncmp(*argv,"--file=",7) ) cline_filename=*argv+7;
791     else if ( !strcmp (*argv,"--user")    ) cline_user=*++argv;
792     else if ( !strncmp(*argv,"--user=",7) ) cline_user=*argv+7;
793     else if ( !strcmp (*argv,"--reset")   ) cline_reset=0;
794     else if ( !strncmp(*argv,"--reset=",8)) {
795       if ( sscanf(*argv+8,TALLY_FMT,&cline_reset) != 1 )
796         fprintf(stderr,_("%s: Bad number given to --reset=\n"),pname), exit(0);
797     }
798     else if ( !strcmp (*argv,"--quiet")   ) cline_quiet=1;
799     else {
800       fprintf(stderr,_("%s: Unrecognised option %s\n"),pname,*argv);
801       return FALSE;
802     }
803   }
804   return TRUE;
805 }
806
807 int main ( int argc UNUSED, char **argv )
808 {
809   struct fail_s fs, *fsp = &fs;
810
811   if ( ! getopts( argv+1 ) ) {
812     printf(_("%s: [--file rooted-filename] [--user username] "
813              "[--reset[=n]] [--quiet]\n"),
814            *argv);
815     exit(0);
816   }
817
818   umask(077);
819
820   /*
821    * Major difference between individual user and all users:
822    *  --user just handles one user, just like PAM.
823    *  --user=* handles all users, sniffing cline_filename for nonzeros
824    */
825
826   if ( cline_user ) {
827     uid_t uid;
828     tally_t tally=cline_reset;
829     FILE *TALLY=0;
830     struct tally_options opts;
831     int i;
832
833     memset(&opts, 0, sizeof(opts));
834     opts.ctrl = OPT_AUDIT;
835     i=pam_get_uid(NULL, &uid, NULL, &opts);
836     if ( i != PAM_SUCCESS ) {
837       fprintf(stderr,"%s: %s\n",*argv,pam_errors(i));
838       exit(0);
839     }
840
841     i=get_tally(NULL, &tally, uid, cline_filename, &TALLY, fsp);
842     if ( i != PAM_SUCCESS ) {
843       fprintf(stderr,"%s: %s\n",*argv,pam_errors(i));
844       exit(0);
845     }
846
847     if ( !cline_quiet )
848       printf("User %s\t(%lu)\t%s "TALLY_FMT"\n",cline_user,
849              (unsigned long int) uid,
850              (cline_reset!=TALLY_HI)?"had":"has",tally);
851
852     i=set_tally(NULL, cline_reset, uid, cline_filename, &TALLY, fsp);
853     if ( i != PAM_SUCCESS ) {
854       fprintf(stderr,"%s: %s\n",*argv,pam_errors(i));
855       exit(0);
856     }
857   }
858   else /* !cline_user (ie, operate on all users) */ {
859     FILE *TALLY=fopen(cline_filename, "r");
860     uid_t uid=0;
861     if ( !TALLY ) perror(*argv), exit(0);
862
863     for ( ; !feof(TALLY); uid++ ) {
864       tally_t tally;
865       struct passwd *pw;
866       if ( ! fread((char *) &fsp->fs_faillog,
867                    sizeof (struct faillog), 1, TALLY)
868            || ! fsp->fs_faillog.fail_cnt ) {
869         continue;
870       }
871       tally = fsp->fs_faillog.fail_cnt;
872
873       if ( ( pw=getpwuid(uid) ) ) {
874         printf("User %s\t(%lu)\t%s "TALLY_FMT"\n",pw->pw_name,
875                (unsigned long int) uid,
876                (cline_reset!=TALLY_HI)?"had":"has",tally);
877       }
878       else {
879         printf("User [NONAME]\t(%lu)\t%s "TALLY_FMT"\n",
880                (unsigned long int) uid,
881                (cline_reset!=TALLY_HI)?"had":"has",tally);
882       }
883     }
884     fclose(TALLY);
885     if ( cline_reset!=0 && cline_reset!=TALLY_HI ) {
886       fprintf(stderr,_("%s: Can't reset all users to non-zero\n"),*argv);
887     }
888     else if ( !cline_reset ) {
889       TALLY=fopen(cline_filename, "w");
890       if ( !TALLY ) perror(*argv), exit(0);
891       fclose(TALLY);
892     }
893   }
894   return 0;
895 }
896
897
898 #endif   /* #ifndef MAIN */