1 1.101 andvar /* $NetBSD: ypbind.c,v 1.101 2022/04/11 20:57:37 andvar Exp $ */ 2 1.20 cgd 3 1.2 deraadt /* 4 1.7 deraadt * Copyright (c) 1992, 1993 Theo de Raadt <deraadt (at) fsa.ca> 5 1.2 deraadt * All rights reserved. 6 1.2 deraadt * 7 1.2 deraadt * Redistribution and use in source and binary forms, with or without 8 1.2 deraadt * modification, are permitted provided that the following conditions 9 1.2 deraadt * are met: 10 1.2 deraadt * 1. Redistributions of source code must retain the above copyright 11 1.2 deraadt * notice, this list of conditions and the following disclaimer. 12 1.2 deraadt * 2. Redistributions in binary form must reproduce the above copyright 13 1.2 deraadt * notice, this list of conditions and the following disclaimer in the 14 1.2 deraadt * documentation and/or other materials provided with the distribution. 15 1.2 deraadt * 16 1.2 deraadt * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS 17 1.2 deraadt * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 18 1.2 deraadt * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 1.2 deraadt * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY 20 1.2 deraadt * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 1.2 deraadt * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22 1.2 deraadt * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23 1.2 deraadt * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24 1.2 deraadt * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25 1.2 deraadt * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26 1.2 deraadt * SUCH DAMAGE. 27 1.2 deraadt */ 28 1.2 deraadt 29 1.30 lukem #include <sys/cdefs.h> 30 1.2 deraadt #ifndef LINT 31 1.101 andvar __RCSID("$NetBSD: ypbind.c,v 1.101 2022/04/11 20:57:37 andvar Exp $"); 32 1.2 deraadt #endif 33 1.2 deraadt 34 1.67 dholland #include <sys/types.h> 35 1.1 deraadt #include <sys/param.h> 36 1.67 dholland #include <sys/file.h> 37 1.1 deraadt #include <sys/ioctl.h> 38 1.1 deraadt #include <sys/signal.h> 39 1.1 deraadt #include <sys/socket.h> 40 1.67 dholland #include <sys/stat.h> 41 1.67 dholland #include <sys/syslog.h> 42 1.6 deraadt #include <sys/uio.h> 43 1.67 dholland #include <arpa/inet.h> 44 1.67 dholland #include <net/if.h> 45 1.67 dholland #include <ctype.h> 46 1.67 dholland #include <dirent.h> 47 1.67 dholland #include <err.h> 48 1.67 dholland #include <errno.h> 49 1.38 kleink #include <fcntl.h> 50 1.67 dholland #include <ifaddrs.h> 51 1.27 thorpej #include <limits.h> 52 1.67 dholland #include <netdb.h> 53 1.97 dholland #include <signal.h> 54 1.67 dholland #include <stdarg.h> 55 1.1 deraadt #include <stdio.h> 56 1.14 cgd #include <stdlib.h> 57 1.67 dholland #include <string.h> 58 1.37 bouyer #include <syslog.h> 59 1.67 dholland #include <unistd.h> 60 1.67 dholland #include <util.h> 61 1.67 dholland 62 1.1 deraadt #include <rpc/rpc.h> 63 1.1 deraadt #include <rpc/xdr.h> 64 1.1 deraadt #include <rpc/pmap_clnt.h> 65 1.1 deraadt #include <rpc/pmap_prot.h> 66 1.1 deraadt #include <rpc/pmap_rmt.h> 67 1.1 deraadt #include <rpcsvc/yp_prot.h> 68 1.1 deraadt #include <rpcsvc/ypclnt.h> 69 1.1 deraadt 70 1.27 thorpej #include "pathnames.h" 71 1.27 thorpej 72 1.68 dholland #define YPSERVERSSUFF ".ypservers" 73 1.68 dholland #define BINDINGDIR (_PATH_VAR_YP "binding") 74 1.68 dholland 75 1.24 christos #ifndef O_SHLOCK 76 1.24 christos #define O_SHLOCK 0 77 1.24 christos #endif 78 1.24 christos 79 1.68 dholland int _yp_invalid_domain(const char *); /* XXX libc internal */ 80 1.68 dholland 81 1.68 dholland //////////////////////////////////////////////////////////// 82 1.68 dholland // types and globals 83 1.27 thorpej 84 1.68 dholland typedef enum { 85 1.81 dholland YPBIND_DIRECT, YPBIND_BROADCAST, 86 1.68 dholland } ypbind_mode_t; 87 1.1 deraadt 88 1.96 dholland enum domainstates { 89 1.96 dholland DOM_NEW, /* not yet bound */ 90 1.96 dholland DOM_ALIVE, /* bound and healthy */ 91 1.96 dholland DOM_PINGING, /* ping outstanding */ 92 1.96 dholland DOM_LOST, /* binding timed out, looking for a new one */ 93 1.96 dholland DOM_DEAD, /* long-term lost, in exponential backoff */ 94 1.96 dholland }; 95 1.96 dholland 96 1.75 dholland struct domain { 97 1.78 dholland struct domain *dom_next; 98 1.75 dholland 99 1.79 dholland char dom_name[YPMAXDOMAIN + 1]; 100 1.1 deraadt struct sockaddr_in dom_server_addr; 101 1.35 lukem long dom_vers; 102 1.96 dholland time_t dom_checktime; /* time of next check/contact */ 103 1.96 dholland time_t dom_asktime; /* time we were last DOMAIN'd */ 104 1.96 dholland time_t dom_losttime; /* time the binding was lost, or 0 */ 105 1.96 dholland unsigned dom_backofftime; /* current backoff period, when DEAD */ 106 1.1 deraadt int dom_lockfd; 107 1.96 dholland enum domainstates dom_state; 108 1.64 dholland uint32_t dom_xid; 109 1.84 dholland FILE *dom_serversfile; /* /var/yp/binding/foo.ypservers */ 110 1.84 dholland int dom_been_ypset; /* ypset been done on this domain? */ 111 1.83 dholland ypbind_mode_t dom_ypbindmode; /* broadcast or direct */ 112 1.1 deraadt }; 113 1.1 deraadt 114 1.68 dholland #define BUFSIZE 1400 115 1.68 dholland 116 1.95 dholland /* the list of all domains */ 117 1.77 dholland static struct domain *domains; 118 1.24 christos static int check; 119 1.1 deraadt 120 1.95 dholland /* option settings */ 121 1.82 dholland static ypbind_mode_t default_ypbindmode; 122 1.81 dholland static int allow_local_ypset = 0, allow_any_ypset = 0; 123 1.30 lukem static int insecure; 124 1.86 dholland 125 1.95 dholland /* the sockets we use to interact with servers */ 126 1.24 christos static int rpcsock, pingsock; 127 1.95 dholland 128 1.95 dholland /* stuff used for manually interacting with servers */ 129 1.24 christos static struct rmtcallargs rmtca; 130 1.24 christos static struct rmtcallres rmtcr; 131 1.26 ws static bool_t rmtcr_outval; 132 1.64 dholland static unsigned long rmtcr_port; 133 1.95 dholland 134 1.95 dholland /* The ypbind service transports */ 135 1.24 christos static SVCXPRT *udptransp, *tcptransp; 136 1.24 christos 137 1.97 dholland /* set if we get SIGHUP */ 138 1.97 dholland static sig_atomic_t hupped; 139 1.97 dholland 140 1.68 dholland //////////////////////////////////////////////////////////// 141 1.73 dholland // utilities 142 1.73 dholland 143 1.95 dholland /* 144 1.95 dholland * Combo of open() and flock(). 145 1.95 dholland */ 146 1.73 dholland static int 147 1.73 dholland open_locked(const char *path, int flags, mode_t mode) 148 1.73 dholland { 149 1.73 dholland int fd; 150 1.73 dholland 151 1.73 dholland fd = open(path, flags|O_SHLOCK, mode); 152 1.73 dholland if (fd < 0) { 153 1.73 dholland return -1; 154 1.73 dholland } 155 1.73 dholland #if O_SHLOCK == 0 156 1.73 dholland /* dholland 20110522 wouldn't it be better to check this for error? */ 157 1.73 dholland (void)flock(fd, LOCK_SH); 158 1.73 dholland #endif 159 1.73 dholland return fd; 160 1.73 dholland } 161 1.73 dholland 162 1.96 dholland /* 163 1.96 dholland * Exponential backoff for pinging servers for a dead domain. 164 1.96 dholland * 165 1.96 dholland * We go 10 -> 20 -> 40 -> 60 seconds, then 2 -> 4 -> 8 -> 15 -> 30 -> 166 1.96 dholland * 60 minutes, and stay at 60 minutes. This is overengineered. 167 1.96 dholland * 168 1.96 dholland * With a 60 minute max backoff the response time for when things come 169 1.96 dholland * back is not awful, but we only try (and log) about 60 times even if 170 1.96 dholland * things are down for a whole long weekend. This is an acceptable log 171 1.96 dholland * load, I think. 172 1.96 dholland */ 173 1.96 dholland static void 174 1.96 dholland backoff(unsigned *psecs) 175 1.96 dholland { 176 1.96 dholland unsigned secs; 177 1.96 dholland 178 1.96 dholland secs = *psecs; 179 1.96 dholland if (secs < 60) { 180 1.96 dholland secs *= 2; 181 1.96 dholland if (secs > 60) { 182 1.96 dholland secs = 60; 183 1.96 dholland } 184 1.96 dholland } else if (secs < 60 * 15) { 185 1.96 dholland secs *= 2; 186 1.96 dholland if (secs > 60 * 15) { 187 1.96 dholland secs = 60 * 15; 188 1.96 dholland } 189 1.96 dholland } else if (secs < 60 * 60) { 190 1.96 dholland secs *= 2; 191 1.96 dholland } 192 1.96 dholland *psecs = secs; 193 1.96 dholland } 194 1.96 dholland 195 1.73 dholland //////////////////////////////////////////////////////////// 196 1.68 dholland // logging 197 1.68 dholland 198 1.24 christos #ifdef DEBUG 199 1.68 dholland #define DPRINTF(...) (debug ? (void)printf(__VA_ARGS__) : (void)0) 200 1.68 dholland static int debug; 201 1.68 dholland #else 202 1.68 dholland #define DPRINTF(...) 203 1.24 christos #endif 204 1.36 mrg 205 1.69 dholland static void yp_log(int, const char *, ...) __printflike(2, 3); 206 1.69 dholland 207 1.95 dholland /* 208 1.95 dholland * Log some stuff, to syslog or stderr depending on the debug setting. 209 1.95 dholland */ 210 1.37 bouyer static void 211 1.37 bouyer yp_log(int pri, const char *fmt, ...) 212 1.37 bouyer { 213 1.37 bouyer va_list ap; 214 1.37 bouyer 215 1.37 bouyer va_start(ap, fmt); 216 1.37 bouyer 217 1.37 bouyer #if defined(DEBUG) 218 1.72 dholland if (debug) { 219 1.57 christos (void)vprintf(fmt, ap); 220 1.72 dholland (void)printf("\n"); 221 1.72 dholland } else 222 1.37 bouyer #endif 223 1.37 bouyer vsyslog(pri, fmt, ap); 224 1.37 bouyer va_end(ap); 225 1.37 bouyer } 226 1.37 bouyer 227 1.68 dholland //////////////////////////////////////////////////////////// 228 1.71 dholland // ypservers file 229 1.71 dholland 230 1.71 dholland /* 231 1.71 dholland * Get pathname for the ypservers file for a given domain 232 1.71 dholland * (/var/yp/binding/DOMAIN.ypservers) 233 1.71 dholland */ 234 1.71 dholland static const char * 235 1.71 dholland ypservers_filename(const char *domain) 236 1.71 dholland { 237 1.71 dholland static char ret[PATH_MAX]; 238 1.71 dholland 239 1.71 dholland (void)snprintf(ret, sizeof(ret), "%s/%s%s", 240 1.71 dholland BINDINGDIR, domain, YPSERVERSSUFF); 241 1.71 dholland return ret; 242 1.71 dholland } 243 1.71 dholland 244 1.71 dholland //////////////////////////////////////////////////////////// 245 1.75 dholland // struct domain 246 1.68 dholland 247 1.95 dholland /* 248 1.96 dholland * The state transitions of a domain work as follows: 249 1.95 dholland * 250 1.96 dholland * in state NEW: 251 1.96 dholland * nag_servers every 5 seconds 252 1.96 dholland * upon answer, state is ALIVE 253 1.96 dholland * 254 1.96 dholland * in state ALIVE: 255 1.96 dholland * every 60 seconds, send ping and switch to state PINGING 256 1.96 dholland * 257 1.96 dholland * in state PINGING: 258 1.96 dholland * upon answer, go to state ALIVE 259 1.96 dholland * if no answer in 5 seconds, go to state LOST and do nag_servers 260 1.96 dholland * 261 1.96 dholland * in state LOST: 262 1.96 dholland * do nag_servers every 5 seconds 263 1.96 dholland * upon answer, go to state ALIVE 264 1.96 dholland * if no answer in 60 seconds, go to state DEAD 265 1.96 dholland * 266 1.96 dholland * in state DEAD 267 1.96 dholland * do nag_servers every backofftime seconds (starts at 10) 268 1.96 dholland * upon answer go to state ALIVE 269 1.96 dholland * backofftime doubles (approximately) each try, with a cap of 1 hour 270 1.95 dholland */ 271 1.95 dholland 272 1.95 dholland /* 273 1.95 dholland * Look up a domain by the XID we assigned it. 274 1.95 dholland */ 275 1.75 dholland static struct domain * 276 1.76 dholland domain_find(uint32_t xid) 277 1.68 dholland { 278 1.78 dholland struct domain *dom; 279 1.68 dholland 280 1.78 dholland for (dom = domains; dom != NULL; dom = dom->dom_next) 281 1.78 dholland if (dom->dom_xid == xid) 282 1.68 dholland break; 283 1.78 dholland return dom; 284 1.68 dholland } 285 1.68 dholland 286 1.95 dholland /* 287 1.95 dholland * Pick an XID for a domain. 288 1.95 dholland * 289 1.95 dholland * XXX: this should just generate a random number. 290 1.95 dholland */ 291 1.68 dholland static uint32_t 292 1.78 dholland unique_xid(struct domain *dom) 293 1.68 dholland { 294 1.68 dholland uint32_t tmp_xid; 295 1.68 dholland 296 1.78 dholland tmp_xid = ((uint32_t)(unsigned long)dom) & 0xffffffff; 297 1.76 dholland while (domain_find(tmp_xid) != NULL) 298 1.68 dholland tmp_xid++; 299 1.68 dholland 300 1.68 dholland return tmp_xid; 301 1.68 dholland } 302 1.68 dholland 303 1.95 dholland /* 304 1.95 dholland * Construct a new domain. Adds it to the global linked list of all 305 1.95 dholland * domains. 306 1.95 dholland */ 307 1.75 dholland static struct domain * 308 1.78 dholland domain_create(const char *name) 309 1.24 christos { 310 1.78 dholland struct domain *dom; 311 1.82 dholland const char *pathname; 312 1.82 dholland struct stat st; 313 1.1 deraadt 314 1.78 dholland dom = malloc(sizeof *dom); 315 1.78 dholland if (dom == NULL) { 316 1.75 dholland yp_log(LOG_ERR, "domain_create: Out of memory"); 317 1.99 ginsbach return NULL; 318 1.37 bouyer } 319 1.1 deraadt 320 1.80 dholland dom->dom_next = NULL; 321 1.80 dholland 322 1.79 dholland (void)strlcpy(dom->dom_name, name, sizeof(dom->dom_name)); 323 1.80 dholland (void)memset(&dom->dom_server_addr, 0, sizeof(dom->dom_server_addr)); 324 1.80 dholland dom->dom_vers = YPVERS; 325 1.80 dholland dom->dom_checktime = 0; 326 1.80 dholland dom->dom_asktime = 0; 327 1.96 dholland dom->dom_losttime = 0; 328 1.96 dholland dom->dom_backofftime = 10; 329 1.80 dholland dom->dom_lockfd = -1; 330 1.96 dholland dom->dom_state = DOM_NEW; 331 1.80 dholland dom->dom_xid = unique_xid(dom); 332 1.84 dholland dom->dom_been_ypset = 0; 333 1.84 dholland dom->dom_serversfile = NULL; 334 1.80 dholland 335 1.82 dholland /* 336 1.82 dholland * Per traditional ypbind(8) semantics, if a ypservers 337 1.82 dholland * file does not exist, we revert to broadcast mode. 338 1.82 dholland * 339 1.82 dholland * The sysadmin can force broadcast mode by passing the 340 1.82 dholland * -broadcast flag. There is currently no way to fail and 341 1.82 dholland * reject domains for which there is no ypservers file. 342 1.82 dholland */ 343 1.82 dholland dom->dom_ypbindmode = default_ypbindmode; 344 1.82 dholland if (dom->dom_ypbindmode == YPBIND_DIRECT) { 345 1.87 dholland pathname = ypservers_filename(dom->dom_name); 346 1.82 dholland if (stat(pathname, &st) < 0) { 347 1.82 dholland /* XXX syslog a warning here? */ 348 1.82 dholland DPRINTF("%s does not exist, defaulting to broadcast\n", 349 1.82 dholland pathname); 350 1.82 dholland dom->dom_ypbindmode = YPBIND_BROADCAST; 351 1.82 dholland } 352 1.82 dholland } 353 1.82 dholland 354 1.80 dholland /* add to global list */ 355 1.80 dholland dom->dom_next = domains; 356 1.80 dholland domains = dom; 357 1.80 dholland 358 1.78 dholland return dom; 359 1.24 christos } 360 1.24 christos 361 1.68 dholland //////////////////////////////////////////////////////////// 362 1.68 dholland // locks 363 1.68 dholland 364 1.95 dholland /* 365 1.95 dholland * Open a new binding file. Does not write the contents out; the 366 1.95 dholland * caller (there's only one) does that. 367 1.95 dholland */ 368 1.24 christos static int 369 1.78 dholland makelock(struct domain *dom) 370 1.24 christos { 371 1.24 christos int fd; 372 1.24 christos char path[MAXPATHLEN]; 373 1.20 cgd 374 1.36 mrg (void)snprintf(path, sizeof(path), "%s/%s.%ld", BINDINGDIR, 375 1.79 dholland dom->dom_name, dom->dom_vers); 376 1.24 christos 377 1.73 dholland fd = open_locked(path, O_CREAT|O_RDWR|O_TRUNC, 0644); 378 1.73 dholland if (fd == -1) { 379 1.36 mrg (void)mkdir(BINDINGDIR, 0755); 380 1.73 dholland fd = open_locked(path, O_CREAT|O_RDWR|O_TRUNC, 0644); 381 1.73 dholland if (fd == -1) { 382 1.24 christos return -1; 383 1.73 dholland } 384 1.24 christos } 385 1.24 christos 386 1.24 christos return fd; 387 1.24 christos } 388 1.24 christos 389 1.95 dholland /* 390 1.95 dholland * Remove a binding file. 391 1.95 dholland */ 392 1.24 christos static void 393 1.78 dholland removelock(struct domain *dom) 394 1.24 christos { 395 1.24 christos char path[MAXPATHLEN]; 396 1.24 christos 397 1.36 mrg (void)snprintf(path, sizeof(path), "%s/%s.%ld", 398 1.79 dholland BINDINGDIR, dom->dom_name, dom->dom_vers); 399 1.36 mrg (void)unlink(path); 400 1.24 christos } 401 1.24 christos 402 1.59 chuck /* 403 1.95 dholland * purge_bindingdir: remove old binding files (i.e. "rm *.[0-9]" in BINDINGDIR) 404 1.95 dholland * 405 1.95 dholland * The local YP functions [e.g. yp_master()] will fail without even 406 1.95 dholland * talking to ypbind if there is a stale (non-flock'd) binding file 407 1.95 dholland * present. 408 1.59 chuck * 409 1.95 dholland * We have to remove all binding files in BINDINGDIR, not just the one 410 1.95 dholland * for the default domain. 411 1.59 chuck */ 412 1.59 chuck static int 413 1.60 christos purge_bindingdir(const char *dirpath) 414 1.61 skrll { 415 1.59 chuck DIR *dirp; 416 1.59 chuck int unlinkedfiles, l; 417 1.59 chuck struct dirent *dp; 418 1.59 chuck char pathname[MAXPATHLEN]; 419 1.59 chuck 420 1.59 chuck if ((dirp = opendir(dirpath)) == NULL) 421 1.59 chuck return(-1); /* at this point, shouldn't ever happen */ 422 1.59 chuck 423 1.59 chuck do { 424 1.59 chuck unlinkedfiles = 0; 425 1.59 chuck while ((dp = readdir(dirp)) != NULL) { 426 1.59 chuck l = dp->d_namlen; 427 1.59 chuck /* 'rm *.[0-9]' */ 428 1.59 chuck if (l > 2 && dp->d_name[l-2] == '.' && 429 1.59 chuck dp->d_name[l-1] >= '0' && dp->d_name[l-1] <= '9') { 430 1.59 chuck (void)snprintf(pathname, sizeof(pathname), 431 1.59 chuck "%s/%s", dirpath, dp->d_name); 432 1.59 chuck if (unlink(pathname) < 0 && errno != ENOENT) 433 1.59 chuck return(-1); 434 1.59 chuck unlinkedfiles++; 435 1.59 chuck } 436 1.59 chuck } 437 1.59 chuck 438 1.59 chuck /* rescan dir if we removed it */ 439 1.59 chuck if (unlinkedfiles) 440 1.59 chuck rewinddir(dirp); 441 1.59 chuck 442 1.59 chuck } while (unlinkedfiles); 443 1.59 chuck 444 1.59 chuck closedir(dirp); 445 1.59 chuck return(0); 446 1.59 chuck } 447 1.59 chuck 448 1.68 dholland //////////////////////////////////////////////////////////// 449 1.68 dholland // sunrpc twaddle 450 1.68 dholland 451 1.68 dholland /* 452 1.92 dholland * Check if the info coming in is (at least somewhat) valid. 453 1.92 dholland */ 454 1.92 dholland static int 455 1.92 dholland rpc_is_valid_response(char *name, struct sockaddr_in *addr) 456 1.92 dholland { 457 1.92 dholland if (name == NULL) { 458 1.92 dholland return 0; 459 1.92 dholland } 460 1.92 dholland 461 1.92 dholland if (_yp_invalid_domain(name)) { 462 1.92 dholland return 0; 463 1.92 dholland } 464 1.92 dholland 465 1.92 dholland /* don't support insecure servers by default */ 466 1.92 dholland if (!insecure && ntohs(addr->sin_port) >= IPPORT_RESERVED) { 467 1.92 dholland return 0; 468 1.92 dholland } 469 1.92 dholland 470 1.92 dholland return 1; 471 1.92 dholland } 472 1.92 dholland 473 1.92 dholland /* 474 1.95 dholland * Take note of the fact that we've received a reply from a ypserver. 475 1.95 dholland * Or, in the case of being ypset, that we've been ypset, which 476 1.95 dholland * functions much the same. 477 1.95 dholland * 478 1.95 dholland * Note that FORCE is set if and only if IS_YPSET is set. 479 1.95 dholland * 480 1.95 dholland * This function has also for the past 20+ years carried the annotation 481 1.95 dholland * 482 1.95 dholland * LOOPBACK IS MORE IMPORTANT: PUT IN HACK 483 1.95 dholland * 484 1.95 dholland * whose meaning isn't entirely clear. 485 1.68 dholland */ 486 1.99 ginsbach static int 487 1.83 dholland rpc_received(char *dom_name, struct sockaddr_in *raddrp, int force, 488 1.83 dholland int is_ypset) 489 1.68 dholland { 490 1.78 dholland struct domain *dom; 491 1.68 dholland struct iovec iov[2]; 492 1.68 dholland struct ypbind_resp ybr; 493 1.72 dholland ssize_t result; 494 1.68 dholland int fd; 495 1.68 dholland 496 1.68 dholland DPRINTF("returned from %s about %s\n", 497 1.78 dholland inet_ntoa(raddrp->sin_addr), dom_name); 498 1.68 dholland 499 1.95 dholland /* validate some stuff */ 500 1.92 dholland if (!rpc_is_valid_response(dom_name, raddrp)) { 501 1.99 ginsbach return 0; 502 1.92 dholland } 503 1.68 dholland 504 1.95 dholland /* look for the domain */ 505 1.78 dholland for (dom = domains; dom != NULL; dom = dom->dom_next) 506 1.79 dholland if (!strcmp(dom->dom_name, dom_name)) 507 1.68 dholland break; 508 1.68 dholland 509 1.95 dholland /* if not found, create it, but only if FORCE; otherwise ignore */ 510 1.78 dholland if (dom == NULL) { 511 1.68 dholland if (force == 0) 512 1.99 ginsbach return 0; 513 1.78 dholland dom = domain_create(dom_name); 514 1.99 ginsbach if (dom == NULL) 515 1.99 ginsbach return 0; 516 1.68 dholland } 517 1.68 dholland 518 1.95 dholland /* the domain needs to know if it's been explicitly ypset */ 519 1.83 dholland if (is_ypset) { 520 1.83 dholland dom->dom_been_ypset = 1; 521 1.83 dholland } 522 1.83 dholland 523 1.95 dholland /* 524 1.96 dholland * If the domain is alive and we aren't being called by ypset, 525 1.96 dholland * we shouldn't be getting a response at all. Log it, as it 526 1.96 dholland * might be hostile. 527 1.96 dholland */ 528 1.96 dholland if (dom->dom_state == DOM_ALIVE && force == 0) { 529 1.96 dholland if (!memcmp(&dom->dom_server_addr, raddrp, 530 1.96 dholland sizeof(dom->dom_server_addr))) { 531 1.96 dholland yp_log(LOG_WARNING, 532 1.96 dholland "Unexpected reply from server %s for domain %s", 533 1.96 dholland inet_ntoa(dom->dom_server_addr.sin_addr), 534 1.96 dholland dom->dom_name); 535 1.96 dholland } else { 536 1.96 dholland yp_log(LOG_WARNING, 537 1.96 dholland "Falsified reply from %s for domain %s", 538 1.96 dholland inet_ntoa(dom->dom_server_addr.sin_addr), 539 1.96 dholland dom->dom_name); 540 1.96 dholland } 541 1.99 ginsbach return 0; 542 1.96 dholland } 543 1.96 dholland 544 1.96 dholland /* 545 1.96 dholland * If we're expected a ping response, and we've got it 546 1.96 dholland * (meaning we aren't being called by ypset), we don't need to 547 1.96 dholland * do anything. 548 1.95 dholland */ 549 1.96 dholland if (dom->dom_state == DOM_PINGING && force == 0) { 550 1.95 dholland /* 551 1.95 dholland * If the reply came from the server we expect, set 552 1.96 dholland * dom_state back to ALIVE and ping again in 60 553 1.96 dholland * seconds. 554 1.95 dholland * 555 1.96 dholland * If it came from somewhere else, log it. 556 1.95 dholland */ 557 1.78 dholland if (!memcmp(&dom->dom_server_addr, raddrp, 558 1.78 dholland sizeof(dom->dom_server_addr))) { 559 1.96 dholland dom->dom_state = DOM_ALIVE; 560 1.68 dholland /* recheck binding in 60 sec */ 561 1.78 dholland dom->dom_checktime = time(NULL) + 60; 562 1.96 dholland } else { 563 1.96 dholland yp_log(LOG_WARNING, 564 1.96 dholland "Falsified reply from %s for domain %s", 565 1.96 dholland inet_ntoa(dom->dom_server_addr.sin_addr), 566 1.96 dholland dom->dom_name); 567 1.68 dholland } 568 1.99 ginsbach return 0; 569 1.68 dholland } 570 1.95 dholland 571 1.96 dholland #ifdef HEURISTIC 572 1.96 dholland /* 573 1.96 dholland * If transitioning to the alive state from a non-alive state, 574 1.96 dholland * clear dom_asktime. This will help prevent any requests that 575 1.96 dholland * are still coming in from triggering unnecessary pings via 576 1.96 dholland * the HEURISTIC code. 577 1.96 dholland * 578 1.96 dholland * XXX: this may not be an adequate measure; we may need to 579 1.96 dholland * keep more state so we can disable the HEURISTIC code for 580 1.96 dholland * the first few seconds after rebinding. 581 1.96 dholland */ 582 1.96 dholland if (dom->dom_state == DOM_NEW || 583 1.96 dholland dom->dom_state == DOM_LOST || 584 1.96 dholland dom->dom_state == DOM_DEAD) { 585 1.96 dholland dom->dom_asktime = 0; 586 1.96 dholland } 587 1.96 dholland #endif 588 1.96 dholland 589 1.95 dholland /* 590 1.95 dholland * Take the address we got the message from (or in the case of 591 1.95 dholland * ypset, the explicit address we were given) as the server 592 1.95 dholland * address for this domain, mark the domain alive, and we'll 593 1.95 dholland * check it again in 60 seconds. 594 1.95 dholland * 595 1.95 dholland * XXX: it looks like if we get a random unsolicited reply 596 1.95 dholland * from somewhere, we'll silently switch to that server 597 1.95 dholland * address, regardless of merit. 598 1.95 dholland * 599 1.95 dholland * 1. If we have a foo.ypservers file the address should be 600 1.95 dholland * checked against it and rejected if it's not one of the 601 1.95 dholland * addresses of one of the listed hostnames. Note that it 602 1.95 dholland * might not be the same address we sent to; even fairly smart 603 1.95 dholland * UDP daemons don't always handle multihomed hosts correctly 604 1.95 dholland * and we can't expect sunrpc code to do anything intelligent 605 1.95 dholland * at all. 606 1.95 dholland * 607 1.95 dholland * 2. If we're in broadcast mode the address should be 608 1.95 dholland * checked against the local addresses and netmasks so we 609 1.95 dholland * don't accept responses from Mars. 610 1.95 dholland * 611 1.95 dholland * 2a. If we're in broadcast mode and we've been ypset, we 612 1.95 dholland * should not accept anything else until we drop the ypset 613 1.95 dholland * state for not responding. 614 1.95 dholland * 615 1.95 dholland * 3. Either way we should not accept a response from an 616 1.95 dholland * arbitrary host unless we don't currently have a binding. 617 1.96 dholland * (This is now fixed above.) 618 1.95 dholland * 619 1.95 dholland * Note that for a random unsolicited reply to work it has to 620 1.95 dholland * carry the XID of one of the domains we know about; but 621 1.95 dholland * those values are predictable. 622 1.95 dholland */ 623 1.78 dholland (void)memcpy(&dom->dom_server_addr, raddrp, 624 1.78 dholland sizeof(dom->dom_server_addr)); 625 1.68 dholland /* recheck binding in 60 seconds */ 626 1.78 dholland dom->dom_checktime = time(NULL) + 60; 627 1.96 dholland dom->dom_state = DOM_ALIVE; 628 1.96 dholland 629 1.96 dholland /* Clear the dead/backoff state. */ 630 1.96 dholland dom->dom_losttime = 0; 631 1.96 dholland dom->dom_backofftime = 10; 632 1.68 dholland 633 1.98 dholland if (is_ypset == 0) { 634 1.98 dholland yp_log(LOG_NOTICE, "Domain %s is alive; server %s", 635 1.98 dholland dom->dom_name, 636 1.98 dholland inet_ntoa(dom->dom_server_addr.sin_addr)); 637 1.98 dholland } 638 1.98 dholland 639 1.95 dholland /* 640 1.95 dholland * Generate a new binding file. If this fails, forget about it. 641 1.95 dholland * (But we keep the binding and we'll report it to anyone who 642 1.95 dholland * asks via the ypbind service.) XXX: this will interact badly, 643 1.95 dholland * maybe very badly, with the code in HEURISTIC. 644 1.95 dholland * 645 1.95 dholland * Note that makelock() doesn't log on failure. 646 1.95 dholland */ 647 1.95 dholland 648 1.78 dholland if (dom->dom_lockfd != -1) 649 1.78 dholland (void)close(dom->dom_lockfd); 650 1.68 dholland 651 1.78 dholland if ((fd = makelock(dom)) == -1) 652 1.99 ginsbach return 0; 653 1.68 dholland 654 1.78 dholland dom->dom_lockfd = fd; 655 1.68 dholland 656 1.68 dholland iov[0].iov_base = &(udptransp->xp_port); 657 1.68 dholland iov[0].iov_len = sizeof udptransp->xp_port; 658 1.68 dholland iov[1].iov_base = &ybr; 659 1.68 dholland iov[1].iov_len = sizeof ybr; 660 1.68 dholland 661 1.68 dholland (void)memset(&ybr, 0, sizeof ybr); 662 1.68 dholland ybr.ypbind_status = YPBIND_SUCC_VAL; 663 1.68 dholland ybr.ypbind_respbody.ypbind_bindinfo.ypbind_binding_addr = 664 1.68 dholland raddrp->sin_addr; 665 1.68 dholland ybr.ypbind_respbody.ypbind_bindinfo.ypbind_binding_port = 666 1.68 dholland raddrp->sin_port; 667 1.68 dholland 668 1.78 dholland result = writev(dom->dom_lockfd, iov, 2); 669 1.72 dholland if (result < 0 || (size_t)result != iov[0].iov_len + iov[1].iov_len) { 670 1.72 dholland if (result < 0) 671 1.72 dholland yp_log(LOG_WARNING, "writev: %s", strerror(errno)); 672 1.72 dholland else 673 1.72 dholland yp_log(LOG_WARNING, "writev: short count"); 674 1.78 dholland (void)close(dom->dom_lockfd); 675 1.78 dholland removelock(dom); 676 1.78 dholland dom->dom_lockfd = -1; 677 1.99 ginsbach return 0; 678 1.68 dholland } 679 1.99 ginsbach 680 1.99 ginsbach return 1; 681 1.68 dholland } 682 1.68 dholland 683 1.95 dholland /* 684 1.95 dholland * The NULL call: do nothing. This is obliged to exist because of 685 1.95 dholland * sunrpc silliness. 686 1.95 dholland */ 687 1.24 christos static void * 688 1.57 christos /*ARGSUSED*/ 689 1.47 wiz ypbindproc_null_2(SVCXPRT *transp, void *argp) 690 1.1 deraadt { 691 1.1 deraadt static char res; 692 1.1 deraadt 693 1.65 dholland DPRINTF("ypbindproc_null_2\n"); 694 1.36 mrg (void)memset(&res, 0, sizeof(res)); 695 1.1 deraadt return (void *)&res; 696 1.1 deraadt } 697 1.1 deraadt 698 1.95 dholland /* 699 1.95 dholland * The DOMAIN call: look up the ypserver for a specified domain. 700 1.95 dholland */ 701 1.24 christos static void * 702 1.57 christos /*ARGSUSED*/ 703 1.47 wiz ypbindproc_domain_2(SVCXPRT *transp, void *argp) 704 1.1 deraadt { 705 1.1 deraadt static struct ypbind_resp res; 706 1.78 dholland struct domain *dom; 707 1.24 christos char *arg = *(char **) argp; 708 1.9 deraadt time_t now; 709 1.30 lukem int count; 710 1.1 deraadt 711 1.65 dholland DPRINTF("ypbindproc_domain_2 %s\n", arg); 712 1.95 dholland 713 1.100 ginsbach (void)memset(&res, 0, sizeof res); 714 1.100 ginsbach res.ypbind_status = YPBIND_FAIL_VAL; 715 1.100 ginsbach 716 1.95 dholland /* Reject invalid domains. */ 717 1.99 ginsbach if (_yp_invalid_domain(arg)) { 718 1.99 ginsbach res.ypbind_respbody.ypbind_error = YPBIND_ERR_NOSERV; 719 1.99 ginsbach return &res; 720 1.99 ginsbach } 721 1.30 lukem 722 1.95 dholland /* 723 1.95 dholland * Look for the domain. XXX: Behave erratically if we have 724 1.95 dholland * more than 100 domains. The intent here is to avoid allowing 725 1.95 dholland * arbitrary incoming requests to create more than 100 726 1.95 dholland * domains; but this logic means that if we legitimately have 727 1.95 dholland * more than 100 (e.g. via ypset) we'll only actually bind the 728 1.95 dholland * first 100 and the rest will fail. The test on 'count' should 729 1.95 dholland * be moved further down. 730 1.95 dholland */ 731 1.78 dholland for (count = 0, dom = domains; 732 1.78 dholland dom != NULL; 733 1.78 dholland dom = dom->dom_next, count++) { 734 1.99 ginsbach if (count > 100) { 735 1.99 ginsbach res.ypbind_respbody.ypbind_error = YPBIND_ERR_RESC; 736 1.99 ginsbach return &res; /* prevent denial of service */ 737 1.99 ginsbach } 738 1.79 dholland if (!strcmp(dom->dom_name, arg)) 739 1.1 deraadt break; 740 1.30 lukem } 741 1.1 deraadt 742 1.95 dholland /* 743 1.95 dholland * If the domain doesn't exist, create it, then fail the call 744 1.95 dholland * because we have no information yet. 745 1.95 dholland * 746 1.95 dholland * Set "check" so that checkwork() will run and look for a 747 1.95 dholland * server. 748 1.95 dholland * 749 1.95 dholland * XXX: like during startup there's a spurious call to 750 1.95 dholland * removelock() after domain_create(). 751 1.95 dholland */ 752 1.78 dholland if (dom == NULL) { 753 1.78 dholland dom = domain_create(arg); 754 1.99 ginsbach if (dom != NULL) { 755 1.99 ginsbach removelock(dom); 756 1.99 ginsbach check++; 757 1.99 ginsbach DPRINTF("unknown domain %s\n", arg); 758 1.99 ginsbach res.ypbind_respbody.ypbind_error = YPBIND_ERR_NOSERV; 759 1.99 ginsbach } else { 760 1.99 ginsbach res.ypbind_respbody.ypbind_error = YPBIND_ERR_RESC; 761 1.99 ginsbach } 762 1.99 ginsbach return &res; 763 1.1 deraadt } 764 1.1 deraadt 765 1.96 dholland if (dom->dom_state == DOM_NEW) { 766 1.96 dholland DPRINTF("new domain %s\n", arg); 767 1.99 ginsbach res.ypbind_respbody.ypbind_error = YPBIND_ERR_NOSERV; 768 1.99 ginsbach return &res; 769 1.24 christos } 770 1.1 deraadt 771 1.9 deraadt #ifdef HEURISTIC 772 1.95 dholland /* 773 1.95 dholland * Keep track of the last time we were explicitly asked about 774 1.95 dholland * this domain. If it happens a lot, force a ping. This works 775 1.95 dholland * (or "works") because we only get asked specifically when 776 1.95 dholland * things aren't going; otherwise the client code in libc and 777 1.95 dholland * elsewhere uses the binding file. 778 1.95 dholland * 779 1.95 dholland * Note: HEURISTIC is enabled by default. 780 1.96 dholland * 781 1.96 dholland * dholland 20140609: I think this is part of the mechanism 782 1.96 dholland * that causes ypbind to spam. I'm changing this logic so it 783 1.96 dholland * only triggers when the state is DOM_ALIVE: if the domain 784 1.96 dholland * is new, lost, or dead we shouldn't send more requests than 785 1.96 dholland * the ones already scheduled, and if we're already in the 786 1.96 dholland * middle of pinging there's no point doing it again. 787 1.95 dholland */ 788 1.57 christos (void)time(&now); 789 1.96 dholland if (dom->dom_state == DOM_ALIVE && now < dom->dom_asktime + 5) { 790 1.1 deraadt /* 791 1.9 deraadt * Hmm. More than 2 requests in 5 seconds have indicated 792 1.9 deraadt * that my binding is possibly incorrect. 793 1.9 deraadt * Ok, do an immediate poll of the server. 794 1.1 deraadt */ 795 1.78 dholland if (dom->dom_checktime >= now) { 796 1.9 deraadt /* don't flood it */ 797 1.78 dholland dom->dom_checktime = 0; 798 1.9 deraadt check++; 799 1.9 deraadt } 800 1.1 deraadt } 801 1.78 dholland dom->dom_asktime = now; 802 1.1 deraadt #endif 803 1.1 deraadt 804 1.1 deraadt res.ypbind_status = YPBIND_SUCC_VAL; 805 1.1 deraadt res.ypbind_respbody.ypbind_bindinfo.ypbind_binding_addr.s_addr = 806 1.78 dholland dom->dom_server_addr.sin_addr.s_addr; 807 1.1 deraadt res.ypbind_respbody.ypbind_bindinfo.ypbind_binding_port = 808 1.78 dholland dom->dom_server_addr.sin_port; 809 1.79 dholland DPRINTF("domain %s at %s/%d\n", dom->dom_name, 810 1.78 dholland inet_ntoa(dom->dom_server_addr.sin_addr), 811 1.78 dholland ntohs(dom->dom_server_addr.sin_port)); 812 1.1 deraadt return &res; 813 1.1 deraadt } 814 1.1 deraadt 815 1.95 dholland /* 816 1.95 dholland * The SETDOM call: ypset. 817 1.95 dholland * 818 1.95 dholland * Unless -ypsetme was given on the command line, this is rejected; 819 1.95 dholland * even then it's only allowed from localhost unless -ypset was 820 1.95 dholland * given on the command line. 821 1.95 dholland * 822 1.95 dholland * Allowing anyone anywhere to ypset you (and therefore provide your 823 1.95 dholland * password file and such) is a horrible thing and it isn't clear to 824 1.95 dholland * me why this functionality even exists. 825 1.95 dholland * 826 1.95 dholland * ypset from localhost has some but limited utility. 827 1.95 dholland */ 828 1.24 christos static void * 829 1.47 wiz ypbindproc_setdom_2(SVCXPRT *transp, void *argp) 830 1.1 deraadt { 831 1.24 christos struct ypbind_setdom *sd = argp; 832 1.1 deraadt struct sockaddr_in *fromsin, bindsin; 833 1.13 deraadt static bool_t res; 834 1.1 deraadt 835 1.36 mrg (void)memset(&res, 0, sizeof(res)); 836 1.1 deraadt fromsin = svc_getcaller(transp); 837 1.88 dholland DPRINTF("ypbindproc_setdom_2 from %s\n", inet_ntoa(fromsin->sin_addr)); 838 1.1 deraadt 839 1.95 dholland /* 840 1.95 dholland * Reject unless enabled. 841 1.95 dholland */ 842 1.95 dholland 843 1.81 dholland if (allow_any_ypset) { 844 1.81 dholland /* nothing */ 845 1.81 dholland } else if (allow_local_ypset) { 846 1.24 christos if (fromsin->sin_addr.s_addr != htonl(INADDR_LOOPBACK)) { 847 1.81 dholland DPRINTF("ypset denied from %s\n", 848 1.65 dholland inet_ntoa(fromsin->sin_addr)); 849 1.24 christos return NULL; 850 1.24 christos } 851 1.81 dholland } else { 852 1.65 dholland DPRINTF("ypset denied\n"); 853 1.24 christos return NULL; 854 1.1 deraadt } 855 1.4 deraadt 856 1.95 dholland /* Make a "security" check. */ 857 1.24 christos if (ntohs(fromsin->sin_port) >= IPPORT_RESERVED) { 858 1.65 dholland DPRINTF("ypset from unprivileged port denied\n"); 859 1.13 deraadt return &res; 860 1.24 christos } 861 1.1 deraadt 862 1.95 dholland /* Ignore requests we don't understand. */ 863 1.24 christos if (sd->ypsetdom_vers != YPVERS) { 864 1.65 dholland DPRINTF("ypset with wrong version denied\n"); 865 1.13 deraadt return &res; 866 1.24 christos } 867 1.1 deraadt 868 1.95 dholland /* 869 1.95 dholland * Fetch the arguments out of the xdr-decoded blob and call 870 1.95 dholland * rpc_received(), setting FORCE so that the domain will be 871 1.95 dholland * created if we don't already know about it, and also saying 872 1.95 dholland * that it's actually a ypset. 873 1.95 dholland * 874 1.95 dholland * Effectively we're telilng rpc_received() that we got an 875 1.95 dholland * RPC response from the server specified by ypset. 876 1.95 dholland */ 877 1.36 mrg (void)memset(&bindsin, 0, sizeof bindsin); 878 1.1 deraadt bindsin.sin_family = AF_INET; 879 1.17 mycroft bindsin.sin_len = sizeof(bindsin); 880 1.24 christos bindsin.sin_addr = sd->ypsetdom_addr; 881 1.24 christos bindsin.sin_port = sd->ypsetdom_port; 882 1.99 ginsbach if (rpc_received(sd->ypsetdom_domain, &bindsin, 1, 1)) { 883 1.99 ginsbach DPRINTF("ypset to %s for domain %s succeeded\n", 884 1.99 ginsbach inet_ntoa(bindsin.sin_addr), sd->ypsetdom_domain); 885 1.99 ginsbach res = 1; 886 1.99 ginsbach } 887 1.24 christos 888 1.13 deraadt return &res; 889 1.1 deraadt } 890 1.1 deraadt 891 1.95 dholland /* 892 1.95 dholland * Dispatcher for the ypbind service. 893 1.95 dholland * 894 1.95 dholland * There are three calls: NULL, which does nothing, DOMAIN, which 895 1.95 dholland * gets the binding for a particular domain, and SETDOM, which 896 1.95 dholland * does ypset. 897 1.95 dholland */ 898 1.1 deraadt static void 899 1.47 wiz ypbindprog_2(struct svc_req *rqstp, register SVCXPRT *transp) 900 1.1 deraadt { 901 1.1 deraadt union { 902 1.22 thorpej char ypbindproc_domain_2_arg[YPMAXDOMAIN + 1]; 903 1.1 deraadt struct ypbind_setdom ypbindproc_setdom_2_arg; 904 1.56 tron void *alignment; 905 1.1 deraadt } argument; 906 1.1 deraadt struct authunix_parms *creds; 907 1.1 deraadt char *result; 908 1.24 christos xdrproc_t xdr_argument, xdr_result; 909 1.47 wiz void *(*local)(SVCXPRT *, void *); 910 1.1 deraadt 911 1.1 deraadt switch (rqstp->rq_proc) { 912 1.1 deraadt case YPBINDPROC_NULL: 913 1.90 plunky xdr_argument = (xdrproc_t)xdr_void; 914 1.90 plunky xdr_result = (xdrproc_t)xdr_void; 915 1.24 christos local = ypbindproc_null_2; 916 1.1 deraadt break; 917 1.1 deraadt 918 1.1 deraadt case YPBINDPROC_DOMAIN: 919 1.90 plunky xdr_argument = (xdrproc_t)xdr_ypdomain_wrap_string; 920 1.90 plunky xdr_result = (xdrproc_t)xdr_ypbind_resp; 921 1.24 christos local = ypbindproc_domain_2; 922 1.1 deraadt break; 923 1.1 deraadt 924 1.1 deraadt case YPBINDPROC_SETDOM: 925 1.17 mycroft switch (rqstp->rq_cred.oa_flavor) { 926 1.1 deraadt case AUTH_UNIX: 927 1.1 deraadt creds = (struct authunix_parms *)rqstp->rq_clntcred; 928 1.17 mycroft if (creds->aup_uid != 0) { 929 1.1 deraadt svcerr_auth(transp, AUTH_BADCRED); 930 1.1 deraadt return; 931 1.1 deraadt } 932 1.1 deraadt break; 933 1.1 deraadt default: 934 1.1 deraadt svcerr_auth(transp, AUTH_TOOWEAK); 935 1.1 deraadt return; 936 1.1 deraadt } 937 1.1 deraadt 938 1.90 plunky xdr_argument = (xdrproc_t)xdr_ypbind_setdom; 939 1.90 plunky xdr_result = (xdrproc_t)xdr_void; 940 1.24 christos local = ypbindproc_setdom_2; 941 1.1 deraadt break; 942 1.1 deraadt 943 1.1 deraadt default: 944 1.1 deraadt svcerr_noproc(transp); 945 1.1 deraadt return; 946 1.1 deraadt } 947 1.36 mrg (void)memset(&argument, 0, sizeof(argument)); 948 1.57 christos if (!svc_getargs(transp, xdr_argument, (caddr_t)(void *)&argument)) { 949 1.1 deraadt svcerr_decode(transp); 950 1.1 deraadt return; 951 1.1 deraadt } 952 1.24 christos result = (*local)(transp, &argument); 953 1.1 deraadt if (result != NULL && !svc_sendreply(transp, xdr_result, result)) { 954 1.1 deraadt svcerr_systemerr(transp); 955 1.1 deraadt } 956 1.1 deraadt return; 957 1.1 deraadt } 958 1.1 deraadt 959 1.95 dholland /* 960 1.95 dholland * Set up sunrpc stuff. 961 1.95 dholland * 962 1.95 dholland * This sets up the ypbind service (both TCP and UDP) and also opens 963 1.95 dholland * the sockets we use for talking to ypservers. 964 1.95 dholland */ 965 1.74 dholland static void 966 1.74 dholland sunrpc_setup(void) 967 1.74 dholland { 968 1.74 dholland int one; 969 1.74 dholland 970 1.74 dholland (void)pmap_unset(YPBINDPROG, YPBINDVERS); 971 1.74 dholland 972 1.74 dholland udptransp = svcudp_create(RPC_ANYSOCK); 973 1.74 dholland if (udptransp == NULL) 974 1.74 dholland errx(1, "Cannot create udp service."); 975 1.74 dholland 976 1.74 dholland if (!svc_register(udptransp, YPBINDPROG, YPBINDVERS, ypbindprog_2, 977 1.74 dholland IPPROTO_UDP)) 978 1.74 dholland errx(1, "Unable to register (YPBINDPROG, YPBINDVERS, udp)."); 979 1.74 dholland 980 1.74 dholland tcptransp = svctcp_create(RPC_ANYSOCK, 0, 0); 981 1.74 dholland if (tcptransp == NULL) 982 1.74 dholland errx(1, "Cannot create tcp service."); 983 1.74 dholland 984 1.74 dholland if (!svc_register(tcptransp, YPBINDPROG, YPBINDVERS, ypbindprog_2, 985 1.74 dholland IPPROTO_TCP)) 986 1.74 dholland errx(1, "Unable to register (YPBINDPROG, YPBINDVERS, tcp)."); 987 1.74 dholland 988 1.74 dholland /* XXX use SOCK_STREAM for direct queries? */ 989 1.74 dholland if ((rpcsock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) == -1) 990 1.74 dholland err(1, "rpc socket"); 991 1.74 dholland if ((pingsock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) == -1) 992 1.74 dholland err(1, "ping socket"); 993 1.74 dholland 994 1.74 dholland (void)fcntl(rpcsock, F_SETFL, fcntl(rpcsock, F_GETFL, 0) | FNDELAY); 995 1.74 dholland (void)fcntl(pingsock, F_SETFL, fcntl(pingsock, F_GETFL, 0) | FNDELAY); 996 1.74 dholland 997 1.74 dholland one = 1; 998 1.74 dholland (void)setsockopt(rpcsock, SOL_SOCKET, SO_BROADCAST, &one, 999 1.74 dholland (socklen_t)sizeof(one)); 1000 1.74 dholland rmtca.prog = YPPROG; 1001 1.74 dholland rmtca.vers = YPVERS; 1002 1.74 dholland rmtca.proc = YPPROC_DOMAIN_NONACK; 1003 1.74 dholland rmtca.xdr_args = NULL; /* set at call time */ 1004 1.74 dholland rmtca.args_ptr = NULL; /* set at call time */ 1005 1.74 dholland rmtcr.port_ptr = &rmtcr_port; 1006 1.90 plunky rmtcr.xdr_results = (xdrproc_t)xdr_bool; 1007 1.74 dholland rmtcr.results_ptr = (caddr_t)(void *)&rmtcr_outval; 1008 1.74 dholland } 1009 1.74 dholland 1010 1.68 dholland //////////////////////////////////////////////////////////// 1011 1.68 dholland // operational logic 1012 1.68 dholland 1013 1.95 dholland /* 1014 1.95 dholland * Broadcast an RPC packet to hopefully contact some servers for a 1015 1.95 dholland * domain. 1016 1.95 dholland */ 1017 1.68 dholland static int 1018 1.68 dholland broadcast(char *buf, int outlen) 1019 1.1 deraadt { 1020 1.68 dholland struct ifaddrs *ifap, *ifa; 1021 1.68 dholland struct sockaddr_in bindsin; 1022 1.68 dholland struct in_addr in; 1023 1.68 dholland 1024 1.68 dholland (void)memset(&bindsin, 0, sizeof bindsin); 1025 1.68 dholland bindsin.sin_family = AF_INET; 1026 1.68 dholland bindsin.sin_len = sizeof(bindsin); 1027 1.68 dholland bindsin.sin_port = htons(PMAPPORT); 1028 1.1 deraadt 1029 1.68 dholland if (getifaddrs(&ifap) != 0) { 1030 1.72 dholland yp_log(LOG_WARNING, "broadcast: getifaddrs: %s", 1031 1.72 dholland strerror(errno)); 1032 1.68 dholland return (-1); 1033 1.68 dholland } 1034 1.68 dholland for (ifa = ifap; ifa; ifa = ifa->ifa_next) { 1035 1.68 dholland if (ifa->ifa_addr->sa_family != AF_INET) 1036 1.68 dholland continue; 1037 1.68 dholland if ((ifa->ifa_flags & IFF_UP) == 0) 1038 1.68 dholland continue; 1039 1.1 deraadt 1040 1.68 dholland switch (ifa->ifa_flags & (IFF_LOOPBACK | IFF_BROADCAST)) { 1041 1.68 dholland case IFF_BROADCAST: 1042 1.68 dholland if (!ifa->ifa_broadaddr) 1043 1.68 dholland continue; 1044 1.68 dholland if (ifa->ifa_broadaddr->sa_family != AF_INET) 1045 1.68 dholland continue; 1046 1.68 dholland in = ((struct sockaddr_in *)(void *)ifa->ifa_broadaddr)->sin_addr; 1047 1.68 dholland break; 1048 1.68 dholland case IFF_LOOPBACK: 1049 1.68 dholland in = ((struct sockaddr_in *)(void *)ifa->ifa_addr)->sin_addr; 1050 1.68 dholland break; 1051 1.68 dholland default: 1052 1.68 dholland continue; 1053 1.68 dholland } 1054 1.27 thorpej 1055 1.68 dholland bindsin.sin_addr = in; 1056 1.68 dholland DPRINTF("broadcast %x\n", bindsin.sin_addr.s_addr); 1057 1.68 dholland if (sendto(rpcsock, buf, outlen, 0, 1058 1.68 dholland (struct sockaddr *)(void *)&bindsin, 1059 1.68 dholland (socklen_t)bindsin.sin_len) == -1) 1060 1.72 dholland yp_log(LOG_WARNING, "broadcast: sendto: %s", 1061 1.72 dholland strerror(errno)); 1062 1.1 deraadt } 1063 1.68 dholland freeifaddrs(ifap); 1064 1.68 dholland return (0); 1065 1.68 dholland } 1066 1.1 deraadt 1067 1.95 dholland /* 1068 1.95 dholland * Send an RPC packet to all the configured (in /var/yp/foo.ypservers) 1069 1.95 dholland * servers for a domain. 1070 1.95 dholland * 1071 1.95 dholland * XXX: we should read and parse the file up front and reread it only 1072 1.95 dholland * if it changes. 1073 1.95 dholland */ 1074 1.68 dholland static int 1075 1.84 dholland direct(char *buf, int outlen, struct domain *dom) 1076 1.68 dholland { 1077 1.71 dholland const char *path; 1078 1.68 dholland char line[_POSIX2_LINE_MAX]; 1079 1.68 dholland char *p; 1080 1.68 dholland struct hostent *hp; 1081 1.68 dholland struct sockaddr_in bindsin; 1082 1.68 dholland int i, count = 0; 1083 1.37 bouyer 1084 1.84 dholland /* 1085 1.84 dholland * XXX what happens if someone's editor unlinks and replaces 1086 1.84 dholland * the servers file? 1087 1.84 dholland */ 1088 1.84 dholland 1089 1.84 dholland if (dom->dom_serversfile != NULL) { 1090 1.84 dholland rewind(dom->dom_serversfile); 1091 1.84 dholland } else { 1092 1.84 dholland path = ypservers_filename(dom->dom_name); 1093 1.84 dholland dom->dom_serversfile = fopen(path, "r"); 1094 1.84 dholland if (dom->dom_serversfile == NULL) { 1095 1.84 dholland /* 1096 1.84 dholland * XXX there should be a time restriction on 1097 1.84 dholland * this (and/or on trying the open) so we 1098 1.84 dholland * don't flood the log. Or should we fall back 1099 1.84 dholland * to broadcast mode? 1100 1.84 dholland */ 1101 1.84 dholland yp_log(LOG_ERR, "%s: %s", path, 1102 1.72 dholland strerror(errno)); 1103 1.84 dholland return -1; 1104 1.37 bouyer } 1105 1.27 thorpej } 1106 1.27 thorpej 1107 1.57 christos (void)memset(&bindsin, 0, sizeof bindsin); 1108 1.27 thorpej bindsin.sin_family = AF_INET; 1109 1.27 thorpej bindsin.sin_len = sizeof(bindsin); 1110 1.27 thorpej bindsin.sin_port = htons(PMAPPORT); 1111 1.27 thorpej 1112 1.84 dholland while (fgets(line, (int)sizeof(line), dom->dom_serversfile) != NULL) { 1113 1.27 thorpej /* skip lines that are too big */ 1114 1.27 thorpej p = strchr(line, '\n'); 1115 1.27 thorpej if (p == NULL) { 1116 1.27 thorpej int c; 1117 1.27 thorpej 1118 1.84 dholland while ((c = getc(dom->dom_serversfile)) != '\n' && c != EOF) 1119 1.27 thorpej ; 1120 1.27 thorpej continue; 1121 1.27 thorpej } 1122 1.27 thorpej *p = '\0'; 1123 1.27 thorpej p = line; 1124 1.53 dsl while (isspace((unsigned char)*p)) 1125 1.27 thorpej p++; 1126 1.27 thorpej if (*p == '#') 1127 1.27 thorpej continue; 1128 1.27 thorpej hp = gethostbyname(p); 1129 1.27 thorpej if (!hp) { 1130 1.45 lukem yp_log(LOG_WARNING, "%s: %s", p, hstrerror(h_errno)); 1131 1.27 thorpej continue; 1132 1.27 thorpej } 1133 1.27 thorpej /* step through all addresses in case first is unavailable */ 1134 1.27 thorpej for (i = 0; hp->h_addr_list[i]; i++) { 1135 1.57 christos (void)memcpy(&bindsin.sin_addr, hp->h_addr_list[0], 1136 1.27 thorpej hp->h_length); 1137 1.27 thorpej if (sendto(rpcsock, buf, outlen, 0, 1138 1.57 christos (struct sockaddr *)(void *)&bindsin, 1139 1.57 christos (socklen_t)sizeof(bindsin)) < 0) { 1140 1.72 dholland yp_log(LOG_WARNING, "direct: sendto: %s", 1141 1.72 dholland strerror(errno)); 1142 1.27 thorpej continue; 1143 1.27 thorpej } else 1144 1.27 thorpej count++; 1145 1.27 thorpej } 1146 1.27 thorpej } 1147 1.37 bouyer if (!count) { 1148 1.72 dholland yp_log(LOG_WARNING, "No contactable servers found in %s", 1149 1.84 dholland ypservers_filename(dom->dom_name)); 1150 1.37 bouyer return -1; 1151 1.37 bouyer } 1152 1.27 thorpej return 0; 1153 1.27 thorpej } 1154 1.27 thorpej 1155 1.95 dholland /* 1156 1.95 dholland * Send an RPC packet to the server that's been selected with ypset. 1157 1.95 dholland * (This is only used when in broadcast mode and when ypset is 1158 1.95 dholland * allowed.) 1159 1.95 dholland */ 1160 1.27 thorpej static int 1161 1.78 dholland direct_set(char *buf, int outlen, struct domain *dom) 1162 1.27 thorpej { 1163 1.27 thorpej struct sockaddr_in bindsin; 1164 1.27 thorpej char path[MAXPATHLEN]; 1165 1.27 thorpej struct iovec iov[2]; 1166 1.27 thorpej struct ypbind_resp ybr; 1167 1.27 thorpej SVCXPRT dummy_svc; 1168 1.57 christos int fd; 1169 1.57 christos ssize_t bytes; 1170 1.27 thorpej 1171 1.27 thorpej /* 1172 1.27 thorpej * Gack, we lose if binding file went away. We reset 1173 1.27 thorpej * "been_set" if this happens, otherwise we'll never 1174 1.27 thorpej * bind again. 1175 1.27 thorpej */ 1176 1.57 christos (void)snprintf(path, sizeof(path), "%s/%s.%ld", BINDINGDIR, 1177 1.79 dholland dom->dom_name, dom->dom_vers); 1178 1.27 thorpej 1179 1.73 dholland fd = open_locked(path, O_RDONLY, 0644); 1180 1.73 dholland if (fd == -1) { 1181 1.72 dholland yp_log(LOG_WARNING, "%s: %s", path, strerror(errno)); 1182 1.83 dholland dom->dom_been_ypset = 0; 1183 1.27 thorpej return -1; 1184 1.27 thorpej } 1185 1.27 thorpej 1186 1.27 thorpej /* Read the binding file... */ 1187 1.57 christos iov[0].iov_base = &(dummy_svc.xp_port); 1188 1.27 thorpej iov[0].iov_len = sizeof(dummy_svc.xp_port); 1189 1.57 christos iov[1].iov_base = &ybr; 1190 1.27 thorpej iov[1].iov_len = sizeof(ybr); 1191 1.27 thorpej bytes = readv(fd, iov, 2); 1192 1.27 thorpej (void)close(fd); 1193 1.72 dholland if (bytes <0 || (size_t)bytes != (iov[0].iov_len + iov[1].iov_len)) { 1194 1.27 thorpej /* Binding file corrupt? */ 1195 1.72 dholland if (bytes < 0) 1196 1.72 dholland yp_log(LOG_WARNING, "%s: %s", path, strerror(errno)); 1197 1.72 dholland else 1198 1.72 dholland yp_log(LOG_WARNING, "%s: short read", path); 1199 1.83 dholland dom->dom_been_ypset = 0; 1200 1.27 thorpej return -1; 1201 1.68 dholland } 1202 1.68 dholland 1203 1.68 dholland bindsin.sin_addr = 1204 1.68 dholland ybr.ypbind_respbody.ypbind_bindinfo.ypbind_binding_addr; 1205 1.68 dholland 1206 1.68 dholland if (sendto(rpcsock, buf, outlen, 0, 1207 1.68 dholland (struct sockaddr *)(void *)&bindsin, 1208 1.68 dholland (socklen_t)sizeof(bindsin)) < 0) { 1209 1.72 dholland yp_log(LOG_WARNING, "direct_set: sendto: %s", strerror(errno)); 1210 1.68 dholland return -1; 1211 1.68 dholland } 1212 1.68 dholland 1213 1.68 dholland return 0; 1214 1.68 dholland } 1215 1.68 dholland 1216 1.95 dholland /* 1217 1.95 dholland * Receive and dispatch packets on the general RPC socket. 1218 1.95 dholland */ 1219 1.68 dholland static enum clnt_stat 1220 1.68 dholland handle_replies(void) 1221 1.68 dholland { 1222 1.68 dholland char buf[BUFSIZE]; 1223 1.68 dholland socklen_t fromlen; 1224 1.68 dholland ssize_t inlen; 1225 1.78 dholland struct domain *dom; 1226 1.68 dholland struct sockaddr_in raddr; 1227 1.68 dholland struct rpc_msg msg; 1228 1.68 dholland XDR xdr; 1229 1.68 dholland 1230 1.68 dholland recv_again: 1231 1.68 dholland DPRINTF("handle_replies receiving\n"); 1232 1.68 dholland (void)memset(&xdr, 0, sizeof(xdr)); 1233 1.68 dholland (void)memset(&msg, 0, sizeof(msg)); 1234 1.68 dholland msg.acpted_rply.ar_verf = _null_auth; 1235 1.68 dholland msg.acpted_rply.ar_results.where = (caddr_t)(void *)&rmtcr; 1236 1.90 plunky msg.acpted_rply.ar_results.proc = (xdrproc_t)xdr_rmtcallres; 1237 1.68 dholland 1238 1.68 dholland try_again: 1239 1.68 dholland fromlen = sizeof(struct sockaddr); 1240 1.68 dholland inlen = recvfrom(rpcsock, buf, sizeof buf, 0, 1241 1.68 dholland (struct sockaddr *)(void *)&raddr, &fromlen); 1242 1.68 dholland if (inlen < 0) { 1243 1.68 dholland if (errno == EINTR) 1244 1.68 dholland goto try_again; 1245 1.68 dholland DPRINTF("handle_replies: recvfrom failed (%s)\n", 1246 1.68 dholland strerror(errno)); 1247 1.68 dholland return RPC_CANTRECV; 1248 1.68 dholland } 1249 1.68 dholland if ((size_t)inlen < sizeof(uint32_t)) 1250 1.68 dholland goto recv_again; 1251 1.27 thorpej 1252 1.68 dholland /* 1253 1.68 dholland * see if reply transaction id matches sent id. 1254 1.68 dholland * If so, decode the results. 1255 1.68 dholland */ 1256 1.68 dholland xdrmem_create(&xdr, buf, (unsigned)inlen, XDR_DECODE); 1257 1.68 dholland if (xdr_replymsg(&xdr, &msg)) { 1258 1.68 dholland if ((msg.rm_reply.rp_stat == MSG_ACCEPTED) && 1259 1.68 dholland (msg.acpted_rply.ar_stat == SUCCESS)) { 1260 1.68 dholland raddr.sin_port = htons((uint16_t)rmtcr_port); 1261 1.78 dholland dom = domain_find(msg.rm_xid); 1262 1.78 dholland if (dom != NULL) 1263 1.99 ginsbach (void)rpc_received(dom->dom_name, &raddr, 0, 0); 1264 1.68 dholland } 1265 1.27 thorpej } 1266 1.68 dholland xdr.x_op = XDR_FREE; 1267 1.90 plunky msg.acpted_rply.ar_results.proc = (xdrproc_t)xdr_void; 1268 1.68 dholland xdr_destroy(&xdr); 1269 1.27 thorpej 1270 1.68 dholland return RPC_SUCCESS; 1271 1.1 deraadt } 1272 1.1 deraadt 1273 1.95 dholland /* 1274 1.95 dholland * Receive and dispatch packets on the ping socket. 1275 1.95 dholland */ 1276 1.24 christos static enum clnt_stat 1277 1.68 dholland handle_ping(void) 1278 1.1 deraadt { 1279 1.24 christos char buf[BUFSIZE]; 1280 1.54 mrg socklen_t fromlen; 1281 1.57 christos ssize_t inlen; 1282 1.78 dholland struct domain *dom; 1283 1.1 deraadt struct sockaddr_in raddr; 1284 1.1 deraadt struct rpc_msg msg; 1285 1.1 deraadt XDR xdr; 1286 1.68 dholland bool_t res; 1287 1.1 deraadt 1288 1.1 deraadt recv_again: 1289 1.68 dholland DPRINTF("handle_ping receiving\n"); 1290 1.36 mrg (void)memset(&xdr, 0, sizeof(xdr)); 1291 1.36 mrg (void)memset(&msg, 0, sizeof(msg)); 1292 1.1 deraadt msg.acpted_rply.ar_verf = _null_auth; 1293 1.68 dholland msg.acpted_rply.ar_results.where = (caddr_t)(void *)&res; 1294 1.90 plunky msg.acpted_rply.ar_results.proc = (xdrproc_t)xdr_bool; 1295 1.1 deraadt 1296 1.1 deraadt try_again: 1297 1.68 dholland fromlen = sizeof (struct sockaddr); 1298 1.68 dholland inlen = recvfrom(pingsock, buf, sizeof buf, 0, 1299 1.68 dholland (struct sockaddr *)(void *)&raddr, &fromlen); 1300 1.17 mycroft if (inlen < 0) { 1301 1.17 mycroft if (errno == EINTR) 1302 1.1 deraadt goto try_again; 1303 1.68 dholland DPRINTF("handle_ping: recvfrom failed (%s)\n", 1304 1.65 dholland strerror(errno)); 1305 1.1 deraadt return RPC_CANTRECV; 1306 1.1 deraadt } 1307 1.64 dholland if ((size_t)inlen < sizeof(uint32_t)) 1308 1.1 deraadt goto recv_again; 1309 1.1 deraadt 1310 1.1 deraadt /* 1311 1.1 deraadt * see if reply transaction id matches sent id. 1312 1.1 deraadt * If so, decode the results. 1313 1.1 deraadt */ 1314 1.64 dholland xdrmem_create(&xdr, buf, (unsigned)inlen, XDR_DECODE); 1315 1.17 mycroft if (xdr_replymsg(&xdr, &msg)) { 1316 1.17 mycroft if ((msg.rm_reply.rp_stat == MSG_ACCEPTED) && 1317 1.17 mycroft (msg.acpted_rply.ar_stat == SUCCESS)) { 1318 1.78 dholland dom = domain_find(msg.rm_xid); 1319 1.78 dholland if (dom != NULL) 1320 1.99 ginsbach (void)rpc_received(dom->dom_name, &raddr, 0, 0); 1321 1.1 deraadt } 1322 1.1 deraadt } 1323 1.1 deraadt xdr.x_op = XDR_FREE; 1324 1.90 plunky msg.acpted_rply.ar_results.proc = (xdrproc_t)xdr_void; 1325 1.1 deraadt xdr_destroy(&xdr); 1326 1.1 deraadt 1327 1.1 deraadt return RPC_SUCCESS; 1328 1.1 deraadt } 1329 1.1 deraadt 1330 1.95 dholland /* 1331 1.95 dholland * Contact all known servers for a domain in the hopes that one of 1332 1.95 dholland * them's awake. Also, if we previously had a binding but it timed 1333 1.95 dholland * out, try the portmapper on that host in case ypserv moved ports for 1334 1.95 dholland * some reason. 1335 1.95 dholland * 1336 1.95 dholland * As a side effect, wipe out any existing binding file. 1337 1.95 dholland */ 1338 1.68 dholland static int 1339 1.78 dholland nag_servers(struct domain *dom) 1340 1.68 dholland { 1341 1.79 dholland char *dom_name = dom->dom_name; 1342 1.68 dholland struct rpc_msg msg; 1343 1.68 dholland char buf[BUFSIZE]; 1344 1.68 dholland enum clnt_stat st; 1345 1.68 dholland int outlen; 1346 1.68 dholland AUTH *rpcua; 1347 1.68 dholland XDR xdr; 1348 1.68 dholland 1349 1.68 dholland DPRINTF("nag_servers\n"); 1350 1.90 plunky rmtca.xdr_args = (xdrproc_t)xdr_ypdomain_wrap_string; 1351 1.78 dholland rmtca.args_ptr = (caddr_t)(void *)&dom_name; 1352 1.68 dholland 1353 1.68 dholland (void)memset(&xdr, 0, sizeof xdr); 1354 1.68 dholland (void)memset(&msg, 0, sizeof msg); 1355 1.68 dholland 1356 1.68 dholland rpcua = authunix_create_default(); 1357 1.68 dholland if (rpcua == NULL) { 1358 1.68 dholland DPRINTF("cannot get unix auth\n"); 1359 1.68 dholland return RPC_SYSTEMERROR; 1360 1.68 dholland } 1361 1.68 dholland msg.rm_direction = CALL; 1362 1.68 dholland msg.rm_call.cb_rpcvers = RPC_MSG_VERSION; 1363 1.68 dholland msg.rm_call.cb_prog = PMAPPROG; 1364 1.68 dholland msg.rm_call.cb_vers = PMAPVERS; 1365 1.68 dholland msg.rm_call.cb_proc = PMAPPROC_CALLIT; 1366 1.68 dholland msg.rm_call.cb_cred = rpcua->ah_cred; 1367 1.68 dholland msg.rm_call.cb_verf = rpcua->ah_verf; 1368 1.68 dholland 1369 1.78 dholland msg.rm_xid = dom->dom_xid; 1370 1.68 dholland xdrmem_create(&xdr, buf, (unsigned)sizeof(buf), XDR_ENCODE); 1371 1.68 dholland if (!xdr_callmsg(&xdr, &msg)) { 1372 1.68 dholland st = RPC_CANTENCODEARGS; 1373 1.68 dholland AUTH_DESTROY(rpcua); 1374 1.68 dholland return st; 1375 1.68 dholland } 1376 1.68 dholland if (!xdr_rmtcall_args(&xdr, &rmtca)) { 1377 1.68 dholland st = RPC_CANTENCODEARGS; 1378 1.68 dholland AUTH_DESTROY(rpcua); 1379 1.68 dholland return st; 1380 1.68 dholland } 1381 1.68 dholland outlen = (int)xdr_getpos(&xdr); 1382 1.68 dholland xdr_destroy(&xdr); 1383 1.68 dholland if (outlen < 1) { 1384 1.68 dholland st = RPC_CANTENCODEARGS; 1385 1.68 dholland AUTH_DESTROY(rpcua); 1386 1.68 dholland return st; 1387 1.68 dholland } 1388 1.68 dholland AUTH_DESTROY(rpcua); 1389 1.68 dholland 1390 1.78 dholland if (dom->dom_lockfd != -1) { 1391 1.78 dholland (void)close(dom->dom_lockfd); 1392 1.78 dholland dom->dom_lockfd = -1; 1393 1.78 dholland removelock(dom); 1394 1.68 dholland } 1395 1.68 dholland 1396 1.96 dholland if (dom->dom_state == DOM_PINGING || dom->dom_state == DOM_LOST) { 1397 1.68 dholland /* 1398 1.68 dholland * This resolves the following situation: 1399 1.68 dholland * ypserver on other subnet was once bound, 1400 1.68 dholland * but rebooted and is now using a different port 1401 1.68 dholland */ 1402 1.68 dholland struct sockaddr_in bindsin; 1403 1.68 dholland 1404 1.68 dholland (void)memset(&bindsin, 0, sizeof bindsin); 1405 1.68 dholland bindsin.sin_family = AF_INET; 1406 1.68 dholland bindsin.sin_len = sizeof(bindsin); 1407 1.68 dholland bindsin.sin_port = htons(PMAPPORT); 1408 1.78 dholland bindsin.sin_addr = dom->dom_server_addr.sin_addr; 1409 1.68 dholland 1410 1.68 dholland if (sendto(rpcsock, buf, outlen, 0, 1411 1.68 dholland (struct sockaddr *)(void *)&bindsin, 1412 1.68 dholland (socklen_t)sizeof bindsin) == -1) 1413 1.72 dholland yp_log(LOG_WARNING, "nag_servers: sendto: %s", 1414 1.72 dholland strerror(errno)); 1415 1.68 dholland } 1416 1.68 dholland 1417 1.82 dholland switch (dom->dom_ypbindmode) { 1418 1.81 dholland case YPBIND_BROADCAST: 1419 1.83 dholland if (dom->dom_been_ypset) { 1420 1.78 dholland return direct_set(buf, outlen, dom); 1421 1.81 dholland } 1422 1.68 dholland return broadcast(buf, outlen); 1423 1.68 dholland 1424 1.68 dholland case YPBIND_DIRECT: 1425 1.84 dholland return direct(buf, outlen, dom); 1426 1.68 dholland } 1427 1.68 dholland /*NOTREACHED*/ 1428 1.68 dholland return -1; 1429 1.68 dholland } 1430 1.68 dholland 1431 1.95 dholland /* 1432 1.95 dholland * Send a ping message to a domain's current ypserver. 1433 1.95 dholland */ 1434 1.69 dholland static int 1435 1.78 dholland ping(struct domain *dom) 1436 1.10 deraadt { 1437 1.79 dholland char *dom_name = dom->dom_name; 1438 1.68 dholland struct rpc_msg msg; 1439 1.24 christos char buf[BUFSIZE]; 1440 1.68 dholland enum clnt_stat st; 1441 1.68 dholland int outlen; 1442 1.68 dholland AUTH *rpcua; 1443 1.68 dholland XDR xdr; 1444 1.68 dholland 1445 1.68 dholland (void)memset(&xdr, 0, sizeof xdr); 1446 1.68 dholland (void)memset(&msg, 0, sizeof msg); 1447 1.68 dholland 1448 1.68 dholland rpcua = authunix_create_default(); 1449 1.68 dholland if (rpcua == NULL) { 1450 1.68 dholland DPRINTF("cannot get unix auth\n"); 1451 1.68 dholland return RPC_SYSTEMERROR; 1452 1.68 dholland } 1453 1.68 dholland 1454 1.68 dholland msg.rm_direction = CALL; 1455 1.68 dholland msg.rm_call.cb_rpcvers = RPC_MSG_VERSION; 1456 1.68 dholland msg.rm_call.cb_prog = YPPROG; 1457 1.68 dholland msg.rm_call.cb_vers = YPVERS; 1458 1.68 dholland msg.rm_call.cb_proc = YPPROC_DOMAIN_NONACK; 1459 1.68 dholland msg.rm_call.cb_cred = rpcua->ah_cred; 1460 1.68 dholland msg.rm_call.cb_verf = rpcua->ah_verf; 1461 1.68 dholland 1462 1.78 dholland msg.rm_xid = dom->dom_xid; 1463 1.68 dholland xdrmem_create(&xdr, buf, (unsigned)sizeof(buf), XDR_ENCODE); 1464 1.68 dholland if (!xdr_callmsg(&xdr, &msg)) { 1465 1.68 dholland st = RPC_CANTENCODEARGS; 1466 1.68 dholland AUTH_DESTROY(rpcua); 1467 1.68 dholland return st; 1468 1.68 dholland } 1469 1.78 dholland if (!xdr_ypdomain_wrap_string(&xdr, &dom_name)) { 1470 1.68 dholland st = RPC_CANTENCODEARGS; 1471 1.68 dholland AUTH_DESTROY(rpcua); 1472 1.68 dholland return st; 1473 1.68 dholland } 1474 1.68 dholland outlen = (int)xdr_getpos(&xdr); 1475 1.68 dholland xdr_destroy(&xdr); 1476 1.68 dholland if (outlen < 1) { 1477 1.68 dholland st = RPC_CANTENCODEARGS; 1478 1.68 dholland AUTH_DESTROY(rpcua); 1479 1.68 dholland return st; 1480 1.68 dholland } 1481 1.68 dholland AUTH_DESTROY(rpcua); 1482 1.68 dholland 1483 1.78 dholland DPRINTF("ping %x\n", dom->dom_server_addr.sin_addr.s_addr); 1484 1.68 dholland 1485 1.68 dholland if (sendto(pingsock, buf, outlen, 0, 1486 1.78 dholland (struct sockaddr *)(void *)&dom->dom_server_addr, 1487 1.78 dholland (socklen_t)(sizeof dom->dom_server_addr)) == -1) 1488 1.72 dholland yp_log(LOG_WARNING, "ping: sendto: %s", strerror(errno)); 1489 1.68 dholland return 0; 1490 1.68 dholland 1491 1.68 dholland } 1492 1.68 dholland 1493 1.68 dholland /* 1494 1.95 dholland * Scan for timer-based work to do. 1495 1.95 dholland * 1496 1.95 dholland * If the domain is currently alive, ping the server we're currently 1497 1.95 dholland * bound to. Otherwise, try all known servers and/or broadcast for a 1498 1.95 dholland * server via nag_servers. 1499 1.68 dholland * 1500 1.95 dholland * Try again in five seconds. 1501 1.96 dholland * 1502 1.96 dholland * If we get back here and the state is still DOM_PINGING, it means 1503 1.96 dholland * we didn't receive a ping response within five seconds. Declare the 1504 1.96 dholland * binding lost. If the binding is already lost, and it's been lost 1505 1.96 dholland * for 60 seconds, switch to DOM_DEAD and begin exponential backoff. 1506 1.96 dholland * The exponential backoff starts at 10 seconds and tops out at one 1507 1.96 dholland * hour; see above. 1508 1.68 dholland */ 1509 1.69 dholland static void 1510 1.68 dholland checkwork(void) 1511 1.68 dholland { 1512 1.78 dholland struct domain *dom; 1513 1.68 dholland time_t t; 1514 1.68 dholland 1515 1.68 dholland check = 0; 1516 1.68 dholland 1517 1.68 dholland (void)time(&t); 1518 1.78 dholland for (dom = domains; dom != NULL; dom = dom->dom_next) { 1519 1.96 dholland if (dom->dom_checktime >= t) { 1520 1.96 dholland continue; 1521 1.96 dholland } 1522 1.96 dholland switch (dom->dom_state) { 1523 1.96 dholland case DOM_NEW: 1524 1.96 dholland /* XXX should be a timeout for this state */ 1525 1.96 dholland dom->dom_checktime = t + 5; 1526 1.96 dholland (void)nag_servers(dom); 1527 1.96 dholland break; 1528 1.96 dholland 1529 1.96 dholland case DOM_ALIVE: 1530 1.96 dholland dom->dom_state = DOM_PINGING; 1531 1.96 dholland dom->dom_checktime = t + 5; 1532 1.96 dholland (void)ping(dom); 1533 1.96 dholland break; 1534 1.96 dholland 1535 1.96 dholland case DOM_PINGING: 1536 1.96 dholland dom->dom_state = DOM_LOST; 1537 1.96 dholland dom->dom_losttime = t; 1538 1.78 dholland dom->dom_checktime = t + 5; 1539 1.98 dholland yp_log(LOG_NOTICE, "Domain %s lost its binding to " 1540 1.98 dholland "server %s", dom->dom_name, 1541 1.98 dholland inet_ntoa(dom->dom_server_addr.sin_addr)); 1542 1.96 dholland (void)nag_servers(dom); 1543 1.96 dholland break; 1544 1.96 dholland 1545 1.96 dholland case DOM_LOST: 1546 1.96 dholland if (t > dom->dom_losttime + 60) { 1547 1.96 dholland dom->dom_state = DOM_DEAD; 1548 1.96 dholland dom->dom_backofftime = 10; 1549 1.98 dholland yp_log(LOG_NOTICE, "Domain %s dead; " 1550 1.98 dholland "going to exponential backoff", 1551 1.98 dholland dom->dom_name); 1552 1.96 dholland } 1553 1.96 dholland dom->dom_checktime = t + 5; 1554 1.96 dholland (void)nag_servers(dom); 1555 1.96 dholland break; 1556 1.96 dholland 1557 1.96 dholland case DOM_DEAD: 1558 1.96 dholland dom->dom_checktime = t + dom->dom_backofftime; 1559 1.96 dholland backoff(&dom->dom_backofftime); 1560 1.96 dholland (void)nag_servers(dom); 1561 1.96 dholland break; 1562 1.68 dholland } 1563 1.96 dholland /* re-fetch the time in case we hung sending packets */ 1564 1.96 dholland (void)time(&t); 1565 1.68 dholland } 1566 1.68 dholland } 1567 1.68 dholland 1568 1.97 dholland /* 1569 1.97 dholland * Process a hangup signal. 1570 1.97 dholland * 1571 1.97 dholland * Do an extra nag_servers() for any domains that are DEAD. This way 1572 1.97 dholland * if you know things are back up you can restore service by sending 1573 1.97 dholland * ypbind a SIGHUP rather than waiting for the timeout period. 1574 1.97 dholland */ 1575 1.97 dholland static void 1576 1.97 dholland dohup(void) 1577 1.97 dholland { 1578 1.97 dholland struct domain *dom; 1579 1.97 dholland 1580 1.97 dholland hupped = 0; 1581 1.97 dholland for (dom = domains; dom != NULL; dom = dom->dom_next) { 1582 1.97 dholland if (dom->dom_state == DOM_DEAD) { 1583 1.97 dholland (void)nag_servers(dom); 1584 1.97 dholland } 1585 1.97 dholland } 1586 1.97 dholland } 1587 1.97 dholland 1588 1.97 dholland /* 1589 1.97 dholland * Receive a hangup signal. 1590 1.97 dholland */ 1591 1.97 dholland static void 1592 1.97 dholland hup(int __unused sig) 1593 1.97 dholland { 1594 1.97 dholland hupped = 1; 1595 1.97 dholland } 1596 1.97 dholland 1597 1.97 dholland /* 1598 1.97 dholland * Initialize hangup processing. 1599 1.97 dholland */ 1600 1.97 dholland static void 1601 1.97 dholland starthup(void) 1602 1.97 dholland { 1603 1.97 dholland struct sigaction sa; 1604 1.97 dholland 1605 1.97 dholland sa.sa_handler = hup; 1606 1.97 dholland sigemptyset(&sa.sa_mask); 1607 1.97 dholland sa.sa_flags = SA_RESTART; 1608 1.97 dholland if (sigaction(SIGHUP, &sa, NULL) == -1) { 1609 1.97 dholland err(1, "sigaction"); 1610 1.97 dholland } 1611 1.97 dholland } 1612 1.97 dholland 1613 1.68 dholland //////////////////////////////////////////////////////////// 1614 1.68 dholland // main 1615 1.68 dholland 1616 1.95 dholland /* 1617 1.95 dholland * Usage message. 1618 1.95 dholland */ 1619 1.89 joerg __dead static void 1620 1.68 dholland usage(void) 1621 1.68 dholland { 1622 1.68 dholland const char *opt = ""; 1623 1.68 dholland #ifdef DEBUG 1624 1.68 dholland opt = " [-d]"; 1625 1.68 dholland #endif 1626 1.68 dholland 1627 1.68 dholland (void)fprintf(stderr, 1628 1.68 dholland "Usage: %s [-broadcast] [-insecure] [-ypset] [-ypsetme]%s\n", 1629 1.68 dholland getprogname(), opt); 1630 1.68 dholland exit(1); 1631 1.68 dholland } 1632 1.68 dholland 1633 1.95 dholland /* 1634 1.95 dholland * Main. 1635 1.95 dholland */ 1636 1.68 dholland int 1637 1.68 dholland main(int argc, char *argv[]) 1638 1.68 dholland { 1639 1.68 dholland struct timeval tv; 1640 1.68 dholland fd_set fdsr; 1641 1.68 dholland int width, lockfd; 1642 1.93 dholland int started = 0; 1643 1.91 dholland char *domainname; 1644 1.10 deraadt 1645 1.68 dholland setprogname(argv[0]); 1646 1.10 deraadt 1647 1.95 dholland /* 1648 1.95 dholland * Process arguments. 1649 1.95 dholland */ 1650 1.95 dholland 1651 1.82 dholland default_ypbindmode = YPBIND_DIRECT; 1652 1.68 dholland while (--argc) { 1653 1.68 dholland ++argv; 1654 1.81 dholland if (!strcmp("-insecure", *argv)) { 1655 1.68 dholland insecure = 1; 1656 1.81 dholland } else if (!strcmp("-ypset", *argv)) { 1657 1.81 dholland allow_any_ypset = 1; 1658 1.81 dholland allow_local_ypset = 1; 1659 1.81 dholland } else if (!strcmp("-ypsetme", *argv)) { 1660 1.81 dholland allow_any_ypset = 0; 1661 1.81 dholland allow_local_ypset = 1; 1662 1.81 dholland } else if (!strcmp("-broadcast", *argv)) { 1663 1.82 dholland default_ypbindmode = YPBIND_BROADCAST; 1664 1.68 dholland #ifdef DEBUG 1665 1.81 dholland } else if (!strcmp("-d", *argv)) { 1666 1.81 dholland debug = 1; 1667 1.68 dholland #endif 1668 1.81 dholland } else { 1669 1.68 dholland usage(); 1670 1.81 dholland } 1671 1.10 deraadt } 1672 1.10 deraadt 1673 1.95 dholland /* 1674 1.95 dholland * Look up the name of the default domain. 1675 1.95 dholland */ 1676 1.95 dholland 1677 1.94 dholland (void)yp_get_default_domain(&domainname); 1678 1.94 dholland if (domainname[0] == '\0') 1679 1.94 dholland errx(1, "Domainname not set. Aborting."); 1680 1.94 dholland if (_yp_invalid_domain(domainname)) 1681 1.94 dholland errx(1, "Invalid domainname: %s", domainname); 1682 1.94 dholland 1683 1.95 dholland /* 1684 1.95 dholland * Start things up. 1685 1.95 dholland */ 1686 1.95 dholland 1687 1.95 dholland /* Open the system log. */ 1688 1.68 dholland openlog("ypbind", LOG_PERROR | LOG_PID, LOG_DAEMON); 1689 1.10 deraadt 1690 1.95 dholland /* Acquire /var/run/ypbind.lock. */ 1691 1.73 dholland lockfd = open_locked(_PATH_YPBIND_LOCK, O_CREAT|O_RDWR|O_TRUNC, 0644); 1692 1.68 dholland if (lockfd == -1) 1693 1.68 dholland err(1, "Cannot create %s", _PATH_YPBIND_LOCK); 1694 1.14 cgd 1695 1.97 dholland /* Accept hangups. */ 1696 1.97 dholland starthup(); 1697 1.97 dholland 1698 1.95 dholland /* Initialize sunrpc stuff. */ 1699 1.74 dholland sunrpc_setup(); 1700 1.1 deraadt 1701 1.95 dholland /* Clean out BINDINGDIR, deleting all existing (now stale) bindings */ 1702 1.68 dholland if (purge_bindingdir(BINDINGDIR) < 0) 1703 1.95 dholland errx(1, "Unable to purge old bindings from %s", BINDINGDIR); 1704 1.95 dholland 1705 1.95 dholland /* 1706 1.95 dholland * We start with one binding, for the default domain. It starts 1707 1.95 dholland * out "unsuccessful". 1708 1.95 dholland */ 1709 1.6 deraadt 1710 1.99 ginsbach if (domain_create(domainname) == NULL) 1711 1.99 ginsbach err(1, "initial domain binding failed"); 1712 1.95 dholland 1713 1.95 dholland /* 1714 1.95 dholland * Delete the lock for the default domain again, just in case something 1715 1.95 dholland * magically caused it to appear since purge_bindingdir() was called. 1716 1.95 dholland * XXX: this is useless and redundant; remove it. 1717 1.95 dholland */ 1718 1.77 dholland removelock(domains); 1719 1.6 deraadt 1720 1.95 dholland /* 1721 1.95 dholland * Main loop. Wake up at least once a second and check for 1722 1.95 dholland * timer-based work to do (checkwork) and also handle incoming 1723 1.95 dholland * responses from ypservers and any RPCs made to the ypbind 1724 1.95 dholland * service. 1725 1.95 dholland * 1726 1.95 dholland * There are two sockets used for ypserver traffic: one for 1727 1.95 dholland * pings and one for everything else. These call XDR manually 1728 1.95 dholland * for encoding and are *not* dispatched via the sunrpc 1729 1.95 dholland * libraries. 1730 1.95 dholland * 1731 1.101 andvar * The ypbind service *is* dispatched via the sunrpc libraries. 1732 1.95 dholland * svc_getreqset() does whatever internal muck and ultimately 1733 1.95 dholland * ypbind service calls arrive at ypbindprog_2(). 1734 1.95 dholland */ 1735 1.68 dholland checkwork(); 1736 1.68 dholland for (;;) { 1737 1.68 dholland width = svc_maxfd; 1738 1.68 dholland if (rpcsock > width) 1739 1.68 dholland width = rpcsock; 1740 1.68 dholland if (pingsock > width) 1741 1.68 dholland width = pingsock; 1742 1.68 dholland width++; 1743 1.68 dholland fdsr = svc_fdset; 1744 1.68 dholland FD_SET(rpcsock, &fdsr); 1745 1.68 dholland FD_SET(pingsock, &fdsr); 1746 1.68 dholland tv.tv_sec = 1; 1747 1.68 dholland tv.tv_usec = 0; 1748 1.20 cgd 1749 1.68 dholland switch (select(width, &fdsr, NULL, NULL, &tv)) { 1750 1.68 dholland case 0: 1751 1.95 dholland /* select timed out - check for timer-based work */ 1752 1.97 dholland if (hupped) { 1753 1.97 dholland dohup(); 1754 1.97 dholland } 1755 1.68 dholland checkwork(); 1756 1.68 dholland break; 1757 1.68 dholland case -1: 1758 1.97 dholland if (hupped) { 1759 1.97 dholland dohup(); 1760 1.97 dholland } 1761 1.97 dholland if (errno != EINTR) { 1762 1.97 dholland yp_log(LOG_WARNING, "select: %s", 1763 1.97 dholland strerror(errno)); 1764 1.97 dholland } 1765 1.68 dholland break; 1766 1.68 dholland default: 1767 1.97 dholland if (hupped) { 1768 1.97 dholland dohup(); 1769 1.97 dholland } 1770 1.95 dholland /* incoming of our own; read it */ 1771 1.68 dholland if (FD_ISSET(rpcsock, &fdsr)) 1772 1.68 dholland (void)handle_replies(); 1773 1.68 dholland if (FD_ISSET(pingsock, &fdsr)) 1774 1.68 dholland (void)handle_ping(); 1775 1.95 dholland 1776 1.95 dholland /* read any incoming packets for the ypbind service */ 1777 1.68 dholland svc_getreqset(&fdsr); 1778 1.95 dholland 1779 1.95 dholland /* 1780 1.95 dholland * Only check for timer-based work if 1781 1.95 dholland * something in the incoming RPC logic said 1782 1.95 dholland * to. This might be just a hack to avoid 1783 1.95 dholland * scanning the list unnecessarily, but I 1784 1.95 dholland * suspect it's also a hack to cover wrong 1785 1.95 dholland * state logic. - dholland 20140609 1786 1.95 dholland */ 1787 1.68 dholland if (check) 1788 1.68 dholland checkwork(); 1789 1.20 cgd break; 1790 1.68 dholland } 1791 1.20 cgd 1792 1.95 dholland /* 1793 1.95 dholland * Defer daemonizing until the default domain binds 1794 1.95 dholland * successfully. XXX: there seems to be no timeout 1795 1.95 dholland * on this, which means that if the default domain 1796 1.95 dholland * is dead upstream boot will hang indefinitely. 1797 1.95 dholland */ 1798 1.96 dholland if (!started && domains->dom_state == DOM_ALIVE) { 1799 1.93 dholland started = 1; 1800 1.68 dholland #ifdef DEBUG 1801 1.68 dholland if (!debug) 1802 1.68 dholland #endif 1803 1.68 dholland (void)daemon(0, 0); 1804 1.68 dholland (void)pidfile(NULL); 1805 1.68 dholland } 1806 1.68 dholland } 1807 1.1 deraadt } 1808