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 (at) 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 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 109 void ypserv_sock_hostname(struct host_info *host) 110 { 111 host->name[0] = 0; 112 } 113 114 static void 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 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 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 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 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 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