xref: /illumos-gate/usr/src/cmd/ypcmd/yppasswd/changepasswd.c (revision 7ab4e62e3b5c454f248a38bec0d489e8f5543324)
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 2010 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 /*
27  * Beware those who enter here.
28  * The logic may appear hairy, but it's been ARCed.
29  * See /shared/sac/PSARC/1995/122/mail
30  */
31 
32 #include <sys/types.h>
33 #include <sys/stat.h>
34 #include <ctype.h>
35 #include <unistd.h>
36 #include <stdlib.h>
37 #include <string.h>
38 #include <stdio.h>
39 #include <errno.h>
40 #include <syslog.h>
41 #include <pwd.h>
42 #include <shadow.h>
43 #include <signal.h>
44 #include <crypt.h>
45 #include <rpc/rpc.h>
46 #include <rpcsvc/yppasswd.h>
47 #include <utmpx.h>
48 #include <nss_dbdefs.h>
49 
50 #define	STRSIZE 100
51 #define	FINGERSIZE (4 * STRSIZE - 4)
52 #define	SHELLSIZE (STRSIZE - 2)
53 #define	UTUSERLEN (sizeof (((struct utmpx *)0)->ut_user))
54 
55 /* Prototypes */
56 extern bool_t validloginshell(char *sh, char *arg, int);
57 extern int    validstr(char *str, size_t size);
58 extern int yplckpwdf();
59 extern int ypulckpwdf();
60 
61 static char *
62 cryptoldpasswd(char *oldpass, char *salt, char *acctname)
63 {
64 	char *oldpass_crypt = NULL;
65 
66 	if ((oldpass_crypt = crypt(oldpass, salt)) == NULL) {
67 		if (errno == EINVAL) {
68 			syslog(LOG_ERR,
69 			    "yppasswdd: password not changed for \"%s\" - "
70 			    "crypt module not supported on the master\n",
71 			    acctname);
72 		} else {
73 			syslog(LOG_ERR,
74 			    "yppasswdd: password not changed for \"%s\" - "
75 			    "%s\n", acctname, strerror(errno));
76 		}
77 	}
78 	return (oldpass_crypt);
79 }
80 
81 void
82 changepasswd(SVCXPRT *transp)
83 {
84 	/*
85 	 * Put these numeric constants into const variables so
86 	 *   a) they're visible in a debugger
87 	 *   b) the compiler can play it's cool games with em
88 	 */
89 	static const int cryptpwsize = CRYPT_MAXCIPHERTEXTLEN;
90 	static const int fingersize = FINGERSIZE;
91 	static const int shellsize = SHELLSIZE;
92 
93 	struct yppasswd yppwd;
94 	struct passwd newpw, opwd;
95 	struct spwd ospwd;
96 	struct sigaction sa, osa1, osa2, osa3;
97 	struct stat pwstat, spstat, adjstat;
98 	char *oldpass_crypt = NULL;
99 
100 	char newpasswdfile[FILENAME_MAX];
101 	char newshadowfile[FILENAME_MAX];
102 	char newadjunctfile[FILENAME_MAX];
103 	char tmppasswdfile[FILENAME_MAX];
104 	char tmpshadowfile[FILENAME_MAX];
105 	char tmpadjunctfile[FILENAME_MAX];
106 	char pwbuf[NSS_LINELEN_PASSWD], spbuf[NSS_LINELEN_SHADOW];
107 	char adjbuf[BUFSIZ+1], adjbuf_new[BUFSIZ+1], cmdbuf[BUFSIZ];
108 	char adj_encrypt[CRYPT_MAXCIPHERTEXTLEN + 1];
109 	/*
110 	 * The adj_crypt_* pointers are used to point into adjbuf
111 	 * NOT adj_encrypt
112 	 */
113 	char *adj_crypt_begin, *adj_crypt_end;
114 	char name[UTUSERLEN + sizeof (":")];
115 	char *p;
116 
117 	FILE *opwfp = NULL, *ospfp = NULL, *oadjfp = NULL,
118 	    *npwfp = NULL, *nspfp = NULL, *nadjfp = NULL;
119 	int npwfd = -1, nspfd = -1, nadjfd = -1;
120 
121 	int i, ans, chsh, chpw, chgecos, namelen;
122 	int gotadjunct = 0, gotshadow = 0, gotpasswd = 0;
123 	int doneflag = 0, root_on_master = 0;
124 	pid_t retval;
125 
126 	time_t now;
127 
128 	long pwpos = 0, sppos = 0;
129 
130 	/* Globals :-( */
131 	extern int single, nogecos, noshell, nopw, mflag, Mstart, Argc;
132 	extern char **Argv;
133 	extern char passwd_file[], shadow_file[], adjunct_file[];
134 	extern int useadjunct;
135 	extern int useshadow;
136 
137 	/* Clean out yppwd */
138 	memset(&yppwd, 0, sizeof (struct yppasswd));
139 
140 	/* Get the RPC args */
141 	if (!svc_getargs(transp, xdr_yppasswd, (caddr_t)&yppwd)) {
142 		svcerr_decode(transp);
143 		return;
144 	}
145 
146 	/* Perform basic validation */
147 	if (/* (!validstr(yppwd.oldpass, PWSIZE)) || */ /* see PR:nis/38 */
148 	    (!validstr(yppwd.newpw.pw_passwd, cryptpwsize)) ||
149 	    (!validstr(yppwd.newpw.pw_name, UTUSERLEN)) ||
150 	    (!validstr(yppwd.newpw.pw_gecos, fingersize)) ||
151 	    (!validstr(yppwd.newpw.pw_shell, shellsize))) {
152 		svcerr_decode(transp);
153 		return;
154 	}
155 
156 	/*
157 	 * Special case: root on the master server can change other users'
158 	 * passwords without first entering the old password.  We need to
159 	 * ensure that this is indeed root on the master server. (bug 1253949)
160 	 */
161 	if (strcmp(transp->xp_netid, "ticlts") == 0) {
162 		svc_local_cred_t cred;
163 		if (!svc_get_local_cred(transp, &cred)) {
164 			syslog(LOG_ERR, "yppasswdd: Couldn't get "
165 			    "local user credentials.\n");
166 		} else if (cred.ruid == 0)
167 			root_on_master = 1;
168 	}
169 
170 	newpw = yppwd.newpw;
171 	strcpy(name, newpw.pw_name);
172 	strcat(name, ":");
173 	namelen = strlen(name);
174 	ans = 2;
175 	chsh = chpw = chgecos = 0;
176 
177 	/* Get all the filenames straight */
178 	strcpy(newpasswdfile, passwd_file);
179 	strcat(newpasswdfile, ".ptmp");
180 	strcpy(newshadowfile, shadow_file);
181 	strcat(newshadowfile, ".ptmp");
182 	strcpy(newadjunctfile, adjunct_file);
183 	strcat(newadjunctfile, ".ptmp");
184 
185 	memset(&sa, 0, sizeof (struct sigaction));
186 	sa.sa_handler = SIG_IGN;
187 	sigaction(SIGTSTP, &sa, (struct sigaction *)0);
188 	sigaction(SIGHUP,  &sa, &osa1);
189 	sigaction(SIGINT,  &sa, &osa2);
190 	sigaction(SIGQUIT, &sa, &osa3);
191 
192 	/* Lock, then open the passwd and shadow files */
193 
194 	if (yplckpwdf() < 0) {
195 		syslog(LOG_ERR,
196 		    "yppasswdd: Password file(s) busy. "
197 		    "Try again later.\n");
198 		ans = 8;
199 		goto cleanup;
200 	}
201 
202 	if ((opwfp = fopen(passwd_file, "r")) == NULL) {
203 		syslog(LOG_ERR, "yppasswdd: Could not open %s\n", passwd_file);
204 		goto cleanup;
205 	}
206 
207 	fstat(fileno(opwfp), &pwstat);
208 
209 	if (useshadow) {
210 		if ((ospfp = fopen(shadow_file, "r")) == NULL) {
211 			syslog(LOG_ERR,
212 			    "yppasswdd: Could not open %s\n", shadow_file);
213 			goto cleanup;
214 		}
215 
216 		fstat(fileno(ospfp), &spstat);
217 	}
218 
219 	if (useadjunct) {
220 		if ((oadjfp = fopen(adjunct_file, "r")) == NULL) {
221 			syslog(LOG_ERR,
222 			    "yppasswdd: Could not open %s\n",
223 			    adjunct_file);
224 			goto cleanup;
225 		}
226 
227 		fstat(fileno(oadjfp), &adjstat);
228 	}
229 
230 	/*
231 	 * Open the new passwd and shadow tmp files,
232 	 * first with open and then create a FILE * with fdopen()
233 	 */
234 	if ((npwfd = open(newpasswdfile, O_WRONLY | O_CREAT | O_EXCL,
235 	    pwstat.st_mode)) < 0) {
236 		if (errno == EEXIST) {
237 			syslog(LOG_WARNING,
238 			    "yppasswdd: passwd file busy - try again\n");
239 			ans = 8;
240 		} else {
241 			syslog(LOG_ERR, "yppasswdd: %s: %m",
242 			    newpasswdfile);
243 			ans = 9;
244 		}
245 		goto cleanup;
246 	}
247 
248 	fchown(npwfd, pwstat.st_uid, pwstat.st_gid);
249 
250 	if ((npwfp = fdopen(npwfd, "w")) == NULL) {
251 		syslog(LOG_ERR,
252 		    "yppasswdd: fdopen() on %s failed\n", newpasswdfile);
253 		goto cleanup;
254 	}
255 
256 	if (useshadow) {
257 		if ((nspfd = open(newshadowfile, O_WRONLY | O_CREAT | O_EXCL,
258 		    spstat.st_mode)) < 0) {
259 			if (errno == EEXIST) {
260 				syslog(LOG_WARNING,
261 				    "yppasswdd: shadow file busy - try "
262 				    "again\n");
263 				ans = 8;
264 			} else {
265 				syslog(LOG_ERR, "yppasswdd: %s: %m",
266 				    newshadowfile);
267 				ans = 9;
268 			}
269 			goto cleanup;
270 		}
271 
272 		fchown(nspfd, spstat.st_uid, spstat.st_gid);
273 
274 		if ((nspfp = fdopen(nspfd, "w")) == NULL) {
275 			syslog(LOG_ERR,
276 			    "yppasswdd: fdopen() on %s failed\n",
277 			    newshadowfile);
278 			goto cleanup;
279 		}
280 	}
281 
282 	if (useadjunct) {
283 		if ((nadjfd = open(newadjunctfile, O_WRONLY | O_CREAT | O_EXCL,
284 		    adjstat.st_mode)) < 0) {
285 			if (errno == EEXIST) {
286 				syslog(LOG_WARNING,
287 				    "yppasswdd: adjunct file busy - try "
288 				    "again\n");
289 				ans = 8;
290 			} else {
291 				syslog(LOG_ERR, "yppasswdd: %s: %m",
292 				    newadjunctfile);
293 				ans = 9;
294 			}
295 			goto cleanup;
296 		}
297 
298 		fchown(nadjfd, adjstat.st_uid, adjstat.st_gid);
299 
300 		if ((nadjfp = fdopen(nadjfd, "w")) == NULL) {
301 			syslog(LOG_ERR,
302 			    "yppasswdd: fdopen() on %s failed\n",
303 			    newadjunctfile);
304 			goto cleanup;
305 		}
306 	}
307 
308 	/*
309 	 * The following code may not seem all that elegant, but my
310 	 * interpretation of the man pages relating to the passwd and
311 	 * shadow files would seem to indicate that there is no guarantee
312 	 * that the entries contained in those files will be in the same
313 	 * order...
314 	 *
315 	 * So here's the high level overview:
316 	 *
317 	 *    Loop through the passwd file reading in lines and writing them
318 	 *    out to the new file UNTIL we get to the correct entry.
319 	 *    IF we have a shadow file, loop through it reading in lines and
320 	 *    writing them out to the new file UNTIL we get to the correct
321 	 *    entry. IF we have an adjunct file, loop through it reading in
322 	 *    lines and writing them out to the new file UNTIL we get to the
323 	 *    correct entry.
324 	 *
325 	 *    Figure out what's changing, contruct the new passwd, shadow,
326 	 *    and adjunct entries and spit em out to the temp files.
327 	 *    At this point, set the done flag and leap back into the loop(s)
328 	 *    until you're finished with the files and then leap to the
329 	 *    section that installs the new files.
330 	 */
331 
332 loop_in_files:
333 	/* While we find things in the passwd file */
334 	while (fgets(pwbuf, NSS_LINELEN_PASSWD, opwfp)) {
335 
336 		/*
337 		 * Is this the passwd entry we want?
338 		 * If not, then write it out to the new passwd temp file
339 		 * and remember our position.
340 		 */
341 		if (doneflag || strncmp(name, pwbuf, namelen)) {
342 			if (fputs(pwbuf, npwfp) == EOF) {
343 				syslog(LOG_ERR,
344 				    "yppasswdd: write to passwd file "
345 				    "failed.\n");
346 				goto cleanup;
347 			}
348 			pwpos = ftell(opwfp);
349 			continue;
350 		}
351 		gotpasswd = 1;
352 		break;
353 	}
354 
355 	/* no match */
356 	if (!gotpasswd) {
357 		syslog(LOG_ERR, "yppasswdd: user %s does not exist\n", name);
358 		goto cleanup;
359 	}
360 
361 	/* While we find things in the shadow file */
362 	while (useshadow && fgets(spbuf, NSS_LINELEN_SHADOW, ospfp)) {
363 
364 		/*
365 		 * Is this the shadow entry that we want?
366 		 * If not, write it out to the new shadow temp file
367 		 * and remember our position.
368 		 */
369 		if (doneflag || strncmp(name, spbuf, namelen)) {
370 			if (fputs(spbuf, nspfp) == EOF) {
371 				syslog(LOG_ERR,
372 				    "yppasswdd: write to shadow file "
373 				    "failed.\n");
374 				goto cleanup;
375 			}
376 			sppos = ftell(ospfp);
377 			continue;
378 		}
379 		gotshadow = 1;
380 		break;
381 	}
382 
383 	/* While we find things in the adjunct file */
384 	while (useadjunct && fgets(adjbuf, BUFSIZ, oadjfp)) {
385 
386 		/*
387 		 * is this the adjunct entry that we want?
388 		 * If not, write it out to the new temp file
389 		 * and remember our position.
390 		 */
391 		if (doneflag || strncmp(name, adjbuf, namelen)) {
392 			if (fputs(adjbuf, nadjfp) == EOF) {
393 				syslog(LOG_ERR,
394 				    "yppasswdd: write to adjunct file "
395 				    "failed.\n");
396 				goto cleanup;
397 			}
398 			continue;
399 		}
400 		gotadjunct = 1;
401 		break;
402 	}
403 
404 	if (doneflag)
405 		goto install_files;
406 
407 	if (useshadow && !gotshadow) {
408 		syslog(LOG_ERR, "yppasswdd: no passwd in shadow for %s\n",
409 		    newpw.pw_name);
410 		ans = 4;
411 		goto cleanup;
412 	}
413 	if (useadjunct && !gotadjunct) {
414 		syslog(LOG_ERR, "yppasswdd: no passwd in adjunct for %s\n",
415 		    newpw.pw_name);
416 		ans = 4;
417 		goto cleanup;
418 	}
419 
420 	/*
421 	 * Now that we've read in the correct passwd AND
422 	 * shadow lines, we'll rewind to the beginning of
423 	 * those lines and let the fget*ent() calls do
424 	 * the work.  Since we are only working with the
425 	 * first two fields of the adjunct entry, leave
426 	 * it as a char array.
427 	 */
428 	fseek(opwfp, pwpos, SEEK_SET);
429 	opwd  = *fgetpwent(opwfp);
430 
431 	if (useshadow) {
432 		fseek(ospfp, sppos, SEEK_SET);
433 		ospwd = *fgetspent(ospfp);
434 	}
435 
436 	oldpass_crypt = cryptoldpasswd(yppwd.oldpass, newpw.pw_passwd,
437 	    newpw.pw_name);
438 	if (oldpass_crypt == NULL) {
439 		ans = 3;
440 		goto cleanup;
441 	}
442 	p = newpw.pw_passwd;
443 	if ((!nopw) &&
444 	    p && *p &&
445 	    !((*p++ == '#') && (*p++ == '#') &&
446 	    (strcmp(p, opwd.pw_name) == 0)) &&
447 	    (strcmp(oldpass_crypt, newpw.pw_passwd) != 0))
448 		chpw = 1;
449 	oldpass_crypt = NULL;
450 
451 	if ((!noshell) && (strcmp(opwd.pw_shell, newpw.pw_shell) != 0)) {
452 		if (single)
453 			chpw = 0;
454 		chsh = 1;
455 	}
456 
457 	if ((!nogecos) && (strcmp(opwd.pw_gecos, newpw.pw_gecos) != 0)) {
458 		if (single) {
459 			chpw = 0;
460 			chsh = 0;
461 		}
462 		chgecos = 1;
463 	}
464 
465 	if (!(chpw + chsh + chgecos)) {
466 		syslog(LOG_NOTICE, "yppasswdd: no change for %s\n",
467 		    newpw.pw_name);
468 		ans = 3;
469 		goto cleanup;
470 	}
471 
472 	if (useshadow && !root_on_master) {
473 		oldpass_crypt = cryptoldpasswd(yppwd.oldpass, ospwd.sp_pwdp,
474 		    newpw.pw_name);
475 		if (oldpass_crypt == NULL)
476 			goto cleanup;
477 		if (ospwd.sp_pwdp && *ospwd.sp_pwdp &&
478 		    (strcmp(oldpass_crypt, ospwd.sp_pwdp) != 0)) {
479 
480 			syslog(LOG_NOTICE, "yppasswdd: passwd incorrect\n",
481 			    newpw.pw_name);
482 			ans = 7;
483 			goto cleanup;
484 		}
485 	} else if (useadjunct) {
486 		/*
487 		 * Clear the adj_encrypt array.  Extract the encrypted passwd
488 		 * into adj_encrypt by setting adj_crypt_begin and
489 		 * adj_crypt_end to point at the first character of the
490 		 * encrypted passwd and the first character following the
491 		 * encrypted passwd in adjbuf, respectively, and copy the
492 		 * stuff between (there may not be anything) into adj_ecrypt.
493 		 * Then, check that adj_encrypt contains something and that
494 		 * the old passwd is correct.
495 		 */
496 		memset(adj_encrypt, 0, sizeof (adj_encrypt));
497 		adj_crypt_begin = adjbuf + namelen;
498 		adj_crypt_end = strchr(adj_crypt_begin, ':');
499 		strncpy(adj_encrypt, adj_crypt_begin,
500 		    adj_crypt_end - adj_crypt_begin);
501 		oldpass_crypt = cryptoldpasswd(yppwd.oldpass, adj_encrypt,
502 		    newpw.pw_name);
503 		if (oldpass_crypt == NULL)
504 			goto cleanup;
505 		if (!root_on_master && *adj_encrypt &&
506 		    (strcmp(oldpass_crypt, adj_encrypt) != 0)) {
507 
508 			syslog(LOG_NOTICE, "yppasswdd: passwd incorrect\n",
509 			    newpw.pw_name);
510 			ans = 7;
511 			goto cleanup;
512 		}
513 	} else {
514 		oldpass_crypt = cryptoldpasswd(yppwd.oldpass, opwd.pw_passwd,
515 		    newpw.pw_name);
516 		if (oldpass_crypt == NULL)
517 			goto cleanup;
518 		if (!root_on_master && opwd.pw_passwd && *opwd.pw_passwd &&
519 		    (strcmp(oldpass_crypt, opwd.pw_passwd) != 0)) {
520 
521 			syslog(LOG_NOTICE, "yppasswdd: passwd incorrect\n",
522 			    newpw.pw_name);
523 			ans = 7;
524 			goto cleanup;
525 		}
526 	}
527 
528 #ifdef DEBUG
529 	printf("%d %d %d\n", chsh, chgecos, chpw);
530 
531 	printf("%s %s %s\n",
532 	    yppwd.newpw.pw_shell,
533 	    yppwd.newpw.pw_gecos,
534 	    yppwd.newpw.pw_passwd);
535 
536 	printf("%s %s %s\n",
537 	    opwd.pw_shell,
538 	    opwd.pw_gecos,
539 	    ospwd.sp_pwdp);
540 #endif
541 
542 	if (chsh &&
543 	    !validloginshell(opwd.pw_shell, newpw.pw_shell, root_on_master)) {
544 		goto cleanup;
545 	}
546 
547 	/* security hole fix from original source */
548 	for (p = newpw.pw_name; (*p != '\0'); p++)
549 		if ((*p == ':') || !(isprint(*p)))
550 			*p = '$';	/* you lose buckwheat */
551 	for (p = newpw.pw_passwd; (*p != '\0'); p++)
552 		if ((*p == ':') || !(isprint(*p)))
553 			*p = '$';	/* you lose buckwheat */
554 
555 	if (chgecos)
556 		opwd.pw_gecos = newpw.pw_gecos;
557 
558 	if (chsh)
559 		opwd.pw_shell = newpw.pw_shell;
560 
561 	/*
562 	 * If we're changing the shell or gecos fields and we're
563 	 * using a shadow or adjunct file or not changing the passwd
564 	 * then go ahead and update the passwd file.  The case where
565 	 * the passwd is being changed and we are not using a shadow
566 	 * or adjunct file is handled later.
567 	 */
568 	if ((chsh || chgecos) && (useshadow || useadjunct || !chpw) &&
569 	    putpwent(&opwd, npwfp)) {
570 
571 		syslog(LOG_ERR, "yppasswdd: putpwent failed: %s\n",
572 		    passwd_file);
573 		goto cleanup;
574 	}
575 
576 	if (chpw) {
577 		if (useshadow) {
578 			ospwd.sp_pwdp = newpw.pw_passwd;
579 			now = DAY_NOW;
580 			/* password aging - bug for bug compatibility */
581 			if (ospwd.sp_max != -1) {
582 				if (now < ospwd.sp_lstchg + ospwd.sp_min) {
583 					syslog(LOG_ERR,
584 					    "yppasswdd: Sorry: < %ld days "
585 					    "since the last change.\n",
586 					    ospwd.sp_min);
587 					goto cleanup;
588 				}
589 			}
590 			ospwd.sp_lstchg = now;
591 			if (putspent(&ospwd, nspfp)) {
592 				syslog(LOG_ERR,
593 				    "yppasswdd: putspent failed: %s\n",
594 				    shadow_file);
595 				goto cleanup;
596 			}
597 		} else if (useadjunct) {
598 			sprintf(adjbuf_new,
599 			    "%s%s%s", name, newpw.pw_passwd, adj_crypt_end);
600 			if (fputs(adjbuf_new, nadjfp) == EOF) {
601 				syslog(LOG_ERR,
602 				    "yppasswdd: write to adjunct failed: %s\n",
603 				    adjunct_file);
604 				goto cleanup;
605 			}
606 		} else {
607 			opwd.pw_passwd = newpw.pw_passwd;
608 			if (putpwent(&opwd, npwfp)) {
609 				syslog(LOG_ERR,
610 				    "yppasswdd: putpwent failed: %s\n",
611 				    passwd_file);
612 				goto cleanup;
613 			}
614 		}
615 	}
616 
617 	if (!doneflag) {
618 		doneflag = 1;
619 		goto loop_in_files;
620 	}
621 
622 install_files:
623 	/*
624 	 * Critical section, nothing special needs to be done since we
625 	 * hold exclusive access to the *.ptmp files
626 	 */
627 	fflush(npwfp);
628 	if (useshadow)
629 		fflush(nspfp);
630 	if (useadjunct)
631 		fflush(nadjfp);
632 
633 	strcpy(tmppasswdfile, passwd_file);
634 	strcat(tmppasswdfile, "-");
635 	if (useshadow) {
636 		strcpy(tmpshadowfile, shadow_file);
637 		strcat(tmpshadowfile, "-");
638 	}
639 	if (useadjunct) {
640 		strcpy(tmpadjunctfile, adjunct_file);
641 		strcat(tmpadjunctfile, "-");
642 	}
643 
644 	if ((!useshadow && !useadjunct) || (chsh || chgecos)) {
645 		if (rename(passwd_file, tmppasswdfile) < 0) {
646 			syslog(LOG_CRIT, "yppasswdd: failed to backup "
647 			    "passwd file: %m");
648 			goto cleanup;
649 		} else {
650 			if (rename(newpasswdfile, passwd_file) < 0) {
651 				syslog(LOG_CRIT,
652 				    "yppasswdd: failed to mv passwd: %m");
653 				if (rename(tmppasswdfile, passwd_file) < 0) {
654 					syslog(LOG_CRIT,
655 					    "yppasswdd: failed to restore "
656 					    "backup of passwd file: %m");
657 				}
658 				goto cleanup;
659 			}
660 		}
661 	}
662 
663 	if (useshadow && chpw) {
664 		if (rename(shadow_file, tmpshadowfile) < 0) {
665 			syslog(LOG_CRIT, "yppasswdd: failed to back up "
666 			    "shadow file: %m");
667 			if (rename(tmppasswdfile, passwd_file) < 0) {
668 				syslog(LOG_CRIT,
669 				    "yppasswdd: failed to restore "
670 				    "backup of passwd file: %m");
671 			}
672 			goto cleanup;
673 		} else {
674 			if (rename(newshadowfile, shadow_file) < 0) {
675 				syslog(LOG_CRIT,
676 				    "yppasswdd: failed to mv shadow: %m");
677 				if (rename(tmpshadowfile, shadow_file) < 0) {
678 					syslog(LOG_CRIT,
679 					    "yppasswdd: failed to restore "
680 					    "backup of shadow file: %m");
681 				}
682 				if (rename(tmppasswdfile, passwd_file) < 0) {
683 					syslog(LOG_CRIT,
684 					    "yppasswdd: failed to restore "
685 					    "backup of passwd file: %m");
686 				}
687 				goto cleanup;
688 			}
689 		}
690 	} else if (useadjunct && chpw) {
691 		if (rename(adjunct_file, tmpadjunctfile) < 0) {
692 			syslog(LOG_CRIT, "yppasswdd: failed to back up "
693 			    "adjunct file: %m");
694 			if (rename(tmppasswdfile, passwd_file) < 0) {
695 				syslog(LOG_CRIT,
696 				    "yppasswdd: failed to restore backup "
697 				    "of passwd: %m");
698 			}
699 			goto cleanup;
700 		} else {
701 			if (rename(newadjunctfile, adjunct_file) < 0) {
702 				syslog(LOG_CRIT,
703 				    "yppassdd: failed to mv adjunct: %m");
704 				if (rename(tmppasswdfile, passwd_file) < 0) {
705 					syslog(LOG_CRIT,
706 					    "yppasswdd: failed to restore "
707 					    "backup of passwd file: %m");
708 				}
709 				if (rename(tmpadjunctfile, adjunct_file) < 0) {
710 					syslog(LOG_CRIT,
711 					    "yppasswdd: failed to restore "
712 					    "backup of adjunct file: %m");
713 				}
714 				goto cleanup;
715 			}
716 		}
717 	}
718 
719 	if (doneflag)
720 		ans = 0;
721 	/* End critical section */
722 
723 	/*
724 	 *  Here we have come only after the new files have been successfully
725 	 * renamed to original files. At this point, the temp files would still
726 	 * be existing we need to remove them from the /etc directory
727 	 */
728 	unlink(tmppasswdfile);
729 	if (useshadow)
730 		unlink(tmpshadowfile);
731 	if (useadjunct)
732 		unlink(tmpadjunctfile);
733 
734 cleanup:
735 
736 	/* If we don't have opwfp, then we didn't do anything */
737 	if (opwfp) {
738 		fclose(opwfp);
739 
740 		if (ospfp) {
741 			fclose(ospfp);
742 		}
743 
744 		if (oadjfp) {
745 			fclose(oadjfp);
746 		}
747 
748 		unlink(newpasswdfile);
749 		/* These tests are cheaper than failing syscalls */
750 		if (useshadow)
751 			unlink(newshadowfile);
752 		if (useadjunct)
753 			unlink(newadjunctfile);
754 
755 		if (npwfp) {
756 			fclose(npwfp);
757 
758 			if (nspfp) {
759 				fclose(nspfp);
760 			}
761 			if (nadjfp) {
762 				fclose(nadjfp);
763 			}
764 		}
765 	}
766 
767 	ypulckpwdf();
768 
769 	if (doneflag && mflag) {
770 		retval = fork();
771 		if (retval < 0) {
772 			syslog(LOG_ERR, "yppasswdd: Fork failed %m");
773 		} else if (retval == 0) {
774 			strcpy(cmdbuf, "/usr/ccs/bin/make");
775 			for (i = Mstart + 1; i < Argc; i++) {
776 				strcat(cmdbuf, " ");
777 				strcat(cmdbuf, Argv[i]);
778 			}
779 
780 #ifdef DEBUG
781 			syslog(LOG_ERR, "yppasswdd: about to "
782 			    "execute %s\n", cmdbuf);
783 #else
784 			if (yplckpwdf() < 0) {
785 				syslog(LOG_ERR, "yppasswdd: Couldn't get the "
786 				    "lock to update the maps");
787 			} else {
788 				setpgrp();
789 				system(cmdbuf);
790 				ypulckpwdf();
791 			}
792 #endif
793 			exit(0);
794 		}
795 	}
796 
797 	sigaction(SIGHUP,  &osa1, (struct sigaction *)0);
798 	sigaction(SIGINT,  &osa2, (struct sigaction *)0);
799 	sigaction(SIGQUIT, &osa3, (struct sigaction *)0);
800 
801 	if (!svc_sendreply(transp, xdr_int, (char *)&ans))
802 		syslog(LOG_WARNING,
803 		    "yppasswdd: couldn\'t reply to RPC call\n");
804 }
805