1 /* $NetBSD: rwhod.c,v 1.40 2012/11/04 22:32:01 christos Exp $ */ 2 3 /* 4 * Copyright (c) 1983, 1993 5 * The Regents of the University of California. 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 * 3. Neither the name of the University nor the names of its contributors 16 * may be used to endorse or promote products derived from this software 17 * without specific prior written permission. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 29 * SUCH DAMAGE. 30 */ 31 32 #include <sys/cdefs.h> 33 #ifndef lint 34 __COPYRIGHT("@(#) Copyright (c) 1983, 1993\ 35 The Regents of the University of California. All rights reserved."); 36 #endif /* not lint */ 37 38 #ifndef lint 39 #if 0 40 static char sccsid[] = "@(#)rwhod.c 8.1 (Berkeley) 6/6/93"; 41 #else 42 __RCSID("$NetBSD: rwhod.c,v 1.40 2012/11/04 22:32:01 christos Exp $"); 43 #endif 44 #endif /* not lint */ 45 46 #include <sys/param.h> 47 #include <sys/socket.h> 48 #include <sys/stat.h> 49 #include <sys/signal.h> 50 #include <sys/ioctl.h> 51 #include <sys/sysctl.h> 52 53 #include <net/if.h> 54 #include <net/if_dl.h> 55 #include <net/route.h> 56 #include <netinet/in.h> 57 #include <protocols/rwhod.h> 58 #include <arpa/inet.h> 59 60 #include <ctype.h> 61 #include <pwd.h> 62 #include <err.h> 63 #include <errno.h> 64 #include <fcntl.h> 65 #include <netdb.h> 66 #include <paths.h> 67 #include <poll.h> 68 #include <stdio.h> 69 #include <stdlib.h> 70 #include <string.h> 71 #include <syslog.h> 72 #include <unistd.h> 73 #include <util.h> 74 75 #include "utmpentry.h" 76 77 #define CHECK_INTERVAL (3 * 60) 78 79 /* Time interval limit; ruptime will think that we are down > than this */ 80 #define MAX_INTERVAL (11 * 60) 81 82 83 static char myname[MAXHOSTNAMELEN + 1]; 84 85 /* 86 * We communicate with each neighbor in a list constructed at the time we're 87 * started up. Neighbors are currently directly connected via a hardware 88 * interface. 89 */ 90 struct neighbor { 91 struct neighbor *n_next; 92 char *n_name; /* interface name */ 93 struct sockaddr *n_addr; /* who to send to */ 94 int n_addrlen; /* size of address */ 95 int n_flags; /* should forward?, interface flags */ 96 }; 97 98 static struct neighbor *neighbors; 99 static struct whod mywd; 100 static struct servent *sp; 101 static volatile sig_atomic_t onsighup; 102 103 #define WHDRSIZE (sizeof(mywd) - sizeof(mywd.wd_we)) 104 105 static int configure(int); 106 static void getboottime(void); 107 static void send_host_information(int); 108 static void sighup(int); 109 static void handleread(int); 110 __dead static void quit(const char *); 111 static void rt_xaddrs(void *, void *, struct rt_addrinfo *); 112 static int drop_privs(char *); 113 static void usage(void) __dead; 114 static int verify(const char *); 115 #ifdef DEBUG 116 static char *interval(int, const char *); 117 static ssize_t Sendto(int, const void *, size_t, int, 118 const struct sockaddr *, socklen_t); 119 #else 120 #define Sendto sendto 121 #endif 122 123 int 124 main(int argc, char *argv[]) 125 { 126 int s, ch; 127 int time_interval = 180; /* Default time (180 seconds) */ 128 char *cp, *ep; 129 socklen_t on = 1; 130 struct sockaddr_in sasin; 131 struct pollfd pfd[1]; 132 struct timeval delta, next, now; 133 char *newuser = NULL; 134 135 setprogname(argv[0]); 136 137 if (getuid()) 138 errx(EXIT_FAILURE, "not super user"); 139 140 while ((ch = getopt(argc, argv, "i:u:")) != -1) { 141 switch (ch) { 142 case 'i': 143 time_interval = (int)strtol(optarg, &ep, 10); 144 145 switch (*ep) { 146 case '\0': 147 break; 148 case 'm': 149 case 'M': 150 /* Time in minutes. */ 151 time_interval *= 60; 152 if (ep[1] == '\0') 153 break; 154 /*FALLTHROUGH*/ 155 default: 156 errx(1, "Invalid argument: `%s'", optarg); 157 } 158 159 if (time_interval <= 0) 160 errx(1, "Interval must be greater than 0"); 161 162 if (time_interval > MAX_INTERVAL) 163 errx(1, "Interval cannot be greater than" 164 " %d minutes", MAX_INTERVAL / 60); 165 break; 166 167 case 'u': 168 newuser = optarg; 169 break; 170 171 default: 172 usage(); 173 } 174 } 175 176 sp = getservbyname("who", "udp"); 177 if (sp == NULL) 178 errx(EXIT_FAILURE, "udp/who: unknown service"); 179 #ifndef DEBUG 180 (void)daemon(1, 0); 181 (void)pidfile(NULL); 182 #endif 183 if (chdir(_PATH_RWHODIR) < 0) 184 err(EXIT_FAILURE, "%s", _PATH_RWHODIR); 185 (void)signal(SIGHUP, sighup); 186 openlog(getprogname(), LOG_PID, LOG_DAEMON); 187 /* 188 * Establish host name as returned by system. 189 */ 190 if (gethostname(myname, sizeof(myname) - 1) < 0) { 191 syslog(LOG_ERR, "gethostname: %m"); 192 exit(EXIT_FAILURE); 193 } 194 myname[sizeof(myname) - 1] = '\0'; 195 if ((cp = strchr(myname, '.')) != NULL) 196 *cp = '\0'; 197 (void)strncpy(mywd.wd_hostname, myname, sizeof(mywd.wd_hostname) - 1); 198 getboottime(); 199 if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { 200 syslog(LOG_ERR, "socket: %m"); 201 exit(EXIT_FAILURE); 202 } 203 if (setsockopt(s, SOL_SOCKET, SO_BROADCAST, &on, sizeof(on)) < 0) { 204 syslog(LOG_ERR, "setsockopt SO_BROADCAST: %m"); 205 exit(EXIT_FAILURE); 206 } 207 (void)memset(&sasin, 0, sizeof(sasin)); 208 sasin.sin_family = AF_INET; 209 sasin.sin_port = sp->s_port; 210 if (bind(s, (struct sockaddr *)&sasin, sizeof(sasin)) < 0) { 211 syslog(LOG_ERR, "bind: %m"); 212 exit(EXIT_FAILURE); 213 } 214 if (!configure(s)) 215 exit(EXIT_FAILURE); 216 217 if (newuser) 218 if (!drop_privs(newuser)) 219 exit(EXIT_FAILURE); 220 221 send_host_information(s); 222 delta.tv_sec = time_interval; 223 delta.tv_usec = 0; 224 gettimeofday(&now, NULL); 225 timeradd(&now, &delta, &next); 226 227 pfd[0].fd = s; 228 pfd[0].events = POLLIN; 229 230 for (;;) { 231 int n; 232 233 n = poll(pfd, 1, 1000); 234 235 if (onsighup) { 236 onsighup = 0; 237 getboottime(); 238 } 239 240 if (n == 1) 241 handleread(s); 242 243 (void)gettimeofday(&now, NULL); 244 if (timercmp(&now, &next, >)) { 245 send_host_information(s); 246 timeradd(&now, &delta, &next); 247 } 248 } 249 250 /* NOTREACHED */ 251 return 0; 252 } 253 254 static void 255 sighup(int signo __unused) 256 { 257 onsighup = 1; 258 } 259 260 static void 261 handleread(int s) 262 { 263 struct sockaddr_in from; 264 struct stat st; 265 char path[64]; 266 struct whod wd; 267 int cc, whod; 268 socklen_t len = sizeof(from); 269 270 cc = recvfrom(s, (char *)&wd, sizeof(struct whod), 0, 271 (struct sockaddr *)&from, &len); 272 if (cc <= 0) { 273 if (cc < 0 && errno != EINTR) 274 syslog(LOG_WARNING, "recv: %m"); 275 return; 276 } 277 if (from.sin_port != sp->s_port) { 278 syslog(LOG_WARNING, "%d: bad from port", 279 ntohs(from.sin_port)); 280 return; 281 } 282 if (cc < (int)WHDRSIZE) { 283 syslog(LOG_WARNING, "Short packet from %s", 284 inet_ntoa(from.sin_addr)); 285 return; 286 } 287 288 if (wd.wd_vers != WHODVERSION) 289 return; 290 if (wd.wd_type != WHODTYPE_STATUS) 291 return; 292 /* 293 * Ensure null termination of the name within the packet. 294 * Otherwise we might overflow or read past the end. 295 */ 296 wd.wd_hostname[sizeof(wd.wd_hostname)-1] = 0; 297 if (!verify(wd.wd_hostname)) { 298 syslog(LOG_WARNING, "malformed host name from %s", 299 inet_ntoa(from.sin_addr)); 300 return; 301 } 302 (void)snprintf(path, sizeof(path), "whod.%s", wd.wd_hostname); 303 /* 304 * Rather than truncating and growing the file each time, 305 * use ftruncate if size is less than previous size. 306 */ 307 whod = open(path, O_WRONLY | O_CREAT, 0644); 308 if (whod < 0) { 309 syslog(LOG_WARNING, "%s: %m", path); 310 return; 311 } 312 #if ENDIAN != BIG_ENDIAN 313 { 314 int i, n = (cc - WHDRSIZE) / sizeof(struct whoent); 315 struct whoent *we; 316 317 /* undo header byte swapping before writing to file */ 318 wd.wd_sendtime = ntohl(wd.wd_sendtime); 319 for (i = 0; i < 3; i++) 320 wd.wd_loadav[i] = ntohl(wd.wd_loadav[i]); 321 wd.wd_boottime = ntohl(wd.wd_boottime); 322 we = wd.wd_we; 323 for (i = 0; i < n; i++) { 324 we->we_idle = ntohl(we->we_idle); 325 we->we_utmp.out_time = 326 ntohl(we->we_utmp.out_time); 327 we++; 328 } 329 } 330 #endif 331 wd.wd_recvtime = time(NULL); 332 (void)write(whod, (char *)&wd, cc); 333 if (fstat(whod, &st) < 0 || st.st_size > cc) 334 (void)ftruncate(whod, cc); 335 (void)close(whod); 336 } 337 338 /* 339 * Check out host name for unprintables 340 * and other funnies before allowing a file 341 * to be created. Sorry, but blanks aren't allowed. 342 */ 343 static int 344 verify(const char *name) 345 { 346 int size = 0; 347 348 while (*name) { 349 if (!isascii((unsigned char)*name) || 350 !(isalnum((unsigned char)*name) || 351 ispunct((unsigned char)*name))) 352 return 0; 353 name++, size++; 354 } 355 return size > 0; 356 } 357 358 static void 359 send_host_information(int s) 360 { 361 struct neighbor *np; 362 struct whoent *we = mywd.wd_we, *wlast; 363 int i, cc, utmpent = 0; 364 struct stat stb; 365 double avenrun[3]; 366 time_t now; 367 static struct utmpentry *ohead = NULL; 368 struct utmpentry *ep; 369 static int count = 0; 370 371 now = time(NULL); 372 if (count % 10 == 0) 373 getboottime(); 374 count++; 375 376 (void)getutentries(NULL, &ep); 377 /* XXX probably should expose utmp mtime, check that instead */ 378 if (ep != ohead) { 379 wlast = &mywd.wd_we[1024 / sizeof(struct whoent) - 1]; 380 for (; ep; ep = ep->next) { 381 (void)strncpy(we->we_utmp.out_line, ep->line, 382 sizeof(we->we_utmp.out_line)); 383 (void)strncpy(we->we_utmp.out_name, ep->name, 384 sizeof(we->we_utmp.out_name)); 385 we->we_utmp.out_time = htonl(ep->tv.tv_sec); 386 if (we >= wlast) 387 break; 388 we++; 389 } 390 utmpent = we - mywd.wd_we; 391 } 392 393 /* 394 * The test on utmpent looks silly---after all, if no one is 395 * logged on, why worry about efficiency?---but is useful on 396 * (e.g.) compute servers. 397 */ 398 if (utmpent && chdir(_PATH_DEV)) { 399 syslog(LOG_ERR, "chdir(%s): %m", _PATH_DEV); 400 exit(EXIT_FAILURE); 401 } 402 we = mywd.wd_we; 403 for (i = 0; i < utmpent; i++) { 404 if (stat(we->we_utmp.out_line, &stb) >= 0) 405 we->we_idle = htonl(now - stb.st_atime); 406 we++; 407 } 408 (void)getloadavg(avenrun, sizeof(avenrun)/sizeof(avenrun[0])); 409 for (i = 0; i < 3; i++) 410 mywd.wd_loadav[i] = htonl((u_long)(avenrun[i] * 100)); 411 cc = (char *)we - (char *)&mywd; 412 mywd.wd_sendtime = htonl(time(0)); 413 mywd.wd_vers = WHODVERSION; 414 mywd.wd_type = WHODTYPE_STATUS; 415 for (np = neighbors; np != NULL; np = np->n_next) 416 (void)Sendto(s, (char *)&mywd, cc, 0, 417 np->n_addr, np->n_addrlen); 418 if (utmpent && chdir(_PATH_RWHODIR)) { 419 syslog(LOG_ERR, "chdir(%s): %m", _PATH_RWHODIR); 420 exit(EXIT_FAILURE); 421 } 422 } 423 424 static void 425 getboottime(void) 426 { 427 int mib[2]; 428 size_t size; 429 struct timeval tm; 430 431 mib[0] = CTL_KERN; 432 mib[1] = KERN_BOOTTIME; 433 size = sizeof(tm); 434 if (sysctl(mib, 2, &tm, &size, NULL, 0) == -1) { 435 syslog(LOG_ERR, "cannot get boottime: %m"); 436 exit(EXIT_FAILURE); 437 } 438 mywd.wd_boottime = htonl(tm.tv_sec); 439 } 440 441 static void 442 quit(const char *msg) 443 { 444 syslog(LOG_ERR, "%s", msg); 445 exit(EXIT_FAILURE); 446 } 447 448 #define ROUNDUP(a) \ 449 ((a) > 0 ? (1 + (((a) - 1) | (sizeof(long) - 1))) : sizeof(long)) 450 #define ADVANCE(x, n) ((char *)(x) + ROUNDUP((n)->sa_len)) 451 452 static void 453 rt_xaddrs(void *cp, void *cplim, struct rt_addrinfo *rtinfo) 454 { 455 struct sockaddr *sa; 456 int i; 457 458 (void)memset(rtinfo->rti_info, 0, sizeof(rtinfo->rti_info)); 459 for (i = 0; (i < RTAX_MAX) && (cp < cplim); i++) { 460 if ((rtinfo->rti_addrs & (1 << i)) == 0) 461 continue; 462 rtinfo->rti_info[i] = sa = (struct sockaddr *)cp; 463 cp = ADVANCE(cp, sa); 464 } 465 } 466 467 /* 468 * Figure out device configuration and select 469 * networks which deserve status information. 470 */ 471 static int 472 configure(int s) 473 { 474 struct neighbor *np; 475 struct if_msghdr *ifm; 476 struct ifa_msghdr *ifam; 477 struct sockaddr_dl *sdl; 478 size_t needed; 479 int mib[6], flags = 0, len; 480 char *buf, *lim, *next; 481 struct rt_addrinfo info; 482 struct sockaddr_in dstaddr; 483 484 mib[0] = CTL_NET; 485 mib[1] = PF_ROUTE; 486 mib[2] = 0; 487 mib[3] = AF_INET; 488 mib[4] = NET_RT_IFLIST; 489 mib[5] = 0; 490 if (sysctl(mib, 6, NULL, &needed, NULL, 0) < 0) 491 quit("route-sysctl-estimate"); 492 if ((buf = malloc(needed)) == NULL) 493 quit("malloc"); 494 if (sysctl(mib, 6, buf, &needed, NULL, 0) < 0) 495 quit("actual retrieval of interface table"); 496 lim = buf + needed; 497 498 sdl = NULL; /* XXX just to keep gcc -Wall happy */ 499 for (next = buf; next < lim; next += ifm->ifm_msglen) { 500 ifm = (struct if_msghdr *)next; 501 if (ifm->ifm_type == RTM_IFINFO) { 502 sdl = (struct sockaddr_dl *)(ifm + 1); 503 flags = ifm->ifm_flags; 504 continue; 505 } 506 if ((flags & IFF_UP) == 0 || 507 (flags & (IFF_BROADCAST|IFF_POINTOPOINT)) == 0) 508 continue; 509 if (ifm->ifm_type != RTM_NEWADDR) 510 quit("out of sync parsing NET_RT_IFLIST"); 511 ifam = (struct ifa_msghdr *)ifm; 512 info.rti_addrs = ifam->ifam_addrs; 513 rt_xaddrs((ifam + 1), ifam->ifam_msglen + (char *)ifam, &info); 514 /* gag, wish we could get rid of Internet dependencies */ 515 if (info.rti_info[RTAX_BRD] == NULL || 516 info.rti_info[RTAX_BRD]->sa_family != AF_INET) 517 continue; 518 (void)memcpy(&dstaddr, info.rti_info[RTAX_BRD], 519 sizeof(dstaddr)); 520 #define IPADDR_SA(x) ((struct sockaddr_in *)(x))->sin_addr.s_addr 521 #define PORT_SA(x) ((struct sockaddr_in *)(x))->sin_port 522 PORT_SA(&dstaddr) = sp->s_port; 523 for (np = neighbors; np != NULL; np = np->n_next) 524 if (memcmp(sdl->sdl_data, np->n_name, 525 sdl->sdl_nlen) == 0 && 526 IPADDR_SA(np->n_addr) == IPADDR_SA(&dstaddr)) 527 break; 528 if (np != NULL) 529 continue; 530 len = sizeof(*np) + dstaddr.sin_len + sdl->sdl_nlen + 1; 531 np = (struct neighbor *)malloc(len); 532 if (np == NULL) 533 quit("malloc of neighbor structure"); 534 (void)memset(np, 0, len); 535 np->n_flags = flags; 536 np->n_addr = (struct sockaddr *)(np + 1); 537 np->n_addrlen = dstaddr.sin_len; 538 np->n_name = np->n_addrlen + (char *)np->n_addr; 539 np->n_next = neighbors; 540 neighbors = np; 541 (void)memcpy(np->n_addr, &dstaddr, np->n_addrlen); 542 (void)memcpy(np->n_name, sdl->sdl_data, sdl->sdl_nlen); 543 } 544 free(buf); 545 return (1); 546 } 547 548 #ifdef DEBUG 549 static ssize_t 550 Sendto(int s, const void *buf, size_t cc, int flags, const struct sockaddr *to, 551 socklen_t tolen) 552 { 553 struct whod *w = (struct whod *)buf; 554 struct whoent *we; 555 struct sockaddr_in *sasin = (struct sockaddr_in *)to; 556 ssize_t ret; 557 558 ret = sendto(s, buf, cc, flags, to, tolen); 559 560 printf("sendto %s.%d\n", inet_ntoa(sasin->sin_addr), 561 ntohs(sasin->sin_port)); 562 printf("hostname %s %s\n", w->wd_hostname, 563 interval(ntohl(w->wd_sendtime) - ntohl(w->wd_boottime), " up")); 564 printf("load %4.2f, %4.2f, %4.2f\n", 565 ntohl(w->wd_loadav[0]) / 100.0, ntohl(w->wd_loadav[1]) / 100.0, 566 ntohl(w->wd_loadav[2]) / 100.0); 567 cc -= WHDRSIZE; 568 for (we = w->wd_we, cc /= sizeof(struct whoent); cc > 0; cc--, we++) { 569 time_t t = ntohl(we->we_utmp.out_time); 570 printf("%-8.8s %s:%s %.12s", we->we_utmp.out_name, 571 w->wd_hostname, we->we_utmp.out_line, ctime(&t)+4); 572 we->we_idle = ntohl(we->we_idle) / 60; 573 if (we->we_idle) { 574 if (we->we_idle >= 100*60) 575 we->we_idle = 100*60 - 1; 576 if (we->we_idle >= 60) 577 printf(" %2d", we->we_idle / 60); 578 else 579 printf(" "); 580 printf(":%02d", we->we_idle % 60); 581 } 582 printf("\n"); 583 } 584 return ret; 585 } 586 587 static char * 588 interval(int time, const char *updown) 589 { 590 static char resbuf[32]; 591 int days, hours, minutes; 592 593 if (time < 0 || time > 3*30*24*60*60) { 594 (void)snprintf(resbuf, sizeof(resbuf), " %s ??:??", updown); 595 return (resbuf); 596 } 597 minutes = (time + 59) / 60; /* round to minutes */ 598 hours = minutes / 60; minutes %= 60; 599 days = hours / 24; hours %= 24; 600 if (days) 601 (void)snprintf(resbuf, sizeof(resbuf), "%s %2d+%02d:%02d", 602 updown, days, hours, minutes); 603 else 604 (void)snprintf(resbuf, sizeof(resbuf), "%s %2d:%02d", 605 updown, hours, minutes); 606 return resbuf; 607 } 608 #endif 609 610 static int 611 drop_privs(char *newuser) 612 { 613 struct passwd *pw; 614 gid_t gidset[1]; 615 616 pw = getpwnam(newuser); 617 if (pw == NULL) { 618 syslog(LOG_ERR, "no user %.100s", newuser); 619 return 0; 620 } 621 622 endpwent(); 623 624 gidset[0] = pw->pw_gid; 625 if (setgroups(1, gidset) == -1) { 626 syslog(LOG_ERR, "setgroups: %m"); 627 return 0; 628 } 629 630 if (setgid(pw->pw_gid) == -1) { 631 syslog(LOG_ERR, "setgid: %m"); 632 return 0; 633 } 634 635 if (setuid(pw->pw_uid) == -1) { 636 syslog(LOG_ERR, "setuid: %m"); 637 return 0; 638 } 639 640 return 1; 641 } 642 643 static void 644 usage(void) 645 { 646 (void)fprintf(stderr, "Usage: %s [-i interval] [-u user]\n", getprogname()); 647 exit(EXIT_FAILURE); 648 } 649