yppasswdd_server.c revision 79452
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 79452 2001-07-09 09:24:06Z brian $";
3630377Scharnier#endif /* not lint */
3730377Scharnier
3814062Swpaul#include <stdio.h>
3914062Swpaul#include <string.h>
4014062Swpaul#include <ctype.h>
4114062Swpaul#include <stdlib.h>
4214062Swpaul#include <unistd.h>
4314062Swpaul#include <dirent.h>
4414062Swpaul#include <sys/stat.h>
4514062Swpaul#include <sys/socket.h>
4614062Swpaul#include <netinet/in.h>
4714062Swpaul#include <arpa/inet.h>
4814062Swpaul#include <limits.h>
4914062Swpaul#include <db.h>
5014062Swpaul#include <pwd.h>
5114062Swpaul#include <errno.h>
5214062Swpaul#include <signal.h>
5314062Swpaul#include <rpc/rpc.h>
5414062Swpaul#include <rpcsvc/yp.h>
5514062Swpaul#include <sys/types.h>
5614062Swpaul#include <sys/wait.h>
5714062Swpaul#include <sys/param.h>
5816134Swpaul#include <sys/fcntl.h>
5914062Swpaulstruct dom_binding {};
6014062Swpaul#include <rpcsvc/ypclnt.h>
6114062Swpaul#include "yppasswdd_extern.h"
6214062Swpaul#include "yppasswd.h"
6314062Swpaul#include "yppasswd_private.h"
6414062Swpaul
6574660Salfredstruct cmessage {
6674660Salfred        struct cmsghdr cmsg;
6774660Salfred        struct cmsgcred cmcred;
6874660Salfred};
6974660Salfred
7014062Swpaulchar *tempname;
7114062Swpaul
7214062Swpaulvoid reaper(sig)
7314062Swpaul	int sig;
7414062Swpaul{
7514062Swpaul	extern pid_t pid;
7614062Swpaul	extern int pstat;
7714062Swpaul	int st;
7836639Swpaul	int saved_errno;
7914062Swpaul
8036639Swpaul	saved_errno = errno;
8136639Swpaul
8214062Swpaul	if (sig > 0) {
8314062Swpaul		if (sig == SIGCHLD)
8414062Swpaul			while(wait3(&st, WNOHANG, NULL) > 0) ;
8514062Swpaul	} else {
8614062Swpaul		pid = waitpid(pid, &pstat, 0);
8714062Swpaul	}
8836639Swpaul
8936639Swpaul	errno = saved_errno;
9014062Swpaul	return;
9114062Swpaul}
9214062Swpaul
9314062Swpaulvoid install_reaper(on)
9414062Swpaul	int on;
9514062Swpaul{
9614062Swpaul	if (on) {
9714062Swpaul		signal(SIGCHLD, reaper);
9814062Swpaul	} else {
9914062Swpaul		signal(SIGCHLD, SIG_DFL);
10014062Swpaul	}
10114062Swpaul	return;
10214062Swpaul}
10314062Swpaul
10414062Swpaulstatic struct passwd yp_password;
10514062Swpaul
10614062Swpaulstatic void copy_yp_pass(p, x, m)
10714062Swpaulchar *p;
10814062Swpaulint x, m;
10914062Swpaul{
11014062Swpaul	register char *t, *s = p;
11114062Swpaul	static char *buf;
11214062Swpaul
11314062Swpaul	yp_password.pw_fields = 0;
11414062Swpaul
11514062Swpaul	buf = (char *)realloc(buf, m + 10);
11614062Swpaul	bzero(buf, m + 10);
11714062Swpaul
11814062Swpaul	/* Turn all colons into NULLs */
11914062Swpaul	while (strchr(s, ':')) {
12014062Swpaul		s = (strchr(s, ':') + 1);
12114062Swpaul		*(s - 1)= '\0';
12214062Swpaul	}
12314062Swpaul
12414062Swpaul	t = buf;
12514062Swpaul#define EXPAND(e)       e = t; while ((*t++ = *p++));
12614062Swpaul        EXPAND(yp_password.pw_name);
12714062Swpaul	yp_password.pw_fields |= _PWF_NAME;
12814062Swpaul        EXPAND(yp_password.pw_passwd);
12914062Swpaul	yp_password.pw_fields |= _PWF_PASSWD;
13014062Swpaul	yp_password.pw_uid = atoi(p);
13114062Swpaul        p += (strlen(p) + 1);
13214062Swpaul	yp_password.pw_fields |= _PWF_UID;
13314062Swpaul	yp_password.pw_gid = atoi(p);
13414062Swpaul        p += (strlen(p) + 1);
13514062Swpaul	yp_password.pw_fields |= _PWF_GID;
13614062Swpaul	if (x) {
13714062Swpaul		EXPAND(yp_password.pw_class);
13814062Swpaul		yp_password.pw_fields |= _PWF_CLASS;
13914062Swpaul		yp_password.pw_change = atol(p);
14014062Swpaul		p += (strlen(p) + 1);
14114062Swpaul		yp_password.pw_fields |= _PWF_CHANGE;
14214062Swpaul		yp_password.pw_expire = atol(p);
14314062Swpaul		p += (strlen(p) + 1);
14414062Swpaul		yp_password.pw_fields |= _PWF_EXPIRE;
14514062Swpaul	}
14614062Swpaul        EXPAND(yp_password.pw_gecos);
14714062Swpaul	yp_password.pw_fields |= _PWF_GECOS;
14814062Swpaul        EXPAND(yp_password.pw_dir);
14914062Swpaul	yp_password.pw_fields |= _PWF_DIR;
15014062Swpaul        EXPAND(yp_password.pw_shell);
15114062Swpaul	yp_password.pw_fields |= _PWF_SHELL;
15214062Swpaul
15314062Swpaul	return;
15414062Swpaul}
15514062Swpaul
15614062Swpaulstatic int validchars(arg)
15714062Swpaul	char *arg;
15814062Swpaul{
15914062Swpaul	int i;
16014062Swpaul
16114062Swpaul	for (i = 0; i < strlen(arg); i++) {
16214062Swpaul		if (iscntrl(arg[i])) {
16314062Swpaul			yp_error("string contains a control character");
16414062Swpaul			return(1);
16514062Swpaul		}
16614062Swpaul		if (arg[i] == ':') {
16714062Swpaul			yp_error("string contains a colon");
16814062Swpaul			return(1);
16914062Swpaul		}
17014062Swpaul		/* Be evil: truncate strings with \n in them silently. */
17114062Swpaul		if (arg[i] == '\n') {
17214062Swpaul			arg[i] = '\0';
17314062Swpaul			return(0);
17414062Swpaul		}
17514062Swpaul	}
17614062Swpaul	return(0);
17714062Swpaul}
17814062Swpaul
17914062Swpaulstatic int validate_master(opw, npw)
18014062Swpaul	struct passwd *opw;
18114062Swpaul	struct x_master_passwd *npw;
18214062Swpaul{
18314062Swpaul
18414062Swpaul	if (npw->pw_name[0] == '+' || npw->pw_name[0] == '-') {
18514062Swpaul		yp_error("client tried to modify an NIS entry");
18614062Swpaul		return(1);
18714062Swpaul	}
18814062Swpaul
18914062Swpaul	if (validchars(npw->pw_shell)) {
19014062Swpaul		yp_error("specified shell contains invalid characters");
19114062Swpaul		return(1);
19214062Swpaul	}
19314062Swpaul
19414062Swpaul	if (validchars(npw->pw_gecos)) {
19514062Swpaul		yp_error("specified gecos field contains invalid characters");
19614062Swpaul		return(1);
19714062Swpaul	}
19814062Swpaul
19914062Swpaul	if (validchars(npw->pw_passwd)) {
20014062Swpaul		yp_error("specified password contains invalid characters");
20114062Swpaul		return(1);
20214062Swpaul	}
20314062Swpaul	return(0);
20414062Swpaul}
20514062Swpaul
20614062Swpaulstatic int validate(opw, npw)
20714062Swpaul	struct passwd *opw;
20814062Swpaul	struct x_passwd *npw;
20914062Swpaul{
21014062Swpaul
21114062Swpaul	if (npw->pw_name[0] == '+' || npw->pw_name[0] == '-') {
21214062Swpaul		yp_error("client tried to modify an NIS entry");
21314062Swpaul		return(1);
21414062Swpaul	}
21514062Swpaul
21614062Swpaul	if (npw->pw_uid != opw->pw_uid) {
21714062Swpaul		yp_error("UID mismatch: client says user %s has UID %d",
21814062Swpaul			 npw->pw_name, npw->pw_uid);
21914062Swpaul		yp_error("database says user %s has UID %d", opw->pw_name,
22014062Swpaul			 opw->pw_uid);
22114062Swpaul		return(1);
22214062Swpaul	}
22314062Swpaul
22414062Swpaul	if (npw->pw_gid != opw->pw_gid) {
22514062Swpaul		yp_error("GID mismatch: client says user %s has GID %d",
22614062Swpaul			 npw->pw_name, npw->pw_gid);
22714062Swpaul		yp_error("database says user %s has GID %d", opw->pw_name,
22814062Swpaul			 opw->pw_gid);
22914062Swpaul		return(1);
23014062Swpaul	}
23114062Swpaul
23214062Swpaul	/*
23314062Swpaul	 * Don't allow the user to shoot himself in the foot,
23414062Swpaul	 * even on purpose.
23514062Swpaul	 */
23614062Swpaul	if (!ok_shell(npw->pw_shell)) {
23714062Swpaul		yp_error("%s is not a valid shell", npw->pw_shell);
23814062Swpaul		return(1);
23914062Swpaul	}
24014062Swpaul
24114062Swpaul	if (validchars(npw->pw_shell)) {
24214062Swpaul		yp_error("specified shell contains invalid characters");
24314062Swpaul		return(1);
24414062Swpaul	}
24514062Swpaul
24614062Swpaul	if (validchars(npw->pw_gecos)) {
24714062Swpaul		yp_error("specified gecos field contains invalid characters");
24814062Swpaul		return(1);
24914062Swpaul	}
25014062Swpaul
25114062Swpaul	if (validchars(npw->pw_passwd)) {
25214062Swpaul		yp_error("specified password contains invalid characters");
25314062Swpaul		return(1);
25414062Swpaul	}
25514062Swpaul	return(0);
25614062Swpaul}
25714062Swpaul
25814062Swpaul/*
25914062Swpaul * Kludge alert:
26014062Swpaul * In order to have one rpc.yppasswdd support multiple domains,
26114062Swpaul * we have to cheat: we search each directory under /var/yp
26214062Swpaul * and try to match the user in each master.passwd.byname
26314062Swpaul * map that we find. If the user matches (username, uid and gid
26414062Swpaul * all agree), then we use that domain. If we match the user in
26514062Swpaul * more than one database, we must abort.
26614062Swpaul */
26714062Swpaulstatic char *find_domain(pw)
26814062Swpaul	struct x_passwd *pw;
26914062Swpaul{
27014062Swpaul	struct stat statbuf;
27114062Swpaul	struct dirent *dirp;
27214062Swpaul	DIR *dird;
27314062Swpaul	char yp_mapdir[MAXPATHLEN + 2];
27415687Swpaul	static char domain[YPMAXDOMAIN];
27514062Swpaul	char *tmp = NULL;
27614062Swpaul	DBT key, data;
27714062Swpaul	int hit = 0;
27814062Swpaul
27914062Swpaul	yp_error("performing multidomain lookup");
28014062Swpaul
28114062Swpaul	if ((dird = opendir(yp_dir)) == NULL) {
28214062Swpaul		yp_error("opendir(%s) failed: %s", yp_dir, strerror(errno));
28314062Swpaul		return(NULL);
28414062Swpaul	}
28514062Swpaul
28614062Swpaul	while ((dirp = readdir(dird)) != NULL) {
28714062Swpaul		snprintf(yp_mapdir, sizeof(yp_mapdir), "%s/%s",
28814062Swpaul							yp_dir, dirp->d_name);
28914062Swpaul		if (stat(yp_mapdir, &statbuf) < 0) {
29014062Swpaul			yp_error("stat(%s) failed: %s", yp_mapdir,
29114062Swpaul							strerror(errno));
29214062Swpaul			closedir(dird);
29314062Swpaul			return(NULL);
29414062Swpaul		}
29514062Swpaul		if (S_ISDIR(statbuf.st_mode)) {
29614062Swpaul			tmp = (char *)dirp->d_name;
29714062Swpaul			key.data = pw->pw_name;
29814062Swpaul			key.size = strlen(pw->pw_name);
29914062Swpaul
30014062Swpaul			if (yp_get_record(tmp,"master.passwd.byname",
30114062Swpaul			  		&key, &data, 0) != YP_TRUE) {
30214062Swpaul				continue;
30314062Swpaul			}
30414062Swpaul			*(char *)(data.data + data.size) = '\0';
30514062Swpaul			copy_yp_pass(data.data, 1, data.size);
30614062Swpaul			if (yp_password.pw_uid == pw->pw_uid &&
30714062Swpaul			    yp_password.pw_gid == pw->pw_gid) {
30814062Swpaul				hit++;
30915687Swpaul				snprintf(domain, YPMAXDOMAIN, "%s", tmp);
31014062Swpaul			}
31114062Swpaul		}
31214062Swpaul	}
31314062Swpaul
31414062Swpaul	closedir(dird);
31514062Swpaul	if (hit > 1) {
31614062Swpaul		yp_error("found same user in two different domains");
31714062Swpaul		return(NULL);
31814062Swpaul	} else
31916134Swpaul		return((char *)&domain);
32014062Swpaul}
32114062Swpaul
32216134Swpaulstatic int update_inplace(pw, domain)
32316134Swpaul	struct passwd *pw;
32416134Swpaul	char *domain;
32516134Swpaul{
32616134Swpaul	DB *dbp = NULL;
32716134Swpaul	DBT key = { NULL, 0 };
32816134Swpaul	DBT data = { NULL, 0 };
32916134Swpaul	char pwbuf[YPMAXRECORD];
33016134Swpaul	char keybuf[20];
33119778Speter	int i;
33216134Swpaul	char *maps[] = { "master.passwd.byname", "master.passwd.byuid",
33316134Swpaul			 "passwd.byname", "passwd.byuid" };
33416134Swpaul
33516134Swpaul	char *formats[] = { "%s:%s:%d:%d:%s:%ld:%ld:%s:%s:%s",
33616134Swpaul			    "%s:%s:%d:%d:%s:%ld:%ld:%s:%s:%s",
33716134Swpaul			    "%s:%s:%d:%d:%s:%s:%s", "%s:%s:%d:%d:%s:%s:%s" };
33816134Swpaul	char *ptr = NULL;
33916134Swpaul	char *yp_last = "YP_LAST_MODIFIED";
34016134Swpaul	char yplastbuf[YPMAXRECORD];
34116134Swpaul
34216134Swpaul	snprintf(yplastbuf, sizeof(yplastbuf), "%lu", time(NULL));
34316134Swpaul
34416134Swpaul	for (i = 0; i < 4; i++) {
34516134Swpaul
34616134Swpaul		if (i % 2) {
34716134Swpaul			snprintf(keybuf, sizeof(keybuf), "%ld", pw->pw_uid);
34816134Swpaul			key.data = (char *)&keybuf;
34916134Swpaul			key.size = strlen(keybuf);
35016134Swpaul		} else {
35116134Swpaul			key.data = pw->pw_name;
35216134Swpaul			key.size = strlen(pw->pw_name);
35316134Swpaul		}
35416134Swpaul
35516134Swpaul		/*
35616134Swpaul		 * XXX The passwd.byname and passwd.byuid maps come in
35716134Swpaul		 * two flavors: secure and insecure. The secure version
35816134Swpaul		 * has a '*' in the password field whereas the insecure one
35916134Swpaul		 * has a real crypted password. The maps will be insecure
36016134Swpaul		 * if they were built with 'unsecure = TRUE' enabled in
36116134Swpaul		 * /var/yp/Makefile, but we'd have no way of knowing if
36216134Swpaul		 * this has been done unless we were to try parsing the
36316134Swpaul		 * Makefile, which is a disgusting thought. Instead, we
36416134Swpaul		 * read the records from the maps, skip to the first ':'
36516134Swpaul		 * in them, and then look at the character immediately
36616134Swpaul		 * following it. If it's an '*' then the map is 'secure'
36716134Swpaul		 * and we must not insert a real password into the pw_passwd
36816134Swpaul		 * field. If it's not an '*', then we put the real crypted
36916134Swpaul		 * password in.
37016134Swpaul		 */
37116134Swpaul		if (yp_get_record(domain,maps[i],&key,&data,1) != YP_TRUE) {
37216134Swpaul			yp_error("couldn't read %s/%s: %s", domain,
37316134Swpaul						maps[i], strerror(errno));
37416134Swpaul			return(1);
37516134Swpaul		}
37616134Swpaul
37716134Swpaul		if ((ptr = strchr(data.data, ':')) == NULL) {
37816134Swpaul			yp_error("no colon in passwd record?!");
37916134Swpaul			return(1);
38016134Swpaul		}
38116134Swpaul
38219138Swpaul		/*
38319138Swpaul		 * XXX Supposing we have more than one user with the same
38419138Swpaul		 * UID? (Or more than one user with the same name?) We could
38519138Swpaul		 * end up modifying the wrong record if were not careful.
38619138Swpaul		 */
38719138Swpaul		if (i % 2) {
38819138Swpaul			if (strncmp(data.data, pw->pw_name,
38919138Swpaul							strlen(pw->pw_name))) {
39019138Swpaul				yp_error("warning: found entry for UID %d \
39119138Swpaulin map %s@%s with wrong name (%.*s)", pw->pw_uid, maps[i], domain,
39219138Swpaul					ptr - (char *)data.data, data.data);
39319138Swpaul				yp_error("there may be more than one user \
39419138Swpaulwith the same UID - continuing");
39519138Swpaul				continue;
39619138Swpaul			}
39719138Swpaul		} else {
39819138Swpaul			/*
39919138Swpaul			 * We're really being ultra-paranoid here.
40019138Swpaul			 * This is generally a 'can't happen' condition.
40119138Swpaul			 */
40219138Swpaul			snprintf(pwbuf, sizeof(pwbuf), ":%d:%d:", pw->pw_uid,
40319138Swpaul								  pw->pw_gid);
40419138Swpaul			if (!strstr(data.data, pwbuf)) {
40519138Swpaul				yp_error("warning: found entry for user %s \
40619138Swpaulin map %s@%s with wrong UID", pw->pw_name, maps[i], domain);
40778369Sdd				yp_error("there may be more than one user
40819138Swpaulwith the same name - continuing");
40919138Swpaul				continue;
41019138Swpaul			}
41119138Swpaul		}
41219138Swpaul
41316134Swpaul		if (i < 2) {
41416134Swpaul			snprintf(pwbuf, sizeof(pwbuf), formats[i],
41516134Swpaul			   pw->pw_name, pw->pw_passwd, pw->pw_uid,
41616134Swpaul			   pw->pw_gid, pw->pw_class, pw->pw_change,
41716134Swpaul			   pw->pw_expire, pw->pw_gecos, pw->pw_dir,
41816134Swpaul			   pw->pw_shell);
41916134Swpaul		} else {
42016134Swpaul			snprintf(pwbuf, sizeof(pwbuf), formats[i],
42116134Swpaul			   pw->pw_name, *(ptr+1) == '*' ? "*" : pw->pw_passwd,
42216134Swpaul			   pw->pw_uid, pw->pw_gid, pw->pw_gecos, pw->pw_dir,
42316134Swpaul			   pw->pw_shell);
42416134Swpaul		}
42516134Swpaul
42616134Swpaul#define FLAGS O_RDWR|O_CREAT
42716134Swpaul
42816134Swpaul		if ((dbp = yp_open_db_rw(domain, maps[i], FLAGS)) == NULL) {
42916134Swpaul			yp_error("couldn't open %s/%s r/w: %s",domain,
43016134Swpaul						maps[i],strerror(errno));
43116134Swpaul			return(1);
43216134Swpaul		}
43316134Swpaul
43416134Swpaul		data.data = pwbuf;
43516134Swpaul		data.size = strlen(pwbuf);
43616134Swpaul
43716134Swpaul		if (yp_put_record(dbp, &key, &data, 1) != YP_TRUE) {
43816134Swpaul			yp_error("failed to update record in %s/%s", domain,
43916134Swpaul								maps[i]);
44016134Swpaul			(void)(dbp->close)(dbp);
44116134Swpaul			return(1);
44216134Swpaul		}
44316134Swpaul
44416134Swpaul		key.data = yp_last;
44516134Swpaul		key.size = strlen(yp_last);
44616134Swpaul		data.data = (char *)&yplastbuf;
44716134Swpaul		data.size = strlen(yplastbuf);
44816134Swpaul
44916134Swpaul		if (yp_put_record(dbp, &key, &data, 1) != YP_TRUE) {
45016134Swpaul			yp_error("failed to update timestamp in %s/%s", domain,
45116134Swpaul								maps[i]);
45216134Swpaul			(void)(dbp->close)(dbp);
45316134Swpaul			return(1);
45416134Swpaul		}
45516134Swpaul
45616134Swpaul		(void)(dbp->close)(dbp);
45716134Swpaul	}
45816134Swpaul
45916134Swpaul	return(0);
46016134Swpaul}
46116134Swpaul
46217431Swpaulstatic char *yp_mktmpnam()
46317431Swpaul{
46417431Swpaul	static char path[MAXPATHLEN];
46517431Swpaul	char *p;
46616134Swpaul
46717431Swpaul	sprintf(path,"%s",passfile);
46817431Swpaul	if ((p = strrchr(path, '/')))
46917431Swpaul		++p;
47017431Swpaul	else
47117431Swpaul		p = path;
47217431Swpaul	strcpy(p, "yppwtmp.XXXXXX");
47317431Swpaul	return(mktemp(path));
47417431Swpaul}
47517431Swpaul
47614062Swpaulint *
47714062Swpaulyppasswdproc_update_1_svc(yppasswd *argp, struct svc_req *rqstp)
47814062Swpaul{
47914062Swpaul	static int  result;
48014062Swpaul	struct sockaddr_in *rqhost;
48114062Swpaul	DBT key, data;
48214062Swpaul	int rval = 0;
48314062Swpaul	int pfd, tfd;
48414062Swpaul	int pid;
48514062Swpaul	int passwd_changed = 0;
48614062Swpaul	int shell_changed = 0;
48714062Swpaul	int gecos_changed = 0;
48814062Swpaul	char *oldshell = NULL;
48914062Swpaul	char *oldgecos = NULL;
49014062Swpaul	char *passfile_hold;
49114062Swpaul	char passfile_buf[MAXPATHLEN + 2];
49214062Swpaul	char *domain = yppasswd_domain;
49317431Swpaul	static struct sockaddr_in clntaddr;
49417431Swpaul	static struct timeval t_saved, t_test;
49514062Swpaul
49614062Swpaul	/*
49714062Swpaul	 * Normal user updates always use the 'default' master.passwd file.
49814062Swpaul	 */
49914062Swpaul
50014062Swpaul	passfile = passfile_default;
50114062Swpaul	result = 1;
50214062Swpaul
50314062Swpaul	rqhost = svc_getcaller(rqstp->rq_xprt);
50414062Swpaul
50517431Swpaul	gettimeofday(&t_test, NULL);
50617431Swpaul	if (!bcmp((char *)rqhost, (char *)&clntaddr,
50717431Swpaul						sizeof(struct sockaddr_in)) &&
50817431Swpaul		t_test.tv_sec > t_saved.tv_sec &&
50917431Swpaul		t_test.tv_sec - t_saved.tv_sec < 300) {
51017431Swpaul
51117431Swpaul		bzero((char *)&clntaddr, sizeof(struct sockaddr_in));
51217431Swpaul		bzero((char *)&t_saved, sizeof(struct timeval));
51317431Swpaul		return(NULL);
51417431Swpaul	}
51517431Swpaul
51617431Swpaul	bcopy((char *)rqhost, (char *)&clntaddr, sizeof(struct sockaddr_in));
51717431Swpaul	gettimeofday(&t_saved, NULL);
51817431Swpaul
51914241Swpaul	if (yp_access(resvport ? "master.passwd.byname" : NULL, rqstp)) {
52014241Swpaul		yp_error("rejected update request from unauthorized host");
52114241Swpaul		svcerr_auth(rqstp->rq_xprt, AUTH_BADCRED);
52214241Swpaul		return(&result);
52314241Swpaul	}
52414241Swpaul
52514062Swpaul	/*
52614062Swpaul	 * Step one: find the user. (It's kinda pointless to
52714062Swpaul	 * proceed if the user doesn't exist.) We look for the
52814062Swpaul	 * user in the master.passwd.byname database, _NOT_ by
52914062Swpaul	 * using getpwent() and friends! We can't use getpwent()
53014062Swpaul	 * since the NIS master server is not guaranteed to be
53114062Swpaul	 * configured as an NIS client.
53214062Swpaul	 */
53314062Swpaul
53414062Swpaul	if (multidomain) {
53514062Swpaul		if ((domain = find_domain(&argp->newpw)) == NULL) {
53614062Swpaul			yp_error("multidomain lookup failed - aborting update");
53714062Swpaul			return(&result);
53814062Swpaul		} else
53914062Swpaul			yp_error("updating user %s in domain %s",
54014062Swpaul					argp->newpw.pw_name, domain);
54114062Swpaul	}
54214062Swpaul
54314062Swpaul	key.data = argp->newpw.pw_name;
54414062Swpaul	key.size = strlen(argp->newpw.pw_name);
54514062Swpaul
54614062Swpaul	if ((rval=yp_get_record(domain,"master.passwd.byname",
54714062Swpaul		  	&key, &data, 0)) != YP_TRUE) {
54814062Swpaul		if (rval == YP_NOKEY) {
54914062Swpaul			yp_error("user %s not found in passwd database",
55014062Swpaul			 	argp->newpw.pw_name);
55114062Swpaul		} else {
55214062Swpaul			yp_error("database access error: %s",
55314062Swpaul			 	yperr_string(rval));
55414062Swpaul		}
55514062Swpaul		return(&result);
55614062Swpaul	}
55714062Swpaul
55814062Swpaul	/* Nul terminate, please. */
55914062Swpaul	*(char *)(data.data + data.size) = '\0';
56014062Swpaul
56114062Swpaul	copy_yp_pass(data.data, 1, data.size);
56214062Swpaul
56314062Swpaul	/* Step 2: check that the supplied oldpass is valid. */
56414062Swpaul
56514062Swpaul	if (strcmp(crypt(argp->oldpass, yp_password.pw_passwd),
56614062Swpaul					yp_password.pw_passwd)) {
56714062Swpaul		yp_error("rejected change attempt -- bad password");
56814062Swpaul		yp_error("client address: %s username: %s",
56914062Swpaul			  inet_ntoa(rqhost->sin_addr),
57014062Swpaul			  argp->newpw.pw_name);
57114062Swpaul		return(&result);
57214062Swpaul	}
57314062Swpaul
57414062Swpaul	/* Step 3: validate the arguments passed to us by the client. */
57514062Swpaul
57614062Swpaul	if (validate(&yp_password, &argp->newpw)) {
57714062Swpaul		yp_error("rejecting change attempt: bad arguments");
57814062Swpaul		yp_error("client address: %s username: %s",
57914062Swpaul			 inet_ntoa(rqhost->sin_addr),
58014062Swpaul			 argp->newpw.pw_name);
58114062Swpaul		svcerr_decode(rqstp->rq_xprt);
58214062Swpaul		return(&result);
58314062Swpaul	}
58414062Swpaul
58514062Swpaul	/* Step 4: update the user's passwd structure. */
58614062Swpaul
58714062Swpaul	if (!no_chsh && strcmp(argp->newpw.pw_shell, yp_password.pw_shell)) {
58814062Swpaul		oldshell = yp_password.pw_shell;
58914062Swpaul		yp_password.pw_shell = argp->newpw.pw_shell;
59014062Swpaul		shell_changed++;
59114062Swpaul	}
59214062Swpaul
59314062Swpaul
59414062Swpaul	if (!no_chfn && strcmp(argp->newpw.pw_gecos, yp_password.pw_gecos)) {
59514062Swpaul		oldgecos = yp_password.pw_gecos;
59614062Swpaul		yp_password.pw_gecos = argp->newpw.pw_gecos;
59714062Swpaul		gecos_changed++;
59814062Swpaul	}
59914062Swpaul
60014062Swpaul	if (strcmp(argp->newpw.pw_passwd, yp_password.pw_passwd)) {
60114062Swpaul		yp_password.pw_passwd = argp->newpw.pw_passwd;
60218047Swpaul		yp_password.pw_change = 0;
60314062Swpaul		passwd_changed++;
60414062Swpaul	}
60514062Swpaul
60614062Swpaul	/*
60714062Swpaul	 * If the caller specified a domain other than our 'default'
60814062Swpaul	 * domain, change the path to master.passwd accordingly.
60914062Swpaul	 */
61014062Swpaul
61114062Swpaul	if (strcmp(domain, yppasswd_domain)) {
61214062Swpaul		snprintf(passfile_buf, sizeof(passfile_buf),
61316649Swpaul			"%s/%s/master.passwd", yp_dir, domain);
61414062Swpaul		passfile = (char *)&passfile_buf;
61514062Swpaul	}
61614062Swpaul
61714062Swpaul	/* Step 5: make a new password file with the updated info. */
61814062Swpaul
61914062Swpaul	if ((pfd = pw_lock()) < 0) {
62014062Swpaul		return (&result);
62114062Swpaul	}
62214062Swpaul	if ((tfd = pw_tmp()) < 0) {
62314062Swpaul		return (&result);
62414062Swpaul	}
62514062Swpaul
62614062Swpaul	if (pw_copy(pfd, tfd, &yp_password)) {
62714062Swpaul		yp_error("failed to created updated password file -- \
62814062Swpaulcleaning up and bailing out");
62914062Swpaul		unlink(tempname);
63014062Swpaul		return(&result);
63114062Swpaul	}
63214062Swpaul
63317431Swpaul	passfile_hold = yp_mktmpnam();
63414062Swpaul	rename(passfile, passfile_hold);
63514062Swpaul	if (strcmp(passfile, _PATH_MASTERPASSWD)) {
63614062Swpaul		rename(tempname, passfile);
63714062Swpaul	} else {
63816876Sguido		if (pw_mkdb(argp->newpw.pw_name) < 0) {
63914062Swpaul			yp_error("pwd_mkdb failed");
64014062Swpaul			return(&result);
64114062Swpaul		}
64214062Swpaul	}
64314062Swpaul
64416134Swpaul	if (inplace) {
64516134Swpaul		if ((rval = update_inplace(&yp_password, domain))) {
64616134Swpaul			yp_error("inplace update failed -- rebuilding maps");
64716134Swpaul		}
64816134Swpaul	}
64916134Swpaul
65014062Swpaul	switch((pid = fork())) {
65114062Swpaul	case 0:
65216134Swpaul		if (inplace && !rval) {
65316134Swpaul    			execlp(MAP_UPDATE_PATH, MAP_UPDATE, passfile,
65479452Sbrian				yppasswd_domain, "pushpw", (char *)NULL);
65516134Swpaul		} else {
65616134Swpaul    			execlp(MAP_UPDATE_PATH, MAP_UPDATE, passfile,
65779452Sbrian				yppasswd_domain, (char *)NULL);
65816134Swpaul		}
65914062Swpaul    		yp_error("couldn't exec map update process: %s",
66014062Swpaul					strerror(errno));
66114062Swpaul		unlink(passfile);
66214062Swpaul		rename(passfile_hold, passfile);
66314062Swpaul    		exit(1);
66414062Swpaul		break;
66514062Swpaul	case -1:
66614062Swpaul		yp_error("fork() failed: %s", strerror(errno));
66714062Swpaul		unlink(passfile);
66814062Swpaul		rename(passfile_hold, passfile);
66917431Swpaul		return(&result);
67014062Swpaul		break;
67114062Swpaul	default:
67217431Swpaul		unlink(passfile_hold);
67314062Swpaul		break;
67414062Swpaul	}
67514062Swpaul
67614062Swpaul	if (verbose) {
67714062Swpaul		yp_error("update completed for user %s (uid %d):",
67814062Swpaul						argp->newpw.pw_name,
67914062Swpaul						argp->newpw.pw_uid);
68014062Swpaul
68114062Swpaul		if (passwd_changed)
68214062Swpaul			yp_error("password changed");
68314062Swpaul
68414062Swpaul		if (gecos_changed)
68514062Swpaul			yp_error("gecos changed ('%s' -> '%s')",
68614062Swpaul					oldgecos, argp->newpw.pw_gecos);
68714062Swpaul
68814062Swpaul		if (shell_changed)
68914062Swpaul			yp_error("shell changed ('%s' -> '%s')",
69014062Swpaul					oldshell, argp->newpw.pw_shell);
69114062Swpaul	}
69214062Swpaul
69314062Swpaul	result = 0;
69414062Swpaul	return (&result);
69514062Swpaul}
69614062Swpaul
69714062Swpaul/*
69814062Swpaul * Note that this function performs a little less sanity checking
69914062Swpaul * than the last one. Since only the superuser is allowed to use it,
70014062Swpaul * it is assumed that the caller knows what he's doing.
70114062Swpaul */
70227758Swpaulint *yppasswdproc_update_master_1_svc(master_yppasswd *argp,
70327758Swpaul					struct svc_req *rqstp)
70414062Swpaul{
70527758Swpaul	static int result;
70614062Swpaul	int pfd, tfd;
70714062Swpaul	int pid;
70814062Swpaul	int rval = 0;
70914062Swpaul	DBT key, data;
71014062Swpaul	char *passfile_hold;
71114062Swpaul	char passfile_buf[MAXPATHLEN + 2];
71227758Swpaul	struct sockaddr_in *rqhost;
71327758Swpaul	struct cmessage			*cm;
71427758Swpaul	SVCXPRT				*transp;
71514062Swpaul
71614062Swpaul	result = 1;
71727758Swpaul
71827758Swpaul	/*
71927758Swpaul	 * NO AF_INET CONNETCIONS ALLOWED!
72027758Swpaul	 */
72127758Swpaul	rqhost = svc_getcaller(rqstp->rq_xprt);
72227758Swpaul	if (rqhost->sin_family != AF_UNIX) {
72327758Swpaul		yp_error("Alert! %s/%d attempted to use superuser-only \
72427758Swpaulprocedure!\n", inet_ntoa(rqhost->sin_addr), rqhost->sin_port);
72527758Swpaul		svcerr_auth(rqstp->rq_xprt, AUTH_BADCRED);
72627758Swpaul		return(&result);
72727758Swpaul	}
72827758Swpaul
72927758Swpaul	transp = rqstp->rq_xprt;
73027758Swpaul
73127758Swpaul	if (transp->xp_verf.oa_length < sizeof(struct cmessage) ||
73227758Swpaul		transp->xp_verf.oa_base == NULL ||
73327758Swpaul		transp->xp_verf.oa_flavor != AUTH_UNIX) {
73427758Swpaul		yp_error("caller didn't send proper credentials");
73527758Swpaul		svcerr_auth(rqstp->rq_xprt, AUTH_BADCRED);
73627758Swpaul		return(&result);
73727758Swpaul	}
73827758Swpaul
73927758Swpaul	cm = (struct cmessage *)transp->xp_verf.oa_base;
74027758Swpaul	if (cm->cmsg.cmsg_type != SCM_CREDS) {
74127758Swpaul		yp_error("caller didn't send proper credentials");
74227758Swpaul		svcerr_auth(rqstp->rq_xprt, AUTH_BADCRED);
74327758Swpaul		return(&result);
74427758Swpaul	}
74527758Swpaul
74627758Swpaul 	if (cm->cmcred.cmcred_euid) {
74727758Swpaul		yp_error("caller euid is %d, expecting 0 -- rejecting request",
74827758Swpaul				cm->cmcred.cmcred_euid);
74927758Swpaul		svcerr_auth(rqstp->rq_xprt, AUTH_BADCRED);
75027758Swpaul		return(&result);
75127758Swpaul	}
75227758Swpaul
75314062Swpaul	passfile = passfile_default;
75414062Swpaul
75514062Swpaul	key.data = argp->newpw.pw_name;
75614062Swpaul	key.size = strlen(argp->newpw.pw_name);
75714062Swpaul
75814062Swpaul	/*
75914062Swpaul	 * The superuser may add entries to the passwd maps if
76014062Swpaul	 * rpc.yppasswdd is started with the -a flag. Paranoia
76114062Swpaul	 * prevents me from allowing additions by default.
76214062Swpaul	 */
76314062Swpaul	if ((rval = yp_get_record(argp->domain, "master.passwd.byname",
76414062Swpaul			  &key, &data, 0)) != YP_TRUE) {
76514062Swpaul		if (rval == YP_NOKEY) {
76614062Swpaul			yp_error("user %s not found in passwd database",
76714062Swpaul				 argp->newpw.pw_name);
76814062Swpaul			if (allow_additions)
76914062Swpaul				yp_error("notice: adding user %s to \
77014062Swpaulmaster.passwd database for domain %s", argp->newpw.pw_name, argp->domain);
77114062Swpaul			else
77230377Scharnier				yp_error("restart rpc.yppasswdd with the -a flag to \
77330377Scharnierallow additions to be made to the password database");
77414062Swpaul		} else {
77514062Swpaul			yp_error("database access error: %s",
77614062Swpaul				 yperr_string(rval));
77714062Swpaul		}
77814062Swpaul		if (!allow_additions)
77927758Swpaul			return(&result);
78014062Swpaul	} else {
78114062Swpaul
78214062Swpaul		/* Nul terminate, please. */
78314062Swpaul		*(char *)(data.data + data.size) = '\0';
78414062Swpaul
78514062Swpaul		copy_yp_pass(data.data, 1, data.size);
78614062Swpaul	}
78714062Swpaul
78814062Swpaul	/*
78914062Swpaul	 * Perform a small bit of sanity checking.
79014062Swpaul	 */
79114062Swpaul	if (validate_master(rval == YP_TRUE ? &yp_password:NULL,&argp->newpw)){
79214062Swpaul		yp_error("rejecting update attempt for %s: bad arguments",
79314062Swpaul			 argp->newpw.pw_name);
79427758Swpaul		return(&result);
79514062Swpaul	}
79614062Swpaul
79714062Swpaul	/*
79814062Swpaul	 * If the caller specified a domain other than our 'default'
79914062Swpaul	 * domain, change the path to master.passwd accordingly.
80014062Swpaul	 */
80114062Swpaul
80214062Swpaul	if (strcmp(argp->domain, yppasswd_domain)) {
80314062Swpaul		snprintf(passfile_buf, sizeof(passfile_buf),
80416649Swpaul			"%s/%s/master.passwd", yp_dir, argp->domain);
80514062Swpaul		passfile = (char *)&passfile_buf;
80614062Swpaul	}
80714062Swpaul
80814062Swpaul	if ((pfd = pw_lock()) < 0) {
80927758Swpaul		return (&result);
81014062Swpaul	}
81114062Swpaul	if ((tfd = pw_tmp()) < 0) {
81227758Swpaul		return (&result);
81314062Swpaul	}
81414062Swpaul
81514062Swpaul	if (pw_copy(pfd, tfd, (struct passwd  *)&argp->newpw)) {
81614062Swpaul		yp_error("failed to created updated password file -- \
81714062Swpaulcleaning up and bailing out");
81814062Swpaul		unlink(tempname);
81927758Swpaul		return(&result);
82014062Swpaul	}
82114062Swpaul
82217431Swpaul	passfile_hold = yp_mktmpnam();
82314062Swpaul	rename(passfile, passfile_hold);
82414062Swpaul	if (strcmp(passfile, _PATH_MASTERPASSWD)) {
82514062Swpaul		rename(tempname, passfile);
82614062Swpaul	} else {
82716876Sguido		if (pw_mkdb(argp->newpw.pw_name) < 0) {
82814062Swpaul			yp_error("pwd_mkdb failed");
82927758Swpaul			return(&result);
83014062Swpaul		}
83114062Swpaul	}
83214062Swpaul
83316134Swpaul	if (inplace) {
83416134Swpaul		if ((rval = update_inplace((struct passwd *)&argp->newpw,
83516134Swpaul							argp->domain))) {
83616134Swpaul			yp_error("inplace update failed -- rebuilding maps");
83716134Swpaul		}
83816134Swpaul	}
83916134Swpaul
84014062Swpaul	switch((pid = fork())) {
84114062Swpaul	case 0:
84216134Swpaul		if (inplace && !rval) {
84316134Swpaul    			execlp(MAP_UPDATE_PATH, MAP_UPDATE, passfile,
84479452Sbrian				argp->domain, "pushpw", (char *)NULL);
84516134Swpaul    		} else {
84616134Swpaul			execlp(MAP_UPDATE_PATH, MAP_UPDATE, passfile,
84779452Sbrian				argp->domain, (char *)NULL);
84816134Swpaul		}
84914062Swpaul    		yp_error("couldn't exec map update process: %s",
85014062Swpaul					strerror(errno));
85114062Swpaul		unlink(passfile);
85214062Swpaul		rename(passfile_hold, passfile);
85314062Swpaul    		exit(1);
85414062Swpaul		break;
85514062Swpaul	case -1:
85614062Swpaul		yp_error("fork() failed: %s", strerror(errno));
85714062Swpaul		unlink(passfile);
85814062Swpaul		rename(passfile_hold, passfile);
85927758Swpaul		return(&result);
86014062Swpaul		break;
86114062Swpaul	default:
86217431Swpaul		unlink(passfile_hold);
86314062Swpaul		break;
86414062Swpaul	}
86514062Swpaul
86614062Swpaul	yp_error("performed update of user %s (uid %d) domain %s",
86714062Swpaul						argp->newpw.pw_name,
86814062Swpaul						argp->newpw.pw_uid,
86914062Swpaul						argp->domain);
87014062Swpaul
87114062Swpaul	result = 0;
87227758Swpaul	return(&result);
87314062Swpaul}
874