ypclnt_passwd.c revision 96198
11541Srgrimes/*-
21541Srgrimes * Copyright (c) 2002 Networks Associates Technology, Inc.
31541Srgrimes * All rights reserved.
41541Srgrimes *
51541Srgrimes * This software was developed for the FreeBSD Project by ThinkSec AS and
61541Srgrimes * NAI Labs, the Security Research Division of Network Associates, Inc.
71541Srgrimes * under DARPA/SPAWAR contract N66001-01-C-8035 ("CBOSS"), as part of the
81541Srgrimes * DARPA CHATS research program.
91541Srgrimes *
101541Srgrimes * Redistribution and use in source and binary forms, with or without
111541Srgrimes * modification, are permitted provided that the following conditions
121541Srgrimes * are met:
131541Srgrimes * 1. Redistributions of source code must retain the above copyright
141541Srgrimes *    notice, this list of conditions and the following disclaimer.
151541Srgrimes * 2. Redistributions in binary form must reproduce the above copyright
161541Srgrimes *    notice, this list of conditions and the following disclaimer in the
171541Srgrimes *    documentation and/or other materials provided with the distribution.
181541Srgrimes * 3. The name of the author may not be used to endorse or promote
191541Srgrimes *    products derived from this software without specific prior written
201541Srgrimes *    permission.
211541Srgrimes *
221541Srgrimes * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
231541Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
241541Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
251541Srgrimes * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
261541Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
271541Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
281541Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
291541Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
301541Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
311541Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
321541Srgrimes * SUCH DAMAGE.
331541Srgrimes *
341541Srgrimes * $FreeBSD: head/lib/libypclnt/ypclnt_passwd.c 96198 2002-05-08 00:48:39Z des $
351541Srgrimes */
361541Srgrimes
3710222Sdfr#include <sys/types.h>
381541Srgrimes#include <sys/socket.h>
391541Srgrimes#include <netinet/in.h>
402175Spaul
412175Spaul#include <err.h>
422175Spaul#include <errno.h>
439336Sdfr#include <netconfig.h>
441541Srgrimes#include <netdb.h>
451541Srgrimes#include <pwd.h>
461541Srgrimes#include <stdlib.h>
471541Srgrimes#include <string.h>
481541Srgrimes#include <unistd.h>
491541Srgrimes
501541Srgrimes#include <rpcsvc/ypclnt.h>
511541Srgrimes#include <rpcsvc/yppasswd.h>
521541Srgrimes
5310222Sdfr#include "ypclnt.h"
5410222Sdfr#include "yppasswd_private.h"
5510222Sdfr
5610222Sdfrstatic int yppasswd_remote(ypclnt_t *, const struct passwd *, const char *);
5710222Sdfrstatic int yppasswd_local(ypclnt_t *, const struct passwd *, const char *);
5810222Sdfr
5910222Sdfr/*
601541Srgrimes * Determines the availability of rpc.yppasswdd.  Returns -1 for not
611541Srgrimes * available (or unable to determine), 0 for available, 1 for available in
621541Srgrimes * master mode.
631541Srgrimes */
641541Srgrimesint
651541Srgrimesypclnt_havepasswdd(ypclnt_t *ypclnt)
661541Srgrimes{
671541Srgrimes	struct addrinfo hints, *res;
681541Srgrimes	int sd;
691541Srgrimes
701541Srgrimes	/* check that rpc.yppasswdd is running */
711541Srgrimes	if (getrpcport(ypclnt->server, YPPASSWDPROG,
721541Srgrimes		YPPASSWDPROC_UPDATE, IPPROTO_UDP) == 0) {
731541Srgrimes		ypclnt_error(ypclnt, __func__, "no rpc.yppasswdd on server");
741541Srgrimes		return (-1);
751541Srgrimes	}
761541Srgrimes
771541Srgrimes	/* if we're not root, use remote method */
781541Srgrimes	if (getuid() != 0)
791541Srgrimes		return (0);
801541Srgrimes
811541Srgrimes	/* try to determine if we are the server */
821541Srgrimes	memset(&hints, 0, sizeof(hints));
831541Srgrimes	hints.ai_family = AF_UNSPEC;
841541Srgrimes	hints.ai_socktype = SOCK_STREAM;
851541Srgrimes	hints.ai_protocol = 0;
861541Srgrimes	if (getaddrinfo(ypclnt->server, NULL, &hints, &res) != 0)
871541Srgrimes		return (0);
881541Srgrimes	sd = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
891541Srgrimes	if (sd == -1) {
901541Srgrimes		freeaddrinfo(res);
911541Srgrimes		return (0);
921541Srgrimes	}
931541Srgrimes	if (bind(sd, res->ai_addr, res->ai_addrlen) == -1) {
941541Srgrimes		close(sd);
951541Srgrimes		freeaddrinfo(res);
961541Srgrimes		return (0);
971541Srgrimes	}
981541Srgrimes	freeaddrinfo(res);
991541Srgrimes	close(sd);
1009336Sdfr	return (1);
1011541Srgrimes}
1021541Srgrimes
1031541Srgrimes/*
1041541Srgrimes * Updates the NIS user information for the specified user.
1059336Sdfr */
1069336Sdfrint
1079336Sdfrypclnt_passwd(ypclnt_t *ypclnt, const struct passwd *pwd, const char *passwd)
1089336Sdfr{
1091541Srgrimes	switch (ypclnt_havepasswdd(ypclnt)) {
1109336Sdfr	case 0:
1119336Sdfr		YPCLNT_DEBUG("using remote update method");
1129336Sdfr		return (yppasswd_remote(ypclnt, pwd, passwd));
1139336Sdfr	case 1:
1149336Sdfr		YPCLNT_DEBUG("using local update method");
1159336Sdfr		return (yppasswd_local(ypclnt, pwd, passwd));
1169336Sdfr	default:
1179336Sdfr		YPCLNT_DEBUG("no rpc.yppasswdd");
1189336Sdfr		return (-1);
1199336Sdfr	}
1209336Sdfr}
1219336Sdfr
1229336Sdfr/*
1239336Sdfr * yppasswd_remote and yppasswd_local are quite similar but still
1249336Sdfr * sufficiently different that merging them into one makes the code
1259336Sdfr * significantly less readable, IMHO, so we keep them separate.
1263305Sphk */
1273305Sphk
1289336Sdfrstatic int
1299336Sdfryppasswd_local(ypclnt_t *ypclnt, const struct passwd *pwd, const char *passwd)
1309336Sdfr{
1311541Srgrimes	struct master_yppasswd yppwd;
1321541Srgrimes	struct rpc_err rpcerr;
1339336Sdfr	struct netconfig *nc = NULL;
1349336Sdfr	CLIENT *clnt = NULL;
1359336Sdfr	int ret, *result;
1369336Sdfr
1379336Sdfr	/* fill the master_yppasswd structure */
1389336Sdfr	memset(&yppwd, 0, sizeof yppwd);
1399336Sdfr	yppwd.newpw.pw_uid = pwd->pw_uid;
1409336Sdfr	yppwd.newpw.pw_gid = pwd->pw_gid;
1419336Sdfr	yppwd.newpw.pw_change = pwd->pw_change;
1421541Srgrimes	yppwd.newpw.pw_expire = pwd->pw_expire;
1439336Sdfr	yppwd.newpw.pw_fields = pwd->pw_fields;
1449336Sdfr	if ((yppwd.newpw.pw_name = strdup(pwd->pw_name)) == NULL ||
1459336Sdfr	    (yppwd.newpw.pw_passwd = strdup(pwd->pw_passwd)) == NULL ||
1469336Sdfr	    (yppwd.newpw.pw_class = strdup(pwd->pw_class)) == NULL ||
1479336Sdfr	    (yppwd.newpw.pw_gecos = strdup(pwd->pw_gecos)) == NULL ||
1489336Sdfr	    (yppwd.newpw.pw_dir = strdup(pwd->pw_dir)) == NULL ||
1491541Srgrimes	    (yppwd.newpw.pw_shell = strdup(pwd->pw_shell)) == NULL ||
1509336Sdfr	    (yppwd.oldpass = strdup(passwd ? passwd : "")) == NULL) {
1519336Sdfr		ypclnt_error(ypclnt, __func__, strerror(errno));
1529336Sdfr		ret = -1;
1539336Sdfr		goto done;
1549336Sdfr	}
1559336Sdfr
1569336Sdfr	/* connect to rpc.yppasswdd */
1579336Sdfr	nc = getnetconfigent("unix");
1589336Sdfr	clnt = clnt_tp_create(ypclnt->server, YPPASSWDPROG, YPPASSWDVERS, nc);
1599336Sdfr	if (clnt == NULL) {
1609336Sdfr		ypclnt_error(ypclnt, __func__,
1619336Sdfr		    "failed to connect to rpc.yppasswdd: %s",
1629336Sdfr		    clnt_spcreateerror(ypclnt->server));
1639336Sdfr		ret = -1;
1649336Sdfr		goto done;
1659336Sdfr	}
1661541Srgrimes	clnt->cl_auth = authunix_create_default();
1679336Sdfr
1689336Sdfr	/* request the update */
1699336Sdfr	result = yppasswdproc_update_master_1(&yppwd, clnt);
1709336Sdfr
1719336Sdfr	/* check for RPC errors */
1729336Sdfr	clnt_geterr(clnt, &rpcerr);
1739336Sdfr	if (rpcerr.re_status != RPC_SUCCESS) {
1749336Sdfr		ypclnt_error(ypclnt, __func__,
1759336Sdfr		    "NIS password update failed: %s",
1761541Srgrimes		    clnt_sperror(clnt, ypclnt->server));
1771541Srgrimes		ret = -1;
1789336Sdfr		goto done;
1799336Sdfr	}
1809336Sdfr
1819336Sdfr	/* check the result of the update */
1829336Sdfr	if (result == NULL || *result != 0) {
1839336Sdfr		ypclnt_error(ypclnt, __func__,
1849336Sdfr		    "NIS password update failed");
1859336Sdfr		/* XXX how do we get more details? */
1869336Sdfr		ret = -1;
1879336Sdfr		goto done;
1889336Sdfr	}
1899336Sdfr
1909336Sdfr	ypclnt_error(ypclnt, NULL, NULL);
1919336Sdfr	ret = 0;
1929336Sdfr
1939336Sdfr done:
1949336Sdfr	if (clnt != NULL) {
1951541Srgrimes		auth_destroy(clnt->cl_auth);
1961541Srgrimes		clnt_destroy(clnt);
1971541Srgrimes	}
1989336Sdfr	if (nc != NULL)
1991541Srgrimes		freenetconfigent(nc);
2009336Sdfr	free(yppwd.newpw.pw_name);
2019336Sdfr	if (yppwd.newpw.pw_passwd != NULL) {
2029336Sdfr		memset(yppwd.newpw.pw_passwd, 0, strlen(yppwd.newpw.pw_passwd));
2039336Sdfr		free(yppwd.newpw.pw_passwd);
2049336Sdfr	}
2059336Sdfr	free(yppwd.newpw.pw_class);
2069336Sdfr	free(yppwd.newpw.pw_gecos);
2079336Sdfr	free(yppwd.newpw.pw_dir);
2089336Sdfr	free(yppwd.newpw.pw_shell);
2099336Sdfr	if (yppwd.oldpass != NULL) {
2109336Sdfr		memset(yppwd.oldpass, 0, strlen(yppwd.oldpass));
2119336Sdfr		free(yppwd.oldpass);
2129336Sdfr	}
2139336Sdfr	return (ret);
2149336Sdfr}
2159336Sdfr
2169336Sdfrstatic int
2179336Sdfryppasswd_remote(ypclnt_t *ypclnt, const struct passwd *pwd, const char *passwd)
2189336Sdfr{
2199336Sdfr	struct yppasswd yppwd;
2209336Sdfr	struct rpc_err rpcerr;
2219336Sdfr	CLIENT *clnt = NULL;
2229336Sdfr	int ret, *result;
2239336Sdfr
2249336Sdfr	/* fill the yppasswd structure */
2259336Sdfr	memset(&yppwd, 0, sizeof yppwd);
2269336Sdfr	yppwd.newpw.pw_uid = pwd->pw_uid;
2279336Sdfr	yppwd.newpw.pw_gid = pwd->pw_gid;
2289336Sdfr	if ((yppwd.newpw.pw_name = strdup(pwd->pw_name)) == NULL ||
2299336Sdfr	    (yppwd.newpw.pw_passwd = strdup(pwd->pw_passwd)) == NULL ||
2309336Sdfr	    (yppwd.newpw.pw_gecos = strdup(pwd->pw_gecos)) == NULL ||
2319336Sdfr	    (yppwd.newpw.pw_dir = strdup(pwd->pw_dir)) == NULL ||
2329336Sdfr	    (yppwd.newpw.pw_shell = strdup(pwd->pw_shell)) == NULL ||
2339336Sdfr	    (yppwd.oldpass = strdup(passwd ? passwd : "")) == NULL) {
2349336Sdfr		ypclnt_error(ypclnt, __func__, strerror(errno));
2359336Sdfr		ret = -1;
2369336Sdfr		goto done;
2379336Sdfr	}
2389336Sdfr
2399336Sdfr	/* connect to rpc.yppasswdd */
2409336Sdfr	clnt = clnt_create(ypclnt->server, YPPASSWDPROG, YPPASSWDVERS, "udp");
2419336Sdfr	if (clnt == NULL) {
2429336Sdfr		ypclnt_error(ypclnt, __func__,
2439336Sdfr		    "failed to connect to rpc.yppasswdd: %s",
2449336Sdfr		    clnt_spcreateerror(ypclnt->server));
2459336Sdfr		ret = -1;
2469336Sdfr		goto done;
2479336Sdfr	}
2481541Srgrimes	clnt->cl_auth = authunix_create_default();
2491541Srgrimes
2501541Srgrimes	/* request the update */
2511541Srgrimes	result = yppasswdproc_update_1(&yppwd, clnt);
2521541Srgrimes
2531541Srgrimes	/* check for RPC errors */
2541541Srgrimes	clnt_geterr(clnt, &rpcerr);
2551541Srgrimes	if (rpcerr.re_status != RPC_SUCCESS) {
2561541Srgrimes		ypclnt_error(ypclnt, __func__,
2571541Srgrimes		    "NIS password update failed: %s",
2581541Srgrimes		    clnt_sperror(clnt, ypclnt->server));
2591541Srgrimes		ret = -1;
2601541Srgrimes		goto done;
2611541Srgrimes	}
2621541Srgrimes
2639336Sdfr	/* check the result of the update */
2649336Sdfr	if (result == NULL || *result != 0) {
2659336Sdfr		ypclnt_error(ypclnt, __func__,
2669336Sdfr		    "NIS password update failed");
2679336Sdfr		/* XXX how do we get more details? */
2689336Sdfr		ret = -1;
2699336Sdfr		goto done;
2709336Sdfr	}
2719336Sdfr
2729336Sdfr	ypclnt_error(ypclnt, NULL, NULL);
2731541Srgrimes	ret = 0;
2741541Srgrimes
2759336Sdfr done:
2769336Sdfr	if (clnt != NULL) {
2771541Srgrimes		auth_destroy(clnt->cl_auth);
2781541Srgrimes		clnt_destroy(clnt);
2791541Srgrimes	}
2801541Srgrimes	free(yppwd.newpw.pw_name);
2811541Srgrimes	if (yppwd.newpw.pw_passwd != NULL) {
2829336Sdfr		memset(yppwd.newpw.pw_passwd, 0, strlen(yppwd.newpw.pw_passwd));
2839336Sdfr		free(yppwd.newpw.pw_passwd);
2841541Srgrimes	}
2851541Srgrimes	free(yppwd.newpw.pw_gecos);
2861541Srgrimes	free(yppwd.newpw.pw_dir);
2871541Srgrimes	free(yppwd.newpw.pw_shell);
2881541Srgrimes	if (yppwd.oldpass != NULL) {
2891541Srgrimes		memset(yppwd.oldpass, 0, strlen(yppwd.oldpass));
2901541Srgrimes		free(yppwd.oldpass);
2911541Srgrimes	}
2928876Srgrimes	return (ret);
2931541Srgrimes}
2941541Srgrimes