114062Swpaul/*
214062Swpaul * Copyright (c) 1995, 1996
314062Swpaul *	Bill Paul <wpaul@ctr.columbia.edu>.  All rights reserved.
414062Swpaul *
514062Swpaul * Redistribution and use in source and binary forms, with or without
614062Swpaul * modification, are permitted provided that the following conditions
714062Swpaul * are met:
814062Swpaul * 1. Redistributions of source code must retain the above copyright
914062Swpaul *    notice, this list of conditions and the following disclaimer.
1014062Swpaul * 2. Redistributions in binary form must reproduce the above copyright
1114062Swpaul *    notice, this list of conditions and the following disclaimer in the
1214062Swpaul *    documentation and/or other materials provided with the distribution.
1314062Swpaul * 3. All advertising materials mentioning features or use of this software
1414062Swpaul *    must display the following acknowledgement:
1514062Swpaul *	This product includes software developed by Bill Paul.
1614062Swpaul * 4. Neither the name of the author nor the names of any co-contributors
1714062Swpaul *    may be used to endorse or promote products derived from this software
1814062Swpaul *    without specific prior written permission.
1914062Swpaul *
2014062Swpaul * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND
2114062Swpaul * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2214062Swpaul * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2314062Swpaul * ARE DISCLAIMED.  IN NO EVENT SHALL Bill Paul OR CONTRIBUTORS BE LIABLE
2414062Swpaul * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2514062Swpaul * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2614062Swpaul * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2714062Swpaul * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2814062Swpaul * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2914062Swpaul * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3014062Swpaul * SUCH DAMAGE.
3114062Swpaul */
3214062Swpaul
33114601Sobrien#include <sys/cdefs.h>
34114601Sobrien__FBSDID("$FreeBSD$");
3530377Scharnier
3696222Sdes#include <sys/param.h>
3796222Sdes#include <sys/fcntl.h>
3896222Sdes#include <sys/socket.h>
3996222Sdes#include <sys/stat.h>
4096222Sdes#include <sys/wait.h>
4196222Sdes
4296222Sdes#include <arpa/inet.h>
4396222Sdes#include <netinet/in.h>
4496222Sdes
4514062Swpaul#include <ctype.h>
4696222Sdes#include <db.h>
4714062Swpaul#include <dirent.h>
4896222Sdes#include <errno.h>
4914062Swpaul#include <limits.h>
5014062Swpaul#include <pwd.h>
5114062Swpaul#include <signal.h>
5296222Sdes#include <stdio.h>
5396222Sdes#include <stdlib.h>
5496222Sdes#include <string.h>
5596222Sdes#include <unistd.h>
5696222Sdes
5796222Sdes#include <libgen.h>
5896222Sdes#include <libutil.h>
5996222Sdes
6014062Swpaul#include <rpc/rpc.h>
6114062Swpaul#include <rpcsvc/yp.h>
6296222Sdesstruct dom_binding;
6314062Swpaul#include <rpcsvc/ypclnt.h>
6414062Swpaul#include "yppasswdd_extern.h"
6514062Swpaul#include "yppasswd.h"
6614062Swpaul#include "yppasswd_private.h"
6796222Sdes#include "ypxfr_extern.h"
6896222Sdes#include "yp_extern.h"
6914062Swpaul
7014062Swpaulstatic struct passwd yp_password;
7114062Swpaul
7290298Sdesstatic void
73188766Simpxlate_passwd(struct x_master_passwd *xpwd, struct passwd *pwd)
74188766Simp{
75188766Simp	pwd->pw_name = xpwd->pw_name;
76188766Simp	pwd->pw_passwd = xpwd->pw_passwd;
77188766Simp	pwd->pw_uid = xpwd->pw_uid;
78188766Simp	pwd->pw_gid = xpwd->pw_gid;
79188766Simp	pwd->pw_change = xpwd->pw_change;
80188766Simp	pwd->pw_class = xpwd->pw_class;
81188766Simp	pwd->pw_gecos = xpwd->pw_gecos;
82188766Simp	pwd->pw_dir = xpwd->pw_dir;
83188766Simp	pwd->pw_shell = xpwd->pw_shell;
84188766Simp	pwd->pw_expire = xpwd->pw_expire;
85188766Simp	pwd->pw_fields = xpwd->pw_fields;
86188766Simp}
87188766Simp
88188766Simpstatic void
8990298Sdescopy_yp_pass(char *p, int x, int m)
9014062Swpaul{
9196222Sdes	char *t, *s = p;
9214062Swpaul	static char *buf;
9314062Swpaul
9414062Swpaul	yp_password.pw_fields = 0;
9514062Swpaul
9696222Sdes	buf = realloc(buf, m + 10);
9714062Swpaul	bzero(buf, m + 10);
9814062Swpaul
9914062Swpaul	/* Turn all colons into NULLs */
10014062Swpaul	while (strchr(s, ':')) {
10114062Swpaul		s = (strchr(s, ':') + 1);
10214062Swpaul		*(s - 1)= '\0';
10314062Swpaul	}
10414062Swpaul
10514062Swpaul	t = buf;
10614062Swpaul#define EXPAND(e)       e = t; while ((*t++ = *p++));
10714062Swpaul        EXPAND(yp_password.pw_name);
10814062Swpaul	yp_password.pw_fields |= _PWF_NAME;
10914062Swpaul        EXPAND(yp_password.pw_passwd);
11014062Swpaul	yp_password.pw_fields |= _PWF_PASSWD;
11114062Swpaul	yp_password.pw_uid = atoi(p);
11214062Swpaul        p += (strlen(p) + 1);
11314062Swpaul	yp_password.pw_fields |= _PWF_UID;
11414062Swpaul	yp_password.pw_gid = atoi(p);
11514062Swpaul        p += (strlen(p) + 1);
11614062Swpaul	yp_password.pw_fields |= _PWF_GID;
11714062Swpaul	if (x) {
11814062Swpaul		EXPAND(yp_password.pw_class);
11914062Swpaul		yp_password.pw_fields |= _PWF_CLASS;
12014062Swpaul		yp_password.pw_change = atol(p);
12114062Swpaul		p += (strlen(p) + 1);
12214062Swpaul		yp_password.pw_fields |= _PWF_CHANGE;
12314062Swpaul		yp_password.pw_expire = atol(p);
12414062Swpaul		p += (strlen(p) + 1);
12514062Swpaul		yp_password.pw_fields |= _PWF_EXPIRE;
12614062Swpaul	}
12714062Swpaul        EXPAND(yp_password.pw_gecos);
12814062Swpaul	yp_password.pw_fields |= _PWF_GECOS;
12914062Swpaul        EXPAND(yp_password.pw_dir);
13014062Swpaul	yp_password.pw_fields |= _PWF_DIR;
13114062Swpaul        EXPAND(yp_password.pw_shell);
13214062Swpaul	yp_password.pw_fields |= _PWF_SHELL;
13314062Swpaul
13414062Swpaul	return;
13514062Swpaul}
13614062Swpaul
13790298Sdesstatic int
13890298Sdesvalidchars(char *arg)
13914062Swpaul{
14096222Sdes	size_t i;
14114062Swpaul
14214062Swpaul	for (i = 0; i < strlen(arg); i++) {
14314062Swpaul		if (iscntrl(arg[i])) {
14414062Swpaul			yp_error("string contains a control character");
14514062Swpaul			return(1);
14614062Swpaul		}
14714062Swpaul		if (arg[i] == ':') {
14814062Swpaul			yp_error("string contains a colon");
14914062Swpaul			return(1);
15014062Swpaul		}
15114062Swpaul		/* Be evil: truncate strings with \n in them silently. */
15214062Swpaul		if (arg[i] == '\n') {
15314062Swpaul			arg[i] = '\0';
15414062Swpaul			return(0);
15514062Swpaul		}
15614062Swpaul	}
15714062Swpaul	return(0);
15814062Swpaul}
15914062Swpaul
16090298Sdesstatic int
16196222Sdesvalidate_master(struct passwd *opw __unused, struct x_master_passwd *npw)
16214062Swpaul{
16314062Swpaul
16414062Swpaul	if (npw->pw_name[0] == '+' || npw->pw_name[0] == '-') {
16514062Swpaul		yp_error("client tried to modify an NIS entry");
16614062Swpaul		return(1);
16714062Swpaul	}
16814062Swpaul
16914062Swpaul	if (validchars(npw->pw_shell)) {
17014062Swpaul		yp_error("specified shell contains invalid characters");
17114062Swpaul		return(1);
17214062Swpaul	}
17314062Swpaul
17414062Swpaul	if (validchars(npw->pw_gecos)) {
17514062Swpaul		yp_error("specified gecos field contains invalid characters");
17614062Swpaul		return(1);
17714062Swpaul	}
17814062Swpaul
17914062Swpaul	if (validchars(npw->pw_passwd)) {
18014062Swpaul		yp_error("specified password contains invalid characters");
18114062Swpaul		return(1);
18214062Swpaul	}
18314062Swpaul	return(0);
18414062Swpaul}
18514062Swpaul
18690298Sdesstatic int
18790298Sdesvalidate(struct passwd *opw, struct x_passwd *npw)
18814062Swpaul{
18914062Swpaul
19014062Swpaul	if (npw->pw_name[0] == '+' || npw->pw_name[0] == '-') {
19114062Swpaul		yp_error("client tried to modify an NIS entry");
19214062Swpaul		return(1);
19314062Swpaul	}
19414062Swpaul
19596222Sdes	if ((uid_t)npw->pw_uid != opw->pw_uid) {
19614062Swpaul		yp_error("UID mismatch: client says user %s has UID %d",
19714062Swpaul			 npw->pw_name, npw->pw_uid);
19814062Swpaul		yp_error("database says user %s has UID %d", opw->pw_name,
19914062Swpaul			 opw->pw_uid);
20014062Swpaul		return(1);
20114062Swpaul	}
20214062Swpaul
20396222Sdes	if ((gid_t)npw->pw_gid != opw->pw_gid) {
20414062Swpaul		yp_error("GID mismatch: client says user %s has GID %d",
20514062Swpaul			 npw->pw_name, npw->pw_gid);
20614062Swpaul		yp_error("database says user %s has GID %d", opw->pw_name,
20714062Swpaul			 opw->pw_gid);
20814062Swpaul		return(1);
20914062Swpaul	}
21014062Swpaul
21114062Swpaul	/*
21214062Swpaul	 * Don't allow the user to shoot himself in the foot,
21314062Swpaul	 * even on purpose.
21414062Swpaul	 */
21514062Swpaul	if (!ok_shell(npw->pw_shell)) {
21614062Swpaul		yp_error("%s is not a valid shell", npw->pw_shell);
21714062Swpaul		return(1);
21814062Swpaul	}
21914062Swpaul
22014062Swpaul	if (validchars(npw->pw_shell)) {
22114062Swpaul		yp_error("specified shell contains invalid characters");
22214062Swpaul		return(1);
22314062Swpaul	}
22414062Swpaul
22514062Swpaul	if (validchars(npw->pw_gecos)) {
22614062Swpaul		yp_error("specified gecos field contains invalid characters");
22714062Swpaul		return(1);
22814062Swpaul	}
22914062Swpaul
23014062Swpaul	if (validchars(npw->pw_passwd)) {
23114062Swpaul		yp_error("specified password contains invalid characters");
23214062Swpaul		return(1);
23314062Swpaul	}
23414062Swpaul	return(0);
23514062Swpaul}
23614062Swpaul
23714062Swpaul/*
23814062Swpaul * Kludge alert:
23914062Swpaul * In order to have one rpc.yppasswdd support multiple domains,
24014062Swpaul * we have to cheat: we search each directory under /var/yp
24114062Swpaul * and try to match the user in each master.passwd.byname
24214062Swpaul * map that we find. If the user matches (username, uid and gid
24314062Swpaul * all agree), then we use that domain. If we match the user in
24414062Swpaul * more than one database, we must abort.
24514062Swpaul */
24690298Sdesstatic char *
24790298Sdesfind_domain(struct x_passwd *pw)
24814062Swpaul{
24914062Swpaul	struct stat statbuf;
25014062Swpaul	struct dirent *dirp;
25114062Swpaul	DIR *dird;
25214062Swpaul	char yp_mapdir[MAXPATHLEN + 2];
25315687Swpaul	static char domain[YPMAXDOMAIN];
25414062Swpaul	char *tmp = NULL;
25514062Swpaul	DBT key, data;
25614062Swpaul	int hit = 0;
25714062Swpaul
25814062Swpaul	yp_error("performing multidomain lookup");
25914062Swpaul
26014062Swpaul	if ((dird = opendir(yp_dir)) == NULL) {
26114062Swpaul		yp_error("opendir(%s) failed: %s", yp_dir, strerror(errno));
26214062Swpaul		return(NULL);
26314062Swpaul	}
26414062Swpaul
26514062Swpaul	while ((dirp = readdir(dird)) != NULL) {
26696222Sdes		snprintf(yp_mapdir, sizeof yp_mapdir, "%s/%s",
26714062Swpaul							yp_dir, dirp->d_name);
26814062Swpaul		if (stat(yp_mapdir, &statbuf) < 0) {
26914062Swpaul			yp_error("stat(%s) failed: %s", yp_mapdir,
27014062Swpaul							strerror(errno));
27114062Swpaul			closedir(dird);
27214062Swpaul			return(NULL);
27314062Swpaul		}
27414062Swpaul		if (S_ISDIR(statbuf.st_mode)) {
27514062Swpaul			tmp = (char *)dirp->d_name;
27614062Swpaul			key.data = pw->pw_name;
27714062Swpaul			key.size = strlen(pw->pw_name);
27814062Swpaul
27914062Swpaul			if (yp_get_record(tmp,"master.passwd.byname",
28014062Swpaul			  		&key, &data, 0) != YP_TRUE) {
28114062Swpaul				continue;
28214062Swpaul			}
28396222Sdes			*((char *)data.data + data.size) = '\0';
28414062Swpaul			copy_yp_pass(data.data, 1, data.size);
28596222Sdes			if (yp_password.pw_uid == (uid_t)pw->pw_uid &&
28696222Sdes			    yp_password.pw_gid == (gid_t)pw->pw_gid) {
28714062Swpaul				hit++;
28815687Swpaul				snprintf(domain, YPMAXDOMAIN, "%s", tmp);
28914062Swpaul			}
29014062Swpaul		}
29114062Swpaul	}
29214062Swpaul
29314062Swpaul	closedir(dird);
29414062Swpaul	if (hit > 1) {
29514062Swpaul		yp_error("found same user in two different domains");
29614062Swpaul		return(NULL);
29714062Swpaul	} else
29816134Swpaul		return((char *)&domain);
29914062Swpaul}
30014062Swpaul
30196222Sdesstatic const char *maps[] = {
30296222Sdes	"master.passwd.byname",
30396222Sdes	"master.passwd.byuid",
30496222Sdes	"passwd.byname",
30596222Sdes	"passwd.byuid"
30696222Sdes};
30796222Sdes
30896222Sdesstatic const char *formats[] = {
30996222Sdes	"%s:%s:%d:%d:%s:%ld:%ld:%s:%s:%s",
31096222Sdes	"%s:%s:%d:%d:%s:%ld:%ld:%s:%s:%s",
31196222Sdes	"%s:%s:%d:%d:%s:%s:%s",
31296222Sdes	"%s:%s:%d:%d:%s:%s:%s"
31396222Sdes};
31496222Sdes
31590298Sdesstatic int
31690298Sdesupdate_inplace(struct passwd *pw, char *domain)
31716134Swpaul{
31816134Swpaul	DB *dbp = NULL;
31916134Swpaul	DBT key = { NULL, 0 };
32016134Swpaul	DBT data = { NULL, 0 };
32116134Swpaul	char pwbuf[YPMAXRECORD];
32216134Swpaul	char keybuf[20];
32319778Speter	int i;
32416134Swpaul	char *ptr = NULL;
32596222Sdes	static char yp_last[] = "YP_LAST_MODIFIED";
32616134Swpaul	char yplastbuf[YPMAXRECORD];
32716134Swpaul
32896222Sdes	snprintf(yplastbuf, sizeof yplastbuf, "%llu",
32996222Sdes	    (unsigned long long)time(NULL));
33016134Swpaul
33116134Swpaul	for (i = 0; i < 4; i++) {
33216134Swpaul
33316134Swpaul		if (i % 2) {
33496222Sdes			snprintf(keybuf, sizeof keybuf,
33596222Sdes			    "%llu", (unsigned long long)pw->pw_uid);
33696222Sdes			key.data = &keybuf;
33716134Swpaul			key.size = strlen(keybuf);
33816134Swpaul		} else {
33916134Swpaul			key.data = pw->pw_name;
34016134Swpaul			key.size = strlen(pw->pw_name);
34116134Swpaul		}
34216134Swpaul
34316134Swpaul		/*
34416134Swpaul		 * XXX The passwd.byname and passwd.byuid maps come in
34516134Swpaul		 * two flavors: secure and insecure. The secure version
34616134Swpaul		 * has a '*' in the password field whereas the insecure one
34716134Swpaul		 * has a real crypted password. The maps will be insecure
34816134Swpaul		 * if they were built with 'unsecure = TRUE' enabled in
34916134Swpaul		 * /var/yp/Makefile, but we'd have no way of knowing if
35016134Swpaul		 * this has been done unless we were to try parsing the
35116134Swpaul		 * Makefile, which is a disgusting thought. Instead, we
35216134Swpaul		 * read the records from the maps, skip to the first ':'
35316134Swpaul		 * in them, and then look at the character immediately
35416134Swpaul		 * following it. If it's an '*' then the map is 'secure'
35516134Swpaul		 * and we must not insert a real password into the pw_passwd
35616134Swpaul		 * field. If it's not an '*', then we put the real crypted
35716134Swpaul		 * password in.
35816134Swpaul		 */
35916134Swpaul		if (yp_get_record(domain,maps[i],&key,&data,1) != YP_TRUE) {
36016134Swpaul			yp_error("couldn't read %s/%s: %s", domain,
36116134Swpaul						maps[i], strerror(errno));
36216134Swpaul			return(1);
36316134Swpaul		}
36416134Swpaul
36516134Swpaul		if ((ptr = strchr(data.data, ':')) == NULL) {
36616134Swpaul			yp_error("no colon in passwd record?!");
36716134Swpaul			return(1);
36816134Swpaul		}
36916134Swpaul
37019138Swpaul		/*
37119138Swpaul		 * XXX Supposing we have more than one user with the same
37219138Swpaul		 * UID? (Or more than one user with the same name?) We could
37319138Swpaul		 * end up modifying the wrong record if were not careful.
37419138Swpaul		 */
37519138Swpaul		if (i % 2) {
37619138Swpaul			if (strncmp(data.data, pw->pw_name,
37719138Swpaul							strlen(pw->pw_name))) {
37819138Swpaul				yp_error("warning: found entry for UID %d \
37919138Swpaulin map %s@%s with wrong name (%.*s)", pw->pw_uid, maps[i], domain,
38096644Sdes				    (int)(ptr - (char *)data.data),
38196644Sdes				    (char *)data.data);
38219138Swpaul				yp_error("there may be more than one user \
38319138Swpaulwith the same UID - continuing");
38419138Swpaul				continue;
38519138Swpaul			}
38619138Swpaul		} else {
38719138Swpaul			/*
38819138Swpaul			 * We're really being ultra-paranoid here.
38919138Swpaul			 * This is generally a 'can't happen' condition.
39019138Swpaul			 */
39196222Sdes			snprintf(pwbuf, sizeof pwbuf, ":%d:%d:", pw->pw_uid,
39219138Swpaul								  pw->pw_gid);
39319138Swpaul			if (!strstr(data.data, pwbuf)) {
39419138Swpaul				yp_error("warning: found entry for user %s \
39519138Swpaulin map %s@%s with wrong UID", pw->pw_name, maps[i], domain);
39696389Salfred				yp_error("there may be more than one user \
39719138Swpaulwith the same name - continuing");
39819138Swpaul				continue;
39919138Swpaul			}
40019138Swpaul		}
40119138Swpaul
40216134Swpaul		if (i < 2) {
40396222Sdes			snprintf(pwbuf, sizeof pwbuf, formats[i],
40416134Swpaul			   pw->pw_name, pw->pw_passwd, pw->pw_uid,
40516134Swpaul			   pw->pw_gid, pw->pw_class, pw->pw_change,
40616134Swpaul			   pw->pw_expire, pw->pw_gecos, pw->pw_dir,
40716134Swpaul			   pw->pw_shell);
40816134Swpaul		} else {
40996222Sdes			snprintf(pwbuf, sizeof pwbuf, formats[i],
41016134Swpaul			   pw->pw_name, *(ptr+1) == '*' ? "*" : pw->pw_passwd,
41116134Swpaul			   pw->pw_uid, pw->pw_gid, pw->pw_gecos, pw->pw_dir,
41216134Swpaul			   pw->pw_shell);
41316134Swpaul		}
41416134Swpaul
41516134Swpaul#define FLAGS O_RDWR|O_CREAT
41616134Swpaul
41716134Swpaul		if ((dbp = yp_open_db_rw(domain, maps[i], FLAGS)) == NULL) {
41816134Swpaul			yp_error("couldn't open %s/%s r/w: %s",domain,
41916134Swpaul						maps[i],strerror(errno));
42016134Swpaul			return(1);
42116134Swpaul		}
42216134Swpaul
42316134Swpaul		data.data = pwbuf;
42416134Swpaul		data.size = strlen(pwbuf);
42516134Swpaul
42616134Swpaul		if (yp_put_record(dbp, &key, &data, 1) != YP_TRUE) {
42716134Swpaul			yp_error("failed to update record in %s/%s", domain,
42816134Swpaul								maps[i]);
42916134Swpaul			(void)(dbp->close)(dbp);
43016134Swpaul			return(1);
43116134Swpaul		}
43216134Swpaul
43316134Swpaul		key.data = yp_last;
43416134Swpaul		key.size = strlen(yp_last);
43516134Swpaul		data.data = (char *)&yplastbuf;
43616134Swpaul		data.size = strlen(yplastbuf);
43716134Swpaul
43816134Swpaul		if (yp_put_record(dbp, &key, &data, 1) != YP_TRUE) {
43916134Swpaul			yp_error("failed to update timestamp in %s/%s", domain,
44016134Swpaul								maps[i]);
44116134Swpaul			(void)(dbp->close)(dbp);
44216134Swpaul			return(1);
44316134Swpaul		}
44416134Swpaul
44516134Swpaul		(void)(dbp->close)(dbp);
44616134Swpaul	}
44716134Swpaul
44816134Swpaul	return(0);
44916134Swpaul}
45016134Swpaul
45114062Swpaulint *
45214062Swpaulyppasswdproc_update_1_svc(yppasswd *argp, struct svc_req *rqstp)
45314062Swpaul{
45414062Swpaul	static int  result;
45514062Swpaul	struct sockaddr_in *rqhost;
45614062Swpaul	DBT key, data;
45714062Swpaul	int rval = 0;
45814062Swpaul	int pfd, tfd;
45914062Swpaul	int pid;
46014062Swpaul	int passwd_changed = 0;
46114062Swpaul	int shell_changed = 0;
46214062Swpaul	int gecos_changed = 0;
46314062Swpaul	char *oldshell = NULL;
46414062Swpaul	char *oldgecos = NULL;
46514062Swpaul	char *passfile_hold;
46614062Swpaul	char passfile_buf[MAXPATHLEN + 2];
467116392Smbr	char passfile_hold_buf[MAXPATHLEN + 2];
46814062Swpaul	char *domain = yppasswd_domain;
46917431Swpaul	static struct sockaddr_in clntaddr;
47017431Swpaul	static struct timeval t_saved, t_test;
47114062Swpaul
47214062Swpaul	/*
47314062Swpaul	 * Normal user updates always use the 'default' master.passwd file.
47414062Swpaul	 */
47514062Swpaul
47614062Swpaul	passfile = passfile_default;
47714062Swpaul	result = 1;
47814062Swpaul
47914062Swpaul	rqhost = svc_getcaller(rqstp->rq_xprt);
48014062Swpaul
48117431Swpaul	gettimeofday(&t_test, NULL);
48296222Sdes	if (!bcmp(rqhost, &clntaddr, sizeof *rqhost) &&
48317431Swpaul		t_test.tv_sec > t_saved.tv_sec &&
48417431Swpaul		t_test.tv_sec - t_saved.tv_sec < 300) {
48517431Swpaul
48696222Sdes		bzero(&clntaddr, sizeof clntaddr);
48796222Sdes		bzero(&t_saved, sizeof t_saved);
48817431Swpaul		return(NULL);
48917431Swpaul	}
49017431Swpaul
49196222Sdes	bcopy(rqhost, &clntaddr, sizeof clntaddr);
49217431Swpaul	gettimeofday(&t_saved, NULL);
49317431Swpaul
49414241Swpaul	if (yp_access(resvport ? "master.passwd.byname" : NULL, rqstp)) {
49514241Swpaul		yp_error("rejected update request from unauthorized host");
49614241Swpaul		svcerr_auth(rqstp->rq_xprt, AUTH_BADCRED);
49714241Swpaul		return(&result);
49814241Swpaul	}
49914241Swpaul
50014062Swpaul	/*
50114062Swpaul	 * Step one: find the user. (It's kinda pointless to
50214062Swpaul	 * proceed if the user doesn't exist.) We look for the
50314062Swpaul	 * user in the master.passwd.byname database, _NOT_ by
50414062Swpaul	 * using getpwent() and friends! We can't use getpwent()
50514062Swpaul	 * since the NIS master server is not guaranteed to be
50614062Swpaul	 * configured as an NIS client.
50714062Swpaul	 */
50814062Swpaul
50914062Swpaul	if (multidomain) {
51014062Swpaul		if ((domain = find_domain(&argp->newpw)) == NULL) {
51114062Swpaul			yp_error("multidomain lookup failed - aborting update");
51214062Swpaul			return(&result);
51314062Swpaul		} else
51414062Swpaul			yp_error("updating user %s in domain %s",
51514062Swpaul					argp->newpw.pw_name, domain);
51614062Swpaul	}
51714062Swpaul
51814062Swpaul	key.data = argp->newpw.pw_name;
51914062Swpaul	key.size = strlen(argp->newpw.pw_name);
52014062Swpaul
52190297Sdes	if ((rval = yp_get_record(domain,"master.passwd.byname",
52214062Swpaul		  	&key, &data, 0)) != YP_TRUE) {
52314062Swpaul		if (rval == YP_NOKEY) {
52414062Swpaul			yp_error("user %s not found in passwd database",
52514062Swpaul			 	argp->newpw.pw_name);
52614062Swpaul		} else {
52714062Swpaul			yp_error("database access error: %s",
52814062Swpaul			 	yperr_string(rval));
52914062Swpaul		}
53014062Swpaul		return(&result);
53114062Swpaul	}
53214062Swpaul
53314062Swpaul	/* Nul terminate, please. */
53496222Sdes	*((char *)data.data + data.size) = '\0';
53514062Swpaul
53614062Swpaul	copy_yp_pass(data.data, 1, data.size);
53714062Swpaul
53814062Swpaul	/* Step 2: check that the supplied oldpass is valid. */
53914062Swpaul
54014062Swpaul	if (strcmp(crypt(argp->oldpass, yp_password.pw_passwd),
54114062Swpaul					yp_password.pw_passwd)) {
54214062Swpaul		yp_error("rejected change attempt -- bad password");
54314062Swpaul		yp_error("client address: %s username: %s",
54414062Swpaul			  inet_ntoa(rqhost->sin_addr),
54514062Swpaul			  argp->newpw.pw_name);
54614062Swpaul		return(&result);
54714062Swpaul	}
54814062Swpaul
54914062Swpaul	/* Step 3: validate the arguments passed to us by the client. */
55014062Swpaul
55114062Swpaul	if (validate(&yp_password, &argp->newpw)) {
55214062Swpaul		yp_error("rejecting change attempt: bad arguments");
55314062Swpaul		yp_error("client address: %s username: %s",
55414062Swpaul			 inet_ntoa(rqhost->sin_addr),
55514062Swpaul			 argp->newpw.pw_name);
55614062Swpaul		svcerr_decode(rqstp->rq_xprt);
55714062Swpaul		return(&result);
55814062Swpaul	}
55914062Swpaul
56014062Swpaul	/* Step 4: update the user's passwd structure. */
56114062Swpaul
56214062Swpaul	if (!no_chsh && strcmp(argp->newpw.pw_shell, yp_password.pw_shell)) {
56314062Swpaul		oldshell = yp_password.pw_shell;
56414062Swpaul		yp_password.pw_shell = argp->newpw.pw_shell;
56514062Swpaul		shell_changed++;
56614062Swpaul	}
56714062Swpaul
56814062Swpaul
56914062Swpaul	if (!no_chfn && strcmp(argp->newpw.pw_gecos, yp_password.pw_gecos)) {
57014062Swpaul		oldgecos = yp_password.pw_gecos;
57114062Swpaul		yp_password.pw_gecos = argp->newpw.pw_gecos;
57214062Swpaul		gecos_changed++;
57314062Swpaul	}
57414062Swpaul
57514062Swpaul	if (strcmp(argp->newpw.pw_passwd, yp_password.pw_passwd)) {
57614062Swpaul		yp_password.pw_passwd = argp->newpw.pw_passwd;
57718047Swpaul		yp_password.pw_change = 0;
57814062Swpaul		passwd_changed++;
57914062Swpaul	}
58014062Swpaul
58114062Swpaul	/*
58214062Swpaul	 * If the caller specified a domain other than our 'default'
58314062Swpaul	 * domain, change the path to master.passwd accordingly.
58414062Swpaul	 */
58514062Swpaul
58614062Swpaul	if (strcmp(domain, yppasswd_domain)) {
58714062Swpaul		snprintf(passfile_buf, sizeof(passfile_buf),
58816649Swpaul			"%s/%s/master.passwd", yp_dir, domain);
58914062Swpaul		passfile = (char *)&passfile_buf;
59014062Swpaul	}
59114062Swpaul
592116392Smbr	/*
593116392Smbr	 * Create a filename to hold the original master.passwd
594116392Smbr	 * so if our call to yppwupdate fails we can roll back
595116392Smbr	 */
596116392Smbr	snprintf(passfile_hold_buf, sizeof(passfile_hold_buf),
597116392Smbr	    "%s.hold", passfile);
598116392Smbr	passfile_hold = (char *)&passfile_hold_buf;
599116392Smbr
600116392Smbr
60114062Swpaul	/* Step 5: make a new password file with the updated info. */
60214062Swpaul
60396222Sdes	if (pw_init(dirname(passfile), passfile)) {
60496222Sdes		yp_error("pw_init() failed");
60596222Sdes		return &result;
60614062Swpaul	}
60796222Sdes	if ((pfd = pw_lock()) == -1) {
60896222Sdes		pw_fini();
60996222Sdes		yp_error("pw_lock() failed");
61096222Sdes		return &result;
61114062Swpaul	}
61296222Sdes	if ((tfd = pw_tmp(-1)) == -1) {
61396222Sdes		pw_fini();
61496222Sdes		yp_error("pw_tmp() failed");
61596222Sdes		return &result;
61614062Swpaul	}
61796222Sdes	if (pw_copy(pfd, tfd, &yp_password, NULL) == -1) {
61896222Sdes		pw_fini();
61996222Sdes		yp_error("pw_copy() failed");
62096222Sdes		return &result;
62114062Swpaul	}
622116392Smbr	if (rename(passfile, passfile_hold) == -1) {
62396222Sdes		pw_fini();
624116392Smbr		yp_error("rename of %s to %s failed", passfile,
625116392Smbr		    passfile_hold);
62696222Sdes		return &result;
62796222Sdes	}
628116392Smbr
629116392Smbr	if (strcmp(passfile, _PATH_MASTERPASSWD) == 0) {
630116392Smbr		/*
631116392Smbr		 * NIS server is exporting the system's master.passwd.
632116392Smbr		 * Call pw_mkdb to rebuild passwd and the .db files
633116392Smbr		 */
634116392Smbr		if (pw_mkdb(yp_password.pw_name) == -1) {
635116392Smbr			pw_fini();
636116392Smbr			yp_error("pw_mkdb() failed");
637116392Smbr			rename(passfile_hold, passfile);
638116392Smbr			return &result;
639116392Smbr		}
640116392Smbr	} else {
641116392Smbr		/*
642116392Smbr		 * NIS server is exporting a private master.passwd.
643116392Smbr		 * Rename tempfile into final location
644116392Smbr		 */
645116392Smbr		if (rename(pw_tempname(), passfile) == -1) {
646116392Smbr			pw_fini();
647116392Smbr			yp_error("rename of %s to %s failed",
648116392Smbr			    pw_tempname(), passfile);
649116392Smbr			rename(passfile_hold, passfile);
650116392Smbr			return &result;
651116392Smbr		}
652116392Smbr	}
653116392Smbr
65496222Sdes	pw_fini();
65514062Swpaul
65616134Swpaul	if (inplace) {
65716134Swpaul		if ((rval = update_inplace(&yp_password, domain))) {
65816134Swpaul			yp_error("inplace update failed -- rebuilding maps");
65916134Swpaul		}
66016134Swpaul	}
66116134Swpaul
66290297Sdes	switch ((pid = fork())) {
66314062Swpaul	case 0:
66416134Swpaul		if (inplace && !rval) {
66516134Swpaul    			execlp(MAP_UPDATE_PATH, MAP_UPDATE, passfile,
66679452Sbrian				yppasswd_domain, "pushpw", (char *)NULL);
66716134Swpaul		} else {
66816134Swpaul    			execlp(MAP_UPDATE_PATH, MAP_UPDATE, passfile,
66979452Sbrian				yppasswd_domain, (char *)NULL);
67016134Swpaul		}
67114062Swpaul    		yp_error("couldn't exec map update process: %s",
67214062Swpaul					strerror(errno));
67314062Swpaul		unlink(passfile);
67414062Swpaul		rename(passfile_hold, passfile);
67514062Swpaul    		exit(1);
67614062Swpaul		break;
67714062Swpaul	case -1:
67814062Swpaul		yp_error("fork() failed: %s", strerror(errno));
67914062Swpaul		unlink(passfile);
68014062Swpaul		rename(passfile_hold, passfile);
68117431Swpaul		return(&result);
68214062Swpaul		break;
68314062Swpaul	default:
68417431Swpaul		unlink(passfile_hold);
68514062Swpaul		break;
68614062Swpaul	}
68714062Swpaul
68814062Swpaul	if (verbose) {
689116392Smbr		yp_error("update completed for user %s (uid %d) in %s:",
690116392Smbr		    argp->newpw.pw_name, argp->newpw.pw_uid, passfile);
69114062Swpaul
69214062Swpaul		if (passwd_changed)
69314062Swpaul			yp_error("password changed");
69414062Swpaul
69514062Swpaul		if (gecos_changed)
69614062Swpaul			yp_error("gecos changed ('%s' -> '%s')",
69714062Swpaul					oldgecos, argp->newpw.pw_gecos);
69814062Swpaul
69914062Swpaul		if (shell_changed)
70014062Swpaul			yp_error("shell changed ('%s' -> '%s')",
70114062Swpaul					oldshell, argp->newpw.pw_shell);
70214062Swpaul	}
70314062Swpaul
70414062Swpaul	result = 0;
70514062Swpaul	return (&result);
70614062Swpaul}
70714062Swpaul
70814062Swpaul/*
70914062Swpaul * Note that this function performs a little less sanity checking
71014062Swpaul * than the last one. Since only the superuser is allowed to use it,
71114062Swpaul * it is assumed that the caller knows what he's doing.
71214062Swpaul */
71390298Sdesint *
71490298Sdesyppasswdproc_update_master_1_svc(master_yppasswd *argp,
71590298Sdes    struct svc_req *rqstp)
71614062Swpaul{
71727758Swpaul	static int result;
71814062Swpaul	int pfd, tfd;
71914062Swpaul	int pid;
72090253Salfred	uid_t uid;
72114062Swpaul	int rval = 0;
72214062Swpaul	DBT key, data;
72314062Swpaul	char *passfile_hold;
72414062Swpaul	char passfile_buf[MAXPATHLEN + 2];
725116421Smbr	char passfile_hold_buf[MAXPATHLEN + 2];
72627758Swpaul	struct sockaddr_in *rqhost;
72790253Salfred	SVCXPRT	*transp;
728188766Simp	struct passwd newpasswd;
72914062Swpaul
73014062Swpaul	result = 1;
73190253Salfred	transp = rqstp->rq_xprt;
73227758Swpaul
73327758Swpaul	/*
73427758Swpaul	 * NO AF_INET CONNETCIONS ALLOWED!
73527758Swpaul	 */
73690253Salfred	rqhost = svc_getcaller(transp);
73727758Swpaul	if (rqhost->sin_family != AF_UNIX) {
73827758Swpaul		yp_error("Alert! %s/%d attempted to use superuser-only \
73927758Swpaulprocedure!\n", inet_ntoa(rqhost->sin_addr), rqhost->sin_port);
74090253Salfred		svcerr_auth(transp, AUTH_BADCRED);
74127758Swpaul		return(&result);
74227758Swpaul	}
74327758Swpaul
74490253Salfred	if (rqstp->rq_cred.oa_flavor != AUTH_SYS) {
74527758Swpaul		yp_error("caller didn't send proper credentials");
74690253Salfred		svcerr_auth(transp, AUTH_BADCRED);
74727758Swpaul		return(&result);
74827758Swpaul	}
74927758Swpaul
75090253Salfred	if (__rpc_get_local_uid(transp, &uid) < 0) {
75127758Swpaul		yp_error("caller didn't send proper credentials");
75290253Salfred		svcerr_auth(transp, AUTH_BADCRED);
75327758Swpaul		return(&result);
75427758Swpaul	}
75590297Sdes
75690253Salfred	if (uid) {
75727758Swpaul		yp_error("caller euid is %d, expecting 0 -- rejecting request",
75890253Salfred		    uid);
75927758Swpaul		svcerr_auth(rqstp->rq_xprt, AUTH_BADCRED);
76027758Swpaul		return(&result);
76127758Swpaul	}
76227758Swpaul
76314062Swpaul	passfile = passfile_default;
76414062Swpaul
76514062Swpaul	key.data = argp->newpw.pw_name;
76614062Swpaul	key.size = strlen(argp->newpw.pw_name);
76714062Swpaul
76814062Swpaul	/*
76914062Swpaul	 * The superuser may add entries to the passwd maps if
77014062Swpaul	 * rpc.yppasswdd is started with the -a flag. Paranoia
77114062Swpaul	 * prevents me from allowing additions by default.
77214062Swpaul	 */
77314062Swpaul	if ((rval = yp_get_record(argp->domain, "master.passwd.byname",
77414062Swpaul			  &key, &data, 0)) != YP_TRUE) {
77514062Swpaul		if (rval == YP_NOKEY) {
77614062Swpaul			yp_error("user %s not found in passwd database",
77714062Swpaul				 argp->newpw.pw_name);
77814062Swpaul			if (allow_additions)
77914062Swpaul				yp_error("notice: adding user %s to \
78014062Swpaulmaster.passwd database for domain %s", argp->newpw.pw_name, argp->domain);
78114062Swpaul			else
78230377Scharnier				yp_error("restart rpc.yppasswdd with the -a flag to \
78330377Scharnierallow additions to be made to the password database");
78414062Swpaul		} else {
78514062Swpaul			yp_error("database access error: %s",
78614062Swpaul				 yperr_string(rval));
78714062Swpaul		}
78814062Swpaul		if (!allow_additions)
78927758Swpaul			return(&result);
79014062Swpaul	} else {
79114062Swpaul
79214062Swpaul		/* Nul terminate, please. */
79396222Sdes		*((char *)data.data + data.size) = '\0';
79414062Swpaul
79514062Swpaul		copy_yp_pass(data.data, 1, data.size);
79614062Swpaul	}
79714062Swpaul
79814062Swpaul	/*
79914062Swpaul	 * Perform a small bit of sanity checking.
80014062Swpaul	 */
80114062Swpaul	if (validate_master(rval == YP_TRUE ? &yp_password:NULL,&argp->newpw)){
80214062Swpaul		yp_error("rejecting update attempt for %s: bad arguments",
80314062Swpaul			 argp->newpw.pw_name);
80427758Swpaul		return(&result);
80514062Swpaul	}
80614062Swpaul
80714062Swpaul	/*
80814062Swpaul	 * If the caller specified a domain other than our 'default'
80914062Swpaul	 * domain, change the path to master.passwd accordingly.
81014062Swpaul	 */
81114062Swpaul
81214062Swpaul	if (strcmp(argp->domain, yppasswd_domain)) {
81314062Swpaul		snprintf(passfile_buf, sizeof(passfile_buf),
81416649Swpaul			"%s/%s/master.passwd", yp_dir, argp->domain);
81514062Swpaul		passfile = (char *)&passfile_buf;
81690297Sdes	}
81714062Swpaul
818116421Smbr	/*
819116421Smbr	 * Create a filename to hold the original master.passwd
820116421Smbr	 * so if our call to yppwupdate fails we can roll back
821116421Smbr	 */
822116421Smbr	snprintf(passfile_hold_buf, sizeof(passfile_hold_buf),
823116421Smbr	    "%s.hold", passfile);
824116421Smbr	passfile_hold = (char *)&passfile_hold_buf;
825116421Smbr
82696222Sdes	if (pw_init(dirname(passfile), passfile)) {
82796222Sdes		yp_error("pw_init() failed");
82896222Sdes		return &result;
82914062Swpaul	}
83096222Sdes	if ((pfd = pw_lock()) == -1) {
83196222Sdes		pw_fini();
83296222Sdes		yp_error("pw_lock() failed");
83396222Sdes		return &result;
83414062Swpaul	}
83596222Sdes	if ((tfd = pw_tmp(-1)) == -1) {
83696222Sdes		pw_fini();
83796222Sdes		yp_error("pw_tmp() failed");
83896222Sdes		return &result;
83914062Swpaul	}
840188766Simp	xlate_passwd(&argp->newpw, &newpasswd);
841188766Simp	if (pw_copy(pfd, tfd, &newpasswd, NULL) == -1) {
84296222Sdes		pw_fini();
84396222Sdes		yp_error("pw_copy() failed");
84496222Sdes		return &result;
84514062Swpaul	}
846116421Smbr	if (rename(passfile, passfile_hold) == -1) {
847116421Smbr		pw_fini();
848116421Smbr		yp_error("rename of %s to %s failed", passfile,
849116421Smbr		    passfile_hold);
850116421Smbr		return &result;
851116421Smbr	}
852116392Smbr	if (strcmp(passfile, _PATH_MASTERPASSWD) == 0) {
853116421Smbr		/*
854116421Smbr		 * NIS server is exporting the system's master.passwd.
855116421Smbr		 * Call pw_mkdb to rebuild passwd and the .db files
856116421Smbr		 */
857116392Smbr		if (pw_mkdb(argp->newpw.pw_name) == -1) {
858116392Smbr			pw_fini();
859116392Smbr			yp_error("pw_mkdb() failed");
860116421Smbr			rename(passfile_hold, passfile);
861116392Smbr			return &result;
862116392Smbr		}
863116421Smbr	} else {
864116421Smbr		/*
865116421Smbr		 * NIS server is exporting a private master.passwd.
866116421Smbr		 * Rename tempfile into final location
867116421Smbr		 */
868116421Smbr		if (rename(pw_tempname(), passfile) == -1) {
869116421Smbr			pw_fini();
870116421Smbr			yp_error("rename of %s to %s failed",
871116421Smbr			    pw_tempname(), passfile);
872116421Smbr			rename(passfile_hold, passfile);
873116421Smbr			return &result;
874116421Smbr		}
87596222Sdes	}
87696222Sdes	pw_fini();
87714062Swpaul
87816134Swpaul	if (inplace) {
879188766Simp		xlate_passwd(&argp->newpw, &newpasswd);
880188766Simp		if ((rval = update_inplace(&newpasswd, argp->domain))) {
88116134Swpaul			yp_error("inplace update failed -- rebuilding maps");
88216134Swpaul		}
88316134Swpaul	}
88416134Swpaul
88590297Sdes	switch ((pid = fork())) {
88614062Swpaul	case 0:
88716134Swpaul		if (inplace && !rval) {
88816134Swpaul    			execlp(MAP_UPDATE_PATH, MAP_UPDATE, passfile,
88979452Sbrian				argp->domain, "pushpw", (char *)NULL);
89016134Swpaul    		} else {
89116134Swpaul			execlp(MAP_UPDATE_PATH, MAP_UPDATE, passfile,
89279452Sbrian				argp->domain, (char *)NULL);
89316134Swpaul		}
89414062Swpaul    		yp_error("couldn't exec map update process: %s",
89514062Swpaul					strerror(errno));
89614062Swpaul		unlink(passfile);
89714062Swpaul		rename(passfile_hold, passfile);
89814062Swpaul    		exit(1);
89914062Swpaul		break;
90014062Swpaul	case -1:
90114062Swpaul		yp_error("fork() failed: %s", strerror(errno));
90214062Swpaul		unlink(passfile);
90314062Swpaul		rename(passfile_hold, passfile);
90427758Swpaul		return(&result);
90514062Swpaul		break;
90614062Swpaul	default:
90717431Swpaul		unlink(passfile_hold);
90814062Swpaul		break;
90914062Swpaul	}
91014062Swpaul
91114062Swpaul	yp_error("performed update of user %s (uid %d) domain %s",
91214062Swpaul						argp->newpw.pw_name,
91314062Swpaul						argp->newpw.pw_uid,
91414062Swpaul						argp->domain);
91514062Swpaul
91614062Swpaul	result = 0;
91727758Swpaul	return(&result);
91814062Swpaul}
919