xref: /illumos-gate/usr/src/cmd/acct/wtmpfix.c (revision 141040e8a310da49386b596573e5dde5580572ec)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 
23 /*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
24 /*	  All Rights Reserved  	*/
25 
26 /*
27  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
28  * Use is subject to license terms.
29  */
30 
31 #pragma ident	"%Z%%M%	%I%	%E% SMI"
32 /*
33  * wtmpfix - adjust wtmpx file and remove date changes.
34  *	wtmpfix <wtmpx1 >wtmpx2
35  *
36  *	code are added to really fix wtmpx if it is corrupted ..
37  */
38 
39 #include <stdio.h>
40 #include <sys/types.h>
41 #include <sys/param.h>
42 #include "acctdef.h"
43 #include <utmpx.h>
44 #include <signal.h>
45 #include <time.h>
46 #include <ctype.h>
47 #include <locale.h>
48 #include <limits.h>
49 #include <stdlib.h>
50 
51 #define	MAXRUNTIME	3600	/* time out after 1 hour */
52 #define	DAYEPOCH	(60 * 60 * 24)
53 #define	wout(f, w)	 fwrite(w, sizeof (struct utmpx), 1, f);
54 
55 FILE	*Wtmpx, *Opw;
56 FILE	*fp;
57 char	Ofile[]	= "/tmp/wXXXXXX";
58 static char time_buf[50];
59 
60 struct	dtab
61 {
62 	off_t	d_off1;		/* file offset start */
63 	off_t	d_off2;		/* file offset stop */
64 	time_t	d_adj;		/* time adjustment */
65 	struct dtab *d_ndp;	/* next record */
66 };
67 
68 struct	dtab	*Fdp;		/* list header */
69 struct	dtab	*Ldp;		/* list trailer */
70 
71 time_t 	lastmonth, nextmonth;
72 off_t	recno;
73 
74 struct	utmpx	Ut, Ut2;
75 
76 int	year, month;
77 int 	ch;
78 int	n;
79 int	multimode;		/* multi user mode	 WHCC */
80 
81 static int winp(FILE *, struct utmpx *);
82 static void mkdtab(off_t);
83 static void setdtab(off_t, struct utmpx *, struct utmpx *);
84 static void adjust(off_t, struct utmpx *);
85 static int invalid(char *);
86 static void intr(int) __NORETURN;
87 static void scanfile(void);
88 static int inrange(void);
89 static void wabort(int);
90 
91 int
92 main(int argc, char **argv)
93 {
94 	time_t tloc;
95 	struct tm *tmp;
96 	int fd;
97 
98 	(void) setlocale(LC_ALL, "");
99 	setbuf(stdout, NULL);
100 	alarm(MAXRUNTIME);
101 
102 	if (signal(SIGALRM, wabort) == SIG_ERR) {
103 		perror("signal");
104 		return (1);
105 	}
106 	if (signal(SIGINT, intr) == SIG_ERR) {
107 		perror("signal");
108 		return (1);
109 	}
110 
111 	time(&tloc);
112 	tmp = localtime(&tloc);
113 	year = tmp->tm_year;
114 	month = tmp->tm_mon + 1;
115 	lastmonth = ((year + 1900 - 1970) * 365 +
116 	    (month - 1) * 30) * DAYEPOCH;
117 	nextmonth = ((year + 1900 - 1970) * 365 +
118 	    (month + 1) * 30) * DAYEPOCH;
119 
120 	if (argc < 2) {
121 		argv[argc] = "-";
122 		argc++;
123 	}
124 
125 	if ((fd = mkstemp(Ofile)) == -1) {
126 		fprintf(stderr, "cannot make temporary: %s\n", Ofile);
127 		intr(0);
128 	}
129 
130 	if ((Opw = fdopen(fd, "w")) == NULL) {
131 		fprintf(stderr, "cannot open temporary: %s\n", Ofile);
132 		intr(0);
133 	}
134 
135 	while (--argc > 0) {
136 		argv++;
137 		if (strcmp(*argv, "-") == 0)
138 			Wtmpx = stdin;
139 		else if ((Wtmpx = fopen(*argv, "r")) == NULL) {
140 			fprintf(stderr, "Cannot open: %s\n", *argv);
141 			intr(0);
142 		}
143 		scanfile();
144 
145 		if (Wtmpx != stdin)
146 			fclose(Wtmpx);
147 	}
148 	fclose(Opw);
149 
150 	if ((Opw = fopen(Ofile, "r")) == NULL) {
151 		fprintf(stderr, "Cannot read from temp: %s\n", Ofile);
152 		intr(0);
153 	}
154 	recno = 0;
155 	while (winp(Opw, &Ut)) {
156 		adjust(recno, &Ut);
157 		recno += sizeof (struct utmpx);
158 		wout(stdout, &Ut);
159 	}
160 	fclose(Opw);
161 	unlink(Ofile);
162 	return (0);
163 }
164 
165 static int
166 winp(FILE *f, struct utmpx *w)
167 {
168 	if (fread(w, sizeof (struct utmpx), 1, f) != 1)
169 		return (0);
170 	if ((w->ut_type >= EMPTY) && (w->ut_type <= UTMAXTYPE))
171 		return (1);
172 	else {
173 		fprintf(stderr, "Bad file at offset %ld\n",
174 			ftell(f) - sizeof (struct utmpx));
175 		cftime(time_buf, DATE_FMT, &w->ut_xtime);
176 		fprintf(stderr, "%-12s %-8s %lu %s",
177 			w->ut_line, w->ut_user, w->ut_xtime, time_buf);
178 		intr(0);
179 	}
180 	/* NOTREACHED */
181 }
182 
183 static void
184 mkdtab(off_t p)
185 {
186 
187 	struct dtab *dp;
188 
189 	dp = Ldp;
190 	if (dp == NULL) {
191 		dp = calloc(sizeof (struct dtab), 1);
192 		if (dp == NULL) {
193 			fprintf(stderr, "out of core\n");
194 			intr(0);
195 		}
196 		Fdp = Ldp = dp;
197 	}
198 	dp->d_off1 = p;
199 }
200 
201 static void
202 setdtab(off_t p, struct utmpx *w1, struct utmpx *w2)
203 {
204 	struct dtab *dp;
205 
206 	if ((dp = Ldp) == NULL) {
207 		fprintf(stderr, "no dtab\n");
208 		intr(0);
209 	}
210 	dp->d_off2 = p;
211 	dp->d_adj = w2->ut_xtime - w1->ut_xtime;
212 	if ((Ldp = calloc(sizeof (struct dtab), 1)) == NULL) {
213 		fprintf(stderr, "out of core\n");
214 		intr(0);
215 	}
216 	Ldp->d_off1 = dp->d_off1;
217 	dp->d_ndp = Ldp;
218 }
219 
220 static void
221 adjust(off_t p, struct utmpx *w)
222 {
223 
224 	off_t pp;
225 	struct dtab *dp;
226 
227 	pp = p;
228 
229 	for (dp = Fdp; dp != NULL; dp = dp->d_ndp) {
230 		if (dp->d_adj == 0)
231 			continue;
232 		if (pp >= dp->d_off1 && pp < dp->d_off2)
233 			w->ut_xtime += dp->d_adj;
234 	}
235 }
236 
237 /*
238  *	invalid() determines whether the name field adheres to
239  *	the criteria set forth in acctcon1.  If the name violates
240  *	conventions, it returns a truth value meaning the name is
241  *	invalid; if the name is okay, it returns false indicating
242  *	the name is not invalid.
243  */
244 
245 static int
246 invalid(char *name)
247 {
248 	int	i;
249 
250 	for (i = 0; i < NSZ; i++) {
251 		if (name[i] == '\0')
252 			return (VALID);
253 		if (! (isalnum(name[i]) || (name[i] == '$') ||
254 		    (name[i] == ' ') || (name[i] == '.') ||
255 		    (name[i] == '_') || (name[i] == '-'))) {
256 			return (INVALID);
257 		}
258 	}
259 	return (VALID);
260 }
261 
262 static void
263 intr(int sig)
264 {
265 	signal(SIGINT, SIG_IGN);
266 	unlink(Ofile);
267 	exit(1);
268 }
269 
270 /*
271  * scanfile:
272  * 1)  	reads the file, to see if the record is within reasonable
273  * 	range; if not, then it will scan the file, delete foreign stuff.
274  * 2)   enter setdtab if in multiuser mode
275  * 3)   change bad login names to INVALID
276  */
277 
278 static void
279 scanfile()
280 {
281 	while ((n = fread(&Ut, sizeof (Ut), 1, Wtmpx)) > 0) {
282 		if (n == 0) {
283 			unlink(Ofile);
284 			exit(0);
285 		}
286 		if (!inrange()) {
287 			for (;;) {
288 				if (fseek(Wtmpx,
289 				    -(off_t)sizeof (Ut), 1) != 0) {
290 					perror("seek error\n");
291 					exit(1);
292 				}
293 				if ((ch = getc(Wtmpx)) == EOF) {
294 					perror("read\n");
295 					exit(1);
296 				}
297 				fprintf(stderr, "checking offset %lo\n",
298 				    ftell(Wtmpx));
299 				if (fread(&Ut, sizeof (Ut), 1, Wtmpx) == 0) {
300 					exit(1);
301 				}
302 				if (inrange())
303 					break;
304 			}
305 		}
306 		/* Now we have a good utmpx record, do more processing */
307 
308 #define	UTYPE	Ut.ut_type
309 #define	ULINE	Ut.ut_line
310 
311 			if (recno == 0 || UTYPE == BOOT_TIME)
312 				mkdtab(recno);
313 			if (UTYPE == RUN_LVL) {
314 				if (strncmp(ULINE, "run-level S", 11) == 0)
315 					multimode = 0;
316 				if (strncmp(ULINE, "run-level 2", 11) == 0)
317 					multimode++;
318 			}
319 			if (invalid(Ut.ut_name)) {
320 				fprintf(stderr,
321 				    "wtmpfix: logname \"%*.*s\" changed "
322 				    "to \"INVALID\"\n", OUTPUT_NSZ,
323 				    OUTPUT_NSZ, Ut.ut_name);
324 				(void) strncpy(Ut.ut_name, "INVALID", NSZ);
325 			}
326 			if (UTYPE == OLD_TIME) {
327 				if (!winp(Wtmpx, &Ut2)) {
328 					fprintf(stderr, "Input truncated at "
329 					    "offset %ld\n", recno);
330 					intr(0);
331 				}
332 				if (Ut2.ut_type != NEW_TIME) {
333 					fprintf(stderr, "New date expected at "
334 					    "offset %ld", recno);
335 					intr(0);
336 				}
337 				if (multimode)  /* multiuser */
338 					setdtab(recno, &Ut, &Ut2);
339 				recno += (2 * sizeof (struct utmpx));
340 				wout(Opw, &Ut);
341 				wout(Opw, &Ut2);
342 				continue;
343 			}
344 			wout(Opw, &Ut);
345 			recno += sizeof (struct utmpx);
346 	}
347 }
348 
349 static int
350 inrange()
351 {
352 	if ((strcmp(Ut.ut_line, RUNLVL_MSG) == 0) ||
353 	    (strcmp(Ut.ut_line, BOOT_MSG) == 0) ||
354 	    (strcmp(Ut.ut_line, "acctg on") == 0) ||
355 	    (strcmp(Ut.ut_line, OTIME_MSG) == 0) ||
356 	    (strcmp(Ut.ut_line, NTIME_MSG) == 0))
357 			return (1);
358 
359 	if (Ut.ut_id != 0 &&
360 		Ut.ut_xtime > 0 &&
361 		Ut.ut_xtime > lastmonth &&
362 		Ut.ut_xtime < nextmonth &&
363 		Ut.ut_type >= EMPTY &&
364 		Ut.ut_type <= UTMAXTYPE &&
365 		Ut.ut_pid >= 0)
366 		return (1);
367 
368 	return (0);
369 }
370 
371 static void
372 wabort(int sig)
373 {
374 	fprintf(stderr, "give up\n");
375 	exit(1);
376 }
377