ypclnt_passwd.c revision 94577
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: head/lib/libypclnt/ypclnt_passwd.c 94577 2002-04-13 06:57:14Z des $
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 *);
5794577Sdesstatic int yppasswd_local(ypclnt_t *, const struct passwd *, const char *);
5894577Sdes
5994575Sdesint
6094575Sdesypclnt_passwd(ypclnt_t *ypclnt, const struct passwd *pwd, const char *passwd)
6194575Sdes{
6294577Sdes	struct addrinfo hints, *res;
6394577Sdes	int sd;
6494575Sdes
6594575Sdes	/* check that rpc.yppasswdd is running */
6694575Sdes	if (getrpcport(ypclnt->server, YPPASSWDPROG,
6794575Sdes		YPPASSWDPROC_UPDATE, IPPROTO_UDP) == 0) {
6894575Sdes		ypclnt_error(ypclnt, __func__, "no rpc.yppasswdd on server");
6994575Sdes		return (-1);
7094575Sdes	}
7194575Sdes
7294577Sdes	/* if we're not root, use remote method */
7394577Sdes	if (getuid() != 0)
7494577Sdes		goto remote;
7594577Sdes
7694577Sdes	/* try to determine if we are the server */
7794577Sdes	memset(&hints, 0, sizeof(hints));
7894577Sdes	hints.ai_family = AF_UNSPEC;
7994577Sdes	hints.ai_socktype = SOCK_STREAM;
8094577Sdes	hints.ai_protocol = 0;
8194577Sdes	if (getaddrinfo(ypclnt->server, NULL, &hints, &res) != 0)
8294577Sdes		goto remote;
8394577Sdes	sd = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
8494577Sdes	if (sd == -1) {
8594577Sdes		freeaddrinfo(res);
8694577Sdes		goto remote;
8794577Sdes	}
8894577Sdes	if (bind(sd, res->ai_addr, res->ai_addrlen) == -1) {
8994577Sdes		close(sd);
9094577Sdes		freeaddrinfo(res);
9194577Sdes		goto remote;
9294577Sdes	}
9394577Sdes	freeaddrinfo(res);
9494577Sdes	close(sd);
9594577Sdes	YPCLNT_DEBUG("using local update method");
9694577Sdes	return (yppasswd_local(ypclnt, pwd, passwd));
9794577Sdes remote:
9894577Sdes	YPCLNT_DEBUG("using remote update method");
9994577Sdes	return (yppasswd_remote(ypclnt, pwd, passwd));
10094577Sdes}
10194577Sdes
10294577Sdes/*
10394577Sdes * yppasswd_remote and yppasswd_local are quite similar but still
10494577Sdes * sufficiently different that merging them into one makes the code
10594577Sdes * significantly less readable, IMHO, so we keep them separate.
10694577Sdes */
10794577Sdes
10894577Sdesstatic int
10994577Sdesyppasswd_local(ypclnt_t *ypclnt, const struct passwd *pwd, const char *passwd)
11094577Sdes{
11194577Sdes	struct master_yppasswd yppwd;
11294577Sdes	struct rpc_err rpcerr;
11394577Sdes	struct netconfig *nc = NULL;
11494577Sdes	CLIENT *clnt = NULL;
11594577Sdes	int ret, *result;
11694577Sdes
11794577Sdes	/* fill the master_yppasswd structure */
11894577Sdes	memset(&yppwd, 0, sizeof yppwd);
11994577Sdes	yppwd.newpw.pw_uid = pwd->pw_uid;
12094577Sdes	yppwd.newpw.pw_gid = pwd->pw_gid;
12194577Sdes	yppwd.newpw.pw_change = pwd->pw_change;
12294577Sdes	yppwd.newpw.pw_expire = pwd->pw_expire;
12394577Sdes	yppwd.newpw.pw_fields = pwd->pw_fields;
12494577Sdes	if ((yppwd.newpw.pw_name = strdup(pwd->pw_name)) == NULL ||
12594577Sdes	    (yppwd.newpw.pw_passwd = strdup(pwd->pw_passwd)) == NULL ||
12694577Sdes	    (yppwd.newpw.pw_class = strdup(pwd->pw_class)) == NULL ||
12794577Sdes	    (yppwd.newpw.pw_gecos = strdup(pwd->pw_gecos)) == NULL ||
12894577Sdes	    (yppwd.newpw.pw_dir = strdup(pwd->pw_dir)) == NULL ||
12994577Sdes	    (yppwd.newpw.pw_shell = strdup(pwd->pw_shell)) == NULL ||
13094577Sdes	    (yppwd.oldpass = strdup(passwd)) == NULL) {
13194577Sdes		ypclnt_error(ypclnt, __func__, strerror(errno));
13294577Sdes		ret = -1;
13394577Sdes		goto done;
13494577Sdes	}
13594577Sdes
13694577Sdes	/* connect to rpc.yppasswdd */
13794577Sdes	nc = getnetconfigent("unix");
13894577Sdes	clnt = clnt_tp_create(ypclnt->server, YPPASSWDPROG, YPPASSWDVERS, nc);
13994577Sdes	if (clnt == NULL) {
14094577Sdes		ypclnt_error(ypclnt, __func__,
14194577Sdes		    "failed to connect to rpc.yppasswdd: %s",
14294577Sdes		    clnt_spcreateerror(ypclnt->server));
14394577Sdes		ret = -1;
14494577Sdes		goto done;
14594577Sdes	}
14694577Sdes	clnt->cl_auth = authunix_create_default();
14794577Sdes
14894577Sdes	/* request the update */
14994577Sdes	result = yppasswdproc_update_master_1(&yppwd, clnt);
15094577Sdes
15194577Sdes	/* check for RPC errors */
15294577Sdes	clnt_geterr(clnt, &rpcerr);
15394577Sdes	if (rpcerr.re_status != RPC_SUCCESS) {
15494577Sdes		ypclnt_error(ypclnt, __func__,
15594577Sdes		    "NIS password update failed: %s",
15694577Sdes		    clnt_sperror(clnt, ypclnt->server));
15794577Sdes		ret = -1;
15894577Sdes		goto done;
15994577Sdes	}
16094577Sdes
16194577Sdes	/* check the result of the update */
16294577Sdes	if (result == NULL || *result != 0) {
16394577Sdes		ypclnt_error(ypclnt, __func__,
16494577Sdes		    "NIS password update failed");
16594577Sdes		/* XXX how do we get more details? */
16694577Sdes		ret = -1;
16794577Sdes		goto done;
16894577Sdes	}
16994577Sdes
17094577Sdes	ypclnt_error(ypclnt, NULL, NULL);
17194577Sdes	ret = 0;
17294577Sdes
17394577Sdes done:
17494577Sdes	if (clnt != NULL) {
17594577Sdes		auth_destroy(clnt->cl_auth);
17694577Sdes		clnt_destroy(clnt);
17794577Sdes	}
17894577Sdes	if (nc != NULL)
17994577Sdes		freenetconfigent(nc);
18094577Sdes	free(yppwd.newpw.pw_name);
18194577Sdes	free(yppwd.newpw.pw_passwd);
18294577Sdes	free(yppwd.newpw.pw_class);
18394577Sdes	free(yppwd.newpw.pw_gecos);
18494577Sdes	free(yppwd.newpw.pw_dir);
18594577Sdes	free(yppwd.newpw.pw_shell);
18694577Sdes	if (yppwd.oldpass != NULL) {
18794577Sdes		memset(yppwd.oldpass, 0, strlen(yppwd.oldpass));
18894577Sdes		free(yppwd.oldpass);
18994577Sdes	}
19094577Sdes	return (ret);
19194577Sdes}
19294577Sdes
19394577Sdesstatic int
19494577Sdesyppasswd_remote(ypclnt_t *ypclnt, const struct passwd *pwd, const char *passwd)
19594577Sdes{
19694577Sdes	struct yppasswd yppwd;
19794577Sdes	struct rpc_err rpcerr;
19894577Sdes	CLIENT *clnt = NULL;
19994577Sdes	int ret, *result;
20094577Sdes
20194575Sdes	/* fill the yppasswd structure */
20294575Sdes	memset(&yppwd, 0, sizeof yppwd);
20394575Sdes	yppwd.newpw.pw_uid = pwd->pw_uid;
20494575Sdes	yppwd.newpw.pw_gid = pwd->pw_gid;
20594575Sdes	if ((yppwd.newpw.pw_name = strdup(pwd->pw_name)) == NULL ||
20694575Sdes	    (yppwd.newpw.pw_passwd = strdup(pwd->pw_passwd)) == NULL ||
20794575Sdes	    (yppwd.newpw.pw_gecos = strdup(pwd->pw_gecos)) == NULL ||
20894575Sdes	    (yppwd.newpw.pw_dir = strdup(pwd->pw_dir)) == NULL ||
20994575Sdes	    (yppwd.newpw.pw_shell = strdup(pwd->pw_shell)) == NULL ||
21094575Sdes	    (yppwd.oldpass = strdup(passwd)) == NULL) {
21194575Sdes		ypclnt_error(ypclnt, __func__, strerror(errno));
21294575Sdes		ret = -1;
21394575Sdes		goto done;
21494575Sdes	}
21594575Sdes
21694575Sdes	/* connect to rpc.yppasswdd */
21794575Sdes	clnt = clnt_create(ypclnt->server, YPPASSWDPROG, YPPASSWDVERS, "udp");
21894575Sdes	if (clnt == NULL) {
21994575Sdes		ypclnt_error(ypclnt, __func__,
22094575Sdes		    "failed to connect to rpc.yppasswdd: %s",
22194575Sdes		    clnt_spcreateerror(ypclnt->server));
22294575Sdes		ret = -1;
22394575Sdes		goto done;
22494575Sdes	}
22594575Sdes	clnt->cl_auth = authunix_create_default();
22694575Sdes
22794575Sdes	/* request the update */
22894575Sdes	result = yppasswdproc_update_1(&yppwd, clnt);
22994575Sdes
23094575Sdes	/* check for RPC errors */
23194575Sdes	clnt_geterr(clnt, &rpcerr);
23294575Sdes	if (rpcerr.re_status != RPC_SUCCESS) {
23394575Sdes		ypclnt_error(ypclnt, __func__,
23494575Sdes		    "NIS password update failed: %s",
23594575Sdes		    clnt_sperror(clnt, ypclnt->server));
23694575Sdes		ret = -1;
23794575Sdes		goto done;
23894575Sdes	}
23994575Sdes
24094575Sdes	/* check the result of the update */
24194575Sdes	if (result == NULL || *result != 0) {
24294575Sdes		ypclnt_error(ypclnt, __func__,
24394575Sdes		    "NIS password update failed");
24494575Sdes		/* XXX how do we get more details? */
24594575Sdes		ret = -1;
24694575Sdes		goto done;
24794575Sdes	}
24894575Sdes
24994575Sdes	ypclnt_error(ypclnt, NULL, NULL);
25094575Sdes	ret = 0;
25194575Sdes
25294575Sdes done:
25394575Sdes	if (clnt != NULL) {
25494575Sdes		auth_destroy(clnt->cl_auth);
25594575Sdes		clnt_destroy(clnt);
25694575Sdes	}
25794575Sdes	free(yppwd.newpw.pw_name);
25894575Sdes	free(yppwd.newpw.pw_passwd);
25994575Sdes	free(yppwd.newpw.pw_gecos);
26094575Sdes	free(yppwd.newpw.pw_dir);
26194575Sdes	free(yppwd.newpw.pw_shell);
26294575Sdes	if (yppwd.oldpass != NULL) {
26394575Sdes		memset(yppwd.oldpass, 0, strlen(yppwd.oldpass));
26494575Sdes		free(yppwd.oldpass);
26594575Sdes	}
26694575Sdes	return (ret);
26794575Sdes}
268