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