builtins.c revision 63045
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 * 2650479Speter * $FreeBSD: head/usr.sbin/inetd/builtins.c 63045 2000-07-12 20:49:06Z dwmalone $ 2749004Sgreen * 2849004Sgreen */ 2949004Sgreen 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> 4348981Ssheldonh#include <limits.h> 4448981Ssheldonh#include <pwd.h> 4548981Ssheldonh#include <signal.h> 4649004Sgreen#include <stdlib.h> 4748981Ssheldonh#include <string.h> 4849030Ssheldonh#include <sysexits.h> 4949030Ssheldonh#include <syslog.h> 5048981Ssheldonh#include <unistd.h> 5148981Ssheldonh 5248981Ssheldonh#include "inetd.h" 5348981Ssheldonh 5448981Ssheldonhextern int debug; 5548981Ssheldonhextern struct servtab *servtab; 5648981Ssheldonh 5748981Ssheldonhchar ring[128]; 5848981Ssheldonhchar *endring; 5948981Ssheldonh 6056590Sshinint check_loop __P((struct sockaddr *, struct servtab *sep)); 6148981Ssheldonhvoid inetd_setproctitle __P((char *, int)); 6248981Ssheldonh 6348981Ssheldonhstruct biltin biltins[] = { 6448981Ssheldonh /* Echo received data */ 6548981Ssheldonh { "echo", SOCK_STREAM, 1, -1, echo_stream }, 6648981Ssheldonh { "echo", SOCK_DGRAM, 0, 1, echo_dg }, 6748981Ssheldonh 6848981Ssheldonh /* Internet /dev/null */ 6948981Ssheldonh { "discard", SOCK_STREAM, 1, -1, discard_stream }, 7048981Ssheldonh { "discard", SOCK_DGRAM, 0, 1, discard_dg }, 7148981Ssheldonh 7248981Ssheldonh /* Return 32 bit time since 1970 */ 7348981Ssheldonh { "time", SOCK_STREAM, 0, -1, machtime_stream }, 7448981Ssheldonh { "time", SOCK_DGRAM, 0, 1, machtime_dg }, 7548981Ssheldonh 7648981Ssheldonh /* Return human-readable time */ 7748981Ssheldonh { "daytime", SOCK_STREAM, 0, -1, daytime_stream }, 7848981Ssheldonh { "daytime", SOCK_DGRAM, 0, 1, daytime_dg }, 7948981Ssheldonh 8048981Ssheldonh /* Familiar character generator */ 8148981Ssheldonh { "chargen", SOCK_STREAM, 1, -1, chargen_stream }, 8248981Ssheldonh { "chargen", SOCK_DGRAM, 0, 1, chargen_dg }, 8348981Ssheldonh 8448981Ssheldonh { "tcpmux", SOCK_STREAM, 1, -1, (void (*)())tcpmux }, 8548981Ssheldonh 8648981Ssheldonh { "auth", SOCK_STREAM, 1, -1, ident_stream }, 8748981Ssheldonh 8848981Ssheldonh { NULL } 8948981Ssheldonh}; 9048981Ssheldonh 9149052Ssheldonh/* 9249052Ssheldonh * RFC864 Character Generator Protocol. Generates character data without 9349052Ssheldonh * any regard for input. 9449052Ssheldonh */ 9549052Ssheldonh 9648981Ssheldonhvoid 9748981Ssheldonhinitring() 9848981Ssheldonh{ 9948981Ssheldonh int i; 10048981Ssheldonh 10148981Ssheldonh endring = ring; 10248981Ssheldonh 10348981Ssheldonh for (i = 0; i <= 128; ++i) 10448981Ssheldonh if (isprint(i)) 10548981Ssheldonh *endring++ = i; 10648981Ssheldonh} 10748981Ssheldonh 10848981Ssheldonh/* ARGSUSED */ 10948981Ssheldonhvoid 11048981Ssheldonhchargen_dg(s, sep) /* Character generator */ 11148981Ssheldonh int s; 11248981Ssheldonh struct servtab *sep; 11348981Ssheldonh{ 11456590Sshin struct sockaddr_storage ss; 11548981Ssheldonh static char *rs; 11657857Sshin int len; 11757857Sshin socklen_t size; 11848981Ssheldonh char text[LINESIZ+2]; 11948981Ssheldonh 12048981Ssheldonh if (endring == 0) { 12148981Ssheldonh initring(); 12248981Ssheldonh rs = ring; 12348981Ssheldonh } 12448981Ssheldonh 12556590Sshin size = sizeof(ss); 12648981Ssheldonh if (recvfrom(s, text, sizeof(text), 0, 12756590Sshin (struct sockaddr *)&ss, &size) < 0) 12848981Ssheldonh return; 12948981Ssheldonh 13056590Sshin if (check_loop((struct sockaddr *)&ss, sep)) 13148981Ssheldonh return; 13248981Ssheldonh 13348981Ssheldonh if ((len = endring - rs) >= LINESIZ) 13448981Ssheldonh memmove(text, rs, LINESIZ); 13548981Ssheldonh else { 13648981Ssheldonh memmove(text, rs, len); 13748981Ssheldonh memmove(text + len, ring, LINESIZ - len); 13848981Ssheldonh } 13948981Ssheldonh if (++rs == endring) 14048981Ssheldonh rs = ring; 14148981Ssheldonh text[LINESIZ] = '\r'; 14248981Ssheldonh text[LINESIZ + 1] = '\n'; 14357857Sshin (void) sendto(s, text, sizeof(text), 0, (struct sockaddr *)&ss, size); 14448981Ssheldonh} 14548981Ssheldonh 14648981Ssheldonh/* ARGSUSED */ 14748981Ssheldonhvoid 14848981Ssheldonhchargen_stream(s, sep) /* Character generator */ 14948981Ssheldonh int s; 15048981Ssheldonh struct servtab *sep; 15148981Ssheldonh{ 15248981Ssheldonh int len; 15348981Ssheldonh char *rs, text[LINESIZ+2]; 15448981Ssheldonh 15548981Ssheldonh inetd_setproctitle(sep->se_service, s); 15648981Ssheldonh 15748981Ssheldonh if (!endring) { 15848981Ssheldonh initring(); 15948981Ssheldonh rs = ring; 16048981Ssheldonh } 16148981Ssheldonh 16248981Ssheldonh text[LINESIZ] = '\r'; 16348981Ssheldonh text[LINESIZ + 1] = '\n'; 16448981Ssheldonh for (rs = ring;;) { 16548981Ssheldonh if ((len = endring - rs) >= LINESIZ) 16648981Ssheldonh memmove(text, rs, LINESIZ); 16748981Ssheldonh else { 16848981Ssheldonh memmove(text, rs, len); 16948981Ssheldonh memmove(text + len, ring, LINESIZ - len); 17048981Ssheldonh } 17148981Ssheldonh if (++rs == endring) 17248981Ssheldonh rs = ring; 17348981Ssheldonh if (write(s, text, sizeof(text)) != sizeof(text)) 17448981Ssheldonh break; 17548981Ssheldonh } 17648981Ssheldonh exit(0); 17748981Ssheldonh} 17848981Ssheldonh 17949052Ssheldonh/* 18049052Ssheldonh * RFC867 Daytime Protocol. Sends the current date and time as an ascii 18149052Ssheldonh * character string without any regard for input. 18249052Ssheldonh */ 18349052Ssheldonh 18448981Ssheldonh/* ARGSUSED */ 18548981Ssheldonhvoid 18648981Ssheldonhdaytime_dg(s, sep) /* Return human-readable time of day */ 18748981Ssheldonh int s; 18848981Ssheldonh struct servtab *sep; 18948981Ssheldonh{ 19048981Ssheldonh char buffer[256]; 19148981Ssheldonh time_t clock; 19256590Sshin struct sockaddr_storage ss; 19357857Sshin socklen_t size; 19448981Ssheldonh 19548981Ssheldonh clock = time((time_t *) 0); 19648981Ssheldonh 19756590Sshin size = sizeof(ss); 19848981Ssheldonh if (recvfrom(s, buffer, sizeof(buffer), 0, 19956590Sshin (struct sockaddr *)&ss, &size) < 0) 20048981Ssheldonh return; 20148981Ssheldonh 20256590Sshin if (check_loop((struct sockaddr *)&ss, sep)) 20348981Ssheldonh return; 20448981Ssheldonh 20548981Ssheldonh (void) sprintf(buffer, "%.24s\r\n", ctime(&clock)); 20648981Ssheldonh (void) sendto(s, buffer, strlen(buffer), 0, 20757857Sshin (struct sockaddr *)&ss, size); 20848981Ssheldonh} 20948981Ssheldonh 21048981Ssheldonh/* ARGSUSED */ 21148981Ssheldonhvoid 21248981Ssheldonhdaytime_stream(s, sep) /* Return human-readable time of day */ 21348981Ssheldonh int s; 21448981Ssheldonh struct servtab *sep; 21548981Ssheldonh{ 21648981Ssheldonh char buffer[256]; 21748981Ssheldonh time_t clock; 21848981Ssheldonh 21948981Ssheldonh clock = time((time_t *) 0); 22048981Ssheldonh 22148981Ssheldonh (void) sprintf(buffer, "%.24s\r\n", ctime(&clock)); 22258735Ssheldonh (void) send(s, buffer, strlen(buffer), MSG_EOF); 22348981Ssheldonh} 22448981Ssheldonh 22549052Ssheldonh/* 22649052Ssheldonh * RFC863 Discard Protocol. Any data received is thrown away and no response 22749052Ssheldonh * is sent. 22849052Ssheldonh */ 22949052Ssheldonh 23048981Ssheldonh/* ARGSUSED */ 23148981Ssheldonhvoid 23248981Ssheldonhdiscard_dg(s, sep) /* Discard service -- ignore data */ 23348981Ssheldonh int s; 23448981Ssheldonh struct servtab *sep; 23548981Ssheldonh{ 23648981Ssheldonh char buffer[BUFSIZE]; 23748981Ssheldonh 23848981Ssheldonh (void) read(s, buffer, sizeof(buffer)); 23948981Ssheldonh} 24048981Ssheldonh 24148981Ssheldonh/* ARGSUSED */ 24248981Ssheldonhvoid 24348981Ssheldonhdiscard_stream(s, sep) /* Discard service -- ignore data */ 24448981Ssheldonh int s; 24548981Ssheldonh struct servtab *sep; 24648981Ssheldonh{ 24748981Ssheldonh int ret; 24848981Ssheldonh char buffer[BUFSIZE]; 24948981Ssheldonh 25048981Ssheldonh inetd_setproctitle(sep->se_service, s); 25148981Ssheldonh while (1) { 25248981Ssheldonh while ((ret = read(s, buffer, sizeof(buffer))) > 0) 25348981Ssheldonh ; 25448981Ssheldonh if (ret == 0 || errno != EINTR) 25548981Ssheldonh break; 25648981Ssheldonh } 25748981Ssheldonh exit(0); 25848981Ssheldonh} 25948981Ssheldonh 26049052Ssheldonh/* 26149052Ssheldonh * RFC862 Echo Protocol. Any data received is sent back to the sender as 26249052Ssheldonh * received. 26349052Ssheldonh */ 26449052Ssheldonh 26548981Ssheldonh/* ARGSUSED */ 26648981Ssheldonhvoid 26748981Ssheldonhecho_dg(s, sep) /* Echo service -- echo data back */ 26848981Ssheldonh int s; 26948981Ssheldonh struct servtab *sep; 27048981Ssheldonh{ 27148981Ssheldonh char buffer[BUFSIZE]; 27257857Sshin int i; 27357857Sshin socklen_t size; 27456590Sshin struct sockaddr_storage ss; 27548981Ssheldonh 27656590Sshin size = sizeof(ss); 27748981Ssheldonh if ((i = recvfrom(s, buffer, sizeof(buffer), 0, 27856590Sshin (struct sockaddr *)&ss, &size)) < 0) 27948981Ssheldonh return; 28048981Ssheldonh 28156590Sshin if (check_loop((struct sockaddr *)&ss, sep)) 28248981Ssheldonh return; 28348981Ssheldonh 28457857Sshin (void) sendto(s, buffer, i, 0, (struct sockaddr *)&ss, size); 28548981Ssheldonh} 28648981Ssheldonh 28748981Ssheldonh/* ARGSUSED */ 28848981Ssheldonhvoid 28948981Ssheldonhecho_stream(s, sep) /* Echo service -- echo data back */ 29048981Ssheldonh int s; 29148981Ssheldonh struct servtab *sep; 29248981Ssheldonh{ 29348981Ssheldonh char buffer[BUFSIZE]; 29448981Ssheldonh int i; 29548981Ssheldonh 29648981Ssheldonh inetd_setproctitle(sep->se_service, s); 29748981Ssheldonh while ((i = read(s, buffer, sizeof(buffer))) > 0 && 29848981Ssheldonh write(s, buffer, i) > 0) 29948981Ssheldonh ; 30048981Ssheldonh exit(0); 30148981Ssheldonh} 30248981Ssheldonh 30349052Ssheldonh/* 30449052Ssheldonh * RFC1413 Identification Protocol. Given a TCP port number pair, return a 30549052Ssheldonh * character string which identifies the owner of that connection on the 30649057Sgreen * server's system. Extended to allow for ~/.fakeid support and ~/.noident 30749057Sgreen * support. 30849052Ssheldonh */ 30949052Ssheldonh 31048981Ssheldonh/* ARGSUSED */ 31148981Ssheldonhvoid 31249104Sgreeniderror(lport, fport, s, er) /* Generic ident_stream error-sending func */ 31349004Sgreen int lport, fport, s, er; 31448981Ssheldonh{ 31549004Sgreen char *p; 31649004Sgreen 31749030Ssheldonh asprintf(&p, "%d , %d : ERROR : %s\r\n", lport, fport, 31848981Ssheldonh er == -1 ? "HIDDEN-USER" : er ? strerror(er) : "UNKNOWN-ERROR"); 31949030Ssheldonh if (p == NULL) { 32049057Sgreen syslog(LOG_ERR, "asprintf: %m"); 32149030Ssheldonh exit(EX_OSERR); 32249030Ssheldonh } 32358735Ssheldonh send(s, p, strlen(p), MSG_EOF); 32449004Sgreen free(p); 32548981Ssheldonh 32648981Ssheldonh exit(0); 32748981Ssheldonh} 32848981Ssheldonh 32948981Ssheldonh/* ARGSUSED */ 33048981Ssheldonhvoid 33149104Sgreenident_stream(s, sep) /* Ident service (AKA "auth") */ 33248981Ssheldonh int s; 33348981Ssheldonh struct servtab *sep; 33448981Ssheldonh{ 33549089Sgreen struct utsname un; 33649089Sgreen struct stat sb; 33748981Ssheldonh struct sockaddr_in sin[2]; 33857906Sshin#ifdef INET6 33956590Sshin struct sockaddr_in6 sin6[2]; 34057906Sshin#endif 34156590Sshin struct sockaddr_storage ss[2]; 34248981Ssheldonh struct ucred uc; 34349004Sgreen struct timeval tv = { 34449004Sgreen 10, 34549004Sgreen 0 34663045Sdwmalone }, to; 34756298Sgreen struct passwd *pw = NULL; 34849004Sgreen fd_set fdset; 34963045Sdwmalone char buf[BUFSIZE], *cp = NULL, *p, **av, *osname = NULL, garbage[7], e; 35056298Sgreen char *fallback = NULL; 35161099Sgreen socklen_t socklen; 35261099Sgreen ssize_t ssize; 35363045Sdwmalone size_t size, bufsiz; 35461098Sjhb int c, fflag = 0, nflag = 0, rflag = 0, argc = 0, usedfallback = 0; 35561099Sgreen int gflag = 0, getcredfail = 0, onreadlen; 35648981Ssheldonh u_short lport, fport; 35748981Ssheldonh 35848981Ssheldonh inetd_setproctitle(sep->se_service, s); 35949104Sgreen /* 36049104Sgreen * Reset getopt() since we are a fork() but not an exec() from 36149104Sgreen * a parent which used getopt() already. 36249104Sgreen */ 36348981Ssheldonh optind = 1; 36448981Ssheldonh optreset = 1; 36549104Sgreen /* 36649104Sgreen * Take the internal argument vector and count it out to make an 36749104Sgreen * argument count for getopt. This can be used for any internal 36849104Sgreen * service to read arguments and use getopt() easily. 36949104Sgreen */ 37048981Ssheldonh for (av = sep->se_argv; *av; av++) 37148981Ssheldonh argc++; 37248981Ssheldonh if (argc) { 37349054Sgreen int sec, usec; 37456298Sgreen size_t i; 37556298Sgreen u_int32_t random; 37649054Sgreen 37756298Sgreen while ((c = getopt(argc, sep->se_argv, "d:fgno:rt:")) != -1) 37848981Ssheldonh switch (c) { 37956298Sgreen case 'd': 38056298Sgreen fallback = optarg; 38156298Sgreen break; 38248981Ssheldonh case 'f': 38348981Ssheldonh fflag = 1; 38448981Ssheldonh break; 38556298Sgreen case 'g': 38656298Sgreen gflag = 1; 38756298Sgreen random = 0; /* Shush, compiler. */ 38856303Sgreen /* 38956303Sgreen * The number of bits in "random" divided 39056303Sgreen * by the number of bits needed per iteration 39156303Sgreen * gives a more optimal way to reload the 39256303Sgreen * random number only when necessary. 39356303Sgreen * 39456303Sgreen * I'm using base-36, so I need at least 6 39556303Sgreen * bits; round it up to 8 bits to make it 39656303Sgreen * easier. 39756303Sgreen */ 39856298Sgreen for (i = 0; i < sizeof(garbage) - 1; i++) { 39956303Sgreen const char *const base36 = 40056303Sgreen "0123456789" 40156303Sgreen "abcdefghijklmnopqrstuvwxyz"; 40256303Sgreen if (i % (sizeof(random) * 8 / 8) == 0) 40356298Sgreen random = arc4random(); 40456303Sgreen garbage[i] = 40556303Sgreen base36[(random & 0xff) % 36]; 40656303Sgreen random >>= 8; 40756298Sgreen } 40856303Sgreen garbage[i] = '\0'; 40956298Sgreen break; 41049057Sgreen case 'n': 41149057Sgreen nflag = 1; 41248981Ssheldonh break; 41349004Sgreen case 'o': 41449004Sgreen osname = optarg; 41549004Sgreen break; 41649057Sgreen case 'r': 41749057Sgreen rflag = 1; 41849057Sgreen break; 41949051Ssheldonh case 't': 42049030Ssheldonh switch (sscanf(optarg, "%d.%d", &sec, &usec)) { 42149030Ssheldonh case 2: 42249030Ssheldonh tv.tv_usec = usec; 42349030Ssheldonh case 1: 42449030Ssheldonh tv.tv_sec = sec; 42549030Ssheldonh break; 42649030Ssheldonh default: 42749030Ssheldonh if (debug) 42849030Ssheldonh warnx("bad -t argument"); 42949030Ssheldonh break; 43049030Ssheldonh } 43149054Sgreen break; 43248981Ssheldonh default: 43348981Ssheldonh break; 43448981Ssheldonh } 43548981Ssheldonh } 43649004Sgreen if (osname == NULL) { 43749033Sgreen if (uname(&un) == -1) 43849004Sgreen iderror(0, 0, s, errno); 43949004Sgreen osname = un.sysname; 44049004Sgreen } 44161099Sgreen socklen = sizeof(ss[0]); 44261099Sgreen if (getsockname(s, (struct sockaddr *)&ss[0], &socklen) == -1) 44349004Sgreen iderror(0, 0, s, errno); 44461099Sgreen socklen = sizeof(ss[1]); 44561099Sgreen if (getpeername(s, (struct sockaddr *)&ss[1], &socklen) == -1) 44649004Sgreen iderror(0, 0, s, errno); 44749104Sgreen /* 44849104Sgreen * We're going to prepare for and execute reception of a 44949104Sgreen * packet of data from the user. The data is in the format 45049104Sgreen * "local_port , foreign_port\r\n" (with local being the 45149104Sgreen * server's port and foreign being the client's.) 45249104Sgreen */ 45363045Sdwmalone gettimeofday(&to, NULL); 45463045Sdwmalone to.tv_sec += tv.tv_sec; 45563045Sdwmalone if ((to.tv_usec += tv.tv_usec) >= 1000000) { 45663045Sdwmalone to.tv_usec -= 1000000; 45763045Sdwmalone to.tv_sec++; 45863045Sdwmalone } 45963045Sdwmalone 46063045Sdwmalone size = 0; 46163045Sdwmalone bufsiz = sizeof(buf) - 1; 46249004Sgreen FD_ZERO(&fdset); 46363045Sdwmalone while (bufsiz > 0 && (size == 0 || buf[size - 1] != '\n')) { 46463045Sdwmalone gettimeofday(&tv, NULL); 46563045Sdwmalone tv.tv_sec = to.tv_sec - tv.tv_sec; 46663045Sdwmalone tv.tv_usec = to.tv_usec - tv.tv_usec; 46763045Sdwmalone if (tv.tv_usec < 0) { 46863045Sdwmalone tv.tv_usec += 1000000; 46963045Sdwmalone tv.tv_sec--; 47063045Sdwmalone } 47163045Sdwmalone if (tv.tv_sec < 0) 47263045Sdwmalone break; 47363045Sdwmalone FD_SET(s, &fdset); 47463045Sdwmalone if (select(s + 1, &fdset, NULL, NULL, &tv) == -1) 47563045Sdwmalone iderror(0, 0, s, errno); 47663045Sdwmalone if (ioctl(s, FIONREAD, &onreadlen) == -1) 47763045Sdwmalone iderror(0, 0, s, errno); 47863045Sdwmalone if (onreadlen > bufsiz) 47963045Sdwmalone onreadlen = bufsiz; 48063045Sdwmalone ssize = read(s, &buf[size], (size_t)onreadlen); 48163045Sdwmalone if (ssize == -1) 48263045Sdwmalone iderror(0, 0, s, errno); 48363045Sdwmalone bufsiz -= ssize; 48463045Sdwmalone size += ssize; 48563045Sdwmalone } 48663045Sdwmalone buf[size] = '\0'; 48763045Sdwmalone /* Read two characters, and check for a delimiting character */ 48863045Sdwmalone if (sscanf(buf, "%hu , %hu%c", &lport, &fport, &e) != 3 || isdigit(e)) 48949004Sgreen iderror(0, 0, s, 0); 49056298Sgreen if (gflag) { 49156298Sgreen cp = garbage; 49256298Sgreen goto printit; 49356298Sgreen } 49456298Sgreen 49549104Sgreen /* 49658712Sgreen * If not "real" (-r), send a HIDDEN-USER error for everything. 49758712Sgreen * If -d is used to set a fallback username, this is used to 49858712Sgreen * override it, and the fallback is returned instead. 49958712Sgreen */ 50058712Sgreen if (!rflag) { 50158712Sgreen if (fallback == NULL) 50258712Sgreen iderror(lport, fport, s, -1); 50358712Sgreen else { 50458712Sgreen cp = fallback; 50558712Sgreen goto printit; 50658712Sgreen } 50758712Sgreen } 50858712Sgreen 50958712Sgreen /* 51049104Sgreen * We take the input and construct an array of two sockaddr_ins 51149104Sgreen * which contain the local address information and foreign 51249104Sgreen * address information, respectively, used to look up the 51349104Sgreen * credentials for the socket (which are returned by the 51449104Sgreen * sysctl "net.inet.tcp.getcred" when we call it.) The 51549104Sgreen * arrays have been filled in above via get{peer,sock}name(), 51649104Sgreen * so right here we are only setting the ports. 51749104Sgreen */ 51856590Sshin if (ss[0].ss_family != ss[1].ss_family) 51956590Sshin iderror(lport, fport, s, errno); 52061099Sgreen size = sizeof(uc); 52156590Sshin switch (ss[0].ss_family) { 52256590Sshin case AF_INET: 52356590Sshin sin[0] = *(struct sockaddr_in *)&ss[0]; 52456590Sshin sin[0].sin_port = htons(lport); 52556590Sshin sin[1] = *(struct sockaddr_in *)&ss[1]; 52656590Sshin sin[1].sin_port = htons(fport); 52761099Sgreen if (sysctlbyname("net.inet.tcp.getcred", &uc, &size, sin, 52856590Sshin sizeof(sin)) == -1) 52956590Sshin getcredfail = 1; 53056590Sshin break; 53156590Sshin#ifdef INET6 53256590Sshin case AF_INET6: 53356590Sshin sin6[0] = *(struct sockaddr_in6 *)&ss[0]; 53456590Sshin sin6[0].sin6_port = htons(lport); 53556590Sshin sin6[1] = *(struct sockaddr_in6 *)&ss[1]; 53656590Sshin sin6[1].sin6_port = htons(fport); 53761099Sgreen if (sysctlbyname("net.inet6.tcp6.getcred", &uc, &size, sin6, 53856590Sshin sizeof(sin6)) == -1) 53956590Sshin getcredfail = 1; 54056590Sshin break; 54156590Sshin#endif 54256590Sshin default: /* should not reach here */ 54356590Sshin getcredfail = 1; 54456590Sshin break; 54556590Sshin } 54656590Sshin if (getcredfail != 0) { 54756298Sgreen if (fallback == NULL) /* Use a default, if asked to */ 54856298Sgreen iderror(lport, fport, s, errno); 54956298Sgreen usedfallback = 1; 55056298Sgreen } else { 55156298Sgreen /* Look up the pw to get the username */ 55256298Sgreen pw = getpwuid(uc.cr_uid); 55356298Sgreen } 55456298Sgreen if (pw == NULL && !usedfallback) /* No such user... */ 55549004Sgreen iderror(lport, fport, s, errno); 55649104Sgreen /* 55749104Sgreen * If enabled, we check for a file named ".noident" in the user's 55849104Sgreen * home directory. If found, we return HIDDEN-USER. 55949104Sgreen */ 56056298Sgreen if (nflag && !usedfallback) { 56149089Sgreen if (asprintf(&p, "%s/.noident", pw->pw_dir) == -1) 56249057Sgreen iderror(lport, fport, s, errno); 56349089Sgreen if (lstat(p, &sb) == 0) { 56449089Sgreen free(p); 56549057Sgreen iderror(lport, fport, s, -1); 56649057Sgreen } 56749089Sgreen free(p); 56849057Sgreen } 56949104Sgreen /* 57049104Sgreen * Here, if enabled, we read a user's ".fakeid" file in their 57149104Sgreen * home directory. It consists of a line containing the name 57249104Sgreen * they want. 57349104Sgreen */ 57456298Sgreen if (fflag && !usedfallback) { 57549033Sgreen FILE *fakeid = NULL; 57649033Sgreen 57749089Sgreen if (asprintf(&p, "%s/.fakeid", pw->pw_dir) == -1) 57849057Sgreen iderror(lport, fport, s, errno); 57949104Sgreen /* 58049104Sgreen * Here we set ourself to effectively be the user, so we don't 58149104Sgreen * open any files we have no permission to open, especially 58249104Sgreen * symbolic links to sensitive root-owned files or devices. 58349104Sgreen */ 58448981Ssheldonh seteuid(pw->pw_uid); 58548981Ssheldonh setegid(pw->pw_gid); 58649104Sgreen /* 58749104Sgreen * If we were to lstat() here, it would do no good, since it 58849104Sgreen * would introduce a race condition and could be defeated. 58949104Sgreen * Therefore, we open the file we have permissions to open 59049104Sgreen * and if it's not a regular file, we close it and end up 59149104Sgreen * returning the user's real username. 59249104Sgreen */ 59349089Sgreen fakeid = fopen(p, "r"); 59449089Sgreen free(p); 59549089Sgreen if (fakeid != NULL && 59648981Ssheldonh fstat(fileno(fakeid), &sb) != -1 && S_ISREG(sb.st_mode)) { 59748981Ssheldonh buf[sizeof(buf) - 1] = '\0'; 59848981Ssheldonh if (fgets(buf, sizeof(buf), fakeid) == NULL) { 59948981Ssheldonh cp = pw->pw_name; 60048981Ssheldonh fclose(fakeid); 60148981Ssheldonh goto printit; 60248981Ssheldonh } 60348981Ssheldonh fclose(fakeid); 60449104Sgreen /* 60549104Sgreen * Usually, the file will have the desired identity 60649104Sgreen * in the form "identity\n", so we use strtok() to 60749104Sgreen * end the string (which fgets() doesn't do.) 60849104Sgreen */ 60961099Sgreen buf[strcspn(buf, "\r\n")] = '\0'; 61048981Ssheldonh cp = buf; 61149104Sgreen /* Allow for beginning white space... */ 61248981Ssheldonh while (isspace(*cp)) 61348981Ssheldonh cp++; 61449104Sgreen /* ...and ending white space. */ 61561099Sgreen cp[strcspn(cp, " \t")] = '\0'; 61661099Sgreen /* User names of >16 characters are invalid */ 61761099Sgreen if (strlen(cp) > 16) 61861099Sgreen cp[16] = '\0'; 61949104Sgreen /* 62049104Sgreen * If the name is a zero-length string or matches 62149104Sgreen * the name of another user, it's invalid, so 62249104Sgreen * we will return their real identity instead. 62349104Sgreen */ 62449104Sgreen 62548981Ssheldonh if (!*cp || getpwnam(cp)) 62648981Ssheldonh cp = getpwuid(uc.cr_uid)->pw_name; 62748981Ssheldonh } else 62848981Ssheldonh cp = pw->pw_name; 62956298Sgreen } else if (!usedfallback) 63048981Ssheldonh cp = pw->pw_name; 63156298Sgreen else 63256298Sgreen cp = fallback; 63348981Ssheldonhprintit: 63449104Sgreen /* Finally, we make and send the reply. */ 63549004Sgreen if (asprintf(&p, "%d , %d : USERID : %s : %s\r\n", lport, fport, osname, 63649051Ssheldonh cp) == -1) { 63749057Sgreen syslog(LOG_ERR, "asprintf: %m"); 63849051Ssheldonh exit(EX_OSERR); 63949051Ssheldonh } 64058735Ssheldonh send(s, p, strlen(p), MSG_EOF); 64149004Sgreen free(p); 64248981Ssheldonh 64348981Ssheldonh exit(0); 64448981Ssheldonh} 64548981Ssheldonh 64648981Ssheldonh/* 64749052Ssheldonh * RFC738 Time Server. 64848981Ssheldonh * Return a machine readable date and time, in the form of the 64948981Ssheldonh * number of seconds since midnight, Jan 1, 1900. Since gettimeofday 65048981Ssheldonh * returns the number of seconds since midnight, Jan 1, 1970, 65148981Ssheldonh * we must add 2208988800 seconds to this figure to make up for 65248981Ssheldonh * some seventy years Bell Labs was asleep. 65348981Ssheldonh */ 65448981Ssheldonh 65548981Ssheldonhunsigned long 65648981Ssheldonhmachtime() 65748981Ssheldonh{ 65848981Ssheldonh struct timeval tv; 65948981Ssheldonh 66048981Ssheldonh if (gettimeofday(&tv, (struct timezone *)NULL) < 0) { 66148981Ssheldonh if (debug) 66248981Ssheldonh warnx("unable to get time of day"); 66348981Ssheldonh return (0L); 66448981Ssheldonh } 66548981Ssheldonh#define OFFSET ((u_long)25567 * 24*60*60) 66648981Ssheldonh return (htonl((long)(tv.tv_sec + OFFSET))); 66748981Ssheldonh#undef OFFSET 66848981Ssheldonh} 66948981Ssheldonh 67048981Ssheldonh/* ARGSUSED */ 67148981Ssheldonhvoid 67248981Ssheldonhmachtime_dg(s, sep) 67348981Ssheldonh int s; 67448981Ssheldonh struct servtab *sep; 67548981Ssheldonh{ 67648981Ssheldonh unsigned long result; 67756590Sshin struct sockaddr_storage ss; 67857857Sshin socklen_t size; 67948981Ssheldonh 68056590Sshin size = sizeof(ss); 68148981Ssheldonh if (recvfrom(s, (char *)&result, sizeof(result), 0, 68256590Sshin (struct sockaddr *)&ss, &size) < 0) 68348981Ssheldonh return; 68448981Ssheldonh 68556590Sshin if (check_loop((struct sockaddr *)&ss, sep)) 68648981Ssheldonh return; 68748981Ssheldonh 68848981Ssheldonh result = machtime(); 68948981Ssheldonh (void) sendto(s, (char *) &result, sizeof(result), 0, 69057857Sshin (struct sockaddr *)&ss, size); 69148981Ssheldonh} 69248981Ssheldonh 69348981Ssheldonh/* ARGSUSED */ 69448981Ssheldonhvoid 69548981Ssheldonhmachtime_stream(s, sep) 69648981Ssheldonh int s; 69748981Ssheldonh struct servtab *sep; 69848981Ssheldonh{ 69948981Ssheldonh unsigned long result; 70048981Ssheldonh 70148981Ssheldonh result = machtime(); 70258735Ssheldonh (void) send(s, (char *) &result, sizeof(result), MSG_EOF); 70348981Ssheldonh} 70448981Ssheldonh 70548981Ssheldonh/* 70649052Ssheldonh * RFC1078 TCP Port Service Multiplexer (TCPMUX). Service connections to 70749052Ssheldonh * services based on the service name sent. 70849052Ssheldonh * 70948981Ssheldonh * Based on TCPMUX.C by Mark K. Lottor November 1988 71048981Ssheldonh * sri-nic::ps:<mkl>tcpmux.c 71148981Ssheldonh */ 71248981Ssheldonh 71348981Ssheldonh#define MAX_SERV_LEN (256+2) /* 2 bytes for \r\n */ 71448981Ssheldonh#define strwrite(fd, buf) (void) write(fd, buf, sizeof(buf)-1) 71548981Ssheldonh 71648981Ssheldonhstatic int /* # of characters upto \r,\n or \0 */ 71748981Ssheldonhgetline(fd, buf, len) 71848981Ssheldonh int fd; 71948981Ssheldonh char *buf; 72048981Ssheldonh int len; 72148981Ssheldonh{ 72248981Ssheldonh int count = 0, n; 72348981Ssheldonh struct sigaction sa; 72448981Ssheldonh 72548981Ssheldonh sa.sa_flags = 0; 72648981Ssheldonh sigemptyset(&sa.sa_mask); 72748981Ssheldonh sa.sa_handler = SIG_DFL; 72848981Ssheldonh sigaction(SIGALRM, &sa, (struct sigaction *)0); 72948981Ssheldonh do { 73048981Ssheldonh alarm(10); 73148981Ssheldonh n = read(fd, buf, len-count); 73248981Ssheldonh alarm(0); 73348981Ssheldonh if (n == 0) 73448981Ssheldonh return (count); 73548981Ssheldonh if (n < 0) 73648981Ssheldonh return (-1); 73748981Ssheldonh while (--n >= 0) { 73848981Ssheldonh if (*buf == '\r' || *buf == '\n' || *buf == '\0') 73948981Ssheldonh return (count); 74048981Ssheldonh count++; 74148981Ssheldonh buf++; 74248981Ssheldonh } 74348981Ssheldonh } while (count < len); 74448981Ssheldonh return (count); 74548981Ssheldonh} 74648981Ssheldonh 74748981Ssheldonhstruct servtab * 74848981Ssheldonhtcpmux(s) 74948981Ssheldonh int s; 75048981Ssheldonh{ 75148981Ssheldonh struct servtab *sep; 75248981Ssheldonh char service[MAX_SERV_LEN+1]; 75348981Ssheldonh int len; 75448981Ssheldonh 75548981Ssheldonh /* Get requested service name */ 75648981Ssheldonh if ((len = getline(s, service, MAX_SERV_LEN)) < 0) { 75748981Ssheldonh strwrite(s, "-Error reading service name\r\n"); 75848981Ssheldonh return (NULL); 75948981Ssheldonh } 76048981Ssheldonh service[len] = '\0'; 76148981Ssheldonh 76248981Ssheldonh if (debug) 76348981Ssheldonh warnx("tcpmux: someone wants %s", service); 76448981Ssheldonh 76548981Ssheldonh /* 76648981Ssheldonh * Help is a required command, and lists available services, 76748981Ssheldonh * one per line. 76848981Ssheldonh */ 76948981Ssheldonh if (!strcasecmp(service, "help")) { 77048981Ssheldonh for (sep = servtab; sep; sep = sep->se_next) { 77148981Ssheldonh if (!ISMUX(sep)) 77248981Ssheldonh continue; 77348981Ssheldonh (void)write(s,sep->se_service,strlen(sep->se_service)); 77448981Ssheldonh strwrite(s, "\r\n"); 77548981Ssheldonh } 77648981Ssheldonh return (NULL); 77748981Ssheldonh } 77848981Ssheldonh 77948981Ssheldonh /* Try matching a service in inetd.conf with the request */ 78048981Ssheldonh for (sep = servtab; sep; sep = sep->se_next) { 78148981Ssheldonh if (!ISMUX(sep)) 78248981Ssheldonh continue; 78348981Ssheldonh if (!strcasecmp(service, sep->se_service)) { 78448981Ssheldonh if (ISMUXPLUS(sep)) { 78548981Ssheldonh strwrite(s, "+Go\r\n"); 78648981Ssheldonh } 78748981Ssheldonh return (sep); 78848981Ssheldonh } 78948981Ssheldonh } 79048981Ssheldonh strwrite(s, "-Service not available\r\n"); 79148981Ssheldonh return (NULL); 79248981Ssheldonh} 793