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