ypclnt_passwd.c revision 96198
190075Sobrien/*-
2169689Skan * Copyright (c) 2002 Networks Associates Technology, Inc.
390075Sobrien * All rights reserved.
490075Sobrien *
590075Sobrien * This software was developed for the FreeBSD Project by ThinkSec AS and
690075Sobrien * NAI Labs, the Security Research Division of Network Associates, Inc.
790075Sobrien * under DARPA/SPAWAR contract N66001-01-C-8035 ("CBOSS"), as part of the
890075Sobrien * DARPA CHATS research program.
990075Sobrien *
1090075Sobrien * Redistribution and use in source and binary forms, with or without
1190075Sobrien * modification, are permitted provided that the following conditions
12132718Skan * are met:
13132718Skan * 1. Redistributions of source code must retain the above copyright
14132718Skan *    notice, this list of conditions and the following disclaimer.
15132718Skan * 2. Redistributions in binary form must reproduce the above copyright
16132718Skan *    notice, this list of conditions and the following disclaimer in the
17132718Skan *    documentation and/or other materials provided with the distribution.
18132718Skan * 3. The name of the author may not be used to endorse or promote
19132718Skan *    products derived from this software without specific prior written
20132718Skan *    permission.
2190075Sobrien *
2290075Sobrien * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
2390075Sobrien * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2490075Sobrien * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2590075Sobrien * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
2690075Sobrien * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2790075Sobrien * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28169689Skan * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29169689Skan * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
3090075Sobrien * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
3190075Sobrien * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3290075Sobrien * SUCH DAMAGE.
33132718Skan *
34132718Skan * $FreeBSD: head/lib/libypclnt/ypclnt_passwd.c 96198 2002-05-08 00:48:39Z des $
3590075Sobrien */
3690075Sobrien
37132718Skan#include <sys/types.h>
38132718Skan#include <sys/socket.h>
39132718Skan#include <netinet/in.h>
4090075Sobrien
4190075Sobrien#include <err.h>
4290075Sobrien#include <errno.h>
43169689Skan#include <netconfig.h>
4490075Sobrien#include <netdb.h>
4590075Sobrien#include <pwd.h>
4690075Sobrien#include <stdlib.h>
4790075Sobrien#include <string.h>
4890075Sobrien#include <unistd.h>
4990075Sobrien
5090075Sobrien#include <rpcsvc/ypclnt.h>
5190075Sobrien#include <rpcsvc/yppasswd.h>
5290075Sobrien
5390075Sobrien#include "ypclnt.h"
5490075Sobrien#include "yppasswd_private.h"
5590075Sobrien
5690075Sobrienstatic int yppasswd_remote(ypclnt_t *, const struct passwd *, const char *);
5790075Sobrienstatic int yppasswd_local(ypclnt_t *, const struct passwd *, const char *);
5890075Sobrien
59132718Skan/*
60132718Skan * Determines the availability of rpc.yppasswdd.  Returns -1 for not
61132718Skan * available (or unable to determine), 0 for available, 1 for available in
62132718Skan * master mode.
63117395Skan */
64117395Skanint
65117395Skanypclnt_havepasswdd(ypclnt_t *ypclnt)
6690075Sobrien{
6790075Sobrien	struct addrinfo hints, *res;
6890075Sobrien	int sd;
6990075Sobrien
7090075Sobrien	/* check that rpc.yppasswdd is running */
7190075Sobrien	if (getrpcport(ypclnt->server, YPPASSWDPROG,
7290075Sobrien		YPPASSWDPROC_UPDATE, IPPROTO_UDP) == 0) {
73169689Skan		ypclnt_error(ypclnt, __func__, "no rpc.yppasswdd on server");
74169689Skan		return (-1);
75169689Skan	}
76169689Skan
77169689Skan	/* if we're not root, use remote method */
78169689Skan	if (getuid() != 0)
79169689Skan		return (0);
80169689Skan
8190075Sobrien	/* try to determine if we are the server */
82169689Skan	memset(&hints, 0, sizeof(hints));
8390075Sobrien	hints.ai_family = AF_UNSPEC;
8490075Sobrien	hints.ai_socktype = SOCK_STREAM;
8590075Sobrien	hints.ai_protocol = 0;
86122180Skan	if (getaddrinfo(ypclnt->server, NULL, &hints, &res) != 0)
8790075Sobrien		return (0);
8890075Sobrien	sd = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
8990075Sobrien	if (sd == -1) {
9090075Sobrien		freeaddrinfo(res);
9190075Sobrien		return (0);
9290075Sobrien	}
9390075Sobrien	if (bind(sd, res->ai_addr, res->ai_addrlen) == -1) {
9490075Sobrien		close(sd);
9590075Sobrien		freeaddrinfo(res);
9690075Sobrien		return (0);
9790075Sobrien	}
9890075Sobrien	freeaddrinfo(res);
9990075Sobrien	close(sd);
10090075Sobrien	return (1);
10190075Sobrien}
102169689Skan
103169689Skan/*
104169689Skan * Updates the NIS user information for the specified user.
105169689Skan */
10690075Sobrienint
10790075Sobrienypclnt_passwd(ypclnt_t *ypclnt, const struct passwd *pwd, const char *passwd)
10890075Sobrien{
10990075Sobrien	switch (ypclnt_havepasswdd(ypclnt)) {
11090075Sobrien	case 0:
11190075Sobrien		YPCLNT_DEBUG("using remote update method");
11290075Sobrien		return (yppasswd_remote(ypclnt, pwd, passwd));
11390075Sobrien	case 1:
11490075Sobrien		YPCLNT_DEBUG("using local update method");
11590075Sobrien		return (yppasswd_local(ypclnt, pwd, passwd));
11690075Sobrien	default:
11790075Sobrien		YPCLNT_DEBUG("no rpc.yppasswdd");
11890075Sobrien		return (-1);
11990075Sobrien	}
12090075Sobrien}
12190075Sobrien
12290075Sobrien/*
12390075Sobrien * yppasswd_remote and yppasswd_local are quite similar but still
12490075Sobrien * sufficiently different that merging them into one makes the code
12590075Sobrien * significantly less readable, IMHO, so we keep them separate.
12690075Sobrien */
12790075Sobrien
12890075Sobrienstatic int
12990075Sobrienyppasswd_local(ypclnt_t *ypclnt, const struct passwd *pwd, const char *passwd)
13090075Sobrien{
13190075Sobrien	struct master_yppasswd yppwd;
13290075Sobrien	struct rpc_err rpcerr;
133169689Skan	struct netconfig *nc = NULL;
134169689Skan	CLIENT *clnt = NULL;
135169689Skan	int ret, *result;
136169689Skan
137169689Skan	/* fill the master_yppasswd structure */
13890075Sobrien	memset(&yppwd, 0, sizeof yppwd);
139169689Skan	yppwd.newpw.pw_uid = pwd->pw_uid;
140169689Skan	yppwd.newpw.pw_gid = pwd->pw_gid;
141169689Skan	yppwd.newpw.pw_change = pwd->pw_change;
142169689Skan	yppwd.newpw.pw_expire = pwd->pw_expire;
143169689Skan	yppwd.newpw.pw_fields = pwd->pw_fields;
144169689Skan	if ((yppwd.newpw.pw_name = strdup(pwd->pw_name)) == NULL ||
145169689Skan	    (yppwd.newpw.pw_passwd = strdup(pwd->pw_passwd)) == NULL ||
146169689Skan	    (yppwd.newpw.pw_class = strdup(pwd->pw_class)) == NULL ||
147169689Skan	    (yppwd.newpw.pw_gecos = strdup(pwd->pw_gecos)) == NULL ||
148169689Skan	    (yppwd.newpw.pw_dir = strdup(pwd->pw_dir)) == NULL ||
149169689Skan	    (yppwd.newpw.pw_shell = strdup(pwd->pw_shell)) == NULL ||
150169689Skan	    (yppwd.oldpass = strdup(passwd ? passwd : "")) == NULL) {
151169689Skan		ypclnt_error(ypclnt, __func__, strerror(errno));
152169689Skan		ret = -1;
153169689Skan		goto done;
154169689Skan	}
155169689Skan
15690075Sobrien	/* connect to rpc.yppasswdd */
15790075Sobrien	nc = getnetconfigent("unix");
15890075Sobrien	clnt = clnt_tp_create(ypclnt->server, YPPASSWDPROG, YPPASSWDVERS, nc);
159132718Skan	if (clnt == NULL) {
160132718Skan		ypclnt_error(ypclnt, __func__,
161132718Skan		    "failed to connect to rpc.yppasswdd: %s",
162146895Skan		    clnt_spcreateerror(ypclnt->server));
163146895Skan		ret = -1;
164146895Skan		goto done;
165146895Skan	}
166146895Skan	clnt->cl_auth = authunix_create_default();
167132718Skan
168169689Skan	/* request the update */
169132718Skan	result = yppasswdproc_update_master_1(&yppwd, clnt);
170132718Skan
171132718Skan	/* check for RPC errors */
172169689Skan	clnt_geterr(clnt, &rpcerr);
173169689Skan	if (rpcerr.re_status != RPC_SUCCESS) {
174169689Skan		ypclnt_error(ypclnt, __func__,
17590075Sobrien		    "NIS password update failed: %s",
176132718Skan		    clnt_sperror(clnt, ypclnt->server));
177132718Skan		ret = -1;
178169689Skan		goto done;
179169689Skan	}
180169689Skan
181169689Skan	/* check the result of the update */
182169689Skan	if (result == NULL || *result != 0) {
18390075Sobrien		ypclnt_error(ypclnt, __func__,
18490075Sobrien		    "NIS password update failed");
185132718Skan		/* XXX how do we get more details? */
186132718Skan		ret = -1;
187132718Skan		goto done;
188132718Skan	}
189132718Skan
190132718Skan	ypclnt_error(ypclnt, NULL, NULL);
191117395Skan	ret = 0;
192117395Skan
193117395Skan done:
194117395Skan	if (clnt != NULL) {
195117395Skan		auth_destroy(clnt->cl_auth);
196132718Skan		clnt_destroy(clnt);
197117395Skan	}
198117395Skan	if (nc != NULL)
199169689Skan		freenetconfigent(nc);
20090075Sobrien	free(yppwd.newpw.pw_name);
20190075Sobrien	if (yppwd.newpw.pw_passwd != NULL) {
20290075Sobrien		memset(yppwd.newpw.pw_passwd, 0, strlen(yppwd.newpw.pw_passwd));
20390075Sobrien		free(yppwd.newpw.pw_passwd);
204132718Skan	}
205132718Skan	free(yppwd.newpw.pw_class);
206132718Skan	free(yppwd.newpw.pw_gecos);
207132718Skan	free(yppwd.newpw.pw_dir);
208169689Skan	free(yppwd.newpw.pw_shell);
209132718Skan	if (yppwd.oldpass != NULL) {
210169689Skan		memset(yppwd.oldpass, 0, strlen(yppwd.oldpass));
211169689Skan		free(yppwd.oldpass);
212169689Skan	}
213169689Skan	return (ret);
214169689Skan}
215169689Skan
216169689Skanstatic int
217132718Skanyppasswd_remote(ypclnt_t *ypclnt, const struct passwd *pwd, const char *passwd)
218132718Skan{
219132718Skan	struct yppasswd yppwd;
220132718Skan	struct rpc_err rpcerr;
221132718Skan	CLIENT *clnt = NULL;
222169689Skan	int ret, *result;
223169689Skan
224169689Skan	/* fill the yppasswd structure */
225169689Skan	memset(&yppwd, 0, sizeof yppwd);
22690075Sobrien	yppwd.newpw.pw_uid = pwd->pw_uid;
22790075Sobrien	yppwd.newpw.pw_gid = pwd->pw_gid;
228132718Skan	if ((yppwd.newpw.pw_name = strdup(pwd->pw_name)) == NULL ||
229132718Skan	    (yppwd.newpw.pw_passwd = strdup(pwd->pw_passwd)) == NULL ||
230132718Skan	    (yppwd.newpw.pw_gecos = strdup(pwd->pw_gecos)) == NULL ||
231132718Skan	    (yppwd.newpw.pw_dir = strdup(pwd->pw_dir)) == NULL ||
232132718Skan	    (yppwd.newpw.pw_shell = strdup(pwd->pw_shell)) == NULL ||
233132718Skan	    (yppwd.oldpass = strdup(passwd ? passwd : "")) == NULL) {
234169689Skan		ypclnt_error(ypclnt, __func__, strerror(errno));
235169689Skan		ret = -1;
236132718Skan		goto done;
237132718Skan	}
238132718Skan
239132718Skan	/* connect to rpc.yppasswdd */
240132718Skan	clnt = clnt_create(ypclnt->server, YPPASSWDPROG, YPPASSWDVERS, "udp");
241132718Skan	if (clnt == NULL) {
242132718Skan		ypclnt_error(ypclnt, __func__,
243132718Skan		    "failed to connect to rpc.yppasswdd: %s",
244132718Skan		    clnt_spcreateerror(ypclnt->server));
245169689Skan		ret = -1;
246169689Skan		goto done;
247132718Skan	}
248132718Skan	clnt->cl_auth = authunix_create_default();
249132718Skan
250169689Skan	/* request the update */
251169689Skan	result = yppasswdproc_update_1(&yppwd, clnt);
252169689Skan
253169689Skan	/* check for RPC errors */
254169689Skan	clnt_geterr(clnt, &rpcerr);
255169689Skan	if (rpcerr.re_status != RPC_SUCCESS) {
256169689Skan		ypclnt_error(ypclnt, __func__,
257169689Skan		    "NIS password update failed: %s",
258169689Skan		    clnt_sperror(clnt, ypclnt->server));
259169689Skan		ret = -1;
260169689Skan		goto done;
261169689Skan	}
262169689Skan
263169689Skan	/* check the result of the update */
264169689Skan	if (result == NULL || *result != 0) {
265169689Skan		ypclnt_error(ypclnt, __func__,
266169689Skan		    "NIS password update failed");
267169689Skan		/* XXX how do we get more details? */
268169689Skan		ret = -1;
269169689Skan		goto done;
270169689Skan	}
271169689Skan
272169689Skan	ypclnt_error(ypclnt, NULL, NULL);
273169689Skan	ret = 0;
27490075Sobrien
27590075Sobrien done:
27690075Sobrien	if (clnt != NULL) {
27790075Sobrien		auth_destroy(clnt->cl_auth);
27890075Sobrien		clnt_destroy(clnt);
27990075Sobrien	}
28090075Sobrien	free(yppwd.newpw.pw_name);
28190075Sobrien	if (yppwd.newpw.pw_passwd != NULL) {
282169689Skan		memset(yppwd.newpw.pw_passwd, 0, strlen(yppwd.newpw.pw_passwd));
283169689Skan		free(yppwd.newpw.pw_passwd);
284169689Skan	}
285169689Skan	free(yppwd.newpw.pw_gecos);
286169689Skan	free(yppwd.newpw.pw_dir);
287169689Skan	free(yppwd.newpw.pw_shell);
288169689Skan	if (yppwd.oldpass != NULL) {
289169689Skan		memset(yppwd.oldpass, 0, strlen(yppwd.oldpass));
290169689Skan		free(yppwd.oldpass);
291169689Skan	}
29290075Sobrien	return (ret);
29390075Sobrien}
29490075Sobrien