149004Sgreen/*- 249004Sgreen * Copyright (c) 1983, 1991, 1993, 1994 349004Sgreen * The Regents of the University of California. All rights reserved. 449004Sgreen * 549004Sgreen * Redistribution and use in source and binary forms, with or without 649004Sgreen * modification, are permitted provided that the following conditions 749004Sgreen * are met: 849004Sgreen * 1. Redistributions of source code must retain the above copyright 949004Sgreen * notice, this list of conditions and the following disclaimer. 1049004Sgreen * 2. Redistributions in binary form must reproduce the above copyright 1149004Sgreen * notice, this list of conditions and the following disclaimer in the 1249004Sgreen * documentation and/or other materials provided with the distribution. 1349004Sgreen * 1449004Sgreen * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 1549004Sgreen * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 1649004Sgreen * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 1749004Sgreen * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 1849004Sgreen * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 1949004Sgreen * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2049004Sgreen * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2149004Sgreen * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2249004Sgreen * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2349004Sgreen * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2449004Sgreen * SUCH DAMAGE. 2549004Sgreen */ 2649004Sgreen 2798563Sjmallett#include <sys/cdefs.h> 2898563Sjmallett__FBSDID("$FreeBSD: stable/10/usr.sbin/inetd/builtins.c 326761 2017-12-11 05:10:11Z delphij $"); 2998563Sjmallett 3049004Sgreen#include <sys/filio.h> 3149004Sgreen#include <sys/ioccom.h> 3248981Ssheldonh#include <sys/param.h> 3348981Ssheldonh#include <sys/stat.h> 3448981Ssheldonh#include <sys/socket.h> 3548981Ssheldonh#include <sys/sysctl.h> 3648981Ssheldonh#include <sys/ucred.h> 3748981Ssheldonh#include <sys/uio.h> 3849004Sgreen#include <sys/utsname.h> 3948981Ssheldonh 4048981Ssheldonh#include <ctype.h> 4148981Ssheldonh#include <err.h> 4248981Ssheldonh#include <errno.h> 4369144Sgreen#include <fcntl.h> 4448981Ssheldonh#include <limits.h> 4548981Ssheldonh#include <pwd.h> 4648981Ssheldonh#include <signal.h> 4749004Sgreen#include <stdlib.h> 4848981Ssheldonh#include <string.h> 4949030Ssheldonh#include <sysexits.h> 5049030Ssheldonh#include <syslog.h> 5148981Ssheldonh#include <unistd.h> 5248981Ssheldonh 5348981Ssheldonh#include "inetd.h" 5448981Ssheldonh 5598562Sjmallettvoid chargen_dg(int, struct servtab *); 5698562Sjmallettvoid chargen_stream(int, struct servtab *); 5798562Sjmallettvoid daytime_dg(int, struct servtab *); 5898562Sjmallettvoid daytime_stream(int, struct servtab *); 5998562Sjmallettvoid discard_dg(int, struct servtab *); 6098562Sjmallettvoid discard_stream(int, struct servtab *); 6198562Sjmallettvoid echo_dg(int, struct servtab *); 6298562Sjmallettvoid echo_stream(int, struct servtab *); 6398562Sjmallettstatic int getline(int, char *, int); 6498562Sjmallettvoid iderror(int, int, int, const char *); 6598562Sjmallettvoid ident_stream(int, struct servtab *); 6698562Sjmallettvoid initring(void); 67157816Sdwmaloneuint32_t machtime(void); 6898562Sjmallettvoid machtime_dg(int, struct servtab *); 6998562Sjmallettvoid machtime_stream(int, struct servtab *); 7048981Ssheldonh 7148981Ssheldonhchar ring[128]; 7248981Ssheldonhchar *endring; 7348981Ssheldonh 7448981Ssheldonh 7548981Ssheldonhstruct biltin biltins[] = { 7648981Ssheldonh /* Echo received data */ 7748981Ssheldonh { "echo", SOCK_STREAM, 1, -1, echo_stream }, 7848981Ssheldonh { "echo", SOCK_DGRAM, 0, 1, echo_dg }, 7948981Ssheldonh 8048981Ssheldonh /* Internet /dev/null */ 8148981Ssheldonh { "discard", SOCK_STREAM, 1, -1, discard_stream }, 8248981Ssheldonh { "discard", SOCK_DGRAM, 0, 1, discard_dg }, 8348981Ssheldonh 8477685Sdwmalone /* Return 32 bit time since 1900 */ 8548981Ssheldonh { "time", SOCK_STREAM, 0, -1, machtime_stream }, 8648981Ssheldonh { "time", SOCK_DGRAM, 0, 1, machtime_dg }, 8748981Ssheldonh 8848981Ssheldonh /* Return human-readable time */ 8948981Ssheldonh { "daytime", SOCK_STREAM, 0, -1, daytime_stream }, 9048981Ssheldonh { "daytime", SOCK_DGRAM, 0, 1, daytime_dg }, 9148981Ssheldonh 9248981Ssheldonh /* Familiar character generator */ 9348981Ssheldonh { "chargen", SOCK_STREAM, 1, -1, chargen_stream }, 9448981Ssheldonh { "chargen", SOCK_DGRAM, 0, 1, chargen_dg }, 9548981Ssheldonh 9678694Sdwmalone { "tcpmux", SOCK_STREAM, 1, -1, (bi_fn_t *)tcpmux }, 9748981Ssheldonh 9848981Ssheldonh { "auth", SOCK_STREAM, 1, -1, ident_stream }, 9948981Ssheldonh 10078694Sdwmalone { NULL, 0, 0, 0, NULL } 10148981Ssheldonh}; 10248981Ssheldonh 10349052Ssheldonh/* 10449052Ssheldonh * RFC864 Character Generator Protocol. Generates character data without 10549052Ssheldonh * any regard for input. 10649052Ssheldonh */ 10749052Ssheldonh 10848981Ssheldonhvoid 10998558Sjmallettinitring(void) 11048981Ssheldonh{ 11148981Ssheldonh int i; 11248981Ssheldonh 11348981Ssheldonh endring = ring; 11448981Ssheldonh 11548981Ssheldonh for (i = 0; i <= 128; ++i) 11648981Ssheldonh if (isprint(i)) 11748981Ssheldonh *endring++ = i; 11848981Ssheldonh} 11948981Ssheldonh 120157820Sdwmalone/* Character generator 121157820Sdwmalone * The RFC says that we should send back a random number of 122157820Sdwmalone * characters chosen from the range 0 to 512. We send LINESIZ+2. 123157820Sdwmalone */ 12448981Ssheldonh/* ARGSUSED */ 12548981Ssheldonhvoid 12698559Sjmallettchargen_dg(int s, struct servtab *sep) 12748981Ssheldonh{ 12856590Sshin struct sockaddr_storage ss; 12948981Ssheldonh static char *rs; 13057857Sshin int len; 13157857Sshin socklen_t size; 13248981Ssheldonh char text[LINESIZ+2]; 13348981Ssheldonh 13448981Ssheldonh if (endring == 0) { 13548981Ssheldonh initring(); 13648981Ssheldonh rs = ring; 13748981Ssheldonh } 13848981Ssheldonh 13956590Sshin size = sizeof(ss); 14048981Ssheldonh if (recvfrom(s, text, sizeof(text), 0, 14156590Sshin (struct sockaddr *)&ss, &size) < 0) 14248981Ssheldonh return; 14348981Ssheldonh 14456590Sshin if (check_loop((struct sockaddr *)&ss, sep)) 14548981Ssheldonh return; 14648981Ssheldonh 14748981Ssheldonh if ((len = endring - rs) >= LINESIZ) 14848981Ssheldonh memmove(text, rs, LINESIZ); 14948981Ssheldonh else { 15048981Ssheldonh memmove(text, rs, len); 15148981Ssheldonh memmove(text + len, ring, LINESIZ - len); 15248981Ssheldonh } 15348981Ssheldonh if (++rs == endring) 15448981Ssheldonh rs = ring; 15548981Ssheldonh text[LINESIZ] = '\r'; 15648981Ssheldonh text[LINESIZ + 1] = '\n'; 15757857Sshin (void) sendto(s, text, sizeof(text), 0, (struct sockaddr *)&ss, size); 15848981Ssheldonh} 15948981Ssheldonh 16098559Sjmallett/* Character generator */ 16148981Ssheldonh/* ARGSUSED */ 16248981Ssheldonhvoid 16398559Sjmallettchargen_stream(int s, struct servtab *sep) 16448981Ssheldonh{ 16548981Ssheldonh int len; 16648981Ssheldonh char *rs, text[LINESIZ+2]; 16748981Ssheldonh 16848981Ssheldonh inetd_setproctitle(sep->se_service, s); 16948981Ssheldonh 170326761Sdelphij if (!endring) 17148981Ssheldonh initring(); 17248981Ssheldonh 17348981Ssheldonh text[LINESIZ] = '\r'; 17448981Ssheldonh text[LINESIZ + 1] = '\n'; 17548981Ssheldonh for (rs = ring;;) { 17648981Ssheldonh if ((len = endring - rs) >= LINESIZ) 17748981Ssheldonh memmove(text, rs, LINESIZ); 17848981Ssheldonh else { 17948981Ssheldonh memmove(text, rs, len); 18048981Ssheldonh memmove(text + len, ring, LINESIZ - len); 18148981Ssheldonh } 18248981Ssheldonh if (++rs == endring) 18348981Ssheldonh rs = ring; 18448981Ssheldonh if (write(s, text, sizeof(text)) != sizeof(text)) 18548981Ssheldonh break; 18648981Ssheldonh } 18748981Ssheldonh exit(0); 18848981Ssheldonh} 18948981Ssheldonh 19049052Ssheldonh/* 19149052Ssheldonh * RFC867 Daytime Protocol. Sends the current date and time as an ascii 19249052Ssheldonh * character string without any regard for input. 19349052Ssheldonh */ 19449052Ssheldonh 19598559Sjmallett/* Return human-readable time of day */ 19648981Ssheldonh/* ARGSUSED */ 19748981Ssheldonhvoid 19898559Sjmallettdaytime_dg(int s, struct servtab *sep) 19948981Ssheldonh{ 20048981Ssheldonh char buffer[256]; 20178694Sdwmalone time_t now; 20256590Sshin struct sockaddr_storage ss; 20357857Sshin socklen_t size; 20448981Ssheldonh 20578694Sdwmalone now = time((time_t *) 0); 20648981Ssheldonh 20756590Sshin size = sizeof(ss); 20848981Ssheldonh if (recvfrom(s, buffer, sizeof(buffer), 0, 20956590Sshin (struct sockaddr *)&ss, &size) < 0) 21048981Ssheldonh return; 21148981Ssheldonh 21256590Sshin if (check_loop((struct sockaddr *)&ss, sep)) 21348981Ssheldonh return; 21448981Ssheldonh 21578694Sdwmalone (void) sprintf(buffer, "%.24s\r\n", ctime(&now)); 21648981Ssheldonh (void) sendto(s, buffer, strlen(buffer), 0, 21757857Sshin (struct sockaddr *)&ss, size); 21848981Ssheldonh} 21948981Ssheldonh 22098559Sjmallett/* Return human-readable time of day */ 22148981Ssheldonh/* ARGSUSED */ 22248981Ssheldonhvoid 22398559Sjmallettdaytime_stream(int s, struct servtab *sep __unused) 22448981Ssheldonh{ 22548981Ssheldonh char buffer[256]; 22678694Sdwmalone time_t now; 22748981Ssheldonh 22878694Sdwmalone now = time((time_t *) 0); 22948981Ssheldonh 23078694Sdwmalone (void) sprintf(buffer, "%.24s\r\n", ctime(&now)); 23158735Ssheldonh (void) send(s, buffer, strlen(buffer), MSG_EOF); 23248981Ssheldonh} 23348981Ssheldonh 23449052Ssheldonh/* 23549052Ssheldonh * RFC863 Discard Protocol. Any data received is thrown away and no response 23649052Ssheldonh * is sent. 23749052Ssheldonh */ 23849052Ssheldonh 23998559Sjmallett/* Discard service -- ignore data */ 24048981Ssheldonh/* ARGSUSED */ 24148981Ssheldonhvoid 24298561Sjmallettdiscard_dg(int s, struct servtab *sep __unused) 24348981Ssheldonh{ 24448981Ssheldonh char buffer[BUFSIZE]; 24548981Ssheldonh 24648981Ssheldonh (void) read(s, buffer, sizeof(buffer)); 24748981Ssheldonh} 24848981Ssheldonh 24998559Sjmallett/* Discard service -- ignore data */ 25048981Ssheldonh/* ARGSUSED */ 25148981Ssheldonhvoid 25298559Sjmallettdiscard_stream(int s, struct servtab *sep) 25348981Ssheldonh{ 25448981Ssheldonh int ret; 25548981Ssheldonh char buffer[BUFSIZE]; 25648981Ssheldonh 25748981Ssheldonh inetd_setproctitle(sep->se_service, s); 25848981Ssheldonh while (1) { 25948981Ssheldonh while ((ret = read(s, buffer, sizeof(buffer))) > 0) 26048981Ssheldonh ; 26148981Ssheldonh if (ret == 0 || errno != EINTR) 26248981Ssheldonh break; 26348981Ssheldonh } 26448981Ssheldonh exit(0); 26548981Ssheldonh} 26648981Ssheldonh 26749052Ssheldonh/* 26849052Ssheldonh * RFC862 Echo Protocol. Any data received is sent back to the sender as 26949052Ssheldonh * received. 27049052Ssheldonh */ 27149052Ssheldonh 27298559Sjmallett/* Echo service -- echo data back */ 27348981Ssheldonh/* ARGSUSED */ 27448981Ssheldonhvoid 27598559Sjmallettecho_dg(int s, struct servtab *sep) 27648981Ssheldonh{ 27777231Sdwmalone char buffer[65536]; /* Should be sizeof(max datagram). */ 27857857Sshin int i; 27957857Sshin socklen_t size; 28056590Sshin struct sockaddr_storage ss; 28148981Ssheldonh 28256590Sshin size = sizeof(ss); 28348981Ssheldonh if ((i = recvfrom(s, buffer, sizeof(buffer), 0, 28456590Sshin (struct sockaddr *)&ss, &size)) < 0) 28548981Ssheldonh return; 28648981Ssheldonh 28756590Sshin if (check_loop((struct sockaddr *)&ss, sep)) 28848981Ssheldonh return; 28948981Ssheldonh 29057857Sshin (void) sendto(s, buffer, i, 0, (struct sockaddr *)&ss, size); 29148981Ssheldonh} 29248981Ssheldonh 29398559Sjmallett/* Echo service -- echo data back */ 29448981Ssheldonh/* ARGSUSED */ 29548981Ssheldonhvoid 29698559Sjmallettecho_stream(int s, struct servtab *sep) 29748981Ssheldonh{ 29848981Ssheldonh char buffer[BUFSIZE]; 29948981Ssheldonh int i; 30048981Ssheldonh 30148981Ssheldonh inetd_setproctitle(sep->se_service, s); 30248981Ssheldonh while ((i = read(s, buffer, sizeof(buffer))) > 0 && 30348981Ssheldonh write(s, buffer, i) > 0) 30448981Ssheldonh ; 30548981Ssheldonh exit(0); 30648981Ssheldonh} 30748981Ssheldonh 30849052Ssheldonh/* 30949052Ssheldonh * RFC1413 Identification Protocol. Given a TCP port number pair, return a 31049052Ssheldonh * character string which identifies the owner of that connection on the 31149057Sgreen * server's system. Extended to allow for ~/.fakeid support and ~/.noident 31249057Sgreen * support. 31349052Ssheldonh */ 31449052Ssheldonh 31577684Sdwmalone/* RFC 1413 says the following are the only errors you can return. */ 31677684Sdwmalone#define ID_INVALID "INVALID-PORT" /* Port number improperly specified. */ 31777684Sdwmalone#define ID_NOUSER "NO-USER" /* Port not in use/not identifable. */ 31877684Sdwmalone#define ID_HIDDEN "HIDDEN-USER" /* Hiden at user's request. */ 31977684Sdwmalone#define ID_UNKNOWN "UNKNOWN-ERROR" /* Everything else. */ 32077684Sdwmalone 32198559Sjmallett/* Generic ident_stream error-sending func */ 32248981Ssheldonh/* ARGSUSED */ 32348981Ssheldonhvoid 32498559Sjmallettiderror(int lport, int fport, int s, const char *er) 32548981Ssheldonh{ 32649004Sgreen char *p; 32749004Sgreen 32877684Sdwmalone asprintf(&p, "%d , %d : ERROR : %s\r\n", lport, fport, er); 32949030Ssheldonh if (p == NULL) { 33049057Sgreen syslog(LOG_ERR, "asprintf: %m"); 33149030Ssheldonh exit(EX_OSERR); 33249030Ssheldonh } 33358735Ssheldonh send(s, p, strlen(p), MSG_EOF); 33449004Sgreen free(p); 33548981Ssheldonh 33648981Ssheldonh exit(0); 33748981Ssheldonh} 33848981Ssheldonh 33998559Sjmallett/* Ident service (AKA "auth") */ 34048981Ssheldonh/* ARGSUSED */ 34148981Ssheldonhvoid 34298559Sjmallettident_stream(int s, struct servtab *sep) 34348981Ssheldonh{ 34449089Sgreen struct utsname un; 34549089Sgreen struct stat sb; 34678694Sdwmalone struct sockaddr_in sin4[2]; 34757906Sshin#ifdef INET6 34856590Sshin struct sockaddr_in6 sin6[2]; 34957906Sshin#endif 35056590Sshin struct sockaddr_storage ss[2]; 35172650Sgreen struct xucred uc; 35249004Sgreen struct timeval tv = { 35349004Sgreen 10, 35449004Sgreen 0 35563045Sdwmalone }, to; 35656298Sgreen struct passwd *pw = NULL; 35749004Sgreen fd_set fdset; 35877684Sdwmalone char buf[BUFSIZE], *p, **av, *osname = NULL, e; 35977684Sdwmalone char idbuf[MAXLOGNAME] = ""; /* Big enough to hold uid in decimal. */ 36061099Sgreen socklen_t socklen; 36161099Sgreen ssize_t ssize; 36263045Sdwmalone size_t size, bufsiz; 36377684Sdwmalone int c, fflag = 0, nflag = 0, rflag = 0, argc = 0; 36477684Sdwmalone int gflag = 0, iflag = 0, Fflag = 0, getcredfail = 0, onreadlen; 36548981Ssheldonh u_short lport, fport; 36648981Ssheldonh 36748981Ssheldonh inetd_setproctitle(sep->se_service, s); 36849104Sgreen /* 36949104Sgreen * Reset getopt() since we are a fork() but not an exec() from 37049104Sgreen * a parent which used getopt() already. 37149104Sgreen */ 37248981Ssheldonh optind = 1; 37348981Ssheldonh optreset = 1; 37449104Sgreen /* 37549104Sgreen * Take the internal argument vector and count it out to make an 37649104Sgreen * argument count for getopt. This can be used for any internal 37749104Sgreen * service to read arguments and use getopt() easily. 37849104Sgreen */ 37948981Ssheldonh for (av = sep->se_argv; *av; av++) 38048981Ssheldonh argc++; 38148981Ssheldonh if (argc) { 38249054Sgreen int sec, usec; 38356298Sgreen size_t i; 38478694Sdwmalone u_int32_t rnd32; 38549054Sgreen 38677684Sdwmalone while ((c = getopt(argc, sep->se_argv, "d:fFgino:rt:")) != -1) 38748981Ssheldonh switch (c) { 38856298Sgreen case 'd': 38977684Sdwmalone if (!gflag) 39077684Sdwmalone strlcpy(idbuf, optarg, sizeof(idbuf)); 39156298Sgreen break; 39248981Ssheldonh case 'f': 39348981Ssheldonh fflag = 1; 39448981Ssheldonh break; 39569620Sdwmalone case 'F': 39669620Sdwmalone fflag = 1; 39769620Sdwmalone Fflag=1; 39869620Sdwmalone break; 39956298Sgreen case 'g': 40056298Sgreen gflag = 1; 40178694Sdwmalone rnd32 = 0; /* Shush, compiler. */ 40256303Sgreen /* 40378694Sdwmalone * The number of bits in "rnd32" divided 40456303Sgreen * by the number of bits needed per iteration 40556303Sgreen * gives a more optimal way to reload the 40656303Sgreen * random number only when necessary. 40756303Sgreen * 408100498Sfanf * 32 bits from arc4random corresponds to 40977684Sdwmalone * about 6 base-36 digits, so we reseed evey 6. 41056303Sgreen */ 41177684Sdwmalone for (i = 0; i < sizeof(idbuf) - 1; i++) { 41277684Sdwmalone static const char *const base36 = 41356303Sgreen "0123456789" 41456303Sgreen "abcdefghijklmnopqrstuvwxyz"; 41577684Sdwmalone if (i % 6 == 0) 41678694Sdwmalone rnd32 = arc4random(); 41778694Sdwmalone idbuf[i] = base36[rnd32 % 36]; 41878694Sdwmalone rnd32 /= 36; 41956298Sgreen } 42077684Sdwmalone idbuf[i] = '\0'; 42156298Sgreen break; 42277684Sdwmalone case 'i': 42377684Sdwmalone iflag = 1; 42477684Sdwmalone break; 42549057Sgreen case 'n': 42649057Sgreen nflag = 1; 42748981Ssheldonh break; 42849004Sgreen case 'o': 42949004Sgreen osname = optarg; 43049004Sgreen break; 43149057Sgreen case 'r': 43249057Sgreen rflag = 1; 43349057Sgreen break; 43449051Ssheldonh case 't': 43549030Ssheldonh switch (sscanf(optarg, "%d.%d", &sec, &usec)) { 43649030Ssheldonh case 2: 43749030Ssheldonh tv.tv_usec = usec; 43877684Sdwmalone /* FALLTHROUGH */ 43949030Ssheldonh case 1: 44049030Ssheldonh tv.tv_sec = sec; 44149030Ssheldonh break; 44249030Ssheldonh default: 44349030Ssheldonh if (debug) 44449030Ssheldonh warnx("bad -t argument"); 44549030Ssheldonh break; 44649030Ssheldonh } 44749054Sgreen break; 44848981Ssheldonh default: 44948981Ssheldonh break; 45048981Ssheldonh } 45148981Ssheldonh } 45249004Sgreen if (osname == NULL) { 45349033Sgreen if (uname(&un) == -1) 45477684Sdwmalone iderror(0, 0, s, ID_UNKNOWN); 45549004Sgreen osname = un.sysname; 45649004Sgreen } 45777684Sdwmalone 45849104Sgreen /* 45949104Sgreen * We're going to prepare for and execute reception of a 46049104Sgreen * packet of data from the user. The data is in the format 46149104Sgreen * "local_port , foreign_port\r\n" (with local being the 46249104Sgreen * server's port and foreign being the client's.) 46349104Sgreen */ 46463045Sdwmalone gettimeofday(&to, NULL); 46563045Sdwmalone to.tv_sec += tv.tv_sec; 46669532Sgreen to.tv_usec += tv.tv_usec; 46769532Sgreen if (to.tv_usec >= 1000000) { 46863045Sdwmalone to.tv_usec -= 1000000; 46963045Sdwmalone to.tv_sec++; 47063045Sdwmalone } 47163045Sdwmalone 47263045Sdwmalone size = 0; 47363045Sdwmalone bufsiz = sizeof(buf) - 1; 47449004Sgreen FD_ZERO(&fdset); 47574934Sdwmalone while (bufsiz > 0) { 47663045Sdwmalone gettimeofday(&tv, NULL); 47763045Sdwmalone tv.tv_sec = to.tv_sec - tv.tv_sec; 47863045Sdwmalone tv.tv_usec = to.tv_usec - tv.tv_usec; 47963045Sdwmalone if (tv.tv_usec < 0) { 48063045Sdwmalone tv.tv_usec += 1000000; 48163045Sdwmalone tv.tv_sec--; 48263045Sdwmalone } 48363045Sdwmalone if (tv.tv_sec < 0) 48463045Sdwmalone break; 48563045Sdwmalone FD_SET(s, &fdset); 48663045Sdwmalone if (select(s + 1, &fdset, NULL, NULL, &tv) == -1) 48777684Sdwmalone iderror(0, 0, s, ID_UNKNOWN); 48863045Sdwmalone if (ioctl(s, FIONREAD, &onreadlen) == -1) 48977684Sdwmalone iderror(0, 0, s, ID_UNKNOWN); 49078694Sdwmalone if ((size_t)onreadlen > bufsiz) 49163045Sdwmalone onreadlen = bufsiz; 49263045Sdwmalone ssize = read(s, &buf[size], (size_t)onreadlen); 49363045Sdwmalone if (ssize == -1) 49477684Sdwmalone iderror(0, 0, s, ID_UNKNOWN); 49566543Sdwmalone else if (ssize == 0) 49666543Sdwmalone break; 49763045Sdwmalone bufsiz -= ssize; 49863045Sdwmalone size += ssize; 49974934Sdwmalone if (memchr(&buf[size - ssize], '\n', ssize) != NULL) 50074934Sdwmalone break; 50163045Sdwmalone } 50263045Sdwmalone buf[size] = '\0'; 50363045Sdwmalone /* Read two characters, and check for a delimiting character */ 50463045Sdwmalone if (sscanf(buf, "%hu , %hu%c", &lport, &fport, &e) != 3 || isdigit(e)) 50577684Sdwmalone iderror(0, 0, s, ID_INVALID); 50677684Sdwmalone 50777684Sdwmalone /* Send garbage? */ 50877684Sdwmalone if (gflag) 50956298Sgreen goto printit; 51077684Sdwmalone 51149104Sgreen /* 51258712Sgreen * If not "real" (-r), send a HIDDEN-USER error for everything. 51358712Sgreen * If -d is used to set a fallback username, this is used to 51458712Sgreen * override it, and the fallback is returned instead. 51558712Sgreen */ 51658712Sgreen if (!rflag) { 51777684Sdwmalone if (*idbuf == '\0') 51877684Sdwmalone iderror(lport, fport, s, ID_HIDDEN); 51977684Sdwmalone goto printit; 52058712Sgreen } 52177684Sdwmalone 52258712Sgreen /* 52349104Sgreen * We take the input and construct an array of two sockaddr_ins 52449104Sgreen * which contain the local address information and foreign 52549104Sgreen * address information, respectively, used to look up the 52649104Sgreen * credentials for the socket (which are returned by the 52777684Sdwmalone * sysctl "net.inet.tcp.getcred" when we call it.) 52849104Sgreen */ 52977684Sdwmalone socklen = sizeof(ss[0]); 53077684Sdwmalone if (getsockname(s, (struct sockaddr *)&ss[0], &socklen) == -1) 53177684Sdwmalone iderror(lport, fport, s, ID_UNKNOWN); 53277684Sdwmalone socklen = sizeof(ss[1]); 53377684Sdwmalone if (getpeername(s, (struct sockaddr *)&ss[1], &socklen) == -1) 53477684Sdwmalone iderror(lport, fport, s, ID_UNKNOWN); 53556590Sshin if (ss[0].ss_family != ss[1].ss_family) 53677684Sdwmalone iderror(lport, fport, s, ID_UNKNOWN); 53761099Sgreen size = sizeof(uc); 53856590Sshin switch (ss[0].ss_family) { 53956590Sshin case AF_INET: 54078694Sdwmalone sin4[0] = *(struct sockaddr_in *)&ss[0]; 54178694Sdwmalone sin4[0].sin_port = htons(lport); 54278694Sdwmalone sin4[1] = *(struct sockaddr_in *)&ss[1]; 54378694Sdwmalone sin4[1].sin_port = htons(fport); 54478694Sdwmalone if (sysctlbyname("net.inet.tcp.getcred", &uc, &size, sin4, 54578694Sdwmalone sizeof(sin4)) == -1) 54669532Sgreen getcredfail = errno; 54756590Sshin break; 54856590Sshin#ifdef INET6 54956590Sshin case AF_INET6: 55056590Sshin sin6[0] = *(struct sockaddr_in6 *)&ss[0]; 55156590Sshin sin6[0].sin6_port = htons(lport); 55256590Sshin sin6[1] = *(struct sockaddr_in6 *)&ss[1]; 55356590Sshin sin6[1].sin6_port = htons(fport); 55461099Sgreen if (sysctlbyname("net.inet6.tcp6.getcred", &uc, &size, sin6, 55556590Sshin sizeof(sin6)) == -1) 55669532Sgreen getcredfail = errno; 55756590Sshin break; 55856590Sshin#endif 55956590Sshin default: /* should not reach here */ 56069532Sgreen getcredfail = EAFNOSUPPORT; 56156590Sshin break; 56256590Sshin } 56391354Sdd if (getcredfail != 0 || uc.cr_version != XUCRED_VERSION) { 56477684Sdwmalone if (*idbuf == '\0') 56577684Sdwmalone iderror(lport, fport, s, 56677684Sdwmalone getcredfail == ENOENT ? ID_NOUSER : ID_UNKNOWN); 56777684Sdwmalone goto printit; 56856298Sgreen } 56977684Sdwmalone 57077684Sdwmalone /* Look up the pw to get the username and home directory*/ 57177684Sdwmalone errno = 0; 57277684Sdwmalone pw = getpwuid(uc.cr_uid); 57377684Sdwmalone if (pw == NULL) 57477684Sdwmalone iderror(lport, fport, s, errno == 0 ? ID_NOUSER : ID_UNKNOWN); 57577684Sdwmalone 57677684Sdwmalone if (iflag) 57777684Sdwmalone snprintf(idbuf, sizeof(idbuf), "%u", (unsigned)pw->pw_uid); 57877684Sdwmalone else 57977684Sdwmalone strlcpy(idbuf, pw->pw_name, sizeof(idbuf)); 58077684Sdwmalone 58149104Sgreen /* 58249104Sgreen * If enabled, we check for a file named ".noident" in the user's 58349104Sgreen * home directory. If found, we return HIDDEN-USER. 58449104Sgreen */ 58577684Sdwmalone if (nflag) { 58649089Sgreen if (asprintf(&p, "%s/.noident", pw->pw_dir) == -1) 58777684Sdwmalone iderror(lport, fport, s, ID_UNKNOWN); 58849089Sgreen if (lstat(p, &sb) == 0) { 58949089Sgreen free(p); 59077684Sdwmalone iderror(lport, fport, s, ID_HIDDEN); 59149057Sgreen } 59249089Sgreen free(p); 59349057Sgreen } 59477684Sdwmalone 59549104Sgreen /* 59649104Sgreen * Here, if enabled, we read a user's ".fakeid" file in their 59749104Sgreen * home directory. It consists of a line containing the name 59849104Sgreen * they want. 59949104Sgreen */ 60077684Sdwmalone if (fflag) { 60169144Sgreen int fakeid_fd; 60249033Sgreen 60349104Sgreen /* 60449104Sgreen * Here we set ourself to effectively be the user, so we don't 60549104Sgreen * open any files we have no permission to open, especially 60649104Sgreen * symbolic links to sensitive root-owned files or devices. 60749104Sgreen */ 60869144Sgreen if (initgroups(pw->pw_name, pw->pw_gid) == -1) 60977684Sdwmalone iderror(lport, fport, s, ID_UNKNOWN); 61077684Sdwmalone if (seteuid(pw->pw_uid) == -1) 61177684Sdwmalone iderror(lport, fport, s, ID_UNKNOWN); 61249104Sgreen /* 61369532Sgreen * We can't stat() here since that would be a race 61469532Sgreen * condition. 61549104Sgreen * Therefore, we open the file we have permissions to open 61649104Sgreen * and if it's not a regular file, we close it and end up 61749104Sgreen * returning the user's real username. 61849104Sgreen */ 61977684Sdwmalone if (asprintf(&p, "%s/.fakeid", pw->pw_dir) == -1) 62077684Sdwmalone iderror(lport, fport, s, ID_UNKNOWN); 62169144Sgreen fakeid_fd = open(p, O_RDONLY | O_NONBLOCK); 62249089Sgreen free(p); 62377684Sdwmalone if (fakeid_fd == -1 || fstat(fakeid_fd, &sb) == -1 || 62477684Sdwmalone !S_ISREG(sb.st_mode)) 62577684Sdwmalone goto fakeid_fail; 62677684Sdwmalone 62777684Sdwmalone if ((ssize = read(fakeid_fd, buf, sizeof(buf) - 1)) < 0) 62877684Sdwmalone goto fakeid_fail; 62977684Sdwmalone buf[ssize] = '\0'; 63077684Sdwmalone 63177684Sdwmalone /* 63277684Sdwmalone * Usually, the file will have the desired identity 63377684Sdwmalone * in the form "identity\n". Allow for leading white 63477684Sdwmalone * space and trailing white space/end of line. 63577684Sdwmalone */ 63677684Sdwmalone p = buf; 63777684Sdwmalone p += strspn(p, " \t"); 63877684Sdwmalone p[strcspn(p, " \t\r\n")] = '\0'; 63977684Sdwmalone if (strlen(p) > MAXLOGNAME - 1) /* Too long (including nul)? */ 64077684Sdwmalone p[MAXLOGNAME - 1] = '\0'; 64177684Sdwmalone 64277684Sdwmalone /* 64377684Sdwmalone * If the name is a zero-length string or matches it 64477684Sdwmalone * the id or name of another user (unless permitted by -F) 64577684Sdwmalone * then it is invalid. 64677684Sdwmalone */ 64777684Sdwmalone if (*p == '\0') 64877684Sdwmalone goto fakeid_fail; 64977684Sdwmalone if (!Fflag) { 65077684Sdwmalone if (iflag) { 65177684Sdwmalone if (p[strspn(p, "0123456789")] == '\0' && 65277684Sdwmalone getpwuid(atoi(p)) != NULL) 65377684Sdwmalone goto fakeid_fail; 65477684Sdwmalone } else { 65577684Sdwmalone if (getpwnam(p) != NULL) 65677684Sdwmalone goto fakeid_fail; 65748981Ssheldonh } 65877684Sdwmalone } 65977684Sdwmalone 66077684Sdwmalone strlcpy(idbuf, p, sizeof(idbuf)); 66177684Sdwmalone 66277684Sdwmalonefakeid_fail: 66377684Sdwmalone if (fakeid_fd != -1) 66469144Sgreen close(fakeid_fd); 66577684Sdwmalone } 66677684Sdwmalone 66748981Ssheldonhprintit: 66849104Sgreen /* Finally, we make and send the reply. */ 66949004Sgreen if (asprintf(&p, "%d , %d : USERID : %s : %s\r\n", lport, fport, osname, 67077684Sdwmalone idbuf) == -1) { 67149057Sgreen syslog(LOG_ERR, "asprintf: %m"); 67249051Ssheldonh exit(EX_OSERR); 67349051Ssheldonh } 67458735Ssheldonh send(s, p, strlen(p), MSG_EOF); 67549004Sgreen free(p); 67648981Ssheldonh 67748981Ssheldonh exit(0); 67848981Ssheldonh} 67948981Ssheldonh 68048981Ssheldonh/* 681157820Sdwmalone * RFC738/868 Time Server. 68248981Ssheldonh * Return a machine readable date and time, in the form of the 68348981Ssheldonh * number of seconds since midnight, Jan 1, 1900. Since gettimeofday 68448981Ssheldonh * returns the number of seconds since midnight, Jan 1, 1970, 68548981Ssheldonh * we must add 2208988800 seconds to this figure to make up for 68648981Ssheldonh * some seventy years Bell Labs was asleep. 68748981Ssheldonh */ 68848981Ssheldonh 689157816Sdwmaloneuint32_t 69098558Sjmallettmachtime(void) 69148981Ssheldonh{ 69248981Ssheldonh 693157816Sdwmalone#define OFFSET ((uint32_t)25567 * 24*60*60) 694239991Sed return (htonl((uint32_t)(time(NULL) + OFFSET))); 69548981Ssheldonh#undef OFFSET 69648981Ssheldonh} 69748981Ssheldonh 69848981Ssheldonh/* ARGSUSED */ 69948981Ssheldonhvoid 70098558Sjmallettmachtime_dg(int s, struct servtab *sep) 70148981Ssheldonh{ 702157816Sdwmalone uint32_t result; 70356590Sshin struct sockaddr_storage ss; 70457857Sshin socklen_t size; 70548981Ssheldonh 70656590Sshin size = sizeof(ss); 70748981Ssheldonh if (recvfrom(s, (char *)&result, sizeof(result), 0, 70856590Sshin (struct sockaddr *)&ss, &size) < 0) 70948981Ssheldonh return; 71048981Ssheldonh 71156590Sshin if (check_loop((struct sockaddr *)&ss, sep)) 71248981Ssheldonh return; 71348981Ssheldonh 71448981Ssheldonh result = machtime(); 71548981Ssheldonh (void) sendto(s, (char *) &result, sizeof(result), 0, 71657857Sshin (struct sockaddr *)&ss, size); 71748981Ssheldonh} 71848981Ssheldonh 71948981Ssheldonh/* ARGSUSED */ 72048981Ssheldonhvoid 72198561Sjmallettmachtime_stream(int s, struct servtab *sep __unused) 72248981Ssheldonh{ 723157816Sdwmalone uint32_t result; 72448981Ssheldonh 72548981Ssheldonh result = machtime(); 72658735Ssheldonh (void) send(s, (char *) &result, sizeof(result), MSG_EOF); 72748981Ssheldonh} 72848981Ssheldonh 72948981Ssheldonh/* 73049052Ssheldonh * RFC1078 TCP Port Service Multiplexer (TCPMUX). Service connections to 73149052Ssheldonh * services based on the service name sent. 73249052Ssheldonh * 73348981Ssheldonh * Based on TCPMUX.C by Mark K. Lottor November 1988 73448981Ssheldonh * sri-nic::ps:<mkl>tcpmux.c 73548981Ssheldonh */ 73648981Ssheldonh 73748981Ssheldonh#define MAX_SERV_LEN (256+2) /* 2 bytes for \r\n */ 73848981Ssheldonh#define strwrite(fd, buf) (void) write(fd, buf, sizeof(buf)-1) 73948981Ssheldonh 740228990Suqsstatic int /* # of characters up to \r,\n or \0 */ 74198558Sjmallettgetline(int fd, char *buf, int len) 74248981Ssheldonh{ 74348981Ssheldonh int count = 0, n; 74448981Ssheldonh struct sigaction sa; 74548981Ssheldonh 74648981Ssheldonh sa.sa_flags = 0; 74748981Ssheldonh sigemptyset(&sa.sa_mask); 74848981Ssheldonh sa.sa_handler = SIG_DFL; 74948981Ssheldonh sigaction(SIGALRM, &sa, (struct sigaction *)0); 75048981Ssheldonh do { 75148981Ssheldonh alarm(10); 75248981Ssheldonh n = read(fd, buf, len-count); 75348981Ssheldonh alarm(0); 75448981Ssheldonh if (n == 0) 75548981Ssheldonh return (count); 75648981Ssheldonh if (n < 0) 75748981Ssheldonh return (-1); 75848981Ssheldonh while (--n >= 0) { 75948981Ssheldonh if (*buf == '\r' || *buf == '\n' || *buf == '\0') 76048981Ssheldonh return (count); 76148981Ssheldonh count++; 76248981Ssheldonh buf++; 76348981Ssheldonh } 76448981Ssheldonh } while (count < len); 76548981Ssheldonh return (count); 76648981Ssheldonh} 76748981Ssheldonh 76848981Ssheldonhstruct servtab * 76998558Sjmalletttcpmux(int s) 77048981Ssheldonh{ 77148981Ssheldonh struct servtab *sep; 77248981Ssheldonh char service[MAX_SERV_LEN+1]; 77348981Ssheldonh int len; 77448981Ssheldonh 77548981Ssheldonh /* Get requested service name */ 77648981Ssheldonh if ((len = getline(s, service, MAX_SERV_LEN)) < 0) { 77748981Ssheldonh strwrite(s, "-Error reading service name\r\n"); 77848981Ssheldonh return (NULL); 77948981Ssheldonh } 78048981Ssheldonh service[len] = '\0'; 78148981Ssheldonh 78248981Ssheldonh if (debug) 78348981Ssheldonh warnx("tcpmux: someone wants %s", service); 78448981Ssheldonh 78548981Ssheldonh /* 78648981Ssheldonh * Help is a required command, and lists available services, 78748981Ssheldonh * one per line. 78848981Ssheldonh */ 78948981Ssheldonh if (!strcasecmp(service, "help")) { 79048981Ssheldonh for (sep = servtab; sep; sep = sep->se_next) { 79148981Ssheldonh if (!ISMUX(sep)) 79248981Ssheldonh continue; 79348981Ssheldonh (void)write(s,sep->se_service,strlen(sep->se_service)); 79448981Ssheldonh strwrite(s, "\r\n"); 79548981Ssheldonh } 79648981Ssheldonh return (NULL); 79748981Ssheldonh } 79848981Ssheldonh 79948981Ssheldonh /* Try matching a service in inetd.conf with the request */ 80048981Ssheldonh for (sep = servtab; sep; sep = sep->se_next) { 80148981Ssheldonh if (!ISMUX(sep)) 80248981Ssheldonh continue; 80348981Ssheldonh if (!strcasecmp(service, sep->se_service)) { 80448981Ssheldonh if (ISMUXPLUS(sep)) { 80548981Ssheldonh strwrite(s, "+Go\r\n"); 80648981Ssheldonh } 80748981Ssheldonh return (sep); 80848981Ssheldonh } 80948981Ssheldonh } 81048981Ssheldonh strwrite(s, "-Service not available\r\n"); 81148981Ssheldonh return (NULL); 81248981Ssheldonh} 813