ypclnt_passwd.c revision 95588
1/*-
2 * Copyright (c) 2002 Networks Associates Technology, Inc.
3 * All rights reserved.
4 *
5 * This software was developed for the FreeBSD Project by ThinkSec AS and
6 * NAI Labs, the Security Research Division of Network Associates, Inc.
7 * under DARPA/SPAWAR contract N66001-01-C-8035 ("CBOSS"), as part of the
8 * DARPA CHATS research program.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 *    notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 *    notice, this list of conditions and the following disclaimer in the
17 *    documentation and/or other materials provided with the distribution.
18 * 3. The name of the author may not be used to endorse or promote
19 *    products derived from this software without specific prior written
20 *    permission.
21 *
22 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
23 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
26 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32 * SUCH DAMAGE.
33 *
34 * $FreeBSD: head/lib/libypclnt/ypclnt_passwd.c 95588 2002-04-27 21:58:23Z des $
35 */
36
37#include <sys/types.h>
38#include <sys/socket.h>
39#include <netinet/in.h>
40
41#include <err.h>
42#include <errno.h>
43#include <netconfig.h>
44#include <netdb.h>
45#include <pwd.h>
46#include <stdlib.h>
47#include <string.h>
48#include <unistd.h>
49
50#include <rpcsvc/ypclnt.h>
51#include <rpcsvc/yppasswd.h>
52
53#include "ypclnt.h"
54#include "yppasswd_private.h"
55
56static int yppasswd_remote(ypclnt_t *, const struct passwd *, const char *);
57static int yppasswd_local(ypclnt_t *, const struct passwd *, const char *);
58
59int
60ypclnt_passwd(ypclnt_t *ypclnt, const struct passwd *pwd, const char *passwd)
61{
62	struct addrinfo hints, *res;
63	int sd;
64
65	/* check that rpc.yppasswdd is running */
66	if (getrpcport(ypclnt->server, YPPASSWDPROG,
67		YPPASSWDPROC_UPDATE, IPPROTO_UDP) == 0) {
68		ypclnt_error(ypclnt, __func__, "no rpc.yppasswdd on server");
69		return (-1);
70	}
71
72	/* if we're not root, use remote method */
73	if (getuid() != 0)
74		goto remote;
75
76	/* try to determine if we are the server */
77	memset(&hints, 0, sizeof(hints));
78	hints.ai_family = AF_UNSPEC;
79	hints.ai_socktype = SOCK_STREAM;
80	hints.ai_protocol = 0;
81	if (getaddrinfo(ypclnt->server, NULL, &hints, &res) != 0)
82		goto remote;
83	sd = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
84	if (sd == -1) {
85		freeaddrinfo(res);
86		goto remote;
87	}
88	if (bind(sd, res->ai_addr, res->ai_addrlen) == -1) {
89		close(sd);
90		freeaddrinfo(res);
91		goto remote;
92	}
93	freeaddrinfo(res);
94	close(sd);
95	YPCLNT_DEBUG("using local update method");
96	return (yppasswd_local(ypclnt, pwd, passwd));
97 remote:
98	YPCLNT_DEBUG("using remote update method");
99	return (yppasswd_remote(ypclnt, pwd, passwd));
100}
101
102/*
103 * yppasswd_remote and yppasswd_local are quite similar but still
104 * sufficiently different that merging them into one makes the code
105 * significantly less readable, IMHO, so we keep them separate.
106 */
107
108static int
109yppasswd_local(ypclnt_t *ypclnt, const struct passwd *pwd, const char *passwd)
110{
111	struct master_yppasswd yppwd;
112	struct rpc_err rpcerr;
113	struct netconfig *nc = NULL;
114	CLIENT *clnt = NULL;
115	int ret, *result;
116
117	/* fill the master_yppasswd structure */
118	memset(&yppwd, 0, sizeof yppwd);
119	yppwd.newpw.pw_uid = pwd->pw_uid;
120	yppwd.newpw.pw_gid = pwd->pw_gid;
121	yppwd.newpw.pw_change = pwd->pw_change;
122	yppwd.newpw.pw_expire = pwd->pw_expire;
123	yppwd.newpw.pw_fields = pwd->pw_fields;
124	if ((yppwd.newpw.pw_name = strdup(pwd->pw_name)) == NULL ||
125	    (yppwd.newpw.pw_passwd = strdup(pwd->pw_passwd)) == NULL ||
126	    (yppwd.newpw.pw_class = strdup(pwd->pw_class)) == NULL ||
127	    (yppwd.newpw.pw_gecos = strdup(pwd->pw_gecos)) == NULL ||
128	    (yppwd.newpw.pw_dir = strdup(pwd->pw_dir)) == NULL ||
129	    (yppwd.newpw.pw_shell = strdup(pwd->pw_shell)) == NULL ||
130	    (yppwd.oldpass = strdup(passwd)) == NULL) {
131		ypclnt_error(ypclnt, __func__, strerror(errno));
132		ret = -1;
133		goto done;
134	}
135
136	/* connect to rpc.yppasswdd */
137	nc = getnetconfigent("unix");
138	clnt = clnt_tp_create(ypclnt->server, YPPASSWDPROG, YPPASSWDVERS, nc);
139	if (clnt == NULL) {
140		ypclnt_error(ypclnt, __func__,
141		    "failed to connect to rpc.yppasswdd: %s",
142		    clnt_spcreateerror(ypclnt->server));
143		ret = -1;
144		goto done;
145	}
146	clnt->cl_auth = authunix_create_default();
147
148	/* request the update */
149	result = yppasswdproc_update_master_1(&yppwd, clnt);
150
151	/* check for RPC errors */
152	clnt_geterr(clnt, &rpcerr);
153	if (rpcerr.re_status != RPC_SUCCESS) {
154		ypclnt_error(ypclnt, __func__,
155		    "NIS password update failed: %s",
156		    clnt_sperror(clnt, ypclnt->server));
157		ret = -1;
158		goto done;
159	}
160
161	/* check the result of the update */
162	if (result == NULL || *result != 0) {
163		ypclnt_error(ypclnt, __func__,
164		    "NIS password update failed");
165		/* XXX how do we get more details? */
166		ret = -1;
167		goto done;
168	}
169
170	ypclnt_error(ypclnt, NULL, NULL);
171	ret = 0;
172
173 done:
174	if (clnt != NULL) {
175		auth_destroy(clnt->cl_auth);
176		clnt_destroy(clnt);
177	}
178	if (nc != NULL)
179		freenetconfigent(nc);
180	free(yppwd.newpw.pw_name);
181	if (yppwd.newpw.pw_passwd != NULL) {
182		memset(yppwd.newpw.pw_passwd, 0, strlen(yppwd.newpw.pw_passwd));
183		free(yppwd.newpw.pw_passwd);
184	}
185	free(yppwd.newpw.pw_class);
186	free(yppwd.newpw.pw_gecos);
187	free(yppwd.newpw.pw_dir);
188	free(yppwd.newpw.pw_shell);
189	if (yppwd.oldpass != NULL) {
190		memset(yppwd.oldpass, 0, strlen(yppwd.oldpass));
191		free(yppwd.oldpass);
192	}
193	return (ret);
194}
195
196static int
197yppasswd_remote(ypclnt_t *ypclnt, const struct passwd *pwd, const char *passwd)
198{
199	struct yppasswd yppwd;
200	struct rpc_err rpcerr;
201	CLIENT *clnt = NULL;
202	int ret, *result;
203
204	/* fill the yppasswd structure */
205	memset(&yppwd, 0, sizeof yppwd);
206	yppwd.newpw.pw_uid = pwd->pw_uid;
207	yppwd.newpw.pw_gid = pwd->pw_gid;
208	if ((yppwd.newpw.pw_name = strdup(pwd->pw_name)) == NULL ||
209	    (yppwd.newpw.pw_passwd = strdup(pwd->pw_passwd)) == NULL ||
210	    (yppwd.newpw.pw_gecos = strdup(pwd->pw_gecos)) == NULL ||
211	    (yppwd.newpw.pw_dir = strdup(pwd->pw_dir)) == NULL ||
212	    (yppwd.newpw.pw_shell = strdup(pwd->pw_shell)) == NULL ||
213	    (yppwd.oldpass = strdup(passwd)) == NULL) {
214		ypclnt_error(ypclnt, __func__, strerror(errno));
215		ret = -1;
216		goto done;
217	}
218
219	/* connect to rpc.yppasswdd */
220	clnt = clnt_create(ypclnt->server, YPPASSWDPROG, YPPASSWDVERS, "udp");
221	if (clnt == NULL) {
222		ypclnt_error(ypclnt, __func__,
223		    "failed to connect to rpc.yppasswdd: %s",
224		    clnt_spcreateerror(ypclnt->server));
225		ret = -1;
226		goto done;
227	}
228	clnt->cl_auth = authunix_create_default();
229
230	/* request the update */
231	result = yppasswdproc_update_1(&yppwd, clnt);
232
233	/* check for RPC errors */
234	clnt_geterr(clnt, &rpcerr);
235	if (rpcerr.re_status != RPC_SUCCESS) {
236		ypclnt_error(ypclnt, __func__,
237		    "NIS password update failed: %s",
238		    clnt_sperror(clnt, ypclnt->server));
239		ret = -1;
240		goto done;
241	}
242
243	/* check the result of the update */
244	if (result == NULL || *result != 0) {
245		ypclnt_error(ypclnt, __func__,
246		    "NIS password update failed");
247		/* XXX how do we get more details? */
248		ret = -1;
249		goto done;
250	}
251
252	ypclnt_error(ypclnt, NULL, NULL);
253	ret = 0;
254
255 done:
256	if (clnt != NULL) {
257		auth_destroy(clnt->cl_auth);
258		clnt_destroy(clnt);
259	}
260	free(yppwd.newpw.pw_name);
261	if (yppwd.newpw.pw_passwd != NULL) {
262		memset(yppwd.newpw.pw_passwd, 0, strlen(yppwd.newpw.pw_passwd));
263		free(yppwd.newpw.pw_passwd);
264	}
265	free(yppwd.newpw.pw_gecos);
266	free(yppwd.newpw.pw_dir);
267	free(yppwd.newpw.pw_shell);
268	if (yppwd.oldpass != NULL) {
269		memset(yppwd.oldpass, 0, strlen(yppwd.oldpass));
270		free(yppwd.oldpass);
271	}
272	return (ret);
273}
274