1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
|
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
/* All Rights Reserved */
/*
* Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/*
* wtmpfix - adjust wtmpx file and remove date changes.
* wtmpfix <wtmpx1 >wtmpx2
*
* Can recover to some extent from wtmpx corruption.
*/
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/param.h>
#include "acctdef.h"
#include <utmpx.h>
#include <time.h>
#include <ctype.h>
#include <locale.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#define DAYEPOCH (60 * 60 * 24)
#define UTRSZ (sizeof (struct futmpx)) /* file record size */
/*
* The acctsh(8) shell scripts startup(8) and shutacct(8) as well as the
* runacct script each pass their own specific reason strings in the first
* argument to acctwtmp(8), to be propagated into ut_line fields. Additional
* reasons (RUNLVL_MSG, ..., DOWN_MSG), used by compiled code, are defined in
* <utmp.h> as preprocessor constants.
* For simplicity we predefine similar constants for the scripted strings
* here, as no other compiled code uses those.
* Moreover, we need a variant of RUNLVL_MSG without the "%c" at the end.
* We shall use the fact that ut_line[RLVLMSG_LEN] will extract the char
* in the %c position ('S', '2', ...).
* Since all of these string constants are '\0' terminated, they can safely
* be used with strcmp() even when ut_line is not.
*/
#define RUN_LEVEL_MSG "run-level "
#define ACCTG_ON_MSG "acctg on"
#define ACCTG_OFF_MSG "acctg off"
#define RUNACCT_MSG "runacct"
#define RLVLMSG_LEN (sizeof (RUN_LEVEL_MSG) - 1)
/*
* Records encountered are classified as one of the following: corrupted;
* ok but devoid of interest to acctcon downstream; ok and interesting;
* or ok and even redundant enough to latch onto a new alignment whilst
* recovering from a corruption.
* The ordering among these four symbolic values is significant.
*/
typedef enum {
INRANGE_ERR = -1,
INRANGE_DROP,
INRANGE_PASS,
INRANGE_ALIGNED
} inrange_t;
/* input filenames and record numbers, for diagnostics only */
#define STDIN_NAME "<stdin>"
static char *cur_input_name;
static off_t recin;
static FILE *Wtmpx, *Temp;
struct dtab
{
off_t d_off1; /* file offset start */
off_t d_off2; /* file offset stop */
time_t d_adj; /* time adjustment */
struct dtab *d_ndp; /* next record */
};
static struct dtab *Fdp; /* list header */
static struct dtab *Ldp; /* list trailer */
static time_t lastmonth, nextmonth;
static struct futmpx Ut, Ut2;
static int winp(FILE *, struct futmpx *);
static void mkdtab(off_t);
static void setdtab(off_t, struct futmpx *, struct futmpx *);
static void adjust(off_t, struct futmpx *);
static int invalid(char *);
static void scanfile(void);
static inrange_t inrange(void);
static void wcomplain(char *);
int
main(int argc, char **argv)
{
time_t tloc;
struct tm *tmp;
int year;
int month;
off_t rectmpin;
(void) setlocale(LC_ALL, "");
setbuf(stdout, NULL);
(void) time(&tloc);
tmp = localtime(&tloc);
year = tmp->tm_year;
month = tmp->tm_mon + 1;
lastmonth = ((year + 1900 - 1970) * 365 +
(month - 1) * 30) * DAYEPOCH;
nextmonth = ((year + 1900 - 1970) * 365 +
(month + 1) * 30) * DAYEPOCH;
if (argc < 2) {
argv[argc] = "-";
argc++;
}
/*
* Almost all system call failures in this program are unrecoverable
* and therefore fatal. Typical causes might be lack of memory or
* of space in a filesystem. If necessary, the system administrator
* can invoke /usr/lib/acct/runacct interactively after making room
* to complete the remaining phases of last night's accounting.
*/
if ((Temp = tmpfile()) == NULL) {
perror("Cannot create temporary file");
return (EXIT_FAILURE);
}
while (--argc > 0) {
argv++;
if (strcmp(*argv, "-") == 0) {
Wtmpx = stdin;
cur_input_name = STDIN_NAME;
} else if ((Wtmpx = fopen(*argv, "r")) == NULL) {
(void) fprintf(stderr, "Cannot open %s: %s\n",
*argv, strerror(errno));
return (EXIT_FAILURE);
} else {
cur_input_name = *argv;
}
/*
* Filter records reading from current input stream Wtmpx,
* writing to Temp.
*/
scanfile();
if (Wtmpx != stdin)
(void) fclose(Wtmpx);
}
/* flush and rewind Temp for readback */
if (fflush(Temp) != 0) {
perror("<temporary file>: fflush");
return (EXIT_FAILURE);
}
if (fseeko(Temp, (off_t)0L, SEEK_SET) != 0) {
perror("<temporary file>: seek");
return (EXIT_FAILURE);
}
/* second pass: apply time adjustments */
rectmpin = 0;
while (winp(Temp, &Ut)) {
adjust(rectmpin, &Ut);
rectmpin += UTRSZ;
if (fwrite(&Ut, UTRSZ, 1, stdout) < 1) {
perror("<stdout>: fwrite");
return (EXIT_FAILURE);
}
}
(void) fclose(Temp);
/*
* Detect if we've run out of space (say) and exit unsuccessfully
* so that downstream accounting utilities won't start processing an
* incomplete tmpwtmp file.
*/
if (fflush(stdout) != 0) {
perror("<stdout>: fflush");
return (EXIT_FAILURE);
}
return (EXIT_SUCCESS);
}
static int
winp(FILE *f, struct futmpx *w)
{
if (fread(w, (size_t)UTRSZ, (size_t)1, f) != 1)
return (0);
if ((w->ut_type >= EMPTY) && (w->ut_type <= UTMAXTYPE))
return (1);
else {
(void) fprintf(stderr, "Bad temp file at offset %lld\n",
(longlong_t)(ftell(f) - UTRSZ));
/*
* If input was corrupt, neither ut_line nor ut_user can be
* relied on to be \0-terminated. Even fixing the precision
* does not entirely guard against this.
*/
(void) fprintf(stderr,
"ut_line \"%-12.12s\" ut_user \"%-8.8s\" ut_xtime %ld\n",
w->ut_line, w->ut_user, (long)w->ut_xtime);
exit(EXIT_FAILURE);
}
/* NOTREACHED */
}
static void
mkdtab(off_t p)
{
struct dtab *dp;
dp = Ldp;
if (dp == NULL) {
dp = calloc(sizeof (struct dtab), 1);
if (dp == NULL) {
(void) fprintf(stderr, "out of memory\n");
exit(EXIT_FAILURE);
}
Fdp = Ldp = dp;
}
dp->d_off1 = p;
}
static void
setdtab(off_t p, struct futmpx *w1, struct futmpx *w2)
{
struct dtab *dp;
if ((dp = Ldp) == NULL) {
(void) fprintf(stderr, "no dtab\n");
exit(EXIT_FAILURE);
}
dp->d_off2 = p;
dp->d_adj = w2->ut_xtime - w1->ut_xtime;
if ((Ldp = calloc(sizeof (struct dtab), 1)) == NULL) {
(void) fprintf(stderr, "out of memory\n");
exit(EXIT_FAILURE);
}
Ldp->d_off1 = dp->d_off1;
dp->d_ndp = Ldp;
}
static void
adjust(off_t p, struct futmpx *w)
{
off_t pp;
struct dtab *dp;
pp = p;
for (dp = Fdp; dp != NULL; dp = dp->d_ndp) {
if (dp->d_adj == 0)
continue;
if (pp >= dp->d_off1 && pp <= dp->d_off2)
w->ut_xtime += dp->d_adj;
}
}
/*
* invalid() determines whether the name field adheres to the criteria
* set forth in acctcon1. If returns VALID if the name is ok, or
* INVALID if the name violates conventions.
*/
static int
invalid(char *name)
{
int i;
for (i = 0; i < NSZ; i++) {
if (name[i] == '\0')
return (VALID);
if (! (isalnum(name[i]) || (name[i] == '$') ||
(name[i] == ' ') || (name[i] == '.') ||
(name[i] == '_') || (name[i] == '-'))) {
return (INVALID);
}
}
return (VALID);
}
/*
* scanfile:
* 1) reads the current input file
* 2) filters for process records in time range of interest and for
* other types of records deemed interesting to acctcon downstream
* 3) picks up time changes with setdtab() if in multiuser mode, which
* will be applied when the temp file is read back
* 4) changes bad login names to INVALID
* 5) recovers from common cases of wtmpx corruption (loss of record
* alignment).
* All of the static globals are used directly or indirectly.
*
* When wtmpfix is asked to process several input files in succession,
* some state needs to be preserved from one scanfile() invocation to the
* next. Aside from the temp file position, we remember whether we were
* in multi-user mode or not. Absent evidence to the contrary, we begin
* processing assuming multi-user mode, because runacct's wtmpx rotation
* normally gives us a file recently initialized by utmp2wtmp(8) with no
* older RUN_LVL records surviving.
*/
static void
scanfile()
{
struct stat Wtstat;
off_t residue = 0; /* input file size mod UTRSZ */
/*
* lastok will be the offset of the beginning of the most recent
* manifestly plausible and interesting input record in the current
* input file, if any.
* An invariant at loop entry is -UTRSZ <= lastok <= recin - UTRSZ.
*/
off_t lastok = -(off_t)UTRSZ;
static off_t rectmp; /* current temp file position */
static boolean_t multimode = B_TRUE; /* multi-user RUN_LVL in force */
inrange_t is_ok; /* caches inrange() result */
/*
* During normal operation, records are of interest and copied to
* the output when is_ok >= INRANGE_PASS, ignored and dropped when
* is_ok == INRANGE_DROP, and evidence of corruption otherwise.
* While we are trying to recover from a corruption and hunting for
* records with sufficient redundancy to confirm that we have reached
* proper alignment again, we'll want is_ok >= INRANGE_ALIGNED.
* The value of want_ok is the minimum inrange() result of current
* interest. It is raised to INRANGE_ALIGNED during ongoing recovery
* and dropped back to INRANGE_PASS when we have recovered alignment.
*/
inrange_t want_ok = INRANGE_PASS;
boolean_t recovered = B_FALSE; /* true after a successful recovery */
int n;
if (fstat(fileno(Wtmpx), &Wtstat) == -1) {
(void) fprintf(stderr,
"Cannot stat %s (will read sequentially): %s\n",
cur_input_name, strerror(errno));
} else if ((Wtstat.st_mode & S_IFMT) == S_IFREG) {
residue = Wtstat.st_size % UTRSZ;
}
/* if residue != 0, part of the file may be misaligned */
for (recin = 0;
((n = fread(&Ut, (size_t)UTRSZ, (size_t)1, Wtmpx)) > 0) ||
(residue > 0);
recin += UTRSZ) {
if (n == 0) {
/*
* Implying residue > 0 and want_ok == INRANGE_PASS.
* It isn't worth telling an I/O error from EOF here.
* But one case is worth catching to avoid issuing a
* confusing message below. When the previous record
* had been ok, we just drop the current truncated
* record and bail out of the loop -- no seeking back.
*/
if (lastok == recin - UTRSZ) {
wcomplain("file ends in mid-record, "
"final partial record dropped");
break;
} else {
wcomplain("file ends in mid-record");
/* handled below like a corrupted record */
is_ok = INRANGE_ERR;
}
} else
is_ok = inrange();
/* alignment recovery logic */
if ((residue > 0) && (is_ok == INRANGE_ERR)) {
/*
* "Let's go back to the last place where we knew
* where we were..."
* In fact, if the last record had been fine and we
* know there's at least one whole record ahead, we
* might move forward here (by residue bytes, less
* than one record's worth). In any case, we align
* ourselves to an integral number of records before
* the end of the file.
*/
wcomplain("suspecting misaligned records, "
"repositioning");
recin = lastok + UTRSZ + residue;
residue = 0;
if (fseeko(Wtmpx, recin, SEEK_SET) != 0) {
(void) fprintf(stderr, "%s: seek: %s\n",
cur_input_name, strerror(errno));
exit(EXIT_FAILURE);
}
wcomplain("starting re-scan");
/*
* While want_ok is elevated, only unequivocal records
* with inrange() == INRANGE_ALIGNED will be admitted
* to latch onto the tentative new alignment.
*/
want_ok = INRANGE_ALIGNED;
/*
* Compensate for the loop continuation. Doing
* it this way gets the correct offset reported
* in the re-scan message above.
*/
recin -= UTRSZ;
continue;
}
/* assert: residue == 0 or is_ok >= INRANGE_DROP here */
if (is_ok < want_ok)
/* record of no further interest */
continue;
if (want_ok == INRANGE_ALIGNED) {
wcomplain("now recognizing aligned records again");
want_ok = INRANGE_PASS;
recovered = B_TRUE;
}
/*
* lastok must track recin whenever the current record is
* being processed and written out to our temp file, to avoid
* reprocessing any bits already done when we readjust our
* alignment.
*/
lastok = recin;
/* now we have a good wtmpx record, do more processing */
if (rectmp == 0 || Ut.ut_type == BOOT_TIME)
mkdtab(rectmp);
if (Ut.ut_type == RUN_LVL) {
/* inrange() already checked the "run-level " part */
if (Ut.ut_line[RLVLMSG_LEN] == 'S')
multimode = B_FALSE;
else if ((Ut.ut_line[RLVLMSG_LEN] == '2') ||
(Ut.ut_line[RLVLMSG_LEN] == '3') ||
(Ut.ut_line[RLVLMSG_LEN] == '4'))
multimode = B_TRUE;
}
if (invalid(Ut.ut_name) == INVALID) {
(void) fprintf(stderr,
"wtmpfix: logname \"%*.*s\" changed "
"to \"INVALID\"\n", OUTPUT_NSZ,
OUTPUT_NSZ, Ut.ut_name);
(void) strncpy(Ut.ut_name, "INVALID", NSZ);
}
/*
* Special case: OLD_TIME should be immediately followed by
* NEW_TIME.
* We make no attempt at alignment recovery between these
* two: if there's junk at this point in the input, then
* a NEW_TIME seen after the junk probably won't be the one
* we are looking for.
*/
if (Ut.ut_type == OLD_TIME) {
/*
* Make recin refer to the expected NEW_TIME.
* Loop continuation will increment it again
* for the record we're about to read now.
*/
recin += UTRSZ;
if (!fread(&Ut2, (size_t)UTRSZ, (size_t)1, Wtmpx)) {
wcomplain("input truncated after OLD_TIME - "
"giving up");
exit(EXIT_FAILURE);
}
/*
* Rudimentary NEW_TIME sanity check. Not as thorough
* as in inrange(), but then we have redundancy from
* context here, since we're just after a plausible
* OLD_TIME record.
*/
if ((Ut2.ut_type != NEW_TIME) ||
(strcmp(Ut2.ut_line, NTIME_MSG) != 0)) {
wcomplain("NEW_TIME expected but missing "
"after OLD_TIME - giving up");
exit(EXIT_FAILURE);
}
lastok = recin;
if (multimode == B_TRUE)
setdtab(rectmp, &Ut, &Ut2);
rectmp += 2 * UTRSZ;
if ((fwrite(&Ut, UTRSZ, 1, Temp) < 1) ||
(fwrite(&Ut2, UTRSZ, 1, Temp) < 1)) {
perror("<temporary file>: fwrite");
exit(EXIT_FAILURE);
}
continue;
}
if (fwrite(&Ut, UTRSZ, 1, Temp) < 1) {
perror("<temporary file>: fwrite");
exit(EXIT_FAILURE);
}
rectmp += UTRSZ;
}
if (want_ok == INRANGE_ALIGNED) {
wcomplain("EOF reached without recognizing another aligned "
"record with certainty. This file may need to be "
"repaired by hand.\n");
} else if (recovered == B_TRUE) {
/*
* There may have been a number of wcomplain() messages
* since we reported about the re-scan, so it bears repeating
* at the end that not all was well.
*/
wcomplain("EOF reached after recovering from corruption "
"in the middle of the file. This file may need to be "
"repaired by hand.\n");
}
}
/*
* inrange: inspect what we hope to be one wtmpx record.
* Globals: Ut, lastmonth, nextmonth; recin, cur_input_name (diagnostics)
* Return values:
* INRANGE_ERR -- an inconsistency was detected, input file corrupted
* INRANGE_DROP -- Ut appears consistent but isn't of interest
* (of process type and outside the time range we want)
* INRANGE_PASS -- Ut appears consistent and this record is of interest
* INRANGE_ALIGNED -- same, and it is also redundant enough to be sure
* that we're correctly aligned on record boundaries
*/
#define UNEXPECTED_UT_PID \
(Ut.ut_pid != 0) || \
(Ut.ut_exit.e_termination != 0) || \
(Ut.ut_exit.e_exit != 0)
static inrange_t
inrange()
{
/* pid_t is signed so that fork() can return -1. Exploit this. */
if (Ut.ut_pid < 0) {
wcomplain("negative pid");
return (INRANGE_ERR);
}
/* the legal values for ut_type are enumerated in <utmp.h> */
switch (Ut.ut_type) {
case EMPTY:
if (UNEXPECTED_UT_PID) {
wcomplain("nonzero pid or status in EMPTY record");
return (INRANGE_ERR);
}
/*
* We'd like to have Ut.ut_user[0] == '\0' here, but sadly
* this isn't always so, so we can't rely on it.
*/
return (INRANGE_DROP);
case RUN_LVL:
/* ut_line must have come from the RUNLVL_MSG pattern */
if (strncmp(Ut.ut_line, RUN_LEVEL_MSG, RLVLMSG_LEN) != 0) {
wcomplain("RUN_LVL record doesn't say `"
RUN_LEVEL_MSG "'");
return (INRANGE_ERR);
}
/*
* The ut_pid, termination, and exit status fields have
* special meaning in this case, and none of them is
* suitable for checking. And we won't insist on ut_user
* to always be an empty string.
*/
return (INRANGE_ALIGNED);
case BOOT_TIME:
if (UNEXPECTED_UT_PID) {
wcomplain("nonzero pid or status in BOOT_TIME record");
return (INRANGE_ERR);
}
if (strcmp(Ut.ut_line, BOOT_MSG) != 0) {
wcomplain("BOOT_TIME record doesn't say `"
BOOT_MSG "'");
return (INRANGE_ERR);
}
return (INRANGE_ALIGNED);
case OLD_TIME:
if (UNEXPECTED_UT_PID) {
wcomplain("nonzero pid or status in OLD_TIME record");
return (INRANGE_ERR);
}
if (strcmp(Ut.ut_line, OTIME_MSG) != 0) {
wcomplain("OLD_TIME record doesn't say `"
OTIME_MSG "'");
return (INRANGE_ERR);
}
return (INRANGE_ALIGNED);
case NEW_TIME:
/*
* We don't actually expect to see any here. If they follow
* an OLD_TIME record as they should, they'll be handled on
* the fly in scanfile(). But we might still run into one
* if the input is somehow corrupted.
*/
if (UNEXPECTED_UT_PID) {
wcomplain("nonzero pid or status in NEW_TIME record");
return (INRANGE_ERR);
}
if (strcmp(Ut.ut_line, NTIME_MSG) != 0) {
wcomplain("NEW_TIME record doesn't say `"
NTIME_MSG "'");
return (INRANGE_ERR);
}
return (INRANGE_ALIGNED);
/* the four *_PROCESS ut_types have a lot in common */
case USER_PROCESS:
/*
* Catch two special cases first: psradm records have no id
* and no pid, while root login over FTP may not have a
* valid ut_user and may have garbage in ut_id[3].
*/
if ((strcmp(Ut.ut_user, "psradm") == 0) &&
(Ut.ut_id[0] == '\0') &&
(Ut.ut_pid > 0)) {
if ((Ut.ut_xtime > lastmonth) &&
(Ut.ut_xtime < nextmonth)) {
return (INRANGE_ALIGNED);
} else {
return (INRANGE_DROP);
}
}
if ((Ut.ut_user[0] == '\0') &&
(strncmp(Ut.ut_id, "ftp", 3) == 0) &&
(strncmp(Ut.ut_line, "ftp", 3) == 0)) {
if ((Ut.ut_xtime > lastmonth) &&
(Ut.ut_xtime < nextmonth)) {
return (INRANGE_ALIGNED);
} else {
return (INRANGE_DROP);
}
}
/* FALLTHROUGH */
case LOGIN_PROCESS:
if (Ut.ut_user[0] == '\0') {
wcomplain("missing username in process record");
return (INRANGE_ERR);
}
/* FALLTHROUGH */
case INIT_PROCESS:
/*
* INIT_PROCESS and DEAD_PROCESS records can come with an
* empty ut_user in degenerate cases (e.g. syntax errors
* like a comment-only process field in /etc/inittab).
* But in an INIT_PROCESS, LOGIN_PROCESS, or USER_PROCESS
* record, we expect a respectable ut_pid.
*/
if (Ut.ut_pid == 0) {
wcomplain("null pid in process record");
return (INRANGE_ERR);
}
/* FALLTHROUGH */
case DEAD_PROCESS:
/*
* DEAD_PROCESS records with a null ut_pid can be produced
* by gnome-terminal (normally seen in utmpx only, but they
* can leak into wtmpx in rare circumstances).
* Unfortunately, ut_id can't be relied on to contain
* anything in particular. (E.g., sshd might leave it
* 0-initialized.) This leaves almost no verifiable
* redundancy here beyond the ut_type.
* At least we insist on a reasonable timestamp.
*/
if (Ut.ut_xtime <= 0) {
wcomplain("non-positive time in process record");
return (INRANGE_ERR);
}
if ((Ut.ut_xtime > lastmonth) &&
(Ut.ut_xtime < nextmonth)) {
return (INRANGE_PASS);
} else {
return (INRANGE_DROP);
}
case ACCOUNTING:
/*
* If we recognize one of the three reason strings passed
* by the /usr/lib/acct shell scripts to acctwtmp, we
* exploit the available redundancy they offer. But
* acctwtmp could have been invoked by custom scripts or
* interactively with other reason strings in the first
* argument, so anything we don't recognize does not
* constitute evidence for corruption.
*/
if ((strcmp(Ut.ut_line, RUNACCT_MSG) != 0) &&
(strcmp(Ut.ut_line, ACCTG_ON_MSG) != 0) &&
(strcmp(Ut.ut_line, ACCTG_OFF_MSG) != 0)) {
return (INRANGE_DROP);
}
return (INRANGE_ALIGNED);
case DOWN_TIME:
if (UNEXPECTED_UT_PID) {
wcomplain("nonzero pid or status in DOWN_TIME record");
return (INRANGE_ERR);
}
if (strcmp(Ut.ut_line, DOWN_MSG) != 0) {
wcomplain("DOWN_TIME record doesn't say `"
DOWN_MSG "'");
return (INRANGE_ERR);
}
return (INRANGE_ALIGNED);
default:
wcomplain("ut_type out of range");
return (INRANGE_ERR);
}
/* NOTREACHED */
}
static void
wcomplain(char *msg)
{
(void) fprintf(stderr, "%s: offset %lld: %s\n", cur_input_name,
(longlong_t)recin, msg);
}
|