yppasswdd_server.c revision 17431
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 *
3217431Swpaul *	$Id: yppasswdd_server.c,v 1.6 1996/07/01 19:38:38 guido Exp $
3314062Swpaul */
3414062Swpaul
3514062Swpaul#include <stdio.h>
3614062Swpaul#include <string.h>
3714062Swpaul#include <ctype.h>
3814062Swpaul#include <stdlib.h>
3914062Swpaul#include <unistd.h>
4014062Swpaul#include <dirent.h>
4114062Swpaul#include <sys/stat.h>
4214062Swpaul#include <sys/socket.h>
4314062Swpaul#include <netinet/in.h>
4414062Swpaul#include <arpa/inet.h>
4514062Swpaul#include <limits.h>
4614062Swpaul#include <db.h>
4714062Swpaul#include <pwd.h>
4814062Swpaul#include <errno.h>
4914062Swpaul#include <signal.h>
5014062Swpaul#include <rpc/rpc.h>
5114062Swpaul#include <rpcsvc/yp.h>
5214062Swpaul#include <sys/types.h>
5314062Swpaul#include <sys/wait.h>
5414062Swpaul#include <sys/param.h>
5516134Swpaul#include <sys/fcntl.h>
5614062Swpaulstruct dom_binding {};
5714062Swpaul#include <rpcsvc/ypclnt.h>
5814062Swpaul#include "yppasswdd_extern.h"
5914062Swpaul#include "yppasswd.h"
6014062Swpaul#include "yppasswd_private.h"
6114062Swpaul#include "yppasswd_comm.h"
6214062Swpaul
6314062Swpaul#ifndef lint
6417431Swpaulstatic const char rcsid[] = "$Id: yppasswdd_server.c,v 1.6 1996/07/01 19:38:38 guido Exp $";
6514062Swpaul#endif /* not lint */
6614062Swpaul
6714062Swpaulchar *tempname;
6814062Swpaul
6914062Swpaulvoid reaper(sig)
7014062Swpaul	int sig;
7114062Swpaul{
7214062Swpaul	extern pid_t pid;
7314062Swpaul	extern int pstat;
7414062Swpaul	int st;
7514062Swpaul
7614062Swpaul	if (sig > 0) {
7714062Swpaul		if (sig == SIGCHLD)
7814062Swpaul			while(wait3(&st, WNOHANG, NULL) > 0) ;
7914062Swpaul	} else {
8014062Swpaul		pid = waitpid(pid, &pstat, 0);
8114062Swpaul	}
8214062Swpaul	return;
8314062Swpaul}
8414062Swpaul
8514062Swpaulvoid install_reaper(on)
8614062Swpaul	int on;
8714062Swpaul{
8814062Swpaul	if (on) {
8914062Swpaul		signal(SIGCHLD, reaper);
9014062Swpaul	} else {
9114062Swpaul		signal(SIGCHLD, SIG_DFL);
9214062Swpaul	}
9314062Swpaul	return;
9414062Swpaul}
9514062Swpaul
9614062Swpaulstatic struct passwd yp_password;
9714062Swpaul
9814062Swpaulstatic void copy_yp_pass(p, x, m)
9914062Swpaulchar *p;
10014062Swpaulint x, m;
10114062Swpaul{
10214062Swpaul	register char *t, *s = p;
10314062Swpaul	static char *buf;
10414062Swpaul
10514062Swpaul	yp_password.pw_fields = 0;
10614062Swpaul
10714062Swpaul	buf = (char *)realloc(buf, m + 10);
10814062Swpaul	bzero(buf, m + 10);
10914062Swpaul
11014062Swpaul	/* Turn all colons into NULLs */
11114062Swpaul	while (strchr(s, ':')) {
11214062Swpaul		s = (strchr(s, ':') + 1);
11314062Swpaul		*(s - 1)= '\0';
11414062Swpaul	}
11514062Swpaul
11614062Swpaul	t = buf;
11714062Swpaul#define EXPAND(e)       e = t; while ((*t++ = *p++));
11814062Swpaul        EXPAND(yp_password.pw_name);
11914062Swpaul	yp_password.pw_fields |= _PWF_NAME;
12014062Swpaul        EXPAND(yp_password.pw_passwd);
12114062Swpaul	yp_password.pw_fields |= _PWF_PASSWD;
12214062Swpaul	yp_password.pw_uid = atoi(p);
12314062Swpaul        p += (strlen(p) + 1);
12414062Swpaul	yp_password.pw_fields |= _PWF_UID;
12514062Swpaul	yp_password.pw_gid = atoi(p);
12614062Swpaul        p += (strlen(p) + 1);
12714062Swpaul	yp_password.pw_fields |= _PWF_GID;
12814062Swpaul	if (x) {
12914062Swpaul		EXPAND(yp_password.pw_class);
13014062Swpaul		yp_password.pw_fields |= _PWF_CLASS;
13114062Swpaul		yp_password.pw_change = atol(p);
13214062Swpaul		p += (strlen(p) + 1);
13314062Swpaul		yp_password.pw_fields |= _PWF_CHANGE;
13414062Swpaul		yp_password.pw_expire = atol(p);
13514062Swpaul		p += (strlen(p) + 1);
13614062Swpaul		yp_password.pw_fields |= _PWF_EXPIRE;
13714062Swpaul	}
13814062Swpaul        EXPAND(yp_password.pw_gecos);
13914062Swpaul	yp_password.pw_fields |= _PWF_GECOS;
14014062Swpaul        EXPAND(yp_password.pw_dir);
14114062Swpaul	yp_password.pw_fields |= _PWF_DIR;
14214062Swpaul        EXPAND(yp_password.pw_shell);
14314062Swpaul	yp_password.pw_fields |= _PWF_SHELL;
14414062Swpaul
14514062Swpaul	return;
14614062Swpaul}
14714062Swpaul
14814062Swpaulstatic int validchars(arg)
14914062Swpaul	char *arg;
15014062Swpaul{
15114062Swpaul	int i;
15214062Swpaul
15314062Swpaul	for (i = 0; i < strlen(arg); i++) {
15414062Swpaul		if (iscntrl(arg[i])) {
15514062Swpaul			yp_error("string contains a control character");
15614062Swpaul			return(1);
15714062Swpaul		}
15814062Swpaul		if (arg[i] == ':') {
15914062Swpaul			yp_error("string contains a colon");
16014062Swpaul			return(1);
16114062Swpaul		}
16214062Swpaul		/* Be evil: truncate strings with \n in them silently. */
16314062Swpaul		if (arg[i] == '\n') {
16414062Swpaul			arg[i] = '\0';
16514062Swpaul			return(0);
16614062Swpaul		}
16714062Swpaul	}
16814062Swpaul	return(0);
16914062Swpaul}
17014062Swpaul
17114062Swpaulstatic int validate_master(opw, npw)
17214062Swpaul	struct passwd *opw;
17314062Swpaul	struct x_master_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
18114062Swpaul	if (validchars(npw->pw_shell)) {
18214062Swpaul		yp_error("specified shell contains invalid characters");
18314062Swpaul		return(1);
18414062Swpaul	}
18514062Swpaul
18614062Swpaul	if (validchars(npw->pw_gecos)) {
18714062Swpaul		yp_error("specified gecos field contains invalid characters");
18814062Swpaul		return(1);
18914062Swpaul	}
19014062Swpaul
19114062Swpaul	if (validchars(npw->pw_passwd)) {
19214062Swpaul		yp_error("specified password contains invalid characters");
19314062Swpaul		return(1);
19414062Swpaul	}
19514062Swpaul	return(0);
19614062Swpaul}
19714062Swpaul
19814062Swpaulstatic int validate(opw, npw)
19914062Swpaul	struct passwd *opw;
20014062Swpaul	struct x_passwd *npw;
20114062Swpaul{
20214062Swpaul
20314062Swpaul	if (npw->pw_name[0] == '+' || npw->pw_name[0] == '-') {
20414062Swpaul		yp_error("client tried to modify an NIS entry");
20514062Swpaul		return(1);
20614062Swpaul	}
20714062Swpaul
20814062Swpaul	if (npw->pw_uid != opw->pw_uid) {
20914062Swpaul		yp_error("UID mismatch: client says user %s has UID %d",
21014062Swpaul			 npw->pw_name, npw->pw_uid);
21114062Swpaul		yp_error("database says user %s has UID %d", opw->pw_name,
21214062Swpaul			 opw->pw_uid);
21314062Swpaul		return(1);
21414062Swpaul	}
21514062Swpaul
21614062Swpaul	if (npw->pw_gid != opw->pw_gid) {
21714062Swpaul		yp_error("GID mismatch: client says user %s has GID %d",
21814062Swpaul			 npw->pw_name, npw->pw_gid);
21914062Swpaul		yp_error("database says user %s has GID %d", opw->pw_name,
22014062Swpaul			 opw->pw_gid);
22114062Swpaul		return(1);
22214062Swpaul	}
22314062Swpaul
22414062Swpaul	/*
22514062Swpaul	 * Don't allow the user to shoot himself in the foot,
22614062Swpaul	 * even on purpose.
22714062Swpaul	 */
22814062Swpaul	if (!ok_shell(npw->pw_shell)) {
22914062Swpaul		yp_error("%s is not a valid shell", npw->pw_shell);
23014062Swpaul		return(1);
23114062Swpaul	}
23214062Swpaul
23314062Swpaul	if (validchars(npw->pw_shell)) {
23414062Swpaul		yp_error("specified shell contains invalid characters");
23514062Swpaul		return(1);
23614062Swpaul	}
23714062Swpaul
23814062Swpaul	if (validchars(npw->pw_gecos)) {
23914062Swpaul		yp_error("specified gecos field contains invalid characters");
24014062Swpaul		return(1);
24114062Swpaul	}
24214062Swpaul
24314062Swpaul	if (validchars(npw->pw_passwd)) {
24414062Swpaul		yp_error("specified password contains invalid characters");
24514062Swpaul		return(1);
24614062Swpaul	}
24714062Swpaul	return(0);
24814062Swpaul}
24914062Swpaul
25014062Swpaul/*
25114062Swpaul * Kludge alert:
25214062Swpaul * In order to have one rpc.yppasswdd support multiple domains,
25314062Swpaul * we have to cheat: we search each directory under /var/yp
25414062Swpaul * and try to match the user in each master.passwd.byname
25514062Swpaul * map that we find. If the user matches (username, uid and gid
25614062Swpaul * all agree), then we use that domain. If we match the user in
25714062Swpaul * more than one database, we must abort.
25814062Swpaul */
25914062Swpaulstatic char *find_domain(pw)
26014062Swpaul	struct x_passwd *pw;
26114062Swpaul{
26214062Swpaul	struct stat statbuf;
26314062Swpaul	struct dirent *dirp;
26414062Swpaul	DIR *dird;
26514062Swpaul	char yp_mapdir[MAXPATHLEN + 2];
26615687Swpaul	static char domain[YPMAXDOMAIN];
26714062Swpaul	char *tmp = NULL;
26814062Swpaul	DBT key, data;
26914062Swpaul	int hit = 0;
27014062Swpaul
27114062Swpaul	yp_error("performing multidomain lookup");
27214062Swpaul
27314062Swpaul	if ((dird = opendir(yp_dir)) == NULL) {
27414062Swpaul		yp_error("opendir(%s) failed: %s", yp_dir, strerror(errno));
27514062Swpaul		return(NULL);
27614062Swpaul	}
27714062Swpaul
27814062Swpaul	while ((dirp = readdir(dird)) != NULL) {
27914062Swpaul		snprintf(yp_mapdir, sizeof(yp_mapdir), "%s/%s",
28014062Swpaul							yp_dir, dirp->d_name);
28114062Swpaul		if (stat(yp_mapdir, &statbuf) < 0) {
28214062Swpaul			yp_error("stat(%s) failed: %s", yp_mapdir,
28314062Swpaul							strerror(errno));
28414062Swpaul			closedir(dird);
28514062Swpaul			return(NULL);
28614062Swpaul		}
28714062Swpaul		if (S_ISDIR(statbuf.st_mode)) {
28814062Swpaul			tmp = (char *)dirp->d_name;
28914062Swpaul			key.data = pw->pw_name;
29014062Swpaul			key.size = strlen(pw->pw_name);
29114062Swpaul
29214062Swpaul			if (yp_get_record(tmp,"master.passwd.byname",
29314062Swpaul			  		&key, &data, 0) != YP_TRUE) {
29414062Swpaul				continue;
29514062Swpaul			}
29614062Swpaul			*(char *)(data.data + data.size) = '\0';
29714062Swpaul			copy_yp_pass(data.data, 1, data.size);
29814062Swpaul			if (yp_password.pw_uid == pw->pw_uid &&
29914062Swpaul			    yp_password.pw_gid == pw->pw_gid) {
30014062Swpaul				hit++;
30115687Swpaul				snprintf(domain, YPMAXDOMAIN, "%s", tmp);
30214062Swpaul			}
30314062Swpaul		}
30414062Swpaul	}
30514062Swpaul
30614062Swpaul	closedir(dird);
30714062Swpaul	if (hit > 1) {
30814062Swpaul		yp_error("found same user in two different domains");
30914062Swpaul		return(NULL);
31014062Swpaul	} else
31116134Swpaul		return((char *)&domain);
31214062Swpaul}
31314062Swpaul
31416134Swpaulstatic int update_inplace(pw, domain)
31516134Swpaul	struct passwd *pw;
31616134Swpaul	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];
32316134Swpaul	int rval, i;
32416134Swpaul	char *maps[] = { "master.passwd.byname", "master.passwd.byuid",
32516134Swpaul			 "passwd.byname", "passwd.byuid" };
32616134Swpaul
32716134Swpaul	char *formats[] = { "%s:%s:%d:%d:%s:%ld:%ld:%s:%s:%s",
32816134Swpaul			    "%s:%s:%d:%d:%s:%ld:%ld:%s:%s:%s",
32916134Swpaul			    "%s:%s:%d:%d:%s:%s:%s", "%s:%s:%d:%d:%s:%s:%s" };
33016134Swpaul	char *ptr = NULL;
33116134Swpaul	char *yp_last = "YP_LAST_MODIFIED";
33216134Swpaul	char yplastbuf[YPMAXRECORD];
33316134Swpaul
33416134Swpaul	snprintf(yplastbuf, sizeof(yplastbuf), "%lu", time(NULL));
33516134Swpaul
33616134Swpaul	for (i = 0; i < 4; i++) {
33716134Swpaul
33816134Swpaul		if (i % 2) {
33916134Swpaul			snprintf(keybuf, sizeof(keybuf), "%ld", pw->pw_uid);
34016134Swpaul			key.data = (char *)&keybuf;
34116134Swpaul			key.size = strlen(keybuf);
34216134Swpaul		} else {
34316134Swpaul			key.data = pw->pw_name;
34416134Swpaul			key.size = strlen(pw->pw_name);
34516134Swpaul		}
34616134Swpaul
34716134Swpaul		/*
34816134Swpaul		 * XXX The passwd.byname and passwd.byuid maps come in
34916134Swpaul		 * two flavors: secure and insecure. The secure version
35016134Swpaul		 * has a '*' in the password field whereas the insecure one
35116134Swpaul		 * has a real crypted password. The maps will be insecure
35216134Swpaul		 * if they were built with 'unsecure = TRUE' enabled in
35316134Swpaul		 * /var/yp/Makefile, but we'd have no way of knowing if
35416134Swpaul		 * this has been done unless we were to try parsing the
35516134Swpaul		 * Makefile, which is a disgusting thought. Instead, we
35616134Swpaul		 * read the records from the maps, skip to the first ':'
35716134Swpaul		 * in them, and then look at the character immediately
35816134Swpaul		 * following it. If it's an '*' then the map is 'secure'
35916134Swpaul		 * and we must not insert a real password into the pw_passwd
36016134Swpaul		 * field. If it's not an '*', then we put the real crypted
36116134Swpaul		 * password in.
36216134Swpaul		 */
36316134Swpaul		if (yp_get_record(domain,maps[i],&key,&data,1) != YP_TRUE) {
36416134Swpaul			yp_error("couldn't read %s/%s: %s", domain,
36516134Swpaul						maps[i], strerror(errno));
36616134Swpaul			return(1);
36716134Swpaul		}
36816134Swpaul
36916134Swpaul		if ((ptr = strchr(data.data, ':')) == NULL) {
37016134Swpaul			yp_error("no colon in passwd record?!");
37116134Swpaul			return(1);
37216134Swpaul		}
37316134Swpaul
37416134Swpaul		if (i < 2) {
37516134Swpaul			snprintf(pwbuf, sizeof(pwbuf), formats[i],
37616134Swpaul			   pw->pw_name, pw->pw_passwd, pw->pw_uid,
37716134Swpaul			   pw->pw_gid, pw->pw_class, pw->pw_change,
37816134Swpaul			   pw->pw_expire, pw->pw_gecos, pw->pw_dir,
37916134Swpaul			   pw->pw_shell);
38016134Swpaul		} else {
38116134Swpaul			snprintf(pwbuf, sizeof(pwbuf), formats[i],
38216134Swpaul			   pw->pw_name, *(ptr+1) == '*' ? "*" : pw->pw_passwd,
38316134Swpaul			   pw->pw_uid, pw->pw_gid, pw->pw_gecos, pw->pw_dir,
38416134Swpaul			   pw->pw_shell);
38516134Swpaul		}
38616134Swpaul
38716134Swpaul#define FLAGS O_RDWR|O_CREAT
38816134Swpaul
38916134Swpaul		if ((dbp = yp_open_db_rw(domain, maps[i], FLAGS)) == NULL) {
39016134Swpaul			yp_error("couldn't open %s/%s r/w: %s",domain,
39116134Swpaul						maps[i],strerror(errno));
39216134Swpaul			return(1);
39316134Swpaul		}
39416134Swpaul
39516134Swpaul		data.data = pwbuf;
39616134Swpaul		data.size = strlen(pwbuf);
39716134Swpaul
39816134Swpaul		if (yp_put_record(dbp, &key, &data, 1) != YP_TRUE) {
39916134Swpaul			yp_error("failed to update record in %s/%s", domain,
40016134Swpaul								maps[i]);
40116134Swpaul			(void)(dbp->close)(dbp);
40216134Swpaul			return(1);
40316134Swpaul		}
40416134Swpaul
40516134Swpaul		key.data = yp_last;
40616134Swpaul		key.size = strlen(yp_last);
40716134Swpaul		data.data = (char *)&yplastbuf;
40816134Swpaul		data.size = strlen(yplastbuf);
40916134Swpaul
41016134Swpaul		if (yp_put_record(dbp, &key, &data, 1) != YP_TRUE) {
41116134Swpaul			yp_error("failed to update timestamp in %s/%s", domain,
41216134Swpaul								maps[i]);
41316134Swpaul			(void)(dbp->close)(dbp);
41416134Swpaul			return(1);
41516134Swpaul		}
41616134Swpaul
41716134Swpaul		(void)(dbp->close)(dbp);
41816134Swpaul	}
41916134Swpaul
42016134Swpaul	return(0);
42116134Swpaul}
42216134Swpaul
42317431Swpaulstatic char *yp_mktmpnam()
42417431Swpaul{
42517431Swpaul	static char path[MAXPATHLEN];
42617431Swpaul	char *p;
42716134Swpaul
42817431Swpaul	sprintf(path,"%s",passfile);
42917431Swpaul	if ((p = strrchr(path, '/')))
43017431Swpaul		++p;
43117431Swpaul	else
43217431Swpaul		p = path;
43317431Swpaul	strcpy(p, "yppwtmp.XXXXXX");
43417431Swpaul	return(mktemp(path));
43517431Swpaul}
43617431Swpaul
43714062Swpaulint *
43814062Swpaulyppasswdproc_update_1_svc(yppasswd *argp, struct svc_req *rqstp)
43914062Swpaul{
44014062Swpaul	static int  result;
44114062Swpaul	struct sockaddr_in *rqhost;
44214062Swpaul	DBT key, data;
44314062Swpaul	int rval = 0;
44414062Swpaul	int pfd, tfd;
44514062Swpaul	int pid;
44614062Swpaul	int passwd_changed = 0;
44714062Swpaul	int shell_changed = 0;
44814062Swpaul	int gecos_changed = 0;
44914062Swpaul	char *oldshell = NULL;
45014062Swpaul	char *oldgecos = NULL;
45114062Swpaul	char *passfile_hold;
45214062Swpaul	char passfile_buf[MAXPATHLEN + 2];
45314062Swpaul	char *domain = yppasswd_domain;
45417431Swpaul	static struct sockaddr_in clntaddr;
45517431Swpaul	static struct timeval t_saved, t_test;
45614062Swpaul
45714062Swpaul	/*
45814062Swpaul	 * Normal user updates always use the 'default' master.passwd file.
45914062Swpaul	 */
46014062Swpaul
46114062Swpaul	passfile = passfile_default;
46214062Swpaul	result = 1;
46314062Swpaul
46414062Swpaul	rqhost = svc_getcaller(rqstp->rq_xprt);
46514062Swpaul
46617431Swpaul	gettimeofday(&t_test, NULL);
46717431Swpaul	if (!bcmp((char *)rqhost, (char *)&clntaddr,
46817431Swpaul						sizeof(struct sockaddr_in)) &&
46917431Swpaul		t_test.tv_sec > t_saved.tv_sec &&
47017431Swpaul		t_test.tv_sec - t_saved.tv_sec < 300) {
47117431Swpaul
47217431Swpaul		bzero((char *)&clntaddr, sizeof(struct sockaddr_in));
47317431Swpaul		bzero((char *)&t_saved, sizeof(struct timeval));
47417431Swpaul		return(NULL);
47517431Swpaul	}
47617431Swpaul
47717431Swpaul	bcopy((char *)rqhost, (char *)&clntaddr, sizeof(struct sockaddr_in));
47817431Swpaul	gettimeofday(&t_saved, NULL);
47917431Swpaul
48014241Swpaul	if (yp_access(resvport ? "master.passwd.byname" : NULL, rqstp)) {
48114241Swpaul		yp_error("rejected update request from unauthorized host");
48214241Swpaul		svcerr_auth(rqstp->rq_xprt, AUTH_BADCRED);
48314241Swpaul		return(&result);
48414241Swpaul	}
48514241Swpaul
48614062Swpaul	/*
48714062Swpaul	 * Step one: find the user. (It's kinda pointless to
48814062Swpaul	 * proceed if the user doesn't exist.) We look for the
48914062Swpaul	 * user in the master.passwd.byname database, _NOT_ by
49014062Swpaul	 * using getpwent() and friends! We can't use getpwent()
49114062Swpaul	 * since the NIS master server is not guaranteed to be
49214062Swpaul	 * configured as an NIS client.
49314062Swpaul	 */
49414062Swpaul
49514062Swpaul	if (multidomain) {
49614062Swpaul		if ((domain = find_domain(&argp->newpw)) == NULL) {
49714062Swpaul			yp_error("multidomain lookup failed - aborting update");
49814062Swpaul			return(&result);
49914062Swpaul		} else
50014062Swpaul			yp_error("updating user %s in domain %s",
50114062Swpaul					argp->newpw.pw_name, domain);
50214062Swpaul	}
50314062Swpaul
50414062Swpaul	key.data = argp->newpw.pw_name;
50514062Swpaul	key.size = strlen(argp->newpw.pw_name);
50614062Swpaul
50714062Swpaul	if ((rval=yp_get_record(domain,"master.passwd.byname",
50814062Swpaul		  	&key, &data, 0)) != YP_TRUE) {
50914062Swpaul		if (rval == YP_NOKEY) {
51014062Swpaul			yp_error("user %s not found in passwd database",
51114062Swpaul			 	argp->newpw.pw_name);
51214062Swpaul		} else {
51314062Swpaul			yp_error("database access error: %s",
51414062Swpaul			 	yperr_string(rval));
51514062Swpaul		}
51614062Swpaul		return(&result);
51714062Swpaul	}
51814062Swpaul
51914062Swpaul	/* Nul terminate, please. */
52014062Swpaul	*(char *)(data.data + data.size) = '\0';
52114062Swpaul
52214062Swpaul	copy_yp_pass(data.data, 1, data.size);
52314062Swpaul
52414062Swpaul	/* Step 2: check that the supplied oldpass is valid. */
52514062Swpaul
52614062Swpaul	if (strcmp(crypt(argp->oldpass, yp_password.pw_passwd),
52714062Swpaul					yp_password.pw_passwd)) {
52814062Swpaul		yp_error("rejected change attempt -- bad password");
52914062Swpaul		yp_error("client address: %s username: %s",
53014062Swpaul			  inet_ntoa(rqhost->sin_addr),
53114062Swpaul			  argp->newpw.pw_name);
53214062Swpaul		return(&result);
53314062Swpaul	}
53414062Swpaul
53514062Swpaul	/* Step 3: validate the arguments passed to us by the client. */
53614062Swpaul
53714062Swpaul	if (validate(&yp_password, &argp->newpw)) {
53814062Swpaul		yp_error("rejecting change attempt: bad arguments");
53914062Swpaul		yp_error("client address: %s username: %s",
54014062Swpaul			 inet_ntoa(rqhost->sin_addr),
54114062Swpaul			 argp->newpw.pw_name);
54214062Swpaul		svcerr_decode(rqstp->rq_xprt);
54314062Swpaul		return(&result);
54414062Swpaul	}
54514062Swpaul
54614062Swpaul	/* Step 4: update the user's passwd structure. */
54714062Swpaul
54814062Swpaul	if (!no_chsh && strcmp(argp->newpw.pw_shell, yp_password.pw_shell)) {
54914062Swpaul		oldshell = yp_password.pw_shell;
55014062Swpaul		yp_password.pw_shell = argp->newpw.pw_shell;
55114062Swpaul		shell_changed++;
55214062Swpaul	}
55314062Swpaul
55414062Swpaul
55514062Swpaul	if (!no_chfn && strcmp(argp->newpw.pw_gecos, yp_password.pw_gecos)) {
55614062Swpaul		oldgecos = yp_password.pw_gecos;
55714062Swpaul		yp_password.pw_gecos = argp->newpw.pw_gecos;
55814062Swpaul		gecos_changed++;
55914062Swpaul	}
56014062Swpaul
56114062Swpaul	if (strcmp(argp->newpw.pw_passwd, yp_password.pw_passwd)) {
56214062Swpaul		yp_password.pw_passwd = argp->newpw.pw_passwd;
56314062Swpaul		passwd_changed++;
56414062Swpaul	}
56514062Swpaul
56614062Swpaul	/*
56714062Swpaul	 * If the caller specified a domain other than our 'default'
56814062Swpaul	 * domain, change the path to master.passwd accordingly.
56914062Swpaul	 */
57014062Swpaul
57114062Swpaul	if (strcmp(domain, yppasswd_domain)) {
57214062Swpaul		snprintf(passfile_buf, sizeof(passfile_buf),
57316649Swpaul			"%s/%s/master.passwd", yp_dir, domain);
57414062Swpaul		passfile = (char *)&passfile_buf;
57514062Swpaul	}
57614062Swpaul
57714062Swpaul	/* Step 5: make a new password file with the updated info. */
57814062Swpaul
57914062Swpaul	if ((pfd = pw_lock()) < 0) {
58014062Swpaul		return (&result);
58114062Swpaul	}
58214062Swpaul	if ((tfd = pw_tmp()) < 0) {
58314062Swpaul		return (&result);
58414062Swpaul	}
58514062Swpaul
58614062Swpaul	if (pw_copy(pfd, tfd, &yp_password)) {
58714062Swpaul		yp_error("failed to created updated password file -- \
58814062Swpaulcleaning up and bailing out");
58914062Swpaul		unlink(tempname);
59014062Swpaul		return(&result);
59114062Swpaul	}
59214062Swpaul
59317431Swpaul	passfile_hold = yp_mktmpnam();
59414062Swpaul	rename(passfile, passfile_hold);
59514062Swpaul	if (strcmp(passfile, _PATH_MASTERPASSWD)) {
59614062Swpaul		rename(tempname, passfile);
59714062Swpaul	} else {
59816876Sguido		if (pw_mkdb(argp->newpw.pw_name) < 0) {
59914062Swpaul			yp_error("pwd_mkdb failed");
60014062Swpaul			return(&result);
60114062Swpaul		}
60214062Swpaul	}
60314062Swpaul
60416134Swpaul	if (inplace) {
60516134Swpaul		if ((rval = update_inplace(&yp_password, domain))) {
60616134Swpaul			yp_error("inplace update failed -- rebuilding maps");
60716134Swpaul		}
60816134Swpaul	}
60916134Swpaul
61014062Swpaul	switch((pid = fork())) {
61114062Swpaul	case 0:
61216134Swpaul		if (inplace && !rval) {
61316134Swpaul    			execlp(MAP_UPDATE_PATH, MAP_UPDATE, passfile,
61416134Swpaul				yppasswd_domain, "pushpw", NULL);
61516134Swpaul		} else {
61616134Swpaul    			execlp(MAP_UPDATE_PATH, MAP_UPDATE, passfile,
61716134Swpaul				yppasswd_domain, 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 */
66214062Swpaulstatic int update_master(master_yppasswd *argp)
66314062Swpaul{
66414062Swpaul	int result;
66514062Swpaul	int pfd, tfd;
66614062Swpaul	int pid;
66714062Swpaul	int rval = 0;
66814062Swpaul	DBT key, data;
66914062Swpaul	char *passfile_hold;
67014062Swpaul	char passfile_buf[MAXPATHLEN + 2];
67114062Swpaul
67214062Swpaul	result = 1;
67314062Swpaul	passfile = passfile_default;
67414062Swpaul
67514062Swpaul	key.data = argp->newpw.pw_name;
67614062Swpaul	key.size = strlen(argp->newpw.pw_name);
67714062Swpaul
67814062Swpaul	/*
67914062Swpaul	 * The superuser may add entries to the passwd maps if
68014062Swpaul	 * rpc.yppasswdd is started with the -a flag. Paranoia
68114062Swpaul	 * prevents me from allowing additions by default.
68214062Swpaul	 */
68314062Swpaul	if ((rval = yp_get_record(argp->domain, "master.passwd.byname",
68414062Swpaul			  &key, &data, 0)) != YP_TRUE) {
68514062Swpaul		if (rval == YP_NOKEY) {
68614062Swpaul			yp_error("user %s not found in passwd database",
68714062Swpaul				 argp->newpw.pw_name);
68814062Swpaul			if (allow_additions)
68914062Swpaul				yp_error("notice: adding user %s to \
69014062Swpaulmaster.passwd database for domain %s", argp->newpw.pw_name, argp->domain);
69114062Swpaul			else
69214062Swpaul				yp_error("restart %s with the -a flag to \
69314062Swpaulallow additions to be made to the password database", progname);
69414062Swpaul		} else {
69514062Swpaul			yp_error("database access error: %s",
69614062Swpaul				 yperr_string(rval));
69714062Swpaul		}
69814062Swpaul		if (!allow_additions)
69914062Swpaul			return(result);
70014062Swpaul	} else {
70114062Swpaul
70214062Swpaul		/* Nul terminate, please. */
70314062Swpaul		*(char *)(data.data + data.size) = '\0';
70414062Swpaul
70514062Swpaul		copy_yp_pass(data.data, 1, data.size);
70614062Swpaul	}
70714062Swpaul
70814062Swpaul	/*
70914062Swpaul	 * Perform a small bit of sanity checking.
71014062Swpaul	 */
71114062Swpaul	if (validate_master(rval == YP_TRUE ? &yp_password:NULL,&argp->newpw)){
71214062Swpaul		yp_error("rejecting update attempt for %s: bad arguments",
71314062Swpaul			 argp->newpw.pw_name);
71414062Swpaul		return(result);
71514062Swpaul	}
71614062Swpaul
71714062Swpaul	/*
71814062Swpaul	 * If the caller specified a domain other than our 'default'
71914062Swpaul	 * domain, change the path to master.passwd accordingly.
72014062Swpaul	 */
72114062Swpaul
72214062Swpaul	if (strcmp(argp->domain, yppasswd_domain)) {
72314062Swpaul		snprintf(passfile_buf, sizeof(passfile_buf),
72416649Swpaul			"%s/%s/master.passwd", yp_dir, argp->domain);
72514062Swpaul		passfile = (char *)&passfile_buf;
72614062Swpaul	}
72714062Swpaul
72814062Swpaul	if ((pfd = pw_lock()) < 0) {
72914062Swpaul		return (result);
73014062Swpaul	}
73114062Swpaul	if ((tfd = pw_tmp()) < 0) {
73214062Swpaul		return (result);
73314062Swpaul	}
73414062Swpaul
73514062Swpaul	if (pw_copy(pfd, tfd, (struct passwd  *)&argp->newpw)) {
73614062Swpaul		yp_error("failed to created updated password file -- \
73714062Swpaulcleaning up and bailing out");
73814062Swpaul		unlink(tempname);
73914062Swpaul		return(result);
74014062Swpaul	}
74114062Swpaul
74217431Swpaul	passfile_hold = yp_mktmpnam();
74314062Swpaul	rename(passfile, passfile_hold);
74414062Swpaul	if (strcmp(passfile, _PATH_MASTERPASSWD)) {
74514062Swpaul		rename(tempname, passfile);
74614062Swpaul	} else {
74716876Sguido		if (pw_mkdb(argp->newpw.pw_name) < 0) {
74814062Swpaul			yp_error("pwd_mkdb failed");
74914062Swpaul			return(result);
75014062Swpaul		}
75114062Swpaul	}
75214062Swpaul
75316134Swpaul	if (inplace) {
75416134Swpaul		if ((rval = update_inplace((struct passwd *)&argp->newpw,
75516134Swpaul							argp->domain))) {
75616134Swpaul			yp_error("inplace update failed -- rebuilding maps");
75716134Swpaul		}
75816134Swpaul	}
75916134Swpaul
76014062Swpaul	switch((pid = fork())) {
76114062Swpaul	case 0:
76214062Swpaul		close(yp_sock);
76316134Swpaul		if (inplace && !rval) {
76416134Swpaul    			execlp(MAP_UPDATE_PATH, MAP_UPDATE, passfile,
76516134Swpaul				argp->domain, "pushpw", NULL);
76616134Swpaul    		} else {
76716134Swpaul			execlp(MAP_UPDATE_PATH, MAP_UPDATE, passfile,
76816134Swpaul				argp->domain, NULL);
76916134Swpaul		}
77014062Swpaul    		yp_error("couldn't exec map update process: %s",
77114062Swpaul					strerror(errno));
77214062Swpaul		unlink(passfile);
77314062Swpaul		rename(passfile_hold, passfile);
77414062Swpaul    		exit(1);
77514062Swpaul		break;
77614062Swpaul	case -1:
77714062Swpaul		yp_error("fork() failed: %s", strerror(errno));
77814062Swpaul		unlink(passfile);
77914062Swpaul		rename(passfile_hold, passfile);
78014062Swpaul		return(result);
78114062Swpaul		break;
78214062Swpaul	default:
78317431Swpaul		unlink(passfile_hold);
78414062Swpaul		break;
78514062Swpaul	}
78614062Swpaul
78714062Swpaul	yp_error("performed update of user %s (uid %d) domain %s",
78814062Swpaul						argp->newpw.pw_name,
78914062Swpaul						argp->newpw.pw_uid,
79014062Swpaul						argp->domain);
79114062Swpaul
79214062Swpaul	result = 0;
79314062Swpaul	return(result);
79414062Swpaul}
79514062Swpaul
79614062Swpaul/*
79714062Swpaul * Pseudo-dispatcher for private 'superuser-only' update handler.
79814062Swpaul */
79914062Swpaulvoid do_master()
80014062Swpaul{
80114062Swpaul	struct master_yppasswd *pw;
80214062Swpaul
80314062Swpaul	if ((pw = getdat(yp_sock)) == NULL) {
80414062Swpaul		return;
80514062Swpaul	}
80614062Swpaul
80714062Swpaul	yp_error("received update request from superuser on localhost");
80815687Swpaul	sendresp(update_master(pw));
80914062Swpaul
81014062Swpaul	/* Remember to free args. */
81114062Swpaul	xdr_free(xdr_master_yppasswd, (char *)pw);
81214062Swpaul
81314062Swpaul	return;
81414062Swpaul}
815