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 *)NULL)->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();
60 extern struct passwd
*fgetpwent(FILE *);
63 cryptoldpasswd(char *oldpass
, char *salt
, char *acctname
)
65 char *oldpass_crypt
= NULL
;
67 if ((oldpass_crypt
= crypt(oldpass
, salt
)) == NULL
) {
68 if (errno
== EINVAL
) {
70 "yppasswdd: password not changed for \"%s\" - "
71 "crypt module not supported on the master\n",
75 "yppasswdd: password not changed for \"%s\" - "
76 "%s\n", acctname
, strerror(errno
));
79 return (oldpass_crypt
);
83 changepasswd(SVCXPRT
*transp
)
86 * Put these numeric constants into const variables so
87 * a) they're visible in a debugger
88 * b) the compiler can play it's cool games with em
90 static const int cryptpwsize
= CRYPT_MAXCIPHERTEXTLEN
;
91 static const int fingersize
= FINGERSIZE
;
92 static const int shellsize
= SHELLSIZE
;
94 struct yppasswd yppwd
;
95 struct passwd newpw
, opwd
;
97 struct sigaction sa
, osa1
, osa2
, osa3
;
98 struct stat pwstat
, spstat
, adjstat
;
99 char *oldpass_crypt
= NULL
;
101 char newpasswdfile
[FILENAME_MAX
];
102 char newshadowfile
[FILENAME_MAX
];
103 char newadjunctfile
[FILENAME_MAX
];
104 char tmppasswdfile
[FILENAME_MAX
];
105 char tmpshadowfile
[FILENAME_MAX
];
106 char tmpadjunctfile
[FILENAME_MAX
];
107 char pwbuf
[NSS_LINELEN_PASSWD
], spbuf
[NSS_LINELEN_SHADOW
];
108 char adjbuf
[BUFSIZ
+1], adjbuf_new
[BUFSIZ
+1], cmdbuf
[BUFSIZ
];
109 char adj_encrypt
[CRYPT_MAXCIPHERTEXTLEN
+ 1];
111 * The adj_crypt_* pointers are used to point into adjbuf
114 char *adj_crypt_begin
, *adj_crypt_end
;
115 char name
[UTUSERLEN
+ sizeof (":")];
118 FILE *opwfp
= NULL
, *ospfp
= NULL
, *oadjfp
= NULL
,
119 *npwfp
= NULL
, *nspfp
= NULL
, *nadjfp
= NULL
;
120 int npwfd
= -1, nspfd
= -1, nadjfd
= -1;
122 int i
, ans
, chsh
, chpw
, chgecos
, namelen
;
123 int gotadjunct
= 0, gotshadow
= 0, gotpasswd
= 0;
124 int doneflag
= 0, root_on_master
= 0;
129 long pwpos
= 0, sppos
= 0;
132 extern int single
, nogecos
, noshell
, nopw
, mflag
, Mstart
, Argc
;
134 extern char passwd_file
[], shadow_file
[], adjunct_file
[];
135 extern int useadjunct
;
136 extern int useshadow
;
138 /* Clean out yppwd */
139 memset(&yppwd
, 0, sizeof (struct yppasswd
));
141 /* Get the RPC args */
142 if (!svc_getargs(transp
, xdr_yppasswd
, (caddr_t
)&yppwd
)) {
143 svcerr_decode(transp
);
147 /* Perform basic validation */
148 if (/* (!validstr(yppwd.oldpass, PWSIZE)) || */ /* see PR:nis/38 */
149 (!validstr(yppwd
.newpw
.pw_passwd
, cryptpwsize
)) ||
150 (!validstr(yppwd
.newpw
.pw_name
, UTUSERLEN
)) ||
151 (!validstr(yppwd
.newpw
.pw_gecos
, fingersize
)) ||
152 (!validstr(yppwd
.newpw
.pw_shell
, shellsize
))) {
153 svcerr_decode(transp
);
158 * Special case: root on the master server can change other users'
159 * passwords without first entering the old password. We need to
160 * ensure that this is indeed root on the master server. (bug 1253949)
162 if (strcmp(transp
->xp_netid
, "ticlts") == 0) {
163 svc_local_cred_t cred
;
164 if (!svc_get_local_cred(transp
, &cred
)) {
165 syslog(LOG_ERR
, "yppasswdd: Couldn't get "
166 "local user credentials.\n");
167 } else if (cred
.ruid
== 0)
172 strcpy(name
, newpw
.pw_name
);
174 namelen
= strlen(name
);
176 chsh
= chpw
= chgecos
= 0;
178 /* Get all the filenames straight */
179 strcpy(newpasswdfile
, passwd_file
);
180 strcat(newpasswdfile
, ".ptmp");
181 strcpy(newshadowfile
, shadow_file
);
182 strcat(newshadowfile
, ".ptmp");
183 strcpy(newadjunctfile
, adjunct_file
);
184 strcat(newadjunctfile
, ".ptmp");
186 memset(&sa
, 0, sizeof (struct sigaction
));
187 sa
.sa_handler
= SIG_IGN
;
188 sigaction(SIGTSTP
, &sa
, NULL
);
189 sigaction(SIGHUP
, &sa
, &osa1
);
190 sigaction(SIGINT
, &sa
, &osa2
);
191 sigaction(SIGQUIT
, &sa
, &osa3
);
193 /* Lock, then open the passwd and shadow files */
195 if (yplckpwdf() < 0) {
197 "yppasswdd: Password file(s) busy. "
198 "Try again later.\n");
203 if ((opwfp
= fopen(passwd_file
, "r")) == NULL
) {
204 syslog(LOG_ERR
, "yppasswdd: Could not open %s\n", passwd_file
);
208 fstat(fileno(opwfp
), &pwstat
);
211 if ((ospfp
= fopen(shadow_file
, "r")) == NULL
) {
213 "yppasswdd: Could not open %s\n", shadow_file
);
217 fstat(fileno(ospfp
), &spstat
);
221 if ((oadjfp
= fopen(adjunct_file
, "r")) == NULL
) {
223 "yppasswdd: Could not open %s\n",
228 fstat(fileno(oadjfp
), &adjstat
);
232 * Open the new passwd and shadow tmp files,
233 * first with open and then create a FILE * with fdopen()
235 if ((npwfd
= open(newpasswdfile
, O_WRONLY
| O_CREAT
| O_EXCL
,
236 pwstat
.st_mode
)) < 0) {
237 if (errno
== EEXIST
) {
239 "yppasswdd: passwd file busy - try again\n");
242 syslog(LOG_ERR
, "yppasswdd: %s: %m",
249 fchown(npwfd
, pwstat
.st_uid
, pwstat
.st_gid
);
251 if ((npwfp
= fdopen(npwfd
, "w")) == NULL
) {
253 "yppasswdd: fdopen() on %s failed\n", newpasswdfile
);
258 if ((nspfd
= open(newshadowfile
, O_WRONLY
| O_CREAT
| O_EXCL
,
259 spstat
.st_mode
)) < 0) {
260 if (errno
== EEXIST
) {
262 "yppasswdd: shadow file busy - try "
266 syslog(LOG_ERR
, "yppasswdd: %s: %m",
273 fchown(nspfd
, spstat
.st_uid
, spstat
.st_gid
);
275 if ((nspfp
= fdopen(nspfd
, "w")) == NULL
) {
277 "yppasswdd: fdopen() on %s failed\n",
284 if ((nadjfd
= open(newadjunctfile
, O_WRONLY
| O_CREAT
| O_EXCL
,
285 adjstat
.st_mode
)) < 0) {
286 if (errno
== EEXIST
) {
288 "yppasswdd: adjunct file busy - try "
292 syslog(LOG_ERR
, "yppasswdd: %s: %m",
299 fchown(nadjfd
, adjstat
.st_uid
, adjstat
.st_gid
);
301 if ((nadjfp
= fdopen(nadjfd
, "w")) == NULL
) {
303 "yppasswdd: fdopen() on %s failed\n",
310 * The following code may not seem all that elegant, but my
311 * interpretation of the man pages relating to the passwd and
312 * shadow files would seem to indicate that there is no guarantee
313 * that the entries contained in those files will be in the same
316 * So here's the high level overview:
318 * Loop through the passwd file reading in lines and writing them
319 * out to the new file UNTIL we get to the correct entry.
320 * IF we have a shadow file, loop through it reading in lines and
321 * writing them out to the new file UNTIL we get to the correct
322 * entry. IF we have an adjunct file, loop through it reading in
323 * lines and writing them out to the new file UNTIL we get to the
326 * Figure out what's changing, contruct the new passwd, shadow,
327 * and adjunct entries and spit em out to the temp files.
328 * At this point, set the done flag and leap back into the loop(s)
329 * until you're finished with the files and then leap to the
330 * section that installs the new files.
334 /* While we find things in the passwd file */
335 while (fgets(pwbuf
, NSS_LINELEN_PASSWD
, opwfp
)) {
338 * Is this the passwd entry we want?
339 * If not, then write it out to the new passwd temp file
340 * and remember our position.
342 if (doneflag
|| strncmp(name
, pwbuf
, namelen
)) {
343 if (fputs(pwbuf
, npwfp
) == EOF
) {
345 "yppasswdd: write to passwd file "
349 pwpos
= ftell(opwfp
);
358 syslog(LOG_ERR
, "yppasswdd: user %s does not exist\n", name
);
362 /* While we find things in the shadow file */
363 while (useshadow
&& fgets(spbuf
, NSS_LINELEN_SHADOW
, ospfp
)) {
366 * Is this the shadow entry that we want?
367 * If not, write it out to the new shadow temp file
368 * and remember our position.
370 if (doneflag
|| strncmp(name
, spbuf
, namelen
)) {
371 if (fputs(spbuf
, nspfp
) == EOF
) {
373 "yppasswdd: write to shadow file "
377 sppos
= ftell(ospfp
);
384 /* While we find things in the adjunct file */
385 while (useadjunct
&& fgets(adjbuf
, BUFSIZ
, oadjfp
)) {
388 * is this the adjunct entry that we want?
389 * If not, write it out to the new temp file
390 * and remember our position.
392 if (doneflag
|| strncmp(name
, adjbuf
, namelen
)) {
393 if (fputs(adjbuf
, nadjfp
) == EOF
) {
395 "yppasswdd: write to adjunct file "
408 if (useshadow
&& !gotshadow
) {
409 syslog(LOG_ERR
, "yppasswdd: no passwd in shadow for %s\n",
414 if (useadjunct
&& !gotadjunct
) {
415 syslog(LOG_ERR
, "yppasswdd: no passwd in adjunct for %s\n",
422 * Now that we've read in the correct passwd AND
423 * shadow lines, we'll rewind to the beginning of
424 * those lines and let the fget*ent() calls do
425 * the work. Since we are only working with the
426 * first two fields of the adjunct entry, leave
427 * it as a char array.
429 fseek(opwfp
, pwpos
, SEEK_SET
);
430 opwd
= *fgetpwent(opwfp
);
433 fseek(ospfp
, sppos
, SEEK_SET
);
434 ospwd
= *fgetspent(ospfp
);
437 oldpass_crypt
= cryptoldpasswd(yppwd
.oldpass
, newpw
.pw_passwd
,
439 if (oldpass_crypt
== NULL
) {
446 !((*p
++ == '#') && (*p
++ == '#') &&
447 (strcmp(p
, opwd
.pw_name
) == 0)) &&
448 (strcmp(oldpass_crypt
, newpw
.pw_passwd
) != 0))
450 oldpass_crypt
= NULL
;
452 if ((!noshell
) && (strcmp(opwd
.pw_shell
, newpw
.pw_shell
) != 0)) {
458 if ((!nogecos
) && (strcmp(opwd
.pw_gecos
, newpw
.pw_gecos
) != 0)) {
466 if (!(chpw
+ chsh
+ chgecos
)) {
467 syslog(LOG_NOTICE
, "yppasswdd: no change for %s\n",
473 if (useshadow
&& !root_on_master
) {
474 oldpass_crypt
= cryptoldpasswd(yppwd
.oldpass
, ospwd
.sp_pwdp
,
476 if (oldpass_crypt
== NULL
)
478 if (ospwd
.sp_pwdp
&& *ospwd
.sp_pwdp
&&
479 (strcmp(oldpass_crypt
, ospwd
.sp_pwdp
) != 0)) {
481 syslog(LOG_NOTICE
, "yppasswdd: passwd incorrect\n",
486 } else if (useadjunct
) {
488 * Clear the adj_encrypt array. Extract the encrypted passwd
489 * into adj_encrypt by setting adj_crypt_begin and
490 * adj_crypt_end to point at the first character of the
491 * encrypted passwd and the first character following the
492 * encrypted passwd in adjbuf, respectively, and copy the
493 * stuff between (there may not be anything) into adj_ecrypt.
494 * Then, check that adj_encrypt contains something and that
495 * the old passwd is correct.
497 memset(adj_encrypt
, 0, sizeof (adj_encrypt
));
498 adj_crypt_begin
= adjbuf
+ namelen
;
499 adj_crypt_end
= strchr(adj_crypt_begin
, ':');
500 strncpy(adj_encrypt
, adj_crypt_begin
,
501 adj_crypt_end
- adj_crypt_begin
);
502 oldpass_crypt
= cryptoldpasswd(yppwd
.oldpass
, adj_encrypt
,
504 if (oldpass_crypt
== NULL
)
506 if (!root_on_master
&& *adj_encrypt
&&
507 (strcmp(oldpass_crypt
, adj_encrypt
) != 0)) {
509 syslog(LOG_NOTICE
, "yppasswdd: passwd incorrect\n",
515 oldpass_crypt
= cryptoldpasswd(yppwd
.oldpass
, opwd
.pw_passwd
,
517 if (oldpass_crypt
== NULL
)
519 if (!root_on_master
&& opwd
.pw_passwd
&& *opwd
.pw_passwd
&&
520 (strcmp(oldpass_crypt
, opwd
.pw_passwd
) != 0)) {
522 syslog(LOG_NOTICE
, "yppasswdd: passwd incorrect\n",
530 printf("%d %d %d\n", chsh
, chgecos
, chpw
);
533 yppwd
.newpw
.pw_shell
,
534 yppwd
.newpw
.pw_gecos
,
535 yppwd
.newpw
.pw_passwd
);
544 !validloginshell(opwd
.pw_shell
, newpw
.pw_shell
, root_on_master
)) {
548 /* security hole fix from original source */
549 for (p
= newpw
.pw_name
; (*p
!= '\0'); p
++)
550 if ((*p
== ':') || !(isprint(*p
)))
551 *p
= '$'; /* you lose buckwheat */
552 for (p
= newpw
.pw_passwd
; (*p
!= '\0'); p
++)
553 if ((*p
== ':') || !(isprint(*p
)))
554 *p
= '$'; /* you lose buckwheat */
557 opwd
.pw_gecos
= newpw
.pw_gecos
;
560 opwd
.pw_shell
= newpw
.pw_shell
;
563 * If we're changing the shell or gecos fields and we're
564 * using a shadow or adjunct file or not changing the passwd
565 * then go ahead and update the passwd file. The case where
566 * the passwd is being changed and we are not using a shadow
567 * or adjunct file is handled later.
569 if ((chsh
|| chgecos
) && (useshadow
|| useadjunct
|| !chpw
) &&
570 putpwent(&opwd
, npwfp
)) {
572 syslog(LOG_ERR
, "yppasswdd: putpwent failed: %s\n",
579 ospwd
.sp_pwdp
= newpw
.pw_passwd
;
581 /* password aging - bug for bug compatibility */
582 if (ospwd
.sp_max
!= -1) {
583 if (now
< ospwd
.sp_lstchg
+ ospwd
.sp_min
) {
585 "yppasswdd: Sorry: < %ld days "
586 "since the last change.\n",
591 ospwd
.sp_lstchg
= now
;
592 if (putspent(&ospwd
, nspfp
)) {
594 "yppasswdd: putspent failed: %s\n",
598 } else if (useadjunct
) {
600 "%s%s%s", name
, newpw
.pw_passwd
, adj_crypt_end
);
601 if (fputs(adjbuf_new
, nadjfp
) == EOF
) {
603 "yppasswdd: write to adjunct failed: %s\n",
608 opwd
.pw_passwd
= newpw
.pw_passwd
;
609 if (putpwent(&opwd
, npwfp
)) {
611 "yppasswdd: putpwent failed: %s\n",
625 * Critical section, nothing special needs to be done since we
626 * hold exclusive access to the *.ptmp files
634 strcpy(tmppasswdfile
, passwd_file
);
635 strcat(tmppasswdfile
, "-");
637 strcpy(tmpshadowfile
, shadow_file
);
638 strcat(tmpshadowfile
, "-");
641 strcpy(tmpadjunctfile
, adjunct_file
);
642 strcat(tmpadjunctfile
, "-");
645 if ((!useshadow
&& !useadjunct
) || (chsh
|| chgecos
)) {
646 if (rename(passwd_file
, tmppasswdfile
) < 0) {
647 syslog(LOG_CRIT
, "yppasswdd: failed to backup "
651 if (rename(newpasswdfile
, passwd_file
) < 0) {
653 "yppasswdd: failed to mv passwd: %m");
654 if (rename(tmppasswdfile
, passwd_file
) < 0) {
656 "yppasswdd: failed to restore "
657 "backup of passwd file: %m");
664 if (useshadow
&& chpw
) {
665 if (rename(shadow_file
, tmpshadowfile
) < 0) {
666 syslog(LOG_CRIT
, "yppasswdd: failed to back up "
668 if (rename(tmppasswdfile
, passwd_file
) < 0) {
670 "yppasswdd: failed to restore "
671 "backup of passwd file: %m");
675 if (rename(newshadowfile
, shadow_file
) < 0) {
677 "yppasswdd: failed to mv shadow: %m");
678 if (rename(tmpshadowfile
, shadow_file
) < 0) {
680 "yppasswdd: failed to restore "
681 "backup of shadow file: %m");
683 if (rename(tmppasswdfile
, passwd_file
) < 0) {
685 "yppasswdd: failed to restore "
686 "backup of passwd file: %m");
691 } else if (useadjunct
&& chpw
) {
692 if (rename(adjunct_file
, tmpadjunctfile
) < 0) {
693 syslog(LOG_CRIT
, "yppasswdd: failed to back up "
695 if (rename(tmppasswdfile
, passwd_file
) < 0) {
697 "yppasswdd: failed to restore backup "
702 if (rename(newadjunctfile
, adjunct_file
) < 0) {
704 "yppassdd: failed to mv adjunct: %m");
705 if (rename(tmppasswdfile
, passwd_file
) < 0) {
707 "yppasswdd: failed to restore "
708 "backup of passwd file: %m");
710 if (rename(tmpadjunctfile
, adjunct_file
) < 0) {
712 "yppasswdd: failed to restore "
713 "backup of adjunct file: %m");
722 /* End critical section */
725 * Here we have come only after the new files have been successfully
726 * renamed to original files. At this point, the temp files would still
727 * be existing we need to remove them from the /etc directory
729 unlink(tmppasswdfile
);
731 unlink(tmpshadowfile
);
733 unlink(tmpadjunctfile
);
737 /* If we don't have opwfp, then we didn't do anything */
749 unlink(newpasswdfile
);
750 /* These tests are cheaper than failing syscalls */
752 unlink(newshadowfile
);
754 unlink(newadjunctfile
);
770 if (doneflag
&& mflag
) {
773 syslog(LOG_ERR
, "yppasswdd: Fork failed %m");
774 } else if (retval
== 0) {
775 strcpy(cmdbuf
, "/usr/ccs/bin/make");
776 for (i
= Mstart
+ 1; i
< Argc
; i
++) {
778 strcat(cmdbuf
, Argv
[i
]);
782 syslog(LOG_ERR
, "yppasswdd: about to "
783 "execute %s\n", cmdbuf
);
785 if (yplckpwdf() < 0) {
786 syslog(LOG_ERR
, "yppasswdd: Couldn't get the "
787 "lock to update the maps");
798 sigaction(SIGHUP
, &osa1
, NULL
);
799 sigaction(SIGINT
, &osa2
, NULL
);
800 sigaction(SIGQUIT
, &osa3
, NULL
);
802 if (!svc_sendreply(transp
, xdr_int
, (char *)&ans
))
804 "yppasswdd: couldn\'t reply to RPC call\n");