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.
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.
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]
22 * Copyright 2010 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
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
32 #include <sys/types.h>
46 #include <rpcsvc/yppasswd.h>
48 #include <nss_dbdefs.h>
51 #define FINGERSIZE (4 * STRSIZE - 4)
52 #define SHELLSIZE (STRSIZE - 2)
53 #define UTUSERLEN (sizeof (((struct utmpx *)0)->ut_user))
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();
62 cryptoldpasswd(char *oldpass
, char *salt
, char *acctname
)
64 char *oldpass_crypt
= NULL
;
66 if ((oldpass_crypt
= crypt(oldpass
, salt
)) == NULL
) {
67 if (errno
== EINVAL
) {
69 "yppasswdd: password not changed for \"%s\" - "
70 "crypt module not supported on the master\n",
74 "yppasswdd: password not changed for \"%s\" - "
75 "%s\n", acctname
, strerror(errno
));
78 return (oldpass_crypt
);
82 changepasswd(SVCXPRT
*transp
)
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
89 static const int cryptpwsize
= CRYPT_MAXCIPHERTEXTLEN
;
90 static const int fingersize
= FINGERSIZE
;
91 static const int shellsize
= SHELLSIZE
;
93 struct yppasswd yppwd
;
94 struct passwd newpw
, opwd
;
96 struct sigaction sa
, osa1
, osa2
, osa3
;
97 struct stat pwstat
, spstat
, adjstat
;
98 char *oldpass_crypt
= NULL
;
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];
110 * The adj_crypt_* pointers are used to point into adjbuf
113 char *adj_crypt_begin
, *adj_crypt_end
;
114 char name
[UTUSERLEN
+ sizeof (":")];
117 FILE *opwfp
= NULL
, *ospfp
= NULL
, *oadjfp
= NULL
,
118 *npwfp
= NULL
, *nspfp
= NULL
, *nadjfp
= NULL
;
119 int npwfd
= -1, nspfd
= -1, nadjfd
= -1;
121 int i
, ans
, chsh
, chpw
, chgecos
, namelen
;
122 int gotadjunct
= 0, gotshadow
= 0, gotpasswd
= 0;
123 int doneflag
= 0, root_on_master
= 0;
128 long pwpos
= 0, sppos
= 0;
131 extern int single
, nogecos
, noshell
, nopw
, mflag
, Mstart
, Argc
;
133 extern char passwd_file
[], shadow_file
[], adjunct_file
[];
134 extern int useadjunct
;
135 extern int useshadow
;
137 /* Clean out yppwd */
138 memset(&yppwd
, 0, sizeof (struct yppasswd
));
140 /* Get the RPC args */
141 if (!svc_getargs(transp
, xdr_yppasswd
, (caddr_t
)&yppwd
)) {
142 svcerr_decode(transp
);
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
);
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)
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)
171 strcpy(name
, newpw
.pw_name
);
173 namelen
= strlen(name
);
175 chsh
= chpw
= chgecos
= 0;
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");
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
);
192 /* Lock, then open the passwd and shadow files */
194 if (yplckpwdf() < 0) {
196 "yppasswdd: Password file(s) busy. "
197 "Try again later.\n");
202 if ((opwfp
= fopen(passwd_file
, "r")) == NULL
) {
203 syslog(LOG_ERR
, "yppasswdd: Could not open %s\n", passwd_file
);
207 fstat(fileno(opwfp
), &pwstat
);
210 if ((ospfp
= fopen(shadow_file
, "r")) == NULL
) {
212 "yppasswdd: Could not open %s\n", shadow_file
);
216 fstat(fileno(ospfp
), &spstat
);
220 if ((oadjfp
= fopen(adjunct_file
, "r")) == NULL
) {
222 "yppasswdd: Could not open %s\n",
227 fstat(fileno(oadjfp
), &adjstat
);
231 * Open the new passwd and shadow tmp files,
232 * first with open and then create a FILE * with fdopen()
234 if ((npwfd
= open(newpasswdfile
, O_WRONLY
| O_CREAT
| O_EXCL
,
235 pwstat
.st_mode
)) < 0) {
236 if (errno
== EEXIST
) {
238 "yppasswdd: passwd file busy - try again\n");
241 syslog(LOG_ERR
, "yppasswdd: %s: %m",
248 fchown(npwfd
, pwstat
.st_uid
, pwstat
.st_gid
);
250 if ((npwfp
= fdopen(npwfd
, "w")) == NULL
) {
252 "yppasswdd: fdopen() on %s failed\n", newpasswdfile
);
257 if ((nspfd
= open(newshadowfile
, O_WRONLY
| O_CREAT
| O_EXCL
,
258 spstat
.st_mode
)) < 0) {
259 if (errno
== EEXIST
) {
261 "yppasswdd: shadow file busy - try "
265 syslog(LOG_ERR
, "yppasswdd: %s: %m",
272 fchown(nspfd
, spstat
.st_uid
, spstat
.st_gid
);
274 if ((nspfp
= fdopen(nspfd
, "w")) == NULL
) {
276 "yppasswdd: fdopen() on %s failed\n",
283 if ((nadjfd
= open(newadjunctfile
, O_WRONLY
| O_CREAT
| O_EXCL
,
284 adjstat
.st_mode
)) < 0) {
285 if (errno
== EEXIST
) {
287 "yppasswdd: adjunct file busy - try "
291 syslog(LOG_ERR
, "yppasswdd: %s: %m",
298 fchown(nadjfd
, adjstat
.st_uid
, adjstat
.st_gid
);
300 if ((nadjfp
= fdopen(nadjfd
, "w")) == NULL
) {
302 "yppasswdd: fdopen() on %s failed\n",
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
315 * So here's the high level overview:
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
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.
333 /* While we find things in the passwd file */
334 while (fgets(pwbuf
, NSS_LINELEN_PASSWD
, opwfp
)) {
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.
341 if (doneflag
|| strncmp(name
, pwbuf
, namelen
)) {
342 if (fputs(pwbuf
, npwfp
) == EOF
) {
344 "yppasswdd: write to passwd file "
348 pwpos
= ftell(opwfp
);
357 syslog(LOG_ERR
, "yppasswdd: user %s does not exist\n", name
);
361 /* While we find things in the shadow file */
362 while (useshadow
&& fgets(spbuf
, NSS_LINELEN_SHADOW
, ospfp
)) {
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.
369 if (doneflag
|| strncmp(name
, spbuf
, namelen
)) {
370 if (fputs(spbuf
, nspfp
) == EOF
) {
372 "yppasswdd: write to shadow file "
376 sppos
= ftell(ospfp
);
383 /* While we find things in the adjunct file */
384 while (useadjunct
&& fgets(adjbuf
, BUFSIZ
, oadjfp
)) {
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.
391 if (doneflag
|| strncmp(name
, adjbuf
, namelen
)) {
392 if (fputs(adjbuf
, nadjfp
) == EOF
) {
394 "yppasswdd: write to adjunct file "
407 if (useshadow
&& !gotshadow
) {
408 syslog(LOG_ERR
, "yppasswdd: no passwd in shadow for %s\n",
413 if (useadjunct
&& !gotadjunct
) {
414 syslog(LOG_ERR
, "yppasswdd: no passwd in adjunct for %s\n",
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.
428 fseek(opwfp
, pwpos
, SEEK_SET
);
429 opwd
= *fgetpwent(opwfp
);
432 fseek(ospfp
, sppos
, SEEK_SET
);
433 ospwd
= *fgetspent(ospfp
);
436 oldpass_crypt
= cryptoldpasswd(yppwd
.oldpass
, newpw
.pw_passwd
,
438 if (oldpass_crypt
== NULL
) {
445 !((*p
++ == '#') && (*p
++ == '#') &&
446 (strcmp(p
, opwd
.pw_name
) == 0)) &&
447 (strcmp(oldpass_crypt
, newpw
.pw_passwd
) != 0))
449 oldpass_crypt
= NULL
;
451 if ((!noshell
) && (strcmp(opwd
.pw_shell
, newpw
.pw_shell
) != 0)) {
457 if ((!nogecos
) && (strcmp(opwd
.pw_gecos
, newpw
.pw_gecos
) != 0)) {
465 if (!(chpw
+ chsh
+ chgecos
)) {
466 syslog(LOG_NOTICE
, "yppasswdd: no change for %s\n",
472 if (useshadow
&& !root_on_master
) {
473 oldpass_crypt
= cryptoldpasswd(yppwd
.oldpass
, ospwd
.sp_pwdp
,
475 if (oldpass_crypt
== NULL
)
477 if (ospwd
.sp_pwdp
&& *ospwd
.sp_pwdp
&&
478 (strcmp(oldpass_crypt
, ospwd
.sp_pwdp
) != 0)) {
480 syslog(LOG_NOTICE
, "yppasswdd: passwd incorrect\n",
485 } else if (useadjunct
) {
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.
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
,
503 if (oldpass_crypt
== NULL
)
505 if (!root_on_master
&& *adj_encrypt
&&
506 (strcmp(oldpass_crypt
, adj_encrypt
) != 0)) {
508 syslog(LOG_NOTICE
, "yppasswdd: passwd incorrect\n",
514 oldpass_crypt
= cryptoldpasswd(yppwd
.oldpass
, opwd
.pw_passwd
,
516 if (oldpass_crypt
== NULL
)
518 if (!root_on_master
&& opwd
.pw_passwd
&& *opwd
.pw_passwd
&&
519 (strcmp(oldpass_crypt
, opwd
.pw_passwd
) != 0)) {
521 syslog(LOG_NOTICE
, "yppasswdd: passwd incorrect\n",
529 printf("%d %d %d\n", chsh
, chgecos
, chpw
);
532 yppwd
.newpw
.pw_shell
,
533 yppwd
.newpw
.pw_gecos
,
534 yppwd
.newpw
.pw_passwd
);
543 !validloginshell(opwd
.pw_shell
, newpw
.pw_shell
, root_on_master
)) {
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 */
556 opwd
.pw_gecos
= newpw
.pw_gecos
;
559 opwd
.pw_shell
= newpw
.pw_shell
;
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.
568 if ((chsh
|| chgecos
) && (useshadow
|| useadjunct
|| !chpw
) &&
569 putpwent(&opwd
, npwfp
)) {
571 syslog(LOG_ERR
, "yppasswdd: putpwent failed: %s\n",
578 ospwd
.sp_pwdp
= newpw
.pw_passwd
;
580 /* password aging - bug for bug compatibility */
581 if (ospwd
.sp_max
!= -1) {
582 if (now
< ospwd
.sp_lstchg
+ ospwd
.sp_min
) {
584 "yppasswdd: Sorry: < %ld days "
585 "since the last change.\n",
590 ospwd
.sp_lstchg
= now
;
591 if (putspent(&ospwd
, nspfp
)) {
593 "yppasswdd: putspent failed: %s\n",
597 } else if (useadjunct
) {
599 "%s%s%s", name
, newpw
.pw_passwd
, adj_crypt_end
);
600 if (fputs(adjbuf_new
, nadjfp
) == EOF
) {
602 "yppasswdd: write to adjunct failed: %s\n",
607 opwd
.pw_passwd
= newpw
.pw_passwd
;
608 if (putpwent(&opwd
, npwfp
)) {
610 "yppasswdd: putpwent failed: %s\n",
624 * Critical section, nothing special needs to be done since we
625 * hold exclusive access to the *.ptmp files
633 strcpy(tmppasswdfile
, passwd_file
);
634 strcat(tmppasswdfile
, "-");
636 strcpy(tmpshadowfile
, shadow_file
);
637 strcat(tmpshadowfile
, "-");
640 strcpy(tmpadjunctfile
, adjunct_file
);
641 strcat(tmpadjunctfile
, "-");
644 if ((!useshadow
&& !useadjunct
) || (chsh
|| chgecos
)) {
645 if (rename(passwd_file
, tmppasswdfile
) < 0) {
646 syslog(LOG_CRIT
, "yppasswdd: failed to backup "
650 if (rename(newpasswdfile
, passwd_file
) < 0) {
652 "yppasswdd: failed to mv passwd: %m");
653 if (rename(tmppasswdfile
, passwd_file
) < 0) {
655 "yppasswdd: failed to restore "
656 "backup of passwd file: %m");
663 if (useshadow
&& chpw
) {
664 if (rename(shadow_file
, tmpshadowfile
) < 0) {
665 syslog(LOG_CRIT
, "yppasswdd: failed to back up "
667 if (rename(tmppasswdfile
, passwd_file
) < 0) {
669 "yppasswdd: failed to restore "
670 "backup of passwd file: %m");
674 if (rename(newshadowfile
, shadow_file
) < 0) {
676 "yppasswdd: failed to mv shadow: %m");
677 if (rename(tmpshadowfile
, shadow_file
) < 0) {
679 "yppasswdd: failed to restore "
680 "backup of shadow file: %m");
682 if (rename(tmppasswdfile
, passwd_file
) < 0) {
684 "yppasswdd: failed to restore "
685 "backup of passwd file: %m");
690 } else if (useadjunct
&& chpw
) {
691 if (rename(adjunct_file
, tmpadjunctfile
) < 0) {
692 syslog(LOG_CRIT
, "yppasswdd: failed to back up "
694 if (rename(tmppasswdfile
, passwd_file
) < 0) {
696 "yppasswdd: failed to restore backup "
701 if (rename(newadjunctfile
, adjunct_file
) < 0) {
703 "yppassdd: failed to mv adjunct: %m");
704 if (rename(tmppasswdfile
, passwd_file
) < 0) {
706 "yppasswdd: failed to restore "
707 "backup of passwd file: %m");
709 if (rename(tmpadjunctfile
, adjunct_file
) < 0) {
711 "yppasswdd: failed to restore "
712 "backup of adjunct file: %m");
721 /* End critical section */
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
728 unlink(tmppasswdfile
);
730 unlink(tmpshadowfile
);
732 unlink(tmpadjunctfile
);
736 /* If we don't have opwfp, then we didn't do anything */
748 unlink(newpasswdfile
);
749 /* These tests are cheaper than failing syscalls */
751 unlink(newshadowfile
);
753 unlink(newadjunctfile
);
769 if (doneflag
&& mflag
) {
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
++) {
777 strcat(cmdbuf
, Argv
[i
]);
781 syslog(LOG_ERR
, "yppasswdd: about to "
782 "execute %s\n", cmdbuf
);
784 if (yplckpwdf() < 0) {
785 syslog(LOG_ERR
, "yppasswdd: Couldn't get the "
786 "lock to update the maps");
797 sigaction(SIGHUP
, &osa1
, (struct sigaction
*)0);
798 sigaction(SIGINT
, &osa2
, (struct sigaction
*)0);
799 sigaction(SIGQUIT
, &osa3
, (struct sigaction
*)0);
801 if (!svc_sendreply(transp
, xdr_int
, (char *)&ans
))
803 "yppasswdd: couldn\'t reply to RPC call\n");