194575Sdes/*-
294575Sdes * Copyright (c) 2002 Networks Associates Technology, Inc.
394575Sdes * All rights reserved.
494575Sdes *
594575Sdes * This software was developed for the FreeBSD Project by ThinkSec AS and
694575Sdes * NAI Labs, the Security Research Division of Network Associates, Inc.
794575Sdes * under DARPA/SPAWAR contract N66001-01-C-8035 ("CBOSS"), as part of the
894575Sdes * DARPA CHATS research program.
994575Sdes *
1094575Sdes * Redistribution and use in source and binary forms, with or without
1194575Sdes * modification, are permitted provided that the following conditions
1294575Sdes * are met:
1394575Sdes * 1. Redistributions of source code must retain the above copyright
1494575Sdes *    notice, this list of conditions and the following disclaimer.
1594575Sdes * 2. Redistributions in binary form must reproduce the above copyright
1694575Sdes *    notice, this list of conditions and the following disclaimer in the
1794575Sdes *    documentation and/or other materials provided with the distribution.
1894575Sdes * 3. The name of the author may not be used to endorse or promote
1994575Sdes *    products derived from this software without specific prior written
2094575Sdes *    permission.
2194575Sdes *
2294575Sdes * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
2394575Sdes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2494575Sdes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2594575Sdes * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
2694575Sdes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2794575Sdes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2894575Sdes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2994575Sdes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
3094575Sdes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
3194575Sdes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3294575Sdes * SUCH DAMAGE.
3394575Sdes *
3494575Sdes * $FreeBSD$
3594575Sdes */
3694575Sdes
3794577Sdes#include <sys/types.h>
3894577Sdes#include <sys/socket.h>
3994577Sdes#include <netinet/in.h>
4094577Sdes
4194575Sdes#include <err.h>
4294575Sdes#include <errno.h>
4394577Sdes#include <netconfig.h>
4494577Sdes#include <netdb.h>
4594575Sdes#include <pwd.h>
4694575Sdes#include <stdlib.h>
4794575Sdes#include <string.h>
4894577Sdes#include <unistd.h>
4994575Sdes
5094575Sdes#include <rpcsvc/ypclnt.h>
5194575Sdes#include <rpcsvc/yppasswd.h>
5294575Sdes
5394575Sdes#include "ypclnt.h"
5494577Sdes#include "yppasswd_private.h"
5594575Sdes
5694577Sdesstatic int yppasswd_remote(ypclnt_t *, const struct passwd *, const char *);
57116393Smbrstatic int yppasswd_local(ypclnt_t *, const struct passwd *);
5894577Sdes
5996198Sdes/*
6096198Sdes * Determines the availability of rpc.yppasswdd.  Returns -1 for not
6196198Sdes * available (or unable to determine), 0 for available, 1 for available in
6296198Sdes * master mode.
6396198Sdes */
6494575Sdesint
6596198Sdesypclnt_havepasswdd(ypclnt_t *ypclnt)
6694575Sdes{
67116393Smbr	struct netconfig *nc = NULL;
68116393Smbr	void *localhandle = 0;
69116393Smbr	CLIENT *clnt = NULL;
70116393Smbr	int ret;
7194575Sdes
72116393Smbr	/* check if rpc.yppasswdd is running */
7394575Sdes	if (getrpcport(ypclnt->server, YPPASSWDPROG,
7494575Sdes		YPPASSWDPROC_UPDATE, IPPROTO_UDP) == 0) {
7594575Sdes		ypclnt_error(ypclnt, __func__, "no rpc.yppasswdd on server");
7694575Sdes		return (-1);
7794575Sdes	}
7894575Sdes
7994577Sdes	/* if we're not root, use remote method */
8094577Sdes	if (getuid() != 0)
8196198Sdes		return (0);
8294577Sdes
83116393Smbr	/* try to connect to rpc.yppasswdd */
84116393Smbr	localhandle = setnetconfig();
85116393Smbr	while ((nc = getnetconfig(localhandle)) != NULL) {
86116393Smbr		if (nc->nc_protofmly != NULL &&
87116393Smbr			strcmp(nc->nc_protofmly, NC_LOOPBACK) == 0)
88116393Smbr				break;
8994577Sdes	}
90116393Smbr	if (nc == NULL) {
91116393Smbr		ypclnt_error(ypclnt, __func__,
92116393Smbr		    "getnetconfig: %s", nc_sperror());
93116393Smbr		ret = 0;
94116393Smbr		goto done;
9594577Sdes	}
96116393Smbr	if ((clnt = clnt_tp_create(NULL, MASTER_YPPASSWDPROG,
97116393Smbr	    MASTER_YPPASSWDVERS, nc)) == NULL) {
98116393Smbr		ypclnt_error(ypclnt, __func__,
99116393Smbr		    "failed to connect to rpc.yppasswdd: %s",
100116393Smbr		    clnt_spcreateerror(ypclnt->server));
101116393Smbr		ret = 0;
102116393Smbr		goto done;
103116393Smbr	} else
104116393Smbr		ret = 1;
105116393Smbr
106116393Smbrdone:
107116393Smbr	if (clnt != NULL) {
108116393Smbr		clnt_destroy(clnt);
109116393Smbr	}
110116393Smbr	endnetconfig(localhandle);
111116393Smbr	return (ret);
11294577Sdes}
11394577Sdes
11494577Sdes/*
11596198Sdes * Updates the NIS user information for the specified user.
11696198Sdes */
11796198Sdesint
11896198Sdesypclnt_passwd(ypclnt_t *ypclnt, const struct passwd *pwd, const char *passwd)
11996198Sdes{
12096198Sdes	switch (ypclnt_havepasswdd(ypclnt)) {
12196198Sdes	case 0:
12296198Sdes		return (yppasswd_remote(ypclnt, pwd, passwd));
12396198Sdes	case 1:
124116393Smbr		return (yppasswd_local(ypclnt, pwd));
12596198Sdes	default:
12696198Sdes		return (-1);
12796198Sdes	}
12896198Sdes}
12996198Sdes
13096198Sdes/*
13194577Sdes * yppasswd_remote and yppasswd_local are quite similar but still
13294577Sdes * sufficiently different that merging them into one makes the code
13394577Sdes * significantly less readable, IMHO, so we keep them separate.
13494577Sdes */
13594577Sdes
13694577Sdesstatic int
137116393Smbryppasswd_local(ypclnt_t *ypclnt, const struct passwd *pwd)
13894577Sdes{
13994577Sdes	struct master_yppasswd yppwd;
14094577Sdes	struct rpc_err rpcerr;
14194577Sdes	struct netconfig *nc = NULL;
142116393Smbr	void *localhandle = 0;
14394577Sdes	CLIENT *clnt = NULL;
14494577Sdes	int ret, *result;
14594577Sdes
14694577Sdes	/* fill the master_yppasswd structure */
14794577Sdes	memset(&yppwd, 0, sizeof yppwd);
14894577Sdes	yppwd.newpw.pw_uid = pwd->pw_uid;
14994577Sdes	yppwd.newpw.pw_gid = pwd->pw_gid;
15094577Sdes	yppwd.newpw.pw_change = pwd->pw_change;
15194577Sdes	yppwd.newpw.pw_expire = pwd->pw_expire;
15294577Sdes	yppwd.newpw.pw_fields = pwd->pw_fields;
153116393Smbr	yppwd.oldpass = strdup("");
154116393Smbr	yppwd.domain = strdup(ypclnt->domain);
15594577Sdes	if ((yppwd.newpw.pw_name = strdup(pwd->pw_name)) == NULL ||
15694577Sdes	    (yppwd.newpw.pw_passwd = strdup(pwd->pw_passwd)) == NULL ||
15794577Sdes	    (yppwd.newpw.pw_class = strdup(pwd->pw_class)) == NULL ||
15894577Sdes	    (yppwd.newpw.pw_gecos = strdup(pwd->pw_gecos)) == NULL ||
15994577Sdes	    (yppwd.newpw.pw_dir = strdup(pwd->pw_dir)) == NULL ||
160116393Smbr	    (yppwd.newpw.pw_shell = strdup(pwd->pw_shell)) == NULL) {
16194577Sdes		ypclnt_error(ypclnt, __func__, strerror(errno));
16294577Sdes		ret = -1;
16394577Sdes		goto done;
16494577Sdes	}
16594577Sdes
16694577Sdes	/* connect to rpc.yppasswdd */
167116393Smbr	localhandle = setnetconfig();
168116393Smbr	while ((nc = getnetconfig(localhandle)) != NULL) {
169116393Smbr		if (nc->nc_protofmly != NULL &&
170116393Smbr		    strcmp(nc->nc_protofmly, NC_LOOPBACK) == 0)
171116393Smbr			break;
172116393Smbr	}
173116393Smbr	if (nc == NULL) {
17494577Sdes		ypclnt_error(ypclnt, __func__,
175116393Smbr		    "getnetconfig: %s", nc_sperror());
176116393Smbr		ret = -1;
177116393Smbr		goto done;
178116393Smbr	}
179116393Smbr	if ((clnt = clnt_tp_create(NULL, MASTER_YPPASSWDPROG,
180116393Smbr	    MASTER_YPPASSWDVERS, nc)) == NULL) {
181116393Smbr		ypclnt_error(ypclnt, __func__,
18294577Sdes		    "failed to connect to rpc.yppasswdd: %s",
18394577Sdes		    clnt_spcreateerror(ypclnt->server));
18494577Sdes		ret = -1;
18594577Sdes		goto done;
18694577Sdes	}
18794577Sdes	clnt->cl_auth = authunix_create_default();
18894577Sdes
18994577Sdes	/* request the update */
19094577Sdes	result = yppasswdproc_update_master_1(&yppwd, clnt);
19194577Sdes
19294577Sdes	/* check for RPC errors */
19394577Sdes	clnt_geterr(clnt, &rpcerr);
19494577Sdes	if (rpcerr.re_status != RPC_SUCCESS) {
19594577Sdes		ypclnt_error(ypclnt, __func__,
19694577Sdes		    "NIS password update failed: %s",
19794577Sdes		    clnt_sperror(clnt, ypclnt->server));
19894577Sdes		ret = -1;
19994577Sdes		goto done;
20094577Sdes	}
20194577Sdes
20294577Sdes	/* check the result of the update */
20394577Sdes	if (result == NULL || *result != 0) {
20494577Sdes		ypclnt_error(ypclnt, __func__,
20594577Sdes		    "NIS password update failed");
20694577Sdes		/* XXX how do we get more details? */
20794577Sdes		ret = -1;
20894577Sdes		goto done;
20994577Sdes	}
21094577Sdes
21194577Sdes	ypclnt_error(ypclnt, NULL, NULL);
21294577Sdes	ret = 0;
21394577Sdes
21494577Sdes done:
21594577Sdes	if (clnt != NULL) {
21694577Sdes		auth_destroy(clnt->cl_auth);
21794577Sdes		clnt_destroy(clnt);
21894577Sdes	}
219116393Smbr	endnetconfig(localhandle);
22094577Sdes	free(yppwd.newpw.pw_name);
22195588Sdes	if (yppwd.newpw.pw_passwd != NULL) {
22295588Sdes		memset(yppwd.newpw.pw_passwd, 0, strlen(yppwd.newpw.pw_passwd));
22395588Sdes		free(yppwd.newpw.pw_passwd);
22495588Sdes	}
22594577Sdes	free(yppwd.newpw.pw_class);
22694577Sdes	free(yppwd.newpw.pw_gecos);
22794577Sdes	free(yppwd.newpw.pw_dir);
22894577Sdes	free(yppwd.newpw.pw_shell);
22994577Sdes	if (yppwd.oldpass != NULL) {
23094577Sdes		memset(yppwd.oldpass, 0, strlen(yppwd.oldpass));
23194577Sdes		free(yppwd.oldpass);
23294577Sdes	}
23394577Sdes	return (ret);
23494577Sdes}
23594577Sdes
23694577Sdesstatic int
23794577Sdesyppasswd_remote(ypclnt_t *ypclnt, const struct passwd *pwd, const char *passwd)
23894577Sdes{
23994577Sdes	struct yppasswd yppwd;
24094577Sdes	struct rpc_err rpcerr;
24194577Sdes	CLIENT *clnt = NULL;
24294577Sdes	int ret, *result;
24394577Sdes
24494575Sdes	/* fill the yppasswd structure */
24594575Sdes	memset(&yppwd, 0, sizeof yppwd);
24694575Sdes	yppwd.newpw.pw_uid = pwd->pw_uid;
24794575Sdes	yppwd.newpw.pw_gid = pwd->pw_gid;
24894575Sdes	if ((yppwd.newpw.pw_name = strdup(pwd->pw_name)) == NULL ||
24994575Sdes	    (yppwd.newpw.pw_passwd = strdup(pwd->pw_passwd)) == NULL ||
25094575Sdes	    (yppwd.newpw.pw_gecos = strdup(pwd->pw_gecos)) == NULL ||
25194575Sdes	    (yppwd.newpw.pw_dir = strdup(pwd->pw_dir)) == NULL ||
25294575Sdes	    (yppwd.newpw.pw_shell = strdup(pwd->pw_shell)) == NULL ||
25396198Sdes	    (yppwd.oldpass = strdup(passwd ? passwd : "")) == NULL) {
25494575Sdes		ypclnt_error(ypclnt, __func__, strerror(errno));
25594575Sdes		ret = -1;
25694575Sdes		goto done;
25794575Sdes	}
25894575Sdes
25994575Sdes	/* connect to rpc.yppasswdd */
26094575Sdes	clnt = clnt_create(ypclnt->server, YPPASSWDPROG, YPPASSWDVERS, "udp");
26194575Sdes	if (clnt == NULL) {
26294575Sdes		ypclnt_error(ypclnt, __func__,
26394575Sdes		    "failed to connect to rpc.yppasswdd: %s",
26494575Sdes		    clnt_spcreateerror(ypclnt->server));
26594575Sdes		ret = -1;
26694575Sdes		goto done;
26794575Sdes	}
26894575Sdes	clnt->cl_auth = authunix_create_default();
26994575Sdes
27094575Sdes	/* request the update */
27194575Sdes	result = yppasswdproc_update_1(&yppwd, clnt);
27294575Sdes
27394575Sdes	/* check for RPC errors */
27494575Sdes	clnt_geterr(clnt, &rpcerr);
27594575Sdes	if (rpcerr.re_status != RPC_SUCCESS) {
27694575Sdes		ypclnt_error(ypclnt, __func__,
27794575Sdes		    "NIS password update failed: %s",
27894575Sdes		    clnt_sperror(clnt, ypclnt->server));
27994575Sdes		ret = -1;
28094575Sdes		goto done;
28194575Sdes	}
28294575Sdes
28394575Sdes	/* check the result of the update */
28494575Sdes	if (result == NULL || *result != 0) {
28594575Sdes		ypclnt_error(ypclnt, __func__,
28694575Sdes		    "NIS password update failed");
28794575Sdes		/* XXX how do we get more details? */
28894575Sdes		ret = -1;
28994575Sdes		goto done;
29094575Sdes	}
29194575Sdes
29294575Sdes	ypclnt_error(ypclnt, NULL, NULL);
29394575Sdes	ret = 0;
29494575Sdes
29594575Sdes done:
29694575Sdes	if (clnt != NULL) {
29794575Sdes		auth_destroy(clnt->cl_auth);
29894575Sdes		clnt_destroy(clnt);
29994575Sdes	}
30094575Sdes	free(yppwd.newpw.pw_name);
30195588Sdes	if (yppwd.newpw.pw_passwd != NULL) {
30295588Sdes		memset(yppwd.newpw.pw_passwd, 0, strlen(yppwd.newpw.pw_passwd));
30395588Sdes		free(yppwd.newpw.pw_passwd);
30495588Sdes	}
30594575Sdes	free(yppwd.newpw.pw_gecos);
30694575Sdes	free(yppwd.newpw.pw_dir);
30794575Sdes	free(yppwd.newpw.pw_shell);
30894575Sdes	if (yppwd.oldpass != NULL) {
30994575Sdes		memset(yppwd.oldpass, 0, strlen(yppwd.oldpass));
31094575Sdes		free(yppwd.oldpass);
31194575Sdes	}
31294575Sdes	return (ret);
31394575Sdes}
314