xref: /openbsd-src/lib/libc/yp/yp_bind.c (revision b2ea75c1b17e1a9a339660e7ed45cd24946b230e)
1 /*
2  * Copyright (c) 1996 Theo de Raadt <deraadt@theos.com>
3  * Copyright (c) 1992, 1993 Theo de Raadt <deraadt@theos.com>
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  * 3. All advertising materials mentioning features or use of this software
15  *    must display the following acknowledgement:
16  *	This product includes software developed by Theo de Raadt.
17  * 4. The name of the author may not be used to endorse or promote products
18  *    derived from this software without specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
21  * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
22  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
24  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30  * SUCH DAMAGE.
31  */
32 
33 #if defined(LIBC_SCCS) && !defined(lint)
34 static char *rcsid = "$OpenBSD: yp_bind.c,v 1.11 2001/06/27 00:58:57 lebel Exp $";
35 #endif /* LIBC_SCCS and not lint */
36 
37 #include <sys/param.h>
38 #include <sys/types.h>
39 #include <sys/socket.h>
40 #include <sys/uio.h>
41 #include <errno.h>
42 #include <fcntl.h>
43 #include <stdio.h>
44 #include <stdlib.h>
45 #include <string.h>
46 #include <unistd.h>
47 #include <rpc/rpc.h>
48 #include <rpc/xdr.h>
49 #include <rpcsvc/yp.h>
50 #include <rpcsvc/ypclnt.h>
51 #include "ypinternal.h"
52 
53 struct dom_binding *_ypbindlist;
54 char _yp_domain[MAXHOSTNAMELEN];
55 int _yplib_timeout = 10;
56 
57 int
58 _yp_dobind(dom, ypdb)
59 	const char     *dom;
60 	struct dom_binding **ypdb;
61 {
62 	static int      pid = -1;
63 	char            path[MAXPATHLEN];
64 	struct dom_binding *ysd, *ysd2;
65 	struct ypbind_resp ypbr;
66 	struct timeval  tv;
67 	struct sockaddr_in clnt_sin;
68 	struct ypbind_binding *bn;
69 	int             clnt_sock, fd, gpid;
70 	CLIENT         *client;
71 	int             new = 0, r;
72 	int             count = 0;
73 	u_short		port;
74 
75 	/*
76 	 * test if YP is running or not
77 	 */
78 	if ((fd = open(YPBINDLOCK, O_RDONLY)) == -1)
79 		return YPERR_YPBIND;
80 	if (!(flock(fd, LOCK_EX | LOCK_NB) == -1 && errno == EWOULDBLOCK)) {
81 		(void)close(fd);
82 		return YPERR_YPBIND;
83 	}
84 	(void)close(fd);
85 
86 	gpid = getpid();
87 	if (!(pid == -1 || pid == gpid)) {
88 		ysd = _ypbindlist;
89 		while (ysd) {
90 			if (ysd->dom_client)
91 				clnt_destroy(ysd->dom_client);
92 			ysd2 = ysd->dom_pnext;
93 			free(ysd);
94 			ysd = ysd2;
95 		}
96 		_ypbindlist = NULL;
97 	}
98 	pid = gpid;
99 
100 	if (ypdb != NULL)
101 		*ypdb = NULL;
102 
103 	if (dom == NULL || strlen(dom) == 0)
104 		return YPERR_BADARGS;
105 
106 	for (ysd = _ypbindlist; ysd; ysd = ysd->dom_pnext)
107 		if (strcmp(dom, ysd->dom_domain) == 0)
108 			break;
109 	if (ysd == NULL) {
110 		if ((ysd = malloc(sizeof *ysd)) == NULL)
111 			return YPERR_YPERR;
112 		(void)memset(ysd, 0, sizeof *ysd);
113 		ysd->dom_socket = -1;
114 		ysd->dom_vers = 0;
115 		new = 1;
116 	}
117 again:
118 	if (ysd->dom_vers == 0) {
119 		(void) snprintf(path, sizeof(path), "%s/%s.%d",
120 		    BINDINGDIR, dom, 2);
121 		if ((fd = open(path, O_RDONLY)) == -1) {
122 			/*
123 			 * no binding file, YP is dead, or not yet fully
124 			 * alive.
125 			 */
126 			goto trynet;
127 		}
128 		if (flock(fd, LOCK_EX | LOCK_NB) == -1 &&
129 		    errno == EWOULDBLOCK) {
130 			struct iovec    iov[2];
131 			u_short         ypb_port;
132 
133 			/*
134 			 * we fetch the ypbind port number, but do
135 			 * nothing with it.
136 			 */
137 			iov[0].iov_base = (caddr_t) &ypb_port;
138 			iov[0].iov_len = sizeof ypb_port;
139 			iov[1].iov_base = (caddr_t) &ypbr;
140 			iov[1].iov_len = sizeof ypbr;
141 
142 			r = readv(fd, iov, 2);
143 			if (r != iov[0].iov_len + iov[1].iov_len) {
144 				(void)close(fd);
145 				ysd->dom_vers = -1;
146 				goto again;
147 			}
148 			(void)close(fd);
149 			goto gotdata;
150 		} else {
151 			/* no lock on binding file, YP is dead. */
152 			(void)close(fd);
153 			if (new)
154 				free(ysd);
155 			return YPERR_YPBIND;
156 		}
157 	}
158 trynet:
159 	if (ysd->dom_vers == -1 || ysd->dom_vers == 0) {
160 		(void)memset(&clnt_sin, 0, sizeof clnt_sin);
161 		clnt_sin.sin_len = sizeof(struct sockaddr_in);
162 		clnt_sin.sin_family = AF_INET;
163 		clnt_sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
164 
165 		clnt_sock = RPC_ANYSOCK;
166 		client = clnttcp_create(&clnt_sin, YPBINDPROG, YPBINDVERS,
167 		    &clnt_sock, 0, 0);
168 		if (client == NULL) {
169 			clnt_pcreateerror("clnttcp_create");
170 			if (new)
171 				free(ysd);
172 			return YPERR_YPBIND;
173 		}
174 		if (ntohs(clnt_sin.sin_port) >= IPPORT_RESERVED ||
175 		    ntohs(clnt_sin.sin_port) == 20) {
176 			/*
177 			 * YP was not running, but someone has registered
178 			 * ypbind with portmap -- this simply means YP is
179 			 * not running.
180 			 */
181 			clnt_destroy(client);
182 			if (new)
183 				free(ysd);
184 			return YPERR_YPBIND;
185 		}
186 		tv.tv_sec = _yplib_timeout;
187 		tv.tv_usec = 0;
188 		r = clnt_call(client, YPBINDPROC_DOMAIN, xdr_domainname,
189 		    &dom, xdr_ypbind_resp, &ypbr, tv);
190 		if (r != RPC_SUCCESS) {
191 			if (new == 0 || count)
192 				fprintf(stderr,
193 		    "YP server for domain %s not responding, still trying\n",
194 				    dom);
195 			count++;
196 			clnt_destroy(client);
197 			ysd->dom_vers = -1;
198 			goto again;
199 		}
200 		clnt_destroy(client);
201 gotdata:
202 		bn = &ypbr.ypbind_resp_u.ypbind_bindinfo;
203 		memcpy(&port, &bn->ypbind_binding_port, sizeof port);
204 		if (ntohs(port) >= IPPORT_RESERVED ||
205 		    ntohs(port) == 20) {
206 			/*
207 			 * This is bullshit -- the ypbind wants me to
208 			 * communicate to an insecure ypserv.  We are
209 			 * within rights to syslog this as an attack,
210 			 * but for now we'll simply ignore it; real YP
211 			 * is obviously not running.
212 			 */
213 			if (new)
214 				free(ysd);
215 			return YPERR_YPBIND;
216 		}
217 		(void)memset(&ysd->dom_server_addr, 0,
218 		    sizeof ysd->dom_server_addr);
219 		ysd->dom_server_addr.sin_len = sizeof(struct sockaddr_in);
220 		ysd->dom_server_addr.sin_family = AF_INET;
221 		memcpy(&ysd->dom_server_addr.sin_port,
222 		    &bn->ypbind_binding_port,
223 		    sizeof(ysd->dom_server_addr.sin_port));
224 		memcpy(&ysd->dom_server_addr.sin_addr.s_addr,
225 		    &bn->ypbind_binding_addr,
226 		    sizeof(ysd->dom_server_addr.sin_addr.s_addr));
227 		ysd->dom_server_port = ysd->dom_server_addr.sin_port;
228 		ysd->dom_vers = YPVERS;
229 		strlcpy(ysd->dom_domain, dom, sizeof ysd->dom_domain);
230 	}
231 	tv.tv_sec = _yplib_timeout / 2;
232 	tv.tv_usec = 0;
233 	if (ysd->dom_client)
234 		clnt_destroy(ysd->dom_client);
235 	ysd->dom_socket = RPC_ANYSOCK;
236 	ysd->dom_client = clntudp_create(&ysd->dom_server_addr,
237 	    YPPROG, YPVERS, tv, &ysd->dom_socket);
238 	if (ysd->dom_client == NULL) {
239 		clnt_pcreateerror("clntudp_create");
240 		ysd->dom_vers = -1;
241 		goto again;
242 	}
243 	if (fcntl(ysd->dom_socket, F_SETFD, 1) == -1)
244 		perror("fcntl: F_SETFD");
245 
246 	if (new) {
247 		ysd->dom_pnext = _ypbindlist;
248 		_ypbindlist = ysd;
249 	}
250 	if (ypdb != NULL)
251 		*ypdb = ysd;
252 	return 0;
253 }
254 
255 void
256 _yp_unbind(ypb)
257 	struct dom_binding *ypb;
258 {
259 	clnt_destroy(ypb->dom_client);
260 	ypb->dom_client = NULL;
261 	ypb->dom_socket = -1;
262 }
263 
264 int
265 yp_bind(dom)
266 	const char     *dom;
267 {
268 	return _yp_dobind(dom, NULL);
269 }
270 
271 void
272 yp_unbind(dom)
273 	const char     *dom;
274 {
275 	struct dom_binding *ypb, *ypbp;
276 
277 	ypbp = NULL;
278 	for (ypb = _ypbindlist; ypb; ypb = ypb->dom_pnext) {
279 		if (strcmp(dom, ypb->dom_domain) == 0) {
280 			clnt_destroy(ypb->dom_client);
281 			if (ypbp)
282 				ypbp->dom_pnext = ypb->dom_pnext;
283 			else
284 				_ypbindlist = ypb->dom_pnext;
285 			free(ypb);
286 			return;
287 		}
288 		ypbp = ypb;
289 	}
290 }
291