ypclnt_passwd.c revision 116393
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 116393 2003-06-15 10:36:53Z mbr $
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		YPCLNT_DEBUG("using remote update method");
12396198Sdes		return (yppasswd_remote(ypclnt, pwd, passwd));
12496198Sdes	case 1:
12596198Sdes		YPCLNT_DEBUG("using local update method");
126116393Smbr		return (yppasswd_local(ypclnt, pwd));
12796198Sdes	default:
12896198Sdes		YPCLNT_DEBUG("no rpc.yppasswdd");
12996198Sdes		return (-1);
13096198Sdes	}
13196198Sdes}
13296198Sdes
13396198Sdes/*
13494577Sdes * yppasswd_remote and yppasswd_local are quite similar but still
13594577Sdes * sufficiently different that merging them into one makes the code
13694577Sdes * significantly less readable, IMHO, so we keep them separate.
13794577Sdes */
13894577Sdes
13994577Sdesstatic int
140116393Smbryppasswd_local(ypclnt_t *ypclnt, const struct passwd *pwd)
14194577Sdes{
14294577Sdes	struct master_yppasswd yppwd;
14394577Sdes	struct rpc_err rpcerr;
14494577Sdes	struct netconfig *nc = NULL;
145116393Smbr	void *localhandle = 0;
14694577Sdes	CLIENT *clnt = NULL;
14794577Sdes	int ret, *result;
14894577Sdes
14994577Sdes	/* fill the master_yppasswd structure */
15094577Sdes	memset(&yppwd, 0, sizeof yppwd);
15194577Sdes	yppwd.newpw.pw_uid = pwd->pw_uid;
15294577Sdes	yppwd.newpw.pw_gid = pwd->pw_gid;
15394577Sdes	yppwd.newpw.pw_change = pwd->pw_change;
15494577Sdes	yppwd.newpw.pw_expire = pwd->pw_expire;
15594577Sdes	yppwd.newpw.pw_fields = pwd->pw_fields;
156116393Smbr	yppwd.oldpass = strdup("");
157116393Smbr	yppwd.domain = strdup(ypclnt->domain);
15894577Sdes	if ((yppwd.newpw.pw_name = strdup(pwd->pw_name)) == NULL ||
15994577Sdes	    (yppwd.newpw.pw_passwd = strdup(pwd->pw_passwd)) == NULL ||
16094577Sdes	    (yppwd.newpw.pw_class = strdup(pwd->pw_class)) == NULL ||
16194577Sdes	    (yppwd.newpw.pw_gecos = strdup(pwd->pw_gecos)) == NULL ||
16294577Sdes	    (yppwd.newpw.pw_dir = strdup(pwd->pw_dir)) == NULL ||
163116393Smbr	    (yppwd.newpw.pw_shell = strdup(pwd->pw_shell)) == NULL) {
16494577Sdes		ypclnt_error(ypclnt, __func__, strerror(errno));
16594577Sdes		ret = -1;
16694577Sdes		goto done;
16794577Sdes	}
16894577Sdes
16994577Sdes	/* connect to rpc.yppasswdd */
170116393Smbr	localhandle = setnetconfig();
171116393Smbr	while ((nc = getnetconfig(localhandle)) != NULL) {
172116393Smbr		if (nc->nc_protofmly != NULL &&
173116393Smbr		    strcmp(nc->nc_protofmly, NC_LOOPBACK) == 0)
174116393Smbr			break;
175116393Smbr	}
176116393Smbr	if (nc == NULL) {
17794577Sdes		ypclnt_error(ypclnt, __func__,
178116393Smbr		    "getnetconfig: %s", nc_sperror());
179116393Smbr		ret = -1;
180116393Smbr		goto done;
181116393Smbr	}
182116393Smbr	if ((clnt = clnt_tp_create(NULL, MASTER_YPPASSWDPROG,
183116393Smbr	    MASTER_YPPASSWDVERS, nc)) == NULL) {
184116393Smbr		ypclnt_error(ypclnt, __func__,
18594577Sdes		    "failed to connect to rpc.yppasswdd: %s",
18694577Sdes		    clnt_spcreateerror(ypclnt->server));
18794577Sdes		ret = -1;
18894577Sdes		goto done;
18994577Sdes	}
19094577Sdes	clnt->cl_auth = authunix_create_default();
19194577Sdes
19294577Sdes	/* request the update */
19394577Sdes	result = yppasswdproc_update_master_1(&yppwd, clnt);
19494577Sdes
19594577Sdes	/* check for RPC errors */
19694577Sdes	clnt_geterr(clnt, &rpcerr);
19794577Sdes	if (rpcerr.re_status != RPC_SUCCESS) {
19894577Sdes		ypclnt_error(ypclnt, __func__,
19994577Sdes		    "NIS password update failed: %s",
20094577Sdes		    clnt_sperror(clnt, ypclnt->server));
20194577Sdes		ret = -1;
20294577Sdes		goto done;
20394577Sdes	}
20494577Sdes
20594577Sdes	/* check the result of the update */
20694577Sdes	if (result == NULL || *result != 0) {
20794577Sdes		ypclnt_error(ypclnt, __func__,
20894577Sdes		    "NIS password update failed");
20994577Sdes		/* XXX how do we get more details? */
21094577Sdes		ret = -1;
21194577Sdes		goto done;
21294577Sdes	}
21394577Sdes
21494577Sdes	ypclnt_error(ypclnt, NULL, NULL);
21594577Sdes	ret = 0;
21694577Sdes
21794577Sdes done:
21894577Sdes	if (clnt != NULL) {
21994577Sdes		auth_destroy(clnt->cl_auth);
22094577Sdes		clnt_destroy(clnt);
22194577Sdes	}
222116393Smbr	endnetconfig(localhandle);
22394577Sdes	free(yppwd.newpw.pw_name);
22495588Sdes	if (yppwd.newpw.pw_passwd != NULL) {
22595588Sdes		memset(yppwd.newpw.pw_passwd, 0, strlen(yppwd.newpw.pw_passwd));
22695588Sdes		free(yppwd.newpw.pw_passwd);
22795588Sdes	}
22894577Sdes	free(yppwd.newpw.pw_class);
22994577Sdes	free(yppwd.newpw.pw_gecos);
23094577Sdes	free(yppwd.newpw.pw_dir);
23194577Sdes	free(yppwd.newpw.pw_shell);
23294577Sdes	if (yppwd.oldpass != NULL) {
23394577Sdes		memset(yppwd.oldpass, 0, strlen(yppwd.oldpass));
23494577Sdes		free(yppwd.oldpass);
23594577Sdes	}
23694577Sdes	return (ret);
23794577Sdes}
23894577Sdes
23994577Sdesstatic int
24094577Sdesyppasswd_remote(ypclnt_t *ypclnt, const struct passwd *pwd, const char *passwd)
24194577Sdes{
24294577Sdes	struct yppasswd yppwd;
24394577Sdes	struct rpc_err rpcerr;
24494577Sdes	CLIENT *clnt = NULL;
24594577Sdes	int ret, *result;
24694577Sdes
24794575Sdes	/* fill the yppasswd structure */
24894575Sdes	memset(&yppwd, 0, sizeof yppwd);
24994575Sdes	yppwd.newpw.pw_uid = pwd->pw_uid;
25094575Sdes	yppwd.newpw.pw_gid = pwd->pw_gid;
25194575Sdes	if ((yppwd.newpw.pw_name = strdup(pwd->pw_name)) == NULL ||
25294575Sdes	    (yppwd.newpw.pw_passwd = strdup(pwd->pw_passwd)) == NULL ||
25394575Sdes	    (yppwd.newpw.pw_gecos = strdup(pwd->pw_gecos)) == NULL ||
25494575Sdes	    (yppwd.newpw.pw_dir = strdup(pwd->pw_dir)) == NULL ||
25594575Sdes	    (yppwd.newpw.pw_shell = strdup(pwd->pw_shell)) == NULL ||
25696198Sdes	    (yppwd.oldpass = strdup(passwd ? passwd : "")) == NULL) {
25794575Sdes		ypclnt_error(ypclnt, __func__, strerror(errno));
25894575Sdes		ret = -1;
25994575Sdes		goto done;
26094575Sdes	}
26194575Sdes
26294575Sdes	/* connect to rpc.yppasswdd */
26394575Sdes	clnt = clnt_create(ypclnt->server, YPPASSWDPROG, YPPASSWDVERS, "udp");
26494575Sdes	if (clnt == NULL) {
26594575Sdes		ypclnt_error(ypclnt, __func__,
26694575Sdes		    "failed to connect to rpc.yppasswdd: %s",
26794575Sdes		    clnt_spcreateerror(ypclnt->server));
26894575Sdes		ret = -1;
26994575Sdes		goto done;
27094575Sdes	}
27194575Sdes	clnt->cl_auth = authunix_create_default();
27294575Sdes
27394575Sdes	/* request the update */
27494575Sdes	result = yppasswdproc_update_1(&yppwd, clnt);
27594575Sdes
27694575Sdes	/* check for RPC errors */
27794575Sdes	clnt_geterr(clnt, &rpcerr);
27894575Sdes	if (rpcerr.re_status != RPC_SUCCESS) {
27994575Sdes		ypclnt_error(ypclnt, __func__,
28094575Sdes		    "NIS password update failed: %s",
28194575Sdes		    clnt_sperror(clnt, ypclnt->server));
28294575Sdes		ret = -1;
28394575Sdes		goto done;
28494575Sdes	}
28594575Sdes
28694575Sdes	/* check the result of the update */
28794575Sdes	if (result == NULL || *result != 0) {
28894575Sdes		ypclnt_error(ypclnt, __func__,
28994575Sdes		    "NIS password update failed");
29094575Sdes		/* XXX how do we get more details? */
29194575Sdes		ret = -1;
29294575Sdes		goto done;
29394575Sdes	}
29494575Sdes
29594575Sdes	ypclnt_error(ypclnt, NULL, NULL);
29694575Sdes	ret = 0;
29794575Sdes
29894575Sdes done:
29994575Sdes	if (clnt != NULL) {
30094575Sdes		auth_destroy(clnt->cl_auth);
30194575Sdes		clnt_destroy(clnt);
30294575Sdes	}
30394575Sdes	free(yppwd.newpw.pw_name);
30495588Sdes	if (yppwd.newpw.pw_passwd != NULL) {
30595588Sdes		memset(yppwd.newpw.pw_passwd, 0, strlen(yppwd.newpw.pw_passwd));
30695588Sdes		free(yppwd.newpw.pw_passwd);
30795588Sdes	}
30894575Sdes	free(yppwd.newpw.pw_gecos);
30994575Sdes	free(yppwd.newpw.pw_dir);
31094575Sdes	free(yppwd.newpw.pw_shell);
31194575Sdes	if (yppwd.oldpass != NULL) {
31294575Sdes		memset(yppwd.oldpass, 0, strlen(yppwd.oldpass));
31394575Sdes		free(yppwd.oldpass);
31494575Sdes	}
31594575Sdes	return (ret);
31694575Sdes}
317