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 (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21 /*
22 * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
24 */
25
26 /* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */
27 /* All Rights Reserved */
28
29 /*
30 * Portions of this source code were derived from Berkeley 4.3 BSD
31 * under license from the Regents of the University of California.
32 */
33
34 #pragma ident "%Z%%M% %I% %E% SMI"
35
36 #include <sys/types.h>
37 #include <sys/stat.h>
38 #include <sys/file.h>
39 #include <sys/fcntl.h>
40
41 #include <stdio.h>
42 #include <errno.h>
43 #include <signal.h>
44 #include <stdlib.h>
45 #include <strings.h>
46
47 /*
48 * Password file editor with locking.
49 */
50
51 #define DEFAULT_EDITOR "/usr/bin/vi"
52
53 static int copyfile(char *, char *);
54 static int editfile(char *, char *, char *, time_t *);
55 static int sanity_check(char *, time_t *, char *);
56 static int validsh(char *);
57
58 char *ptemp = "/etc/ptmp";
59 char *stemp = "/etc/stmp";
60 char *passwd = "/etc/passwd";
61 char *shadow = "/etc/shadow";
62 char buf[BUFSIZ];
63
64 int
main(void)65 main(void)
66 {
67 int fd;
68 FILE *ft, *fp;
69 char *editor;
70 int ok = 0;
71 time_t o_mtime, n_mtime;
72 struct stat osbuf, sbuf, oshdbuf, shdbuf;
73 char c;
74
75 (void)signal(SIGINT, SIG_IGN);
76 (void)signal(SIGQUIT, SIG_IGN);
77 (void)signal(SIGHUP, SIG_IGN);
78 setbuf(stderr, (char *)NULL);
79
80 editor = getenv("VISUAL");
81 if (editor == 0)
82 editor = getenv("EDITOR");
83 if (editor == 0)
84 editor = DEFAULT_EDITOR;
85
86 (void)umask(0077);
87 if (stat(passwd, &osbuf) < 0) {
88 (void)fprintf(stderr,"vipw: can't stat passwd file.\n");
89 goto bad;
90 }
91
92 if (copyfile(passwd, ptemp))
93 goto bad;
94
95 if (stat(ptemp, &sbuf) < 0) {
96 (void)fprintf(stderr,
97 "vipw: can't stat ptemp file, %s unchanged\n",
98 passwd);
99 goto bad;
100 }
101
102 o_mtime = sbuf.st_mtime;
103
104 if (editfile(editor, ptemp, passwd, &n_mtime)) {
105 if (sanity_check(ptemp, &n_mtime, passwd))
106 goto bad;
107 if (o_mtime >= n_mtime)
108 goto bad;
109 }
110
111 ok++;
112 if (o_mtime < n_mtime) {
113 fprintf(stdout, "\nYou have modified the password file.\n");
114 fprintf(stdout,
115 "Press 'e' to edit the shadow file for consistency,\n 'q' to quit: ");
116 if ((c = getchar()) == 'q') {
117 if (chmod(ptemp, (osbuf.st_mode & 0644)) < 0) {
118 (void) fprintf(stderr, "vipw: %s: ", ptemp);
119 perror("chmod");
120 goto bad;
121 }
122 if (rename(ptemp, passwd) < 0) {
123 (void) fprintf(stderr, "vipw: %s: ", ptemp);
124 perror("rename");
125 goto bad;
126 }
127 if (((osbuf.st_gid != sbuf.st_gid) ||
128 (osbuf.st_uid != sbuf.st_uid)) &&
129 (chown(passwd, osbuf.st_uid, osbuf.st_gid) < 0)) {
130 (void) fprintf(stderr, "vipw: %s ", ptemp);
131 perror("chown");
132 }
133 goto bad;
134 } else if (c == 'e') {
135 if (stat(shadow, &oshdbuf) < 0) {
136 (void) fprintf(stderr,
137 "vipw: can't stat shadow file.\n");
138 goto bad;
139 }
140
141 if (copyfile(shadow, stemp))
142 goto bad;
143 if (stat(stemp, &shdbuf) < 0) {
144 (void) fprintf(stderr,
145 "vipw: can't stat stmp file.\n");
146 goto bad;
147 }
148
149 if (editfile(editor, stemp, shadow, &o_mtime))
150 goto bad;
151 ok++;
152 if (chmod(ptemp, (osbuf.st_mode & 0644)) < 0) {
153 (void) fprintf(stderr, "vipw: %s: ", ptemp);
154 perror("chmod");
155 goto bad;
156 }
157 if (chmod(stemp, (oshdbuf.st_mode & 0400)) < 0) {
158 (void) fprintf(stderr, "vipw: %s: ", stemp);
159 perror("chmod");
160 goto bad;
161 }
162 if (rename(ptemp, passwd) < 0) {
163 (void) fprintf(stderr, "vipw: %s: ", ptemp);
164 perror("rename");
165 goto bad;
166 }
167 if (((osbuf.st_gid != sbuf.st_gid) ||
168 (osbuf.st_uid != sbuf.st_uid)) &&
169 (chown(passwd, osbuf.st_uid, osbuf.st_gid) < 0)) {
170 (void) fprintf(stderr, "vipw: %s ", ptemp);
171 perror("chown");
172 }
173 if (rename(stemp, shadow) < 0) {
174 (void) fprintf(stderr, "vipw: %s: ", stemp);
175 perror("rename");
176 goto bad;
177 } else if (((oshdbuf.st_gid != shdbuf.st_gid) ||
178 (oshdbuf.st_uid != shdbuf.st_uid)) &&
179 (chown(shadow, oshdbuf.st_uid, oshdbuf.st_gid) < 0)) {
180 (void) fprintf(stderr, "vipw: %s ", stemp);
181 perror("chown");
182 }
183 }
184 }
185 bad:
186 (void) unlink(ptemp);
187 (void) unlink(stemp);
188 return (ok ? 0 : 1);
189 /* NOTREACHED */
190 }
191
192
193 int
copyfile(char * from,char * to)194 copyfile(char *from, char *to)
195 {
196 int fd;
197 FILE *fp, *ft;
198
199 fd = open(to, O_WRONLY|O_CREAT|O_EXCL, 0600);
200 if (fd < 0) {
201 if (errno == EEXIST) {
202 (void) fprintf(stderr, "vipw: %s file busy\n", from);
203 exit(1);
204 }
205 (void) fprintf(stderr, "vipw: "); perror(to);
206 exit(1);
207 }
208 ft = fdopen(fd, "w");
209 if (ft == NULL) {
210 (void) fprintf(stderr, "vipw: "); perror(to);
211 return( 1 );
212 }
213 fp = fopen(from, "r");
214 if (fp == NULL) {
215 (void) fprintf(stderr, "vipw: "); perror(from);
216 return( 1 );
217 }
218 while (fgets(buf, sizeof (buf) - 1, fp) != NULL)
219 fputs(buf, ft);
220 (void) fclose(ft);
221 (void) fclose(fp);
222 return( 0 );
223 }
224
225 int
editfile(char * editor,char * temp,char * orig,time_t * mtime)226 editfile(char *editor, char *temp, char *orig, time_t *mtime)
227 {
228 (void)sprintf(buf, "%s %s", editor, temp);
229 if (system(buf) == 0) {
230 return (sanity_check(temp, mtime, orig));
231 }
232 return(1);
233 }
234
235
236 int
validsh(char * rootsh)237 validsh(char *rootsh)
238 {
239
240 char *sh, *getusershell();
241 int ret = 0;
242
243 setusershell();
244 while((sh = getusershell()) != NULL ) {
245 if( strcmp( rootsh, sh) == 0 ) {
246 ret = 1;
247 break;
248 }
249 }
250 endusershell();
251 return(ret);
252 }
253
254 /*
255 * sanity checks
256 * return 0 if ok, 1 otherwise
257 */
258 int
sanity_check(char * temp,time_t * mtime,char * orig)259 sanity_check(char *temp, time_t *mtime, char *orig)
260 {
261 int i, ok = 0;
262 FILE *ft;
263 struct stat sbuf, statbuf;
264 char *ldir;
265 int isshadow = 0;
266
267 if (!strcmp(orig, shadow))
268 isshadow = 1;
269
270 /* sanity checks */
271 if (stat(temp, &sbuf) < 0) {
272 (void)fprintf(stderr,
273 "vipw: can't stat %s file, %s unchanged\n",
274 temp, orig);
275 return(1);
276 }
277 *mtime = sbuf.st_mtime;
278 if (sbuf.st_size == 0) {
279 (void)fprintf(stderr, "vipw: bad %s file, %s unchanged\n",
280 temp, orig);
281 return(1);
282 }
283 ft = fopen(temp, "r");
284 if (ft == NULL) {
285 (void)fprintf(stderr,
286 "vipw: can't reopen %s file, %s unchanged\n",
287 temp, orig);
288 return(1);
289 }
290
291 while (fgets(buf, sizeof (buf) - 1, ft) != NULL) {
292 char *cp;
293
294 cp = index(buf, '\n');
295 if (cp == 0)
296 continue; /* ??? allow very long lines
297 * and passwd files that do
298 * not end in '\n' ???
299 */
300 *cp = '\0';
301
302 cp = index(buf, ':');
303 if (cp == 0) /* lines without colon
304 * separated fields
305 */
306 continue;
307 *cp = '\0';
308
309 if (strcmp(buf, "root"))
310 continue;
311
312 /* root password */
313 *cp = ':';
314 cp = index(cp + 1, ':');
315 if (cp == 0)
316 goto bad_root;
317
318 /* root uid for password */
319 if (!isshadow)
320 if (atoi(cp + 1) != 0) {
321
322 (void)fprintf(stderr, "root UID != 0:\n%s\n",
323 buf);
324 break;
325 }
326 /* root uid for passwd and sp_lstchg for shadow */
327 cp = index(cp + 1, ':');
328 if (cp == 0)
329 goto bad_root;
330
331 /* root's gid for passwd and sp_min for shadow*/
332 cp = index(cp + 1, ':');
333 if (cp == 0)
334 goto bad_root;
335
336 /* root's gecos for passwd and sp_max for shadow*/
337 cp = index(cp + 1, ':');
338 if (isshadow) {
339 for (i=0; i<3; i++)
340 if ((cp = index(cp + 1, ':')) == 0)
341 goto bad_root;
342 } else {
343 if (cp == 0) {
344 bad_root: (void)fprintf(stderr,
345 "Missing fields in root entry:\n%s\n", buf);
346 break;
347 }
348 }
349 if (!isshadow) {
350 /* root's login directory */
351 ldir = ++cp;
352 cp = index(cp, ':');
353 if (cp == 0)
354 goto bad_root;
355 *cp = '\0';
356 if (stat(ldir, &statbuf) < 0) {
357 *cp = ':';
358 (void) fprintf(stderr,
359 "root login dir doesn't exist:\n%s\n",
360 buf);
361 break;
362 } else if (!S_ISDIR(statbuf.st_mode)) {
363 *cp = ':';
364 (void) fprintf(stderr,
365 "root login dir is not a directory:\n%s\n",
366 buf);
367 break;
368 }
369
370 *cp = ':';
371 /* root's login shell */
372 ++cp;
373 if (*cp && ! validsh(cp)) {
374 (void)fprintf(stderr,
375 "Invalid root shell:\n%s\n", buf);
376 break;
377 }
378 }
379
380 ok++;
381 }
382 (void)fclose(ft);
383 if (ok)
384 return(0);
385 else {
386 (void)fprintf(stderr,
387 "vipw: you mangled the %s file, %s unchanged\n",
388 temp, orig);
389 return(1);
390 }
391 }
392