builtins.c revision 69144
1128723Sru/*- 2185562Sluigi * Copyright (c) 1983, 1991, 1993, 1994 3128723Sru * The Regents of the University of California. All rights reserved. 4128723Sru * 5128723Sru * Redistribution and use in source and binary forms, with or without 6128723Sru * modification, are permitted provided that the following conditions 7128723Sru * are met: 8128723Sru * 1. Redistributions of source code must retain the above copyright 9128723Sru * notice, this list of conditions and the following disclaimer. 10128723Sru * 2. Redistributions in binary form must reproduce the above copyright 11128723Sru * notice, this list of conditions and the following disclaimer in the 12128723Sru * documentation and/or other materials provided with the distribution. 13128723Sru * 14128723Sru * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15128723Sru * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16128723Sru * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17128723Sru * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18128723Sru * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19128722Sru * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20185562Sluigi * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21185562Sluigi * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22138048Sjhb * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23128722Sru * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24185562Sluigi * SUCH DAMAGE. 25186598Sluigi * 26185562Sluigi * $FreeBSD: head/usr.sbin/inetd/builtins.c 69144 2000-11-25 04:13:05Z green $ 27185562Sluigi * 28185562Sluigi */ 29185562Sluigi 30185562Sluigi#include <sys/filio.h> 31185562Sluigi#include <sys/ioccom.h> 32185562Sluigi#include <sys/param.h> 33185562Sluigi#include <sys/stat.h> 34185579Sluigi#include <sys/socket.h> 35185579Sluigi#include <sys/sysctl.h> 36185579Sluigi#include <sys/ucred.h> 37185579Sluigi#include <sys/uio.h> 38185579Sluigi#include <sys/utsname.h> 39185579Sluigi 40185579Sluigi#include <ctype.h> 41185562Sluigi#include <err.h> 42185579Sluigi#include <errno.h> 43185579Sluigi#include <fcntl.h> 44185562Sluigi#include <limits.h> 45185562Sluigi#include <pwd.h> 46185562Sluigi#include <signal.h> 47185562Sluigi#include <stdlib.h> 48185562Sluigi#include <string.h> 49185562Sluigi#include <sysexits.h> 50185562Sluigi#include <syslog.h> 51185562Sluigi#include <unistd.h> 52185562Sluigi 53185562Sluigi#include "inetd.h" 54185562Sluigi 55185562Sluigiextern int debug; 56185562Sluigiextern struct servtab *servtab; 57185562Sluigi 58185562Sluigichar ring[128]; 59185562Sluigichar *endring; 60185562Sluigi 61185562Sluigiint check_loop __P((struct sockaddr *, struct servtab *sep)); 62185562Sluigivoid inetd_setproctitle __P((char *, int)); 63185562Sluigi 64185562Sluigistruct biltin biltins[] = { 65185562Sluigi /* Echo received data */ 66185562Sluigi { "echo", SOCK_STREAM, 1, -1, echo_stream }, 67185562Sluigi { "echo", SOCK_DGRAM, 0, 1, echo_dg }, 68185562Sluigi 69185562Sluigi /* Internet /dev/null */ 70185562Sluigi { "discard", SOCK_STREAM, 1, -1, discard_stream }, 71185562Sluigi { "discard", SOCK_DGRAM, 0, 1, discard_dg }, 72185562Sluigi 73185562Sluigi /* Return 32 bit time since 1970 */ 74185562Sluigi { "time", SOCK_STREAM, 0, -1, machtime_stream }, 75185562Sluigi { "time", SOCK_DGRAM, 0, 1, machtime_dg }, 76185562Sluigi 77185562Sluigi /* Return human-readable time */ 78185562Sluigi { "daytime", SOCK_STREAM, 0, -1, daytime_stream }, 79185562Sluigi { "daytime", SOCK_DGRAM, 0, 1, daytime_dg }, 80185562Sluigi 81185562Sluigi /* Familiar character generator */ 82185562Sluigi { "chargen", SOCK_STREAM, 1, -1, chargen_stream }, 83185562Sluigi { "chargen", SOCK_DGRAM, 0, 1, chargen_dg }, 84185562Sluigi 85185562Sluigi { "tcpmux", SOCK_STREAM, 1, -1, (void (*)())tcpmux }, 86185562Sluigi 87185562Sluigi { "auth", SOCK_STREAM, 1, -1, ident_stream }, 88185562Sluigi 89185562Sluigi { NULL } 90185562Sluigi}; 91185562Sluigi 92185562Sluigi/* 93185562Sluigi * RFC864 Character Generator Protocol. Generates character data without 94185562Sluigi * any regard for input. 95185562Sluigi */ 96185562Sluigi 97185776Ssobomaxvoid 98185562Sluigiinitring() 99185562Sluigi{ 100185562Sluigi int i; 101185562Sluigi 102185562Sluigi endring = ring; 103185562Sluigi 104185562Sluigi for (i = 0; i <= 128; ++i) 105185562Sluigi if (isprint(i)) 106185562Sluigi *endring++ = i; 107185562Sluigi} 108185562Sluigi 109185562Sluigi/* ARGSUSED */ 110185562Sluigivoid 111185562Sluigichargen_dg(s, sep) /* Character generator */ 112185562Sluigi int s; 113185562Sluigi struct servtab *sep; 114185562Sluigi{ 115185562Sluigi struct sockaddr_storage ss; 116185562Sluigi static char *rs; 117185562Sluigi int len; 118185562Sluigi socklen_t size; 119185562Sluigi char text[LINESIZ+2]; 120185562Sluigi 121185562Sluigi if (endring == 0) { 122185562Sluigi initring(); 123185562Sluigi rs = ring; 124185562Sluigi } 125185562Sluigi 126185562Sluigi size = sizeof(ss); 127185562Sluigi if (recvfrom(s, text, sizeof(text), 0, 128185579Sluigi (struct sockaddr *)&ss, &size) < 0) 129185562Sluigi return; 130185562Sluigi 131185562Sluigi if (check_loop((struct sockaddr *)&ss, sep)) 132129239Sru return; 133129239Sru 134129239Sru if ((len = endring - rs) >= LINESIZ) 135128722Sru memmove(text, rs, LINESIZ); 136129239Sru else { 137185579Sluigi memmove(text, rs, len); 138128722Sru memmove(text + len, ring, LINESIZ - len); 139129239Sru } 140128722Sru if (++rs == endring) 141129239Sru rs = ring; 142129239Sru text[LINESIZ] = '\r'; 143129239Sru text[LINESIZ + 1] = '\n'; 144128722Sru (void) sendto(s, text, sizeof(text), 0, (struct sockaddr *)&ss, size); 145185562Sluigi} 146129239Sru 147128722Sru/* ARGSUSED */ 148128723Sruvoid 149185562Sluigichargen_stream(s, sep) /* Character generator */ 150185562Sluigi int s; 151185562Sluigi struct servtab *sep; 152185562Sluigi{ 153128723Sru int len; 154185579Sluigi char *rs, text[LINESIZ+2]; 155185579Sluigi 156185579Sluigi inetd_setproctitle(sep->se_service, s); 157185579Sluigi 158185562Sluigi if (!endring) { 159185562Sluigi initring(); 160185562Sluigi rs = ring; 161185579Sluigi } 162185579Sluigi 163185579Sluigi text[LINESIZ] = '\r'; 164185562Sluigi text[LINESIZ + 1] = '\n'; 165128722Sru for (rs = ring;;) { 166185562Sluigi if ((len = endring - rs) >= LINESIZ) 167129239Sru memmove(text, rs, LINESIZ); 168129239Sru else { 169128722Sru memmove(text, rs, len); 170128723Sru memmove(text + len, ring, LINESIZ - len); 171185562Sluigi } 172128723Sru if (++rs == endring) 173128723Sru rs = ring; 174128723Sru if (write(s, text, sizeof(text)) != sizeof(text)) 175185562Sluigi break; 176185562Sluigi } 177185562Sluigi exit(0); 178185562Sluigi} 179185562Sluigi 180128723Sru/* 181129239Sru * RFC867 Daytime Protocol. Sends the current date and time as an ascii 182129239Sru * character string without any regard for input. 183129239Sru */ 184129239Sru 185129239Sru/* ARGSUSED */ 186129239Sruvoid 187128722Srudaytime_dg(s, sep) /* Return human-readable time of day */ 188185562Sluigi int s; 189185562Sluigi struct servtab *sep; 190185562Sluigi{ 191129239Sru char buffer[256]; 192129239Sru time_t clock; 193129239Sru struct sockaddr_storage ss; 194129239Sru socklen_t size; 195129239Sru 196185562Sluigi clock = time((time_t *) 0); 197185562Sluigi 198185562Sluigi size = sizeof(ss); 199185562Sluigi if (recvfrom(s, buffer, sizeof(buffer), 0, 200185562Sluigi (struct sockaddr *)&ss, &size) < 0) 201185562Sluigi return; 202129239Sru 203129239Sru if (check_loop((struct sockaddr *)&ss, sep)) 204129239Sru return; 205129239Sru 206185562Sluigi (void) sprintf(buffer, "%.24s\r\n", ctime(&clock)); 207128722Sru (void) sendto(s, buffer, strlen(buffer), 0, 208185562Sluigi (struct sockaddr *)&ss, size); 209185562Sluigi} 210128722Sru 211138048Sjhb/* ARGSUSED */ 212185562Sluigivoid 213185562Sluigidaytime_stream(s, sep) /* Return human-readable time of day */ 214185562Sluigi int s; 215167914Sthomas struct servtab *sep; 216130632Sphk{ 217138048Sjhb char buffer[256]; 218185562Sluigi time_t clock; 219185562Sluigi 220185562Sluigi clock = time((time_t *) 0); 221185562Sluigi 222185562Sluigi (void) sprintf(buffer, "%.24s\r\n", ctime(&clock)); 223185562Sluigi (void) send(s, buffer, strlen(buffer), MSG_EOF); 224185562Sluigi} 225185562Sluigi 226185562Sluigi/* 227185562Sluigi * RFC863 Discard Protocol. Any data received is thrown away and no response 228185562Sluigi * is sent. 229185562Sluigi */ 230185562Sluigi 231185562Sluigi/* ARGSUSED */ 232129239Sruvoid 233185562Sluigidiscard_dg(s, sep) /* Discard service -- ignore data */ 234185562Sluigi int s; 235185562Sluigi struct servtab *sep; 236185562Sluigi{ 237185562Sluigi char buffer[BUFSIZE]; 238185562Sluigi 239185562Sluigi (void) read(s, buffer, sizeof(buffer)); 240185562Sluigi} 241185562Sluigi 242185562Sluigi/* ARGSUSED */ 243185562Sluigivoid 244185562Sluigidiscard_stream(s, sep) /* Discard service -- ignore data */ 245185562Sluigi int s; 246185562Sluigi struct servtab *sep; 247185562Sluigi{ 248185562Sluigi int ret; 249185562Sluigi char buffer[BUFSIZE]; 250185562Sluigi 251185562Sluigi inetd_setproctitle(sep->se_service, s); 252185562Sluigi while (1) { 253185562Sluigi while ((ret = read(s, buffer, sizeof(buffer))) > 0) 254185562Sluigi ; 255185562Sluigi if (ret == 0 || errno != EINTR) 256185562Sluigi break; 257185562Sluigi } 258185562Sluigi exit(0); 259185562Sluigi} 260185562Sluigi 261129239Sru/* 262129239Sru * RFC862 Echo Protocol. Any data received is sent back to the sender as 263185562Sluigi * received. 264185562Sluigi */ 265185562Sluigi 266185562Sluigi/* ARGSUSED */ 267185562Sluigivoid 268185562Sluigiecho_dg(s, sep) /* Echo service -- echo data back */ 269129239Sru int s; 270185562Sluigi struct servtab *sep; 271129239Sru{ 272185579Sluigi char buffer[BUFSIZE]; 273185579Sluigi int i; 274185562Sluigi socklen_t size; 275185579Sluigi struct sockaddr_storage ss; 276185562Sluigi 277185562Sluigi size = sizeof(ss); 278186598Sluigi if ((i = recvfrom(s, buffer, sizeof(buffer), 0, 279186598Sluigi (struct sockaddr *)&ss, &size)) < 0) 280185562Sluigi return; 281185579Sluigi 282186598Sluigi if (check_loop((struct sockaddr *)&ss, sep)) 283139948Speadar return; 284139948Speadar 285185562Sluigi (void) sendto(s, buffer, i, 0, (struct sockaddr *)&ss, size); 286185562Sluigi} 287185562Sluigi 288185562Sluigi/* ARGSUSED */ 289185562Sluigivoid 290185562Sluigiecho_stream(s, sep) /* Echo service -- echo data back */ 291139948Speadar int s; 292129239Sru struct servtab *sep; 293129239Sru{ 294185562Sluigi char buffer[BUFSIZE]; 295185562Sluigi int i; 296129239Sru 297185562Sluigi inetd_setproctitle(sep->se_service, s); 298185562Sluigi while ((i = read(s, buffer, sizeof(buffer))) > 0 && 299185562Sluigi write(s, buffer, i) > 0) 300185562Sluigi ; 301185562Sluigi exit(0); 302185562Sluigi} 303185562Sluigi 304129239Sru/* 305129239Sru * RFC1413 Identification Protocol. Given a TCP port number pair, return a 306129239Sru * character string which identifies the owner of that connection on the 307185562Sluigi * server's system. Extended to allow for ~/.fakeid support and ~/.noident 308185562Sluigi * support. 309185562Sluigi */ 310185562Sluigi 311129239Sru/* ARGSUSED */ 312185562Sluigivoid 313185562Sluigiiderror(lport, fport, s, er) /* Generic ident_stream error-sending func */ 314185562Sluigi int lport, fport, s, er; 315185562Sluigi{ 316129239Sru char *p; 317185562Sluigi 318185562Sluigi asprintf(&p, "%d , %d : ERROR : %s\r\n", lport, fport, 319185562Sluigi er == -1 ? "HIDDEN-USER" : er ? strerror(er) : "UNKNOWN-ERROR"); 320185562Sluigi if (p == NULL) { 321185562Sluigi syslog(LOG_ERR, "asprintf: %m"); 322185562Sluigi exit(EX_OSERR); 323185562Sluigi } 324129239Sru send(s, p, strlen(p), MSG_EOF); 325129239Sru free(p); 326129239Sru 327185562Sluigi exit(0); 328185562Sluigi} 329186598Sluigi 330185562Sluigi/* ARGSUSED */ 331185562Sluigivoid 332185562Sluigiident_stream(s, sep) /* Ident service (AKA "auth") */ 333129239Sru int s; 334129239Sru struct servtab *sep; 335129239Sru{ 336129239Sru struct utsname un; 337185562Sluigi struct stat sb; 338185562Sluigi struct sockaddr_in sin[2]; 339130343Sphk#ifdef INET6 340185562Sluigi struct sockaddr_in6 sin6[2]; 341130343Sphk#endif 342185562Sluigi struct sockaddr_storage ss[2]; 343185562Sluigi struct ucred uc; 344185562Sluigi struct timeval tv = { 345185562Sluigi 10, 346185562Sluigi 0 347185562Sluigi }, to; 348185562Sluigi struct passwd *pw = NULL; 349185562Sluigi fd_set fdset; 350129239Sru char buf[BUFSIZE], *cp = NULL, *p, **av, *osname = NULL, garbage[7], e; 351129239Sru char *fallback = NULL; 352130343Sphk socklen_t socklen; 353185562Sluigi ssize_t ssize; 354185562Sluigi size_t size, bufsiz; 355185562Sluigi int c, fflag = 0, nflag = 0, rflag = 0, argc = 0, usedfallback = 0; 356185562Sluigi int gflag = 0, getcredfail = 0, onreadlen; 357128722Sru u_short lport, fport; 358129239Sru 359129239Sru inetd_setproctitle(sep->se_service, s); 360128722Sru /* 361129239Sru * Reset getopt() since we are a fork() but not an exec() from 362130343Sphk * a parent which used getopt() already. 363129239Sru */ 364185562Sluigi optind = 1; 365128722Sru optreset = 1; 366185562Sluigi /* 367185562Sluigi * Take the internal argument vector and count it out to make an 368185562Sluigi * argument count for getopt. This can be used for any internal 369129239Sru * service to read arguments and use getopt() easily. 370185562Sluigi */ 371185562Sluigi for (av = sep->se_argv; *av; av++) 372185562Sluigi argc++; 373185562Sluigi if (argc) { 374185562Sluigi int sec, usec; 375185562Sluigi size_t i; 376186598Sluigi u_int32_t random; 377185562Sluigi 378185562Sluigi while ((c = getopt(argc, sep->se_argv, "d:fgno:rt:")) != -1) 379185562Sluigi switch (c) { 380185562Sluigi case 'd': 381185562Sluigi fallback = optarg; 382185562Sluigi break; 383185562Sluigi case 'f': 384185562Sluigi fflag = 1; 385185562Sluigi break; 386185562Sluigi case 'g': 387128722Sru gflag = 1; 388185562Sluigi random = 0; /* Shush, compiler. */ 389185562Sluigi /* 390185562Sluigi * The number of bits in "random" divided 391185562Sluigi * by the number of bits needed per iteration 392138048Sjhb * gives a more optimal way to reload the 393129239Sru * random number only when necessary. 394130343Sphk * 395185562Sluigi * I'm using base-36, so I need at least 6 396138048Sjhb * bits; round it up to 8 bits to make it 397185562Sluigi * easier. 398185562Sluigi */ 399185562Sluigi for (i = 0; i < sizeof(garbage) - 1; i++) { 400185562Sluigi const char *const base36 = 401185562Sluigi "0123456789" 402185562Sluigi "abcdefghijklmnopqrstuvwxyz"; 403185562Sluigi if (i % (sizeof(random) * 8 / 8) == 0) 404185562Sluigi random = arc4random(); 405185562Sluigi garbage[i] = 406185562Sluigi base36[(random & 0xff) % 36]; 407185562Sluigi random >>= 8; 408185562Sluigi } 409185562Sluigi garbage[i] = '\0'; 410185562Sluigi break; 411185562Sluigi case 'n': 412129239Sru nflag = 1; 413185562Sluigi break; 414185562Sluigi case 'o': 415185562Sluigi osname = optarg; 416228738Sjhb break; 417185562Sluigi case 'r': 418185562Sluigi rflag = 1; 419185562Sluigi break; 420185562Sluigi case 't': 421185562Sluigi switch (sscanf(optarg, "%d.%d", &sec, &usec)) { 422185562Sluigi case 2: 423185562Sluigi tv.tv_usec = usec; 424185562Sluigi case 1: 425185562Sluigi tv.tv_sec = sec; 426185562Sluigi break; 427185562Sluigi default: 428185562Sluigi if (debug) 429185562Sluigi warnx("bad -t argument"); 430185562Sluigi break; 431185562Sluigi } 432185562Sluigi break; 433185562Sluigi default: 434185562Sluigi break; 435185562Sluigi } 436185562Sluigi } 437129239Sru if (osname == NULL) { 438185562Sluigi if (uname(&un) == -1) 439185562Sluigi iderror(0, 0, s, errno); 440185562Sluigi osname = un.sysname; 441185562Sluigi } 442185562Sluigi socklen = sizeof(ss[0]); 443185562Sluigi if (getsockname(s, (struct sockaddr *)&ss[0], &socklen) == -1) 444185562Sluigi iderror(0, 0, s, errno); 445185562Sluigi socklen = sizeof(ss[1]); 446185562Sluigi if (getpeername(s, (struct sockaddr *)&ss[1], &socklen) == -1) 447185562Sluigi iderror(0, 0, s, errno); 448129239Sru /* 449129239Sru * We're going to prepare for and execute reception of a 450185562Sluigi * packet of data from the user. The data is in the format 451185562Sluigi * "local_port , foreign_port\r\n" (with local being the 452185562Sluigi * server's port and foreign being the client's.) 453185562Sluigi */ 454185562Sluigi gettimeofday(&to, NULL); 455185562Sluigi to.tv_sec += tv.tv_sec; 456185562Sluigi if ((to.tv_usec += tv.tv_usec) >= 1000000) { 457129239Sru to.tv_usec -= 1000000; 458129239Sru to.tv_sec++; 459129239Sru } 460129239Sru 461185562Sluigi size = 0; 462185562Sluigi bufsiz = sizeof(buf) - 1; 463185562Sluigi FD_ZERO(&fdset); 464185562Sluigi while (bufsiz > 0 && (size == 0 || buf[size - 1] != '\n')) { 465185562Sluigi gettimeofday(&tv, NULL); 466185562Sluigi tv.tv_sec = to.tv_sec - tv.tv_sec; 467185562Sluigi tv.tv_usec = to.tv_usec - tv.tv_usec; 468185562Sluigi if (tv.tv_usec < 0) { 469129239Sru tv.tv_usec += 1000000; 470129239Sru tv.tv_sec--; 471129239Sru } 472185562Sluigi if (tv.tv_sec < 0) 473185562Sluigi break; 474185562Sluigi FD_SET(s, &fdset); 475185562Sluigi if (select(s + 1, &fdset, NULL, NULL, &tv) == -1) 476185562Sluigi iderror(0, 0, s, errno); 477185562Sluigi if (ioctl(s, FIONREAD, &onreadlen) == -1) 478185562Sluigi iderror(0, 0, s, errno); 479185562Sluigi if (onreadlen > bufsiz) 480129239Sru onreadlen = bufsiz; 481129239Sru ssize = read(s, &buf[size], (size_t)onreadlen); 482185562Sluigi if (ssize == -1) 483185562Sluigi iderror(0, 0, s, errno); 484185562Sluigi else if (ssize == 0) 485185562Sluigi break; 486185562Sluigi bufsiz -= ssize; 487129239Sru size += ssize; 488129239Sru } 489185562Sluigi buf[size] = '\0'; 490129239Sru /* Read two characters, and check for a delimiting character */ 491185562Sluigi if (sscanf(buf, "%hu , %hu%c", &lport, &fport, &e) != 3 || isdigit(e)) 492185339Sluigi iderror(0, 0, s, 0); 493185346Sluigi if (gflag) { 494185339Sluigi cp = garbage; 495129239Sru goto printit; 496128722Sru } 497128723Sru 498128723Sru /* 499185562Sluigi * If not "real" (-r), send a HIDDEN-USER error for everything. 500185562Sluigi * If -d is used to set a fallback username, this is used to 501185562Sluigi * override it, and the fallback is returned instead. 502185562Sluigi */ 503185562Sluigi if (!rflag) { 504185562Sluigi if (fallback == NULL) 505185562Sluigi iderror(lport, fport, s, -1); 506128723Sru else { 507128722Sru cp = fallback; 508128723Sru goto printit; 509185562Sluigi } 510185562Sluigi } 511185562Sluigi 512128723Sru /* 513129239Sru * We take the input and construct an array of two sockaddr_ins 514129239Sru * which contain the local address information and foreign 515129239Sru * address information, respectively, used to look up the 516129239Sru * credentials for the socket (which are returned by the 517185579Sluigi * sysctl "net.inet.tcp.getcred" when we call it.) The 518128722Sru * arrays have been filled in above via get{peer,sock}name(), 519129239Sru * so right here we are only setting the ports. 520185579Sluigi */ 521128722Sru if (ss[0].ss_family != ss[1].ss_family) 522185579Sluigi iderror(lport, fport, s, errno); 523185579Sluigi size = sizeof(uc); 524185579Sluigi switch (ss[0].ss_family) { 525185579Sluigi case AF_INET: 526185579Sluigi sin[0] = *(struct sockaddr_in *)&ss[0]; 527185579Sluigi sin[0].sin_port = htons(lport); 528185579Sluigi sin[1] = *(struct sockaddr_in *)&ss[1]; 529185579Sluigi sin[1].sin_port = htons(fport); 530185579Sluigi if (sysctlbyname("net.inet.tcp.getcred", &uc, &size, sin, 531129239Sru sizeof(sin)) == -1) 532129239Sru getcredfail = 1; 533185579Sluigi break; 534185579Sluigi#ifdef INET6 535128722Sru case AF_INET6: 536185562Sluigi sin6[0] = *(struct sockaddr_in6 *)&ss[0]; 537130343Sphk sin6[0].sin6_port = htons(lport); 538129239Sru sin6[1] = *(struct sockaddr_in6 *)&ss[1]; 539129239Sru sin6[1].sin6_port = htons(fport); 540129239Sru if (sysctlbyname("net.inet6.tcp6.getcred", &uc, &size, sin6, 541129239Sru sizeof(sin6)) == -1) 542129239Sru getcredfail = 1; 543128722Sru break; 544130343Sphk#endif 545130343Sphk default: /* should not reach here */ 546129239Sru getcredfail = 1; 547129239Sru break; 548129239Sru } 549129239Sru if (getcredfail != 0) { 550185562Sluigi if (fallback == NULL) /* Use a default, if asked to */ 551130343Sphk iderror(lport, fport, s, errno); 552128722Sru usedfallback = 1; 553128723Sru } else { 554128722Sru /* Look up the pw to get the username */ 555185562Sluigi pw = getpwuid(uc.cr_uid); 556185562Sluigi } 557185562Sluigi if (pw == NULL && !usedfallback) /* No such user... */ 558185562Sluigi iderror(lport, fport, s, errno); 559185562Sluigi /* 560185562Sluigi * If enabled, we check for a file named ".noident" in the user's 561185562Sluigi * home directory. If found, we return HIDDEN-USER. 562185562Sluigi */ 563129239Sru if (nflag && !usedfallback) { 564129239Sru if (asprintf(&p, "%s/.noident", pw->pw_dir) == -1) 565129239Sru iderror(lport, fport, s, errno); 566129239Sru if (lstat(p, &sb) == 0) { 567185562Sluigi free(p); 568185562Sluigi iderror(lport, fport, s, -1); 569185562Sluigi } 570185562Sluigi free(p); 571185562Sluigi } 572185562Sluigi /* 573129239Sru * Here, if enabled, we read a user's ".fakeid" file in their 574129239Sru * home directory. It consists of a line containing the name 575129239Sru * they want. 576129239Sru */ 577129239Sru if (fflag && !usedfallback) { 578129239Sru FILE *fakeid = NULL; 579129239Sru int fakeid_fd; 580129239Sru 581129239Sru if (asprintf(&p, "%s/.fakeid", pw->pw_dir) == -1) 582185562Sluigi iderror(lport, fport, s, errno); 583129239Sru /* 584129239Sru * Here we set ourself to effectively be the user, so we don't 585129239Sru * open any files we have no permission to open, especially 586128722Sru * symbolic links to sensitive root-owned files or devices. 587185562Sluigi */ 588185562Sluigi if (initgroups(pw->pw_name, pw->pw_gid) == -1) 589185562Sluigi iderror(lport, fport, s, errno); 590185562Sluigi seteuid(pw->pw_uid); 591186598Sluigi /* 592185562Sluigi * If we were to lstat() here, it would do no good, since it 593186598Sluigi * would introduce a race condition and could be defeated. 594186598Sluigi * Therefore, we open the file we have permissions to open 595186598Sluigi * and if it's not a regular file, we close it and end up 596185562Sluigi * returning the user's real username. 597128722Sru */ 598128722Sru fakeid_fd = open(p, O_RDONLY | O_NONBLOCK); 599128723Sru free(p); 600128722Sru if ((fakeid = fdopen(fakeid_fd, "r")) != NULL && 601185562Sluigi fstat(fileno(fakeid), &sb) != -1 && S_ISREG(sb.st_mode)) { 602185562Sluigi buf[sizeof(buf) - 1] = '\0'; 603185579Sluigi if (fgets(buf, sizeof(buf), fakeid) == NULL) { 604185579Sluigi cp = pw->pw_name; 605185579Sluigi fclose(fakeid); 606185562Sluigi goto printit; 607186598Sluigi } 608185579Sluigi fclose(fakeid); 609186598Sluigi /* 610185579Sluigi * Usually, the file will have the desired identity 611186598Sluigi * in the form "identity\n", so we use strcspn() to 612186598Sluigi * end the string (which fgets() doesn't do.) 613186598Sluigi */ 614186598Sluigi buf[strcspn(buf, "\r\n")] = '\0'; 615185579Sluigi cp = buf; 616185562Sluigi /* Allow for beginning white space... */ 617185562Sluigi while (isspace(*cp)) 618185562Sluigi cp++; 619185562Sluigi /* ...and ending white space. */ 620185562Sluigi cp[strcspn(cp, " \t")] = '\0'; 621185562Sluigi /* User names of >16 characters are invalid */ 622185562Sluigi if (strlen(cp) > 16) 623185562Sluigi cp[16] = '\0'; 624185562Sluigi /* 625185562Sluigi * If the name is a zero-length string or matches 626186598Sluigi * the name of another user, it's invalid, so 627186598Sluigi * we will return their real identity instead. 628186598Sluigi */ 629186598Sluigi 630185579Sluigi if (!*cp || getpwnam(cp)) { 631185579Sluigi pw = getpwuid(uc.cr_uid); 632185579Sluigi if (pw == NULL) 633186598Sluigi iderror(lport, fport, s, errno); 634186598Sluigi cp = pw->pw_name; 635186598Sluigi } 636186598Sluigi } else 637139948Speadar cp = pw->pw_name; 638185562Sluigi if (fakeid_fd != -1) 639185562Sluigi close(fakeid_fd); 640185562Sluigi } else if (!usedfallback) 641185562Sluigi cp = pw->pw_name; 642185562Sluigi else 643185562Sluigi cp = fallback; 644185562Sluigiprintit: 645186598Sluigi /* Finally, we make and send the reply. */ 646185562Sluigi if (asprintf(&p, "%d , %d : USERID : %s : %s\r\n", lport, fport, osname, 647185562Sluigi cp) == -1) { 648186598Sluigi syslog(LOG_ERR, "asprintf: %m"); 649128722Sru exit(EX_OSERR); 650128722Sru } 651128722Sru send(s, p, strlen(p), MSG_EOF); 652185579Sluigi free(p); 653185579Sluigi 654185579Sluigi exit(0); 655128722Sru} 656185579Sluigi 657128723Sru/* 658185562Sluigi * RFC738 Time Server. 659185562Sluigi * Return a machine readable date and time, in the form of the 660185562Sluigi * number of seconds since midnight, Jan 1, 1900. Since gettimeofday 661185562Sluigi * returns the number of seconds since midnight, Jan 1, 1970, 662128723Sru * we must add 2208988800 seconds to this figure to make up for 663128723Sru * some seventy years Bell Labs was asleep. 664128723Sru */ 665128722Sru 666129239Sruunsigned long 667129239Srumachtime() 668185562Sluigi{ 669129239Sru struct timeval tv; 670185579Sluigi 671185579Sluigi if (gettimeofday(&tv, (struct timezone *)NULL) < 0) { 672185579Sluigi if (debug) 673129239Sru warnx("unable to get time of day"); 674128722Sru return (0L); 675185562Sluigi } 676128723Sru#define OFFSET ((u_long)25567 * 24*60*60) 677137298Skeramida return (htonl((long)(tv.tv_sec + OFFSET))); 678128723Sru#undef OFFSET 679129239Sru} 680129239Sru 681185562Sluigi/* ARGSUSED */ 682185562Sluigivoid 683machtime_dg(s, sep) 684 int s; 685 struct servtab *sep; 686{ 687 unsigned long result; 688 struct sockaddr_storage ss; 689 socklen_t size; 690 691 size = sizeof(ss); 692 if (recvfrom(s, (char *)&result, sizeof(result), 0, 693 (struct sockaddr *)&ss, &size) < 0) 694 return; 695 696 if (check_loop((struct sockaddr *)&ss, sep)) 697 return; 698 699 result = machtime(); 700 (void) sendto(s, (char *) &result, sizeof(result), 0, 701 (struct sockaddr *)&ss, size); 702} 703 704/* ARGSUSED */ 705void 706machtime_stream(s, sep) 707 int s; 708 struct servtab *sep; 709{ 710 unsigned long result; 711 712 result = machtime(); 713 (void) send(s, (char *) &result, sizeof(result), MSG_EOF); 714} 715 716/* 717 * RFC1078 TCP Port Service Multiplexer (TCPMUX). Service connections to 718 * services based on the service name sent. 719 * 720 * Based on TCPMUX.C by Mark K. Lottor November 1988 721 * sri-nic::ps:<mkl>tcpmux.c 722 */ 723 724#define MAX_SERV_LEN (256+2) /* 2 bytes for \r\n */ 725#define strwrite(fd, buf) (void) write(fd, buf, sizeof(buf)-1) 726 727static int /* # of characters upto \r,\n or \0 */ 728getline(fd, buf, len) 729 int fd; 730 char *buf; 731 int len; 732{ 733 int count = 0, n; 734 struct sigaction sa; 735 736 sa.sa_flags = 0; 737 sigemptyset(&sa.sa_mask); 738 sa.sa_handler = SIG_DFL; 739 sigaction(SIGALRM, &sa, (struct sigaction *)0); 740 do { 741 alarm(10); 742 n = read(fd, buf, len-count); 743 alarm(0); 744 if (n == 0) 745 return (count); 746 if (n < 0) 747 return (-1); 748 while (--n >= 0) { 749 if (*buf == '\r' || *buf == '\n' || *buf == '\0') 750 return (count); 751 count++; 752 buf++; 753 } 754 } while (count < len); 755 return (count); 756} 757 758struct servtab * 759tcpmux(s) 760 int s; 761{ 762 struct servtab *sep; 763 char service[MAX_SERV_LEN+1]; 764 int len; 765 766 /* Get requested service name */ 767 if ((len = getline(s, service, MAX_SERV_LEN)) < 0) { 768 strwrite(s, "-Error reading service name\r\n"); 769 return (NULL); 770 } 771 service[len] = '\0'; 772 773 if (debug) 774 warnx("tcpmux: someone wants %s", service); 775 776 /* 777 * Help is a required command, and lists available services, 778 * one per line. 779 */ 780 if (!strcasecmp(service, "help")) { 781 for (sep = servtab; sep; sep = sep->se_next) { 782 if (!ISMUX(sep)) 783 continue; 784 (void)write(s,sep->se_service,strlen(sep->se_service)); 785 strwrite(s, "\r\n"); 786 } 787 return (NULL); 788 } 789 790 /* Try matching a service in inetd.conf with the request */ 791 for (sep = servtab; sep; sep = sep->se_next) { 792 if (!ISMUX(sep)) 793 continue; 794 if (!strcasecmp(service, sep->se_service)) { 795 if (ISMUXPLUS(sep)) { 796 strwrite(s, "+Go\r\n"); 797 } 798 return (sep); 799 } 800 } 801 strwrite(s, "-Service not available\r\n"); 802 return (NULL); 803} 804