1 /* $NetBSD: ypserv.c,v 1.27 2021/03/07 15:09:13 christos Exp $ */
2
3 /*
4 * Copyright (c) 1994 Mats O Jansson <moj@stacken.kth.se>
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
17 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
20 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26 * SUCH DAMAGE.
27 */
28
29 #include <sys/cdefs.h>
30 #ifndef lint
31 __RCSID("$NetBSD: ypserv.c,v 1.27 2021/03/07 15:09:13 christos Exp $");
32 #endif
33
34 #include <sys/types.h>
35 #include <sys/socket.h>
36 #include <sys/wait.h>
37
38 #include <err.h>
39 #include <netdb.h>
40 #include <signal.h>
41 #include <stdio.h>
42 #include <stdlib.h>
43 #include <string.h>
44 #include <syslog.h>
45 #include <unistd.h>
46 #include <util.h>
47 #include <stdarg.h>
48 #include <errno.h>
49
50 #include <rpc/rpc.h>
51 #include <rpc/xdr.h>
52 #include <rpc/pmap_clnt.h>
53
54 #include <rpcsvc/yp_prot.h>
55
56 #include "ypdef.h"
57 #include "ypserv.h"
58
59 #ifdef LIBWRAP
60 #include <tcpd.h>
61
62 int allow_severity = LOG_DAEMON | LOG_INFO;
63 int deny_severity = LOG_DAEMON | LOG_WARNING;
64
65 /* XXX For ypserv_proc.c -- NOT THREAD SAFE! (like any of this code is) */
66 const char *clientstr;
67 const char *svcname;
68 #endif /* LIBWRAP */
69
70 int usedns;
71 #ifdef DEBUG
72 static int foreground = 1;
73 #else
74 static int foreground;
75 #endif
76
77 #ifdef LIBWRAP
78 int lflag;
79 #endif
80
81 static struct bindsock {
82 sa_family_t family;
83 int type;
84 int proto;
85 const char *name;
86 } socklist[] = {
87 { AF_INET, SOCK_DGRAM, IPPROTO_UDP, "udp" },
88 { AF_INET, SOCK_STREAM, IPPROTO_TCP, "tcp" },
89 { AF_INET6, SOCK_DGRAM, IPPROTO_UDP, "udp6" },
90 { AF_INET6, SOCK_STREAM, IPPROTO_TCP, "tcp6" },
91 };
92
93 static void usage(void) __dead;
94 static int bind_resv_port(int, sa_family_t, in_port_t);
95 void ypserv_sock_hostname(struct host_info *host);
96
97 static __printflike(2, 3) void
_msgout(int level,const char * msg,...)98 _msgout(int level, const char *msg, ...)
99 {
100 va_list ap;
101 va_start(ap, msg);
102 if (foreground)
103 vwarnx(msg, ap);
104 else
105 vsyslog(level, msg, ap);
106 va_end(ap);
107 }
108
ypserv_sock_hostname(struct host_info * host)109 void ypserv_sock_hostname(struct host_info *host)
110 {
111 host->name[0] = 0;
112 }
113
114 static void
ypprog_2(struct svc_req * rqstp,SVCXPRT * transp)115 ypprog_2(struct svc_req *rqstp, SVCXPRT *transp)
116 {
117 union {
118 char * ypproc_domain_2_arg;
119 char * ypproc_domain_nonack_2_arg;
120 struct ypreq_key ypproc_match_2_arg;
121 struct ypreq_nokey ypproc_first_2_arg;
122 struct ypreq_key ypproc_next_2_arg;
123 struct ypreq_xfr ypproc_xfr_2_arg;
124 struct ypreq_nokey ypproc_all_2_arg;
125 struct ypreq_nokey ypproc_master_2_arg;
126 struct ypreq_nokey ypproc_order_2_arg;
127 char * ypproc_maplist_2_arg;
128 } argument;
129 void *argp = &argument;
130 char *result;
131 xdrproc_t xdr_argument, xdr_result;
132 void *(*local)(void *, struct svc_req *);
133 #ifdef LIBWRAP
134 struct request_info req;
135 struct sockaddr *caller;
136 #define SVCNAME(x) svcname = x
137 #else
138 #define SVCNAME(x) /* nothing */
139 #endif
140
141 #ifdef LIBWRAP
142 caller = svc_getrpccaller(transp)->buf;
143 (void)request_init(&req, RQ_DAEMON, getprogname(), RQ_CLIENT_SIN,
144 caller, RQ_FILE, transp->xp_fd, NULL);
145 sock_methods(&req);
146
147 /*
148 * Do not do hostname lookups! This avoids possible delays due
149 * to DNS, preventing a possible DoS attack, as well as possible
150 * circular lookups (e.g. a hostname lookup requiring a request
151 * to ourselves).
152 */
153 req.hostname = ypserv_sock_hostname;
154 #endif
155
156 switch (rqstp->rq_proc) {
157 case YPPROC_NULL:
158 xdr_argument = (xdrproc_t)xdr_void;
159 xdr_result = (xdrproc_t)xdr_void;
160 local = ypproc_null_2_svc;
161 SVCNAME("null_2");
162 break;
163
164 case YPPROC_DOMAIN:
165 xdr_argument = (xdrproc_t)xdr_ypdomain_wrap_string;
166 xdr_result = (xdrproc_t)xdr_bool;
167 local = ypproc_domain_2_svc;
168 SVCNAME("domain_2");
169 break;
170
171 case YPPROC_DOMAIN_NONACK:
172 xdr_argument = (xdrproc_t)xdr_ypdomain_wrap_string;
173 xdr_result = (xdrproc_t)xdr_bool;
174 local = ypproc_domain_nonack_2_svc;
175 SVCNAME("domain_nonack_2");
176 break;
177
178 case YPPROC_MATCH:
179 xdr_argument = (xdrproc_t)xdr_ypreq_key;
180 xdr_result = (xdrproc_t)xdr_ypresp_val;
181 local = ypproc_match_2_svc;
182 SVCNAME("match_2");
183 break;
184
185 case YPPROC_FIRST:
186 xdr_argument = (xdrproc_t)xdr_ypreq_nokey;
187 xdr_result = (xdrproc_t)xdr_ypresp_key_val;
188 local = ypproc_first_2_svc;
189 SVCNAME("first_2");
190 break;
191
192 case YPPROC_NEXT:
193 xdr_argument = (xdrproc_t)xdr_ypreq_key;
194 xdr_result = (xdrproc_t)xdr_ypresp_key_val;
195 local = ypproc_next_2_svc;
196 SVCNAME("next_2");
197 break;
198
199 case YPPROC_XFR:
200 xdr_argument = (xdrproc_t)xdr_ypreq_xfr;
201 xdr_result = (xdrproc_t)xdr_ypresp_xfr;
202 local = ypproc_xfr_2_svc;
203 SVCNAME("xfer_2");
204 break;
205
206 case YPPROC_CLEAR:
207 xdr_argument = (xdrproc_t)xdr_void;
208 xdr_result = (xdrproc_t)xdr_void;
209 local = ypproc_clear_2_svc;
210 SVCNAME("clear_2");
211 break;
212
213 case YPPROC_ALL:
214 xdr_argument = (xdrproc_t)xdr_ypreq_nokey;
215 xdr_result = (xdrproc_t)xdr_ypresp_all;
216 local = ypproc_all_2_svc;
217 SVCNAME("all_2");
218 break;
219
220 case YPPROC_MASTER:
221 xdr_argument = (xdrproc_t)xdr_ypreq_nokey;
222 xdr_result = (xdrproc_t)xdr_ypresp_master;
223 local = ypproc_master_2_svc;
224 SVCNAME("master_2");
225 break;
226
227 case YPPROC_ORDER:
228 xdr_argument = (xdrproc_t)xdr_ypreq_nokey;
229 xdr_result = (xdrproc_t)xdr_ypresp_order;
230 local = ypproc_order_2_svc;
231 SVCNAME("order_2");
232 break;
233
234 case YPPROC_MAPLIST:
235 xdr_argument = (xdrproc_t)xdr_ypdomain_wrap_string;
236 xdr_result = (xdrproc_t)xdr_ypresp_maplist;
237 local = ypproc_maplist_2_svc;
238 SVCNAME("maplist_2");
239 break;
240
241 default:
242 svcerr_noproc(transp);
243 return;
244 }
245
246 #ifdef LIBWRAP
247 clientstr = eval_client(&req);
248
249 if (hosts_access(&req) == 0) {
250 syslog(deny_severity,
251 "%s: refused request from %.500s", svcname, clientstr);
252 svcerr_auth(transp, AUTH_FAILED);
253 return;
254 }
255 #endif
256
257 (void)memset(&argument, 0, sizeof (argument));
258 if (!svc_getargs(transp, xdr_argument, argp)) {
259 svcerr_decode(transp);
260 return;
261 }
262 result = (*local)(&argument, rqstp);
263 if (result != NULL && !svc_sendreply(transp, xdr_result, result)) {
264 svcerr_systemerr(transp);
265 }
266 if (!svc_freeargs(transp, xdr_argument, argp)) {
267 _msgout(LOG_ERR, "unable to free arguments");
268 exit(1);
269 }
270 return;
271 }
272
273 /*
274 * limited NIS version 1 support: the null, domain, and domain_nonack
275 * request/reply format is identical between v1 and v2. SunOS4's ypbind
276 * makes v1 domain_nonack calls.
277 */
278 static void
ypprog_1(struct svc_req * rqstp,SVCXPRT * transp)279 ypprog_1(struct svc_req *rqstp, SVCXPRT *transp)
280 {
281 switch (rqstp->rq_proc) {
282 case YPPROC_NULL:
283 case YPPROC_DOMAIN:
284 case YPPROC_DOMAIN_NONACK:
285 ypprog_2(rqstp, transp);
286 return;
287
288 default:
289 svcerr_noproc(transp);
290 return;
291 }
292 }
293
294 int
main(int argc,char * argv[])295 main(int argc, char *argv[])
296 {
297 SVCXPRT *xprt;
298 struct netconfig *cfg = NULL;
299 int s;
300 struct sigaction sa;
301 struct bindsock *bs;
302 in_port_t port = 0;
303 int ch, xcreated = 0, one = 1;
304
305 setprogname(argv[0]);
306
307 #ifdef LIBWRAP
308 #define GETOPTSTR "dflp:"
309 #else
310 #define GETOPTSTR "dfp:"
311 #endif
312 while ((ch = getopt(argc, argv, GETOPTSTR)) != -1) {
313 switch (ch) {
314 case 'd':
315 usedns = 1;
316 break;
317 case 'f':
318 foreground = 1;
319 break;
320 case 'p':
321 port = atoi(optarg);
322 break;
323 #ifdef LIBWRAP
324 case 'l':
325 lflag = 1;
326 break;
327 #endif
328 default:
329 usage();
330 }
331 }
332
333 #undef GETOPTSTR
334
335 /* This program must be run by root. */
336 if (geteuid() != 0)
337 errx(1, "must run as root");
338
339 if (foreground == 0 && daemon(0, 0))
340 err(1, "can't detach");
341
342 openlog("ypserv", LOG_PID, LOG_DAEMON);
343 syslog(LOG_INFO, "starting");
344 (void)pidfile(NULL);
345
346 (void) rpcb_unset((u_int)YPPROG, (u_int)YPVERS, NULL);
347 (void) rpcb_unset((u_int)YPPROG, (u_int)YPVERS_ORIG, NULL);
348
349
350 ypdb_init(); /* init db stuff */
351
352 sa.sa_handler = SIG_IGN;
353 sa.sa_flags = SA_NOCLDWAIT;
354 if (sigemptyset(&sa.sa_mask)) {
355 _msgout(LOG_ERR, "sigemptyset: %s", strerror(errno));
356 exit(1);
357 }
358 if (sigaction(SIGCHLD, &sa, NULL)) {
359 _msgout(LOG_ERR, "sigaction: %s", strerror(errno));
360 exit(1);
361 }
362
363 for (bs = socklist;
364 bs < &socklist[sizeof(socklist) / sizeof(socklist[0])]; bs++) {
365
366 if ((s = socket(bs->family, bs->type, bs->proto)) == -1)
367 continue;
368
369 if (bs->family == AF_INET6) {
370 /*
371 * We're doing host-based access checks here, so don't
372 * allow v4-in-v6 to confuse things.
373 */
374 if (setsockopt(s, IPPROTO_IPV6,
375 IPV6_V6ONLY, &one, sizeof(one)) == -1) {
376 _msgout(LOG_ERR,
377 "can't disable v4-in-v6 on %s socket",
378 bs->name);
379 exit(1);
380 }
381 }
382
383 if ((cfg = getnetconfigent(bs->name)) == NULL) {
384 _msgout(LOG_ERR,
385 "unable to get network configuration for %s port",
386 bs->name);
387 goto out;
388 }
389
390 if (bind_resv_port(s, bs->family, port) != 0)
391 goto out;
392
393 if (bs->type == SOCK_STREAM) {
394 (void)listen(s, SOMAXCONN);
395 xprt = svc_vc_create(s, 0, 0);
396 } else {
397 xprt = svc_dg_create(s, 0, 0);
398 }
399
400 if (xprt == NULL) {
401 _msgout(LOG_WARNING, "unable to create %s service",
402 bs->name);
403 goto out;
404 }
405 if (svc_reg(xprt, (u_int)YPPROG, (u_int)YPVERS_ORIG, ypprog_1,
406 cfg) == 0 ||
407 svc_reg(xprt, (u_int)YPPROG, (u_int)YPVERS, ypprog_2,
408 cfg) == 0) {
409 _msgout(LOG_WARNING, "unable to register %s service",
410 bs->name);
411 goto out;
412 }
413 xcreated++;
414 freenetconfigent(cfg);
415 continue;
416 out:
417 if (s != -1)
418 (void)close(s);
419 if (cfg) {
420 freenetconfigent(cfg);
421 cfg = NULL;
422 }
423 }
424
425 if (xcreated == 0) {
426 _msgout(LOG_ERR, "unable to create any services");
427 exit(1);
428 }
429
430 svc_run();
431 _msgout(LOG_ERR, "svc_run returned");
432 exit(1);
433 /* NOTREACHED */
434 }
435
436 static void
usage(void)437 usage(void)
438 {
439
440 #ifdef LIBWRAP
441 #define USAGESTR "Usage: %s [-dfl] [-p <port>]\n"
442 #else
443 #define USAGESTR "Usage: %s [-df] [-p <port>]\n"
444 #endif
445
446 (void)fprintf(stderr, USAGESTR, getprogname());
447 exit(1);
448
449 #undef USAGESTR
450 }
451
452 /*
453 * _yp_invalid_map: check if given map name isn't legal.
454 * returns non-zero if invalid
455 *
456 * XXX: this probably should be in libc/yp/yplib.c
457 */
458 int
_yp_invalid_map(const char * map)459 _yp_invalid_map(const char *map)
460 {
461 if (map == NULL || *map == '\0')
462 return 1;
463
464 if (strlen(map) > YPMAXMAP)
465 return 1;
466
467 if (strchr(map, '/') != NULL)
468 return 1;
469
470 return 0;
471 }
472
473 static int
bind_resv_port(int sock,sa_family_t family,in_port_t port)474 bind_resv_port(int sock, sa_family_t family, in_port_t port)
475 {
476 struct sockaddr *sa;
477 struct sockaddr_in sasin;
478 struct sockaddr_in6 sasin6;
479
480 switch (family) {
481 case AF_INET:
482 (void)memset(&sasin, 0, sizeof(sasin));
483 sasin.sin_len = sizeof(sasin);
484 sasin.sin_family = family;
485 sasin.sin_port = htons(port);
486 sa = (struct sockaddr *)(void *)&sasin;
487 break;
488 case AF_INET6:
489 (void)memset(&sasin6, 0, sizeof(sasin6));
490 sasin6.sin6_len = sizeof(sasin6);
491 sasin6.sin6_family = family;
492 sasin6.sin6_port = htons(port);
493 sa = (struct sockaddr *)(void *)&sasin6;
494 break;
495 default:
496 _msgout(LOG_ERR, "Unsupported address family %d", family);
497 return -1;
498 }
499 if (bindresvport_sa(sock, sa) == -1) {
500 _msgout(LOG_ERR, "Cannot bind to reserved port %d (%s)", port,
501 strerror(errno));
502 return -1;
503 }
504 return 0;
505 }
506