yppasswdd_server.c revision 96389
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
3330377Scharnier#ifndef lint
3430377Scharnierstatic const char rcsid[] =
3550479Speter  "$FreeBSD: head/usr.sbin/rpc.yppasswdd/yppasswdd_server.c 96389 2002-05-11 03:54:21Z alfred $";
3630377Scharnier#endif /* not lint */
3730377Scharnier
3896222Sdes#include <sys/param.h>
3996222Sdes#include <sys/fcntl.h>
4096222Sdes#include <sys/socket.h>
4196222Sdes#include <sys/stat.h>
4296222Sdes#include <sys/wait.h>
4396222Sdes
4496222Sdes#include <arpa/inet.h>
4596222Sdes#include <netinet/in.h>
4696222Sdes
4714062Swpaul#include <ctype.h>
4896222Sdes#include <db.h>
4914062Swpaul#include <dirent.h>
5096222Sdes#include <errno.h>
5114062Swpaul#include <limits.h>
5214062Swpaul#include <pwd.h>
5314062Swpaul#include <signal.h>
5496222Sdes#include <stdio.h>
5596222Sdes#include <stdlib.h>
5696222Sdes#include <string.h>
5796222Sdes#include <unistd.h>
5896222Sdes
5996222Sdes#include <libgen.h>
6096222Sdes#include <libutil.h>
6196222Sdes
6214062Swpaul#include <rpc/rpc.h>
6314062Swpaul#include <rpcsvc/yp.h>
6496222Sdesstruct dom_binding;
6514062Swpaul#include <rpcsvc/ypclnt.h>
6614062Swpaul#include "yppasswdd_extern.h"
6714062Swpaul#include "yppasswd.h"
6814062Swpaul#include "yppasswd_private.h"
6996222Sdes#include "ypxfr_extern.h"
7096222Sdes#include "yp_extern.h"
7114062Swpaul
7214062Swpaulstatic struct passwd yp_password;
7314062Swpaul
7490298Sdesstatic void
7590298Sdescopy_yp_pass(char *p, int x, int m)
7614062Swpaul{
7796222Sdes	char *t, *s = p;
7814062Swpaul	static char *buf;
7914062Swpaul
8014062Swpaul	yp_password.pw_fields = 0;
8114062Swpaul
8296222Sdes	buf = realloc(buf, m + 10);
8314062Swpaul	bzero(buf, m + 10);
8414062Swpaul
8514062Swpaul	/* Turn all colons into NULLs */
8614062Swpaul	while (strchr(s, ':')) {
8714062Swpaul		s = (strchr(s, ':') + 1);
8814062Swpaul		*(s - 1)= '\0';
8914062Swpaul	}
9014062Swpaul
9114062Swpaul	t = buf;
9214062Swpaul#define EXPAND(e)       e = t; while ((*t++ = *p++));
9314062Swpaul        EXPAND(yp_password.pw_name);
9414062Swpaul	yp_password.pw_fields |= _PWF_NAME;
9514062Swpaul        EXPAND(yp_password.pw_passwd);
9614062Swpaul	yp_password.pw_fields |= _PWF_PASSWD;
9714062Swpaul	yp_password.pw_uid = atoi(p);
9814062Swpaul        p += (strlen(p) + 1);
9914062Swpaul	yp_password.pw_fields |= _PWF_UID;
10014062Swpaul	yp_password.pw_gid = atoi(p);
10114062Swpaul        p += (strlen(p) + 1);
10214062Swpaul	yp_password.pw_fields |= _PWF_GID;
10314062Swpaul	if (x) {
10414062Swpaul		EXPAND(yp_password.pw_class);
10514062Swpaul		yp_password.pw_fields |= _PWF_CLASS;
10614062Swpaul		yp_password.pw_change = atol(p);
10714062Swpaul		p += (strlen(p) + 1);
10814062Swpaul		yp_password.pw_fields |= _PWF_CHANGE;
10914062Swpaul		yp_password.pw_expire = atol(p);
11014062Swpaul		p += (strlen(p) + 1);
11114062Swpaul		yp_password.pw_fields |= _PWF_EXPIRE;
11214062Swpaul	}
11314062Swpaul        EXPAND(yp_password.pw_gecos);
11414062Swpaul	yp_password.pw_fields |= _PWF_GECOS;
11514062Swpaul        EXPAND(yp_password.pw_dir);
11614062Swpaul	yp_password.pw_fields |= _PWF_DIR;
11714062Swpaul        EXPAND(yp_password.pw_shell);
11814062Swpaul	yp_password.pw_fields |= _PWF_SHELL;
11914062Swpaul
12014062Swpaul	return;
12114062Swpaul}
12214062Swpaul
12390298Sdesstatic int
12490298Sdesvalidchars(char *arg)
12514062Swpaul{
12696222Sdes	size_t i;
12714062Swpaul
12814062Swpaul	for (i = 0; i < strlen(arg); i++) {
12914062Swpaul		if (iscntrl(arg[i])) {
13014062Swpaul			yp_error("string contains a control character");
13114062Swpaul			return(1);
13214062Swpaul		}
13314062Swpaul		if (arg[i] == ':') {
13414062Swpaul			yp_error("string contains a colon");
13514062Swpaul			return(1);
13614062Swpaul		}
13714062Swpaul		/* Be evil: truncate strings with \n in them silently. */
13814062Swpaul		if (arg[i] == '\n') {
13914062Swpaul			arg[i] = '\0';
14014062Swpaul			return(0);
14114062Swpaul		}
14214062Swpaul	}
14314062Swpaul	return(0);
14414062Swpaul}
14514062Swpaul
14690298Sdesstatic int
14796222Sdesvalidate_master(struct passwd *opw __unused, struct x_master_passwd *npw)
14814062Swpaul{
14914062Swpaul
15014062Swpaul	if (npw->pw_name[0] == '+' || npw->pw_name[0] == '-') {
15114062Swpaul		yp_error("client tried to modify an NIS entry");
15214062Swpaul		return(1);
15314062Swpaul	}
15414062Swpaul
15514062Swpaul	if (validchars(npw->pw_shell)) {
15614062Swpaul		yp_error("specified shell contains invalid characters");
15714062Swpaul		return(1);
15814062Swpaul	}
15914062Swpaul
16014062Swpaul	if (validchars(npw->pw_gecos)) {
16114062Swpaul		yp_error("specified gecos field contains invalid characters");
16214062Swpaul		return(1);
16314062Swpaul	}
16414062Swpaul
16514062Swpaul	if (validchars(npw->pw_passwd)) {
16614062Swpaul		yp_error("specified password contains invalid characters");
16714062Swpaul		return(1);
16814062Swpaul	}
16914062Swpaul	return(0);
17014062Swpaul}
17114062Swpaul
17290298Sdesstatic int
17390298Sdesvalidate(struct passwd *opw, struct x_passwd *npw)
17414062Swpaul{
17514062Swpaul
17614062Swpaul	if (npw->pw_name[0] == '+' || npw->pw_name[0] == '-') {
17714062Swpaul		yp_error("client tried to modify an NIS entry");
17814062Swpaul		return(1);
17914062Swpaul	}
18014062Swpaul
18196222Sdes	if ((uid_t)npw->pw_uid != opw->pw_uid) {
18214062Swpaul		yp_error("UID mismatch: client says user %s has UID %d",
18314062Swpaul			 npw->pw_name, npw->pw_uid);
18414062Swpaul		yp_error("database says user %s has UID %d", opw->pw_name,
18514062Swpaul			 opw->pw_uid);
18614062Swpaul		return(1);
18714062Swpaul	}
18814062Swpaul
18996222Sdes	if ((gid_t)npw->pw_gid != opw->pw_gid) {
19014062Swpaul		yp_error("GID mismatch: client says user %s has GID %d",
19114062Swpaul			 npw->pw_name, npw->pw_gid);
19214062Swpaul		yp_error("database says user %s has GID %d", opw->pw_name,
19314062Swpaul			 opw->pw_gid);
19414062Swpaul		return(1);
19514062Swpaul	}
19614062Swpaul
19714062Swpaul	/*
19814062Swpaul	 * Don't allow the user to shoot himself in the foot,
19914062Swpaul	 * even on purpose.
20014062Swpaul	 */
20114062Swpaul	if (!ok_shell(npw->pw_shell)) {
20214062Swpaul		yp_error("%s is not a valid shell", npw->pw_shell);
20314062Swpaul		return(1);
20414062Swpaul	}
20514062Swpaul
20614062Swpaul	if (validchars(npw->pw_shell)) {
20714062Swpaul		yp_error("specified shell contains invalid characters");
20814062Swpaul		return(1);
20914062Swpaul	}
21014062Swpaul
21114062Swpaul	if (validchars(npw->pw_gecos)) {
21214062Swpaul		yp_error("specified gecos field contains invalid characters");
21314062Swpaul		return(1);
21414062Swpaul	}
21514062Swpaul
21614062Swpaul	if (validchars(npw->pw_passwd)) {
21714062Swpaul		yp_error("specified password contains invalid characters");
21814062Swpaul		return(1);
21914062Swpaul	}
22014062Swpaul	return(0);
22114062Swpaul}
22214062Swpaul
22314062Swpaul/*
22414062Swpaul * Kludge alert:
22514062Swpaul * In order to have one rpc.yppasswdd support multiple domains,
22614062Swpaul * we have to cheat: we search each directory under /var/yp
22714062Swpaul * and try to match the user in each master.passwd.byname
22814062Swpaul * map that we find. If the user matches (username, uid and gid
22914062Swpaul * all agree), then we use that domain. If we match the user in
23014062Swpaul * more than one database, we must abort.
23114062Swpaul */
23290298Sdesstatic char *
23390298Sdesfind_domain(struct x_passwd *pw)
23414062Swpaul{
23514062Swpaul	struct stat statbuf;
23614062Swpaul	struct dirent *dirp;
23714062Swpaul	DIR *dird;
23814062Swpaul	char yp_mapdir[MAXPATHLEN + 2];
23915687Swpaul	static char domain[YPMAXDOMAIN];
24014062Swpaul	char *tmp = NULL;
24114062Swpaul	DBT key, data;
24214062Swpaul	int hit = 0;
24314062Swpaul
24414062Swpaul	yp_error("performing multidomain lookup");
24514062Swpaul
24614062Swpaul	if ((dird = opendir(yp_dir)) == NULL) {
24714062Swpaul		yp_error("opendir(%s) failed: %s", yp_dir, strerror(errno));
24814062Swpaul		return(NULL);
24914062Swpaul	}
25014062Swpaul
25114062Swpaul	while ((dirp = readdir(dird)) != NULL) {
25296222Sdes		snprintf(yp_mapdir, sizeof yp_mapdir, "%s/%s",
25314062Swpaul							yp_dir, dirp->d_name);
25414062Swpaul		if (stat(yp_mapdir, &statbuf) < 0) {
25514062Swpaul			yp_error("stat(%s) failed: %s", yp_mapdir,
25614062Swpaul							strerror(errno));
25714062Swpaul			closedir(dird);
25814062Swpaul			return(NULL);
25914062Swpaul		}
26014062Swpaul		if (S_ISDIR(statbuf.st_mode)) {
26114062Swpaul			tmp = (char *)dirp->d_name;
26214062Swpaul			key.data = pw->pw_name;
26314062Swpaul			key.size = strlen(pw->pw_name);
26414062Swpaul
26514062Swpaul			if (yp_get_record(tmp,"master.passwd.byname",
26614062Swpaul			  		&key, &data, 0) != YP_TRUE) {
26714062Swpaul				continue;
26814062Swpaul			}
26996222Sdes			*((char *)data.data + data.size) = '\0';
27014062Swpaul			copy_yp_pass(data.data, 1, data.size);
27196222Sdes			if (yp_password.pw_uid == (uid_t)pw->pw_uid &&
27296222Sdes			    yp_password.pw_gid == (gid_t)pw->pw_gid) {
27314062Swpaul				hit++;
27415687Swpaul				snprintf(domain, YPMAXDOMAIN, "%s", tmp);
27514062Swpaul			}
27614062Swpaul		}
27714062Swpaul	}
27814062Swpaul
27914062Swpaul	closedir(dird);
28014062Swpaul	if (hit > 1) {
28114062Swpaul		yp_error("found same user in two different domains");
28214062Swpaul		return(NULL);
28314062Swpaul	} else
28416134Swpaul		return((char *)&domain);
28514062Swpaul}
28614062Swpaul
28796222Sdesstatic const char *maps[] = {
28896222Sdes	"master.passwd.byname",
28996222Sdes	"master.passwd.byuid",
29096222Sdes	"passwd.byname",
29196222Sdes	"passwd.byuid"
29296222Sdes};
29396222Sdes
29496222Sdesstatic const char *formats[] = {
29596222Sdes	"%s:%s:%d:%d:%s:%ld:%ld:%s:%s:%s",
29696222Sdes	"%s:%s:%d:%d:%s:%ld:%ld:%s:%s:%s",
29796222Sdes	"%s:%s:%d:%d:%s:%s:%s",
29896222Sdes	"%s:%s:%d:%d:%s:%s:%s"
29996222Sdes};
30096222Sdes
30190298Sdesstatic int
30290298Sdesupdate_inplace(struct passwd *pw, char *domain)
30316134Swpaul{
30416134Swpaul	DB *dbp = NULL;
30516134Swpaul	DBT key = { NULL, 0 };
30616134Swpaul	DBT data = { NULL, 0 };
30716134Swpaul	char pwbuf[YPMAXRECORD];
30816134Swpaul	char keybuf[20];
30919778Speter	int i;
31016134Swpaul	char *ptr = NULL;
31196222Sdes	static char yp_last[] = "YP_LAST_MODIFIED";
31216134Swpaul	char yplastbuf[YPMAXRECORD];
31316134Swpaul
31496222Sdes	snprintf(yplastbuf, sizeof yplastbuf, "%llu",
31596222Sdes	    (unsigned long long)time(NULL));
31616134Swpaul
31716134Swpaul	for (i = 0; i < 4; i++) {
31816134Swpaul
31916134Swpaul		if (i % 2) {
32096222Sdes			snprintf(keybuf, sizeof keybuf,
32196222Sdes			    "%llu", (unsigned long long)pw->pw_uid);
32296222Sdes			key.data = &keybuf;
32316134Swpaul			key.size = strlen(keybuf);
32416134Swpaul		} else {
32516134Swpaul			key.data = pw->pw_name;
32616134Swpaul			key.size = strlen(pw->pw_name);
32716134Swpaul		}
32816134Swpaul
32916134Swpaul		/*
33016134Swpaul		 * XXX The passwd.byname and passwd.byuid maps come in
33116134Swpaul		 * two flavors: secure and insecure. The secure version
33216134Swpaul		 * has a '*' in the password field whereas the insecure one
33316134Swpaul		 * has a real crypted password. The maps will be insecure
33416134Swpaul		 * if they were built with 'unsecure = TRUE' enabled in
33516134Swpaul		 * /var/yp/Makefile, but we'd have no way of knowing if
33616134Swpaul		 * this has been done unless we were to try parsing the
33716134Swpaul		 * Makefile, which is a disgusting thought. Instead, we
33816134Swpaul		 * read the records from the maps, skip to the first ':'
33916134Swpaul		 * in them, and then look at the character immediately
34016134Swpaul		 * following it. If it's an '*' then the map is 'secure'
34116134Swpaul		 * and we must not insert a real password into the pw_passwd
34216134Swpaul		 * field. If it's not an '*', then we put the real crypted
34316134Swpaul		 * password in.
34416134Swpaul		 */
34516134Swpaul		if (yp_get_record(domain,maps[i],&key,&data,1) != YP_TRUE) {
34616134Swpaul			yp_error("couldn't read %s/%s: %s", domain,
34716134Swpaul						maps[i], strerror(errno));
34816134Swpaul			return(1);
34916134Swpaul		}
35016134Swpaul
35116134Swpaul		if ((ptr = strchr(data.data, ':')) == NULL) {
35216134Swpaul			yp_error("no colon in passwd record?!");
35316134Swpaul			return(1);
35416134Swpaul		}
35516134Swpaul
35619138Swpaul		/*
35719138Swpaul		 * XXX Supposing we have more than one user with the same
35819138Swpaul		 * UID? (Or more than one user with the same name?) We could
35919138Swpaul		 * end up modifying the wrong record if were not careful.
36019138Swpaul		 */
36119138Swpaul		if (i % 2) {
36219138Swpaul			if (strncmp(data.data, pw->pw_name,
36319138Swpaul							strlen(pw->pw_name))) {
36419138Swpaul				yp_error("warning: found entry for UID %d \
36519138Swpaulin map %s@%s with wrong name (%.*s)", pw->pw_uid, maps[i], domain,
36696222Sdes				    ptr - (char *)data.data, (char *)data.data);
36719138Swpaul				yp_error("there may be more than one user \
36819138Swpaulwith the same UID - continuing");
36919138Swpaul				continue;
37019138Swpaul			}
37119138Swpaul		} else {
37219138Swpaul			/*
37319138Swpaul			 * We're really being ultra-paranoid here.
37419138Swpaul			 * This is generally a 'can't happen' condition.
37519138Swpaul			 */
37696222Sdes			snprintf(pwbuf, sizeof pwbuf, ":%d:%d:", pw->pw_uid,
37719138Swpaul								  pw->pw_gid);
37819138Swpaul			if (!strstr(data.data, pwbuf)) {
37919138Swpaul				yp_error("warning: found entry for user %s \
38019138Swpaulin map %s@%s with wrong UID", pw->pw_name, maps[i], domain);
38196389Salfred				yp_error("there may be more than one user \
38219138Swpaulwith the same name - continuing");
38319138Swpaul				continue;
38419138Swpaul			}
38519138Swpaul		}
38619138Swpaul
38716134Swpaul		if (i < 2) {
38896222Sdes			snprintf(pwbuf, sizeof pwbuf, formats[i],
38916134Swpaul			   pw->pw_name, pw->pw_passwd, pw->pw_uid,
39016134Swpaul			   pw->pw_gid, pw->pw_class, pw->pw_change,
39116134Swpaul			   pw->pw_expire, pw->pw_gecos, pw->pw_dir,
39216134Swpaul			   pw->pw_shell);
39316134Swpaul		} else {
39496222Sdes			snprintf(pwbuf, sizeof pwbuf, formats[i],
39516134Swpaul			   pw->pw_name, *(ptr+1) == '*' ? "*" : pw->pw_passwd,
39616134Swpaul			   pw->pw_uid, pw->pw_gid, pw->pw_gecos, pw->pw_dir,
39716134Swpaul			   pw->pw_shell);
39816134Swpaul		}
39916134Swpaul
40016134Swpaul#define FLAGS O_RDWR|O_CREAT
40116134Swpaul
40216134Swpaul		if ((dbp = yp_open_db_rw(domain, maps[i], FLAGS)) == NULL) {
40316134Swpaul			yp_error("couldn't open %s/%s r/w: %s",domain,
40416134Swpaul						maps[i],strerror(errno));
40516134Swpaul			return(1);
40616134Swpaul		}
40716134Swpaul
40816134Swpaul		data.data = pwbuf;
40916134Swpaul		data.size = strlen(pwbuf);
41016134Swpaul
41116134Swpaul		if (yp_put_record(dbp, &key, &data, 1) != YP_TRUE) {
41216134Swpaul			yp_error("failed to update record in %s/%s", domain,
41316134Swpaul								maps[i]);
41416134Swpaul			(void)(dbp->close)(dbp);
41516134Swpaul			return(1);
41616134Swpaul		}
41716134Swpaul
41816134Swpaul		key.data = yp_last;
41916134Swpaul		key.size = strlen(yp_last);
42016134Swpaul		data.data = (char *)&yplastbuf;
42116134Swpaul		data.size = strlen(yplastbuf);
42216134Swpaul
42316134Swpaul		if (yp_put_record(dbp, &key, &data, 1) != YP_TRUE) {
42416134Swpaul			yp_error("failed to update timestamp in %s/%s", domain,
42516134Swpaul								maps[i]);
42616134Swpaul			(void)(dbp->close)(dbp);
42716134Swpaul			return(1);
42816134Swpaul		}
42916134Swpaul
43016134Swpaul		(void)(dbp->close)(dbp);
43116134Swpaul	}
43216134Swpaul
43316134Swpaul	return(0);
43416134Swpaul}
43516134Swpaul
43614062Swpaulint *
43714062Swpaulyppasswdproc_update_1_svc(yppasswd *argp, struct svc_req *rqstp)
43814062Swpaul{
43914062Swpaul	static int  result;
44014062Swpaul	struct sockaddr_in *rqhost;
44114062Swpaul	DBT key, data;
44214062Swpaul	int rval = 0;
44314062Swpaul	int pfd, tfd;
44414062Swpaul	int pid;
44514062Swpaul	int passwd_changed = 0;
44614062Swpaul	int shell_changed = 0;
44714062Swpaul	int gecos_changed = 0;
44814062Swpaul	char *oldshell = NULL;
44914062Swpaul	char *oldgecos = NULL;
45014062Swpaul	char *passfile_hold;
45114062Swpaul	char passfile_buf[MAXPATHLEN + 2];
45214062Swpaul	char *domain = yppasswd_domain;
45317431Swpaul	static struct sockaddr_in clntaddr;
45417431Swpaul	static struct timeval t_saved, t_test;
45514062Swpaul
45614062Swpaul	/*
45714062Swpaul	 * Normal user updates always use the 'default' master.passwd file.
45814062Swpaul	 */
45914062Swpaul
46014062Swpaul	passfile = passfile_default;
46114062Swpaul	result = 1;
46214062Swpaul
46314062Swpaul	rqhost = svc_getcaller(rqstp->rq_xprt);
46414062Swpaul
46517431Swpaul	gettimeofday(&t_test, NULL);
46696222Sdes	if (!bcmp(rqhost, &clntaddr, sizeof *rqhost) &&
46717431Swpaul		t_test.tv_sec > t_saved.tv_sec &&
46817431Swpaul		t_test.tv_sec - t_saved.tv_sec < 300) {
46917431Swpaul
47096222Sdes		bzero(&clntaddr, sizeof clntaddr);
47196222Sdes		bzero(&t_saved, sizeof t_saved);
47217431Swpaul		return(NULL);
47317431Swpaul	}
47417431Swpaul
47596222Sdes	bcopy(rqhost, &clntaddr, sizeof clntaddr);
47617431Swpaul	gettimeofday(&t_saved, NULL);
47717431Swpaul
47814241Swpaul	if (yp_access(resvport ? "master.passwd.byname" : NULL, rqstp)) {
47914241Swpaul		yp_error("rejected update request from unauthorized host");
48014241Swpaul		svcerr_auth(rqstp->rq_xprt, AUTH_BADCRED);
48114241Swpaul		return(&result);
48214241Swpaul	}
48314241Swpaul
48414062Swpaul	/*
48514062Swpaul	 * Step one: find the user. (It's kinda pointless to
48614062Swpaul	 * proceed if the user doesn't exist.) We look for the
48714062Swpaul	 * user in the master.passwd.byname database, _NOT_ by
48814062Swpaul	 * using getpwent() and friends! We can't use getpwent()
48914062Swpaul	 * since the NIS master server is not guaranteed to be
49014062Swpaul	 * configured as an NIS client.
49114062Swpaul	 */
49214062Swpaul
49314062Swpaul	if (multidomain) {
49414062Swpaul		if ((domain = find_domain(&argp->newpw)) == NULL) {
49514062Swpaul			yp_error("multidomain lookup failed - aborting update");
49614062Swpaul			return(&result);
49714062Swpaul		} else
49814062Swpaul			yp_error("updating user %s in domain %s",
49914062Swpaul					argp->newpw.pw_name, domain);
50014062Swpaul	}
50114062Swpaul
50214062Swpaul	key.data = argp->newpw.pw_name;
50314062Swpaul	key.size = strlen(argp->newpw.pw_name);
50414062Swpaul
50590297Sdes	if ((rval = yp_get_record(domain,"master.passwd.byname",
50614062Swpaul		  	&key, &data, 0)) != YP_TRUE) {
50714062Swpaul		if (rval == YP_NOKEY) {
50814062Swpaul			yp_error("user %s not found in passwd database",
50914062Swpaul			 	argp->newpw.pw_name);
51014062Swpaul		} else {
51114062Swpaul			yp_error("database access error: %s",
51214062Swpaul			 	yperr_string(rval));
51314062Swpaul		}
51414062Swpaul		return(&result);
51514062Swpaul	}
51614062Swpaul
51714062Swpaul	/* Nul terminate, please. */
51896222Sdes	*((char *)data.data + data.size) = '\0';
51914062Swpaul
52014062Swpaul	copy_yp_pass(data.data, 1, data.size);
52114062Swpaul
52214062Swpaul	/* Step 2: check that the supplied oldpass is valid. */
52314062Swpaul
52414062Swpaul	if (strcmp(crypt(argp->oldpass, yp_password.pw_passwd),
52514062Swpaul					yp_password.pw_passwd)) {
52614062Swpaul		yp_error("rejected change attempt -- bad password");
52714062Swpaul		yp_error("client address: %s username: %s",
52814062Swpaul			  inet_ntoa(rqhost->sin_addr),
52914062Swpaul			  argp->newpw.pw_name);
53014062Swpaul		return(&result);
53114062Swpaul	}
53214062Swpaul
53314062Swpaul	/* Step 3: validate the arguments passed to us by the client. */
53414062Swpaul
53514062Swpaul	if (validate(&yp_password, &argp->newpw)) {
53614062Swpaul		yp_error("rejecting change attempt: bad arguments");
53714062Swpaul		yp_error("client address: %s username: %s",
53814062Swpaul			 inet_ntoa(rqhost->sin_addr),
53914062Swpaul			 argp->newpw.pw_name);
54014062Swpaul		svcerr_decode(rqstp->rq_xprt);
54114062Swpaul		return(&result);
54214062Swpaul	}
54314062Swpaul
54414062Swpaul	/* Step 4: update the user's passwd structure. */
54514062Swpaul
54614062Swpaul	if (!no_chsh && strcmp(argp->newpw.pw_shell, yp_password.pw_shell)) {
54714062Swpaul		oldshell = yp_password.pw_shell;
54814062Swpaul		yp_password.pw_shell = argp->newpw.pw_shell;
54914062Swpaul		shell_changed++;
55014062Swpaul	}
55114062Swpaul
55214062Swpaul
55314062Swpaul	if (!no_chfn && strcmp(argp->newpw.pw_gecos, yp_password.pw_gecos)) {
55414062Swpaul		oldgecos = yp_password.pw_gecos;
55514062Swpaul		yp_password.pw_gecos = argp->newpw.pw_gecos;
55614062Swpaul		gecos_changed++;
55714062Swpaul	}
55814062Swpaul
55914062Swpaul	if (strcmp(argp->newpw.pw_passwd, yp_password.pw_passwd)) {
56014062Swpaul		yp_password.pw_passwd = argp->newpw.pw_passwd;
56118047Swpaul		yp_password.pw_change = 0;
56214062Swpaul		passwd_changed++;
56314062Swpaul	}
56414062Swpaul
56514062Swpaul	/*
56614062Swpaul	 * If the caller specified a domain other than our 'default'
56714062Swpaul	 * domain, change the path to master.passwd accordingly.
56814062Swpaul	 */
56914062Swpaul
57014062Swpaul	if (strcmp(domain, yppasswd_domain)) {
57114062Swpaul		snprintf(passfile_buf, sizeof(passfile_buf),
57216649Swpaul			"%s/%s/master.passwd", yp_dir, domain);
57314062Swpaul		passfile = (char *)&passfile_buf;
57414062Swpaul	}
57514062Swpaul
57614062Swpaul	/* Step 5: make a new password file with the updated info. */
57714062Swpaul
57896222Sdes	if (pw_init(dirname(passfile), passfile)) {
57996222Sdes		yp_error("pw_init() failed");
58096222Sdes		return &result;
58114062Swpaul	}
58296222Sdes	if ((pfd = pw_lock()) == -1) {
58396222Sdes		pw_fini();
58496222Sdes		yp_error("pw_lock() failed");
58596222Sdes		return &result;
58614062Swpaul	}
58796222Sdes	if ((tfd = pw_tmp(-1)) == -1) {
58896222Sdes		pw_fini();
58996222Sdes		yp_error("pw_tmp() failed");
59096222Sdes		return &result;
59114062Swpaul	}
59296222Sdes	if (pw_copy(pfd, tfd, &yp_password, NULL) == -1) {
59396222Sdes		pw_fini();
59496222Sdes		yp_error("pw_copy() failed");
59596222Sdes		return &result;
59614062Swpaul	}
59796222Sdes	if (pw_mkdb(yp_password.pw_name) == -1) {
59896222Sdes		pw_fini();
59996222Sdes		yp_error("pw_mkdb() failed");
60096222Sdes		return &result;
60196222Sdes	}
60296222Sdes	pw_fini();
60314062Swpaul
60416134Swpaul	if (inplace) {
60516134Swpaul		if ((rval = update_inplace(&yp_password, domain))) {
60616134Swpaul			yp_error("inplace update failed -- rebuilding maps");
60716134Swpaul		}
60816134Swpaul	}
60916134Swpaul
61090297Sdes	switch ((pid = fork())) {
61114062Swpaul	case 0:
61216134Swpaul		if (inplace && !rval) {
61316134Swpaul    			execlp(MAP_UPDATE_PATH, MAP_UPDATE, passfile,
61479452Sbrian				yppasswd_domain, "pushpw", (char *)NULL);
61516134Swpaul		} else {
61616134Swpaul    			execlp(MAP_UPDATE_PATH, MAP_UPDATE, passfile,
61779452Sbrian				yppasswd_domain, (char *)NULL);
61816134Swpaul		}
61914062Swpaul    		yp_error("couldn't exec map update process: %s",
62014062Swpaul					strerror(errno));
62114062Swpaul		unlink(passfile);
62214062Swpaul		rename(passfile_hold, passfile);
62314062Swpaul    		exit(1);
62414062Swpaul		break;
62514062Swpaul	case -1:
62614062Swpaul		yp_error("fork() failed: %s", strerror(errno));
62714062Swpaul		unlink(passfile);
62814062Swpaul		rename(passfile_hold, passfile);
62917431Swpaul		return(&result);
63014062Swpaul		break;
63114062Swpaul	default:
63217431Swpaul		unlink(passfile_hold);
63314062Swpaul		break;
63414062Swpaul	}
63514062Swpaul
63614062Swpaul	if (verbose) {
63714062Swpaul		yp_error("update completed for user %s (uid %d):",
63814062Swpaul						argp->newpw.pw_name,
63914062Swpaul						argp->newpw.pw_uid);
64014062Swpaul
64114062Swpaul		if (passwd_changed)
64214062Swpaul			yp_error("password changed");
64314062Swpaul
64414062Swpaul		if (gecos_changed)
64514062Swpaul			yp_error("gecos changed ('%s' -> '%s')",
64614062Swpaul					oldgecos, argp->newpw.pw_gecos);
64714062Swpaul
64814062Swpaul		if (shell_changed)
64914062Swpaul			yp_error("shell changed ('%s' -> '%s')",
65014062Swpaul					oldshell, argp->newpw.pw_shell);
65114062Swpaul	}
65214062Swpaul
65314062Swpaul	result = 0;
65414062Swpaul	return (&result);
65514062Swpaul}
65614062Swpaul
65714062Swpaul/*
65814062Swpaul * Note that this function performs a little less sanity checking
65914062Swpaul * than the last one. Since only the superuser is allowed to use it,
66014062Swpaul * it is assumed that the caller knows what he's doing.
66114062Swpaul */
66290298Sdesint *
66390298Sdesyppasswdproc_update_master_1_svc(master_yppasswd *argp,
66490298Sdes    struct svc_req *rqstp)
66514062Swpaul{
66627758Swpaul	static int result;
66714062Swpaul	int pfd, tfd;
66814062Swpaul	int pid;
66990253Salfred	uid_t uid;
67014062Swpaul	int rval = 0;
67114062Swpaul	DBT key, data;
67214062Swpaul	char *passfile_hold;
67314062Swpaul	char passfile_buf[MAXPATHLEN + 2];
67427758Swpaul	struct sockaddr_in *rqhost;
67590253Salfred	SVCXPRT	*transp;
67614062Swpaul
67714062Swpaul	result = 1;
67890253Salfred	transp = rqstp->rq_xprt;
67927758Swpaul
68027758Swpaul	/*
68127758Swpaul	 * NO AF_INET CONNETCIONS ALLOWED!
68227758Swpaul	 */
68390253Salfred	rqhost = svc_getcaller(transp);
68427758Swpaul	if (rqhost->sin_family != AF_UNIX) {
68527758Swpaul		yp_error("Alert! %s/%d attempted to use superuser-only \
68627758Swpaulprocedure!\n", inet_ntoa(rqhost->sin_addr), rqhost->sin_port);
68790253Salfred		svcerr_auth(transp, AUTH_BADCRED);
68827758Swpaul		return(&result);
68927758Swpaul	}
69027758Swpaul
69190253Salfred	if (rqstp->rq_cred.oa_flavor != AUTH_SYS) {
69227758Swpaul		yp_error("caller didn't send proper credentials");
69390253Salfred		svcerr_auth(transp, AUTH_BADCRED);
69427758Swpaul		return(&result);
69527758Swpaul	}
69627758Swpaul
69790253Salfred	if (__rpc_get_local_uid(transp, &uid) < 0) {
69827758Swpaul		yp_error("caller didn't send proper credentials");
69990253Salfred		svcerr_auth(transp, AUTH_BADCRED);
70027758Swpaul		return(&result);
70127758Swpaul	}
70290297Sdes
70390253Salfred	if (uid) {
70427758Swpaul		yp_error("caller euid is %d, expecting 0 -- rejecting request",
70590253Salfred		    uid);
70627758Swpaul		svcerr_auth(rqstp->rq_xprt, AUTH_BADCRED);
70727758Swpaul		return(&result);
70827758Swpaul	}
70927758Swpaul
71014062Swpaul	passfile = passfile_default;
71114062Swpaul
71214062Swpaul	key.data = argp->newpw.pw_name;
71314062Swpaul	key.size = strlen(argp->newpw.pw_name);
71414062Swpaul
71514062Swpaul	/*
71614062Swpaul	 * The superuser may add entries to the passwd maps if
71714062Swpaul	 * rpc.yppasswdd is started with the -a flag. Paranoia
71814062Swpaul	 * prevents me from allowing additions by default.
71914062Swpaul	 */
72014062Swpaul	if ((rval = yp_get_record(argp->domain, "master.passwd.byname",
72114062Swpaul			  &key, &data, 0)) != YP_TRUE) {
72214062Swpaul		if (rval == YP_NOKEY) {
72314062Swpaul			yp_error("user %s not found in passwd database",
72414062Swpaul				 argp->newpw.pw_name);
72514062Swpaul			if (allow_additions)
72614062Swpaul				yp_error("notice: adding user %s to \
72714062Swpaulmaster.passwd database for domain %s", argp->newpw.pw_name, argp->domain);
72814062Swpaul			else
72930377Scharnier				yp_error("restart rpc.yppasswdd with the -a flag to \
73030377Scharnierallow additions to be made to the password database");
73114062Swpaul		} else {
73214062Swpaul			yp_error("database access error: %s",
73314062Swpaul				 yperr_string(rval));
73414062Swpaul		}
73514062Swpaul		if (!allow_additions)
73627758Swpaul			return(&result);
73714062Swpaul	} else {
73814062Swpaul
73914062Swpaul		/* Nul terminate, please. */
74096222Sdes		*((char *)data.data + data.size) = '\0';
74114062Swpaul
74214062Swpaul		copy_yp_pass(data.data, 1, data.size);
74314062Swpaul	}
74414062Swpaul
74514062Swpaul	/*
74614062Swpaul	 * Perform a small bit of sanity checking.
74714062Swpaul	 */
74814062Swpaul	if (validate_master(rval == YP_TRUE ? &yp_password:NULL,&argp->newpw)){
74914062Swpaul		yp_error("rejecting update attempt for %s: bad arguments",
75014062Swpaul			 argp->newpw.pw_name);
75127758Swpaul		return(&result);
75214062Swpaul	}
75314062Swpaul
75414062Swpaul	/*
75514062Swpaul	 * If the caller specified a domain other than our 'default'
75614062Swpaul	 * domain, change the path to master.passwd accordingly.
75714062Swpaul	 */
75814062Swpaul
75914062Swpaul	if (strcmp(argp->domain, yppasswd_domain)) {
76014062Swpaul		snprintf(passfile_buf, sizeof(passfile_buf),
76116649Swpaul			"%s/%s/master.passwd", yp_dir, argp->domain);
76214062Swpaul		passfile = (char *)&passfile_buf;
76390297Sdes	}
76414062Swpaul
76596222Sdes	if (pw_init(dirname(passfile), passfile)) {
76696222Sdes		yp_error("pw_init() failed");
76796222Sdes		return &result;
76814062Swpaul	}
76996222Sdes	if ((pfd = pw_lock()) == -1) {
77096222Sdes		pw_fini();
77196222Sdes		yp_error("pw_lock() failed");
77296222Sdes		return &result;
77314062Swpaul	}
77496222Sdes	if ((tfd = pw_tmp(-1)) == -1) {
77596222Sdes		pw_fini();
77696222Sdes		yp_error("pw_tmp() failed");
77796222Sdes		return &result;
77814062Swpaul	}
77996222Sdes	if (pw_copy(pfd, tfd, (struct passwd *)&argp->newpw, NULL) == -1) {
78096222Sdes		pw_fini();
78196222Sdes		yp_error("pw_copy() failed");
78296222Sdes		return &result;
78314062Swpaul	}
78496222Sdes	if (pw_mkdb(argp->newpw.pw_name) == -1) {
78596222Sdes		pw_fini();
78696222Sdes		yp_error("pw_mkdb() failed");
78796222Sdes		return &result;
78896222Sdes	}
78996222Sdes	pw_fini();
79014062Swpaul
79116134Swpaul	if (inplace) {
79216134Swpaul		if ((rval = update_inplace((struct passwd *)&argp->newpw,
79316134Swpaul							argp->domain))) {
79416134Swpaul			yp_error("inplace update failed -- rebuilding maps");
79516134Swpaul		}
79616134Swpaul	}
79716134Swpaul
79890297Sdes	switch ((pid = fork())) {
79914062Swpaul	case 0:
80016134Swpaul		if (inplace && !rval) {
80116134Swpaul    			execlp(MAP_UPDATE_PATH, MAP_UPDATE, passfile,
80279452Sbrian				argp->domain, "pushpw", (char *)NULL);
80316134Swpaul    		} else {
80416134Swpaul			execlp(MAP_UPDATE_PATH, MAP_UPDATE, passfile,
80579452Sbrian				argp->domain, (char *)NULL);
80616134Swpaul		}
80714062Swpaul    		yp_error("couldn't exec map update process: %s",
80814062Swpaul					strerror(errno));
80914062Swpaul		unlink(passfile);
81014062Swpaul		rename(passfile_hold, passfile);
81114062Swpaul    		exit(1);
81214062Swpaul		break;
81314062Swpaul	case -1:
81414062Swpaul		yp_error("fork() failed: %s", strerror(errno));
81514062Swpaul		unlink(passfile);
81614062Swpaul		rename(passfile_hold, passfile);
81727758Swpaul		return(&result);
81814062Swpaul		break;
81914062Swpaul	default:
82017431Swpaul		unlink(passfile_hold);
82114062Swpaul		break;
82214062Swpaul	}
82314062Swpaul
82414062Swpaul	yp_error("performed update of user %s (uid %d) domain %s",
82514062Swpaul						argp->newpw.pw_name,
82614062Swpaul						argp->newpw.pw_uid,
82714062Swpaul						argp->domain);
82814062Swpaul
82914062Swpaul	result = 0;
83027758Swpaul	return(&result);
83114062Swpaul}
832