ypclnt_passwd.c revision 96198
1260684Skaiw/*-
2260684Skaiw * Copyright (c) 2002 Networks Associates Technology, Inc.
3260684Skaiw * All rights reserved.
4260684Skaiw *
5260684Skaiw * This software was developed for the FreeBSD Project by ThinkSec AS and
6260684Skaiw * NAI Labs, the Security Research Division of Network Associates, Inc.
7260684Skaiw * under DARPA/SPAWAR contract N66001-01-C-8035 ("CBOSS"), as part of the
8260684Skaiw * DARPA CHATS research program.
9260684Skaiw *
10260684Skaiw * Redistribution and use in source and binary forms, with or without
11260684Skaiw * modification, are permitted provided that the following conditions
12260684Skaiw * are met:
13260684Skaiw * 1. Redistributions of source code must retain the above copyright
14260684Skaiw *    notice, this list of conditions and the following disclaimer.
15260684Skaiw * 2. Redistributions in binary form must reproduce the above copyright
16260684Skaiw *    notice, this list of conditions and the following disclaimer in the
17260684Skaiw *    documentation and/or other materials provided with the distribution.
18260684Skaiw * 3. The name of the author may not be used to endorse or promote
19260684Skaiw *    products derived from this software without specific prior written
20260684Skaiw *    permission.
21260684Skaiw *
22260684Skaiw * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
23260684Skaiw * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24260684Skaiw * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25367466Sdim * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
26260684Skaiw * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27260684Skaiw * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28367466Sdim * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29260684Skaiw * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30260684Skaiw * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31260684Skaiw * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32260684Skaiw * SUCH DAMAGE.
33260684Skaiw *
34260684Skaiw * $FreeBSD: head/lib/libypclnt/ypclnt_passwd.c 96198 2002-05-08 00:48:39Z des $
35260684Skaiw */
36260684Skaiw
37260684Skaiw#include <sys/types.h>
38260684Skaiw#include <sys/socket.h>
39260684Skaiw#include <netinet/in.h>
40260684Skaiw
41260684Skaiw#include <err.h>
42260684Skaiw#include <errno.h>
43260684Skaiw#include <netconfig.h>
44260684Skaiw#include <netdb.h>
45260684Skaiw#include <pwd.h>
46260684Skaiw#include <stdlib.h>
47260684Skaiw#include <string.h>
48260684Skaiw#include <unistd.h>
49260684Skaiw
50260684Skaiw#include <rpcsvc/ypclnt.h>
51260684Skaiw#include <rpcsvc/yppasswd.h>
52260684Skaiw
53260684Skaiw#include "ypclnt.h"
54260684Skaiw#include "yppasswd_private.h"
55260684Skaiw
56260684Skaiwstatic int yppasswd_remote(ypclnt_t *, const struct passwd *, const char *);
57260684Skaiwstatic int yppasswd_local(ypclnt_t *, const struct passwd *, const char *);
58260684Skaiw
59260684Skaiw/*
60260684Skaiw * Determines the availability of rpc.yppasswdd.  Returns -1 for not
61260684Skaiw * available (or unable to determine), 0 for available, 1 for available in
62260684Skaiw * master mode.
63260684Skaiw */
64260684Skaiwint
65260684Skaiwypclnt_havepasswdd(ypclnt_t *ypclnt)
66260684Skaiw{
67260684Skaiw	struct addrinfo hints, *res;
68260684Skaiw	int sd;
69260684Skaiw
70260684Skaiw	/* check that rpc.yppasswdd is running */
71260684Skaiw	if (getrpcport(ypclnt->server, YPPASSWDPROG,
72260684Skaiw		YPPASSWDPROC_UPDATE, IPPROTO_UDP) == 0) {
73260684Skaiw		ypclnt_error(ypclnt, __func__, "no rpc.yppasswdd on server");
74260684Skaiw		return (-1);
75260684Skaiw	}
76260684Skaiw
77260684Skaiw	/* if we're not root, use remote method */
78260684Skaiw	if (getuid() != 0)
79260684Skaiw		return (0);
80260684Skaiw
81260684Skaiw	/* try to determine if we are the server */
82260684Skaiw	memset(&hints, 0, sizeof(hints));
83260684Skaiw	hints.ai_family = AF_UNSPEC;
84260684Skaiw	hints.ai_socktype = SOCK_STREAM;
85260684Skaiw	hints.ai_protocol = 0;
86260684Skaiw	if (getaddrinfo(ypclnt->server, NULL, &hints, &res) != 0)
87260684Skaiw		return (0);
88260684Skaiw	sd = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
89260684Skaiw	if (sd == -1) {
90		freeaddrinfo(res);
91		return (0);
92	}
93	if (bind(sd, res->ai_addr, res->ai_addrlen) == -1) {
94		close(sd);
95		freeaddrinfo(res);
96		return (0);
97	}
98	freeaddrinfo(res);
99	close(sd);
100	return (1);
101}
102
103/*
104 * Updates the NIS user information for the specified user.
105 */
106int
107ypclnt_passwd(ypclnt_t *ypclnt, const struct passwd *pwd, const char *passwd)
108{
109	switch (ypclnt_havepasswdd(ypclnt)) {
110	case 0:
111		YPCLNT_DEBUG("using remote update method");
112		return (yppasswd_remote(ypclnt, pwd, passwd));
113	case 1:
114		YPCLNT_DEBUG("using local update method");
115		return (yppasswd_local(ypclnt, pwd, passwd));
116	default:
117		YPCLNT_DEBUG("no rpc.yppasswdd");
118		return (-1);
119	}
120}
121
122/*
123 * yppasswd_remote and yppasswd_local are quite similar but still
124 * sufficiently different that merging them into one makes the code
125 * significantly less readable, IMHO, so we keep them separate.
126 */
127
128static int
129yppasswd_local(ypclnt_t *ypclnt, const struct passwd *pwd, const char *passwd)
130{
131	struct master_yppasswd yppwd;
132	struct rpc_err rpcerr;
133	struct netconfig *nc = NULL;
134	CLIENT *clnt = NULL;
135	int ret, *result;
136
137	/* fill the master_yppasswd structure */
138	memset(&yppwd, 0, sizeof yppwd);
139	yppwd.newpw.pw_uid = pwd->pw_uid;
140	yppwd.newpw.pw_gid = pwd->pw_gid;
141	yppwd.newpw.pw_change = pwd->pw_change;
142	yppwd.newpw.pw_expire = pwd->pw_expire;
143	yppwd.newpw.pw_fields = pwd->pw_fields;
144	if ((yppwd.newpw.pw_name = strdup(pwd->pw_name)) == NULL ||
145	    (yppwd.newpw.pw_passwd = strdup(pwd->pw_passwd)) == NULL ||
146	    (yppwd.newpw.pw_class = strdup(pwd->pw_class)) == NULL ||
147	    (yppwd.newpw.pw_gecos = strdup(pwd->pw_gecos)) == NULL ||
148	    (yppwd.newpw.pw_dir = strdup(pwd->pw_dir)) == NULL ||
149	    (yppwd.newpw.pw_shell = strdup(pwd->pw_shell)) == NULL ||
150	    (yppwd.oldpass = strdup(passwd ? passwd : "")) == NULL) {
151		ypclnt_error(ypclnt, __func__, strerror(errno));
152		ret = -1;
153		goto done;
154	}
155
156	/* connect to rpc.yppasswdd */
157	nc = getnetconfigent("unix");
158	clnt = clnt_tp_create(ypclnt->server, YPPASSWDPROG, YPPASSWDVERS, nc);
159	if (clnt == NULL) {
160		ypclnt_error(ypclnt, __func__,
161		    "failed to connect to rpc.yppasswdd: %s",
162		    clnt_spcreateerror(ypclnt->server));
163		ret = -1;
164		goto done;
165	}
166	clnt->cl_auth = authunix_create_default();
167
168	/* request the update */
169	result = yppasswdproc_update_master_1(&yppwd, clnt);
170
171	/* check for RPC errors */
172	clnt_geterr(clnt, &rpcerr);
173	if (rpcerr.re_status != RPC_SUCCESS) {
174		ypclnt_error(ypclnt, __func__,
175		    "NIS password update failed: %s",
176		    clnt_sperror(clnt, ypclnt->server));
177		ret = -1;
178		goto done;
179	}
180
181	/* check the result of the update */
182	if (result == NULL || *result != 0) {
183		ypclnt_error(ypclnt, __func__,
184		    "NIS password update failed");
185		/* XXX how do we get more details? */
186		ret = -1;
187		goto done;
188	}
189
190	ypclnt_error(ypclnt, NULL, NULL);
191	ret = 0;
192
193 done:
194	if (clnt != NULL) {
195		auth_destroy(clnt->cl_auth);
196		clnt_destroy(clnt);
197	}
198	if (nc != NULL)
199		freenetconfigent(nc);
200	free(yppwd.newpw.pw_name);
201	if (yppwd.newpw.pw_passwd != NULL) {
202		memset(yppwd.newpw.pw_passwd, 0, strlen(yppwd.newpw.pw_passwd));
203		free(yppwd.newpw.pw_passwd);
204	}
205	free(yppwd.newpw.pw_class);
206	free(yppwd.newpw.pw_gecos);
207	free(yppwd.newpw.pw_dir);
208	free(yppwd.newpw.pw_shell);
209	if (yppwd.oldpass != NULL) {
210		memset(yppwd.oldpass, 0, strlen(yppwd.oldpass));
211		free(yppwd.oldpass);
212	}
213	return (ret);
214}
215
216static int
217yppasswd_remote(ypclnt_t *ypclnt, const struct passwd *pwd, const char *passwd)
218{
219	struct yppasswd yppwd;
220	struct rpc_err rpcerr;
221	CLIENT *clnt = NULL;
222	int ret, *result;
223
224	/* fill the yppasswd structure */
225	memset(&yppwd, 0, sizeof yppwd);
226	yppwd.newpw.pw_uid = pwd->pw_uid;
227	yppwd.newpw.pw_gid = pwd->pw_gid;
228	if ((yppwd.newpw.pw_name = strdup(pwd->pw_name)) == NULL ||
229	    (yppwd.newpw.pw_passwd = strdup(pwd->pw_passwd)) == NULL ||
230	    (yppwd.newpw.pw_gecos = strdup(pwd->pw_gecos)) == NULL ||
231	    (yppwd.newpw.pw_dir = strdup(pwd->pw_dir)) == NULL ||
232	    (yppwd.newpw.pw_shell = strdup(pwd->pw_shell)) == NULL ||
233	    (yppwd.oldpass = strdup(passwd ? passwd : "")) == NULL) {
234		ypclnt_error(ypclnt, __func__, strerror(errno));
235		ret = -1;
236		goto done;
237	}
238
239	/* connect to rpc.yppasswdd */
240	clnt = clnt_create(ypclnt->server, YPPASSWDPROG, YPPASSWDVERS, "udp");
241	if (clnt == NULL) {
242		ypclnt_error(ypclnt, __func__,
243		    "failed to connect to rpc.yppasswdd: %s",
244		    clnt_spcreateerror(ypclnt->server));
245		ret = -1;
246		goto done;
247	}
248	clnt->cl_auth = authunix_create_default();
249
250	/* request the update */
251	result = yppasswdproc_update_1(&yppwd, clnt);
252
253	/* check for RPC errors */
254	clnt_geterr(clnt, &rpcerr);
255	if (rpcerr.re_status != RPC_SUCCESS) {
256		ypclnt_error(ypclnt, __func__,
257		    "NIS password update failed: %s",
258		    clnt_sperror(clnt, ypclnt->server));
259		ret = -1;
260		goto done;
261	}
262
263	/* check the result of the update */
264	if (result == NULL || *result != 0) {
265		ypclnt_error(ypclnt, __func__,
266		    "NIS password update failed");
267		/* XXX how do we get more details? */
268		ret = -1;
269		goto done;
270	}
271
272	ypclnt_error(ypclnt, NULL, NULL);
273	ret = 0;
274
275 done:
276	if (clnt != NULL) {
277		auth_destroy(clnt->cl_auth);
278		clnt_destroy(clnt);
279	}
280	free(yppwd.newpw.pw_name);
281	if (yppwd.newpw.pw_passwd != NULL) {
282		memset(yppwd.newpw.pw_passwd, 0, strlen(yppwd.newpw.pw_passwd));
283		free(yppwd.newpw.pw_passwd);
284	}
285	free(yppwd.newpw.pw_gecos);
286	free(yppwd.newpw.pw_dir);
287	free(yppwd.newpw.pw_shell);
288	if (yppwd.oldpass != NULL) {
289		memset(yppwd.oldpass, 0, strlen(yppwd.oldpass));
290		free(yppwd.oldpass);
291	}
292	return (ret);
293}
294