builtins.c revision 61099
1139825Simp/*- 241502Swpaul * Copyright (c) 1983, 1991, 1993, 1994 341502Swpaul * The Regents of the University of California. All rights reserved. 441502Swpaul * 541502Swpaul * Redistribution and use in source and binary forms, with or without 641502Swpaul * modification, are permitted provided that the following conditions 741502Swpaul * are met: 841502Swpaul * 1. Redistributions of source code must retain the above copyright 941502Swpaul * notice, this list of conditions and the following disclaimer. 1041502Swpaul * 2. Redistributions in binary form must reproduce the above copyright 1141502Swpaul * notice, this list of conditions and the following disclaimer in the 1241502Swpaul * documentation and/or other materials provided with the distribution. 1341502Swpaul * 1441502Swpaul * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 1541502Swpaul * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 1641502Swpaul * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 1741502Swpaul * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 1841502Swpaul * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 1941502Swpaul * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2041502Swpaul * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2141502Swpaul * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2241502Swpaul * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2341502Swpaul * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2441502Swpaul * SUCH DAMAGE. 2541502Swpaul * 2641502Swpaul * $FreeBSD: head/usr.sbin/inetd/builtins.c 61099 2000-05-30 22:51:05Z green $ 2741502Swpaul * 2841502Swpaul */ 2941502Swpaul 3041502Swpaul#include <sys/filio.h> 3141502Swpaul#include <sys/ioccom.h> 3241502Swpaul#include <sys/param.h> 33122678Sobrien#include <sys/stat.h> 34122678Sobrien#include <sys/socket.h> 35122678Sobrien#include <sys/sysctl.h> 3641502Swpaul#include <sys/ucred.h> 3741502Swpaul#include <sys/uio.h> 3841502Swpaul#include <sys/utsname.h> 3941502Swpaul 4041502Swpaul#include <ctype.h> 4141502Swpaul#include <err.h> 4241502Swpaul#include <errno.h> 4341502Swpaul#include <limits.h> 4441502Swpaul#include <pwd.h> 4541502Swpaul#include <signal.h> 4641502Swpaul#include <stdlib.h> 47131503Sbms#include <string.h> 4841502Swpaul#include <sysexits.h> 4941502Swpaul#include <syslog.h> 5041502Swpaul#include <unistd.h> 5141502Swpaul 5241502Swpaul#include "inetd.h" 5341502Swpaul 5441502Swpaulextern int debug; 5541502Swpaulextern struct servtab *servtab; 5641502Swpaul 5741502Swpaulchar ring[128]; 5841502Swpaulchar *endring; 5941502Swpaul 6041502Swpaulint check_loop __P((struct sockaddr *, struct servtab *sep)); 6141502Swpaulvoid inetd_setproctitle __P((char *, int)); 6241502Swpaul 6341502Swpaulstruct biltin biltins[] = { 6441502Swpaul /* Echo received data */ 6541502Swpaul { "echo", SOCK_STREAM, 1, -1, echo_stream }, 6641502Swpaul { "echo", SOCK_DGRAM, 0, 1, echo_dg }, 6741502Swpaul 6841502Swpaul /* Internet /dev/null */ 69129878Sphk { "discard", SOCK_STREAM, 1, -1, discard_stream }, 7041502Swpaul { "discard", SOCK_DGRAM, 0, 1, discard_dg }, 7141502Swpaul 7241502Swpaul /* Return 32 bit time since 1970 */ 7341502Swpaul { "time", SOCK_STREAM, 0, -1, machtime_stream }, 7441502Swpaul { "time", SOCK_DGRAM, 0, 1, machtime_dg }, 7541502Swpaul 7641502Swpaul /* Return human-readable time */ 77147256Sbrooks { "daytime", SOCK_STREAM, 0, -1, daytime_stream }, 7841502Swpaul { "daytime", SOCK_DGRAM, 0, 1, daytime_dg }, 7941502Swpaul 8041502Swpaul /* Familiar character generator */ 81131503Sbms { "chargen", SOCK_STREAM, 1, -1, chargen_stream }, 82131503Sbms { "chargen", SOCK_DGRAM, 0, 1, chargen_dg }, 8341502Swpaul 8449610Swpaul { "tcpmux", SOCK_STREAM, 1, -1, (void (*)())tcpmux }, 8549610Swpaul 8649610Swpaul { "auth", SOCK_STREAM, 1, -1, ident_stream }, 8741502Swpaul 8851432Swpaul { NULL } 8951432Swpaul}; 9051432Swpaul 91119288Simp/* 92119288Simp * RFC864 Character Generator Protocol. Generates character data without 9341502Swpaul * any regard for input. 9441502Swpaul */ 9541502Swpaul 9641502Swpaulvoid 9741502Swpaulinitring() 98113506Smdodd{ 99113506Smdodd int i; 10059758Speter 10159758Speter endring = ring; 10251432Swpaul 10351432Swpaul for (i = 0; i <= 128; ++i) 10451432Swpaul if (isprint(i)) 105110168Ssilby *endring++ = i; 106110168Ssilby} 10741502Swpaul 10841502Swpaul/* ARGSUSED */ 10941502Swpaulvoid 11041502Swpaulchargen_dg(s, sep) /* Character generator */ 11141502Swpaul int s; 11241502Swpaul struct servtab *sep; 11341502Swpaul{ 11441502Swpaul struct sockaddr_storage ss; 11562653Swpaul static char *rs; 11662653Swpaul int len; 117110170Ssilby socklen_t size; 118110170Ssilby char text[LINESIZ+2]; 119110170Ssilby 120110170Ssilby if (endring == 0) { 12144238Swpaul initring(); 12244238Swpaul rs = ring; 12344238Swpaul } 12444238Swpaul 12541502Swpaul size = sizeof(ss); 12641502Swpaul if (recvfrom(s, text, sizeof(text), 0, 12741502Swpaul (struct sockaddr *)&ss, &size) < 0) 128142407Simp return; 129142407Simp 130142407Simp if (check_loop((struct sockaddr *)&ss, sep)) 13141502Swpaul return; 132142407Simp 133142407Simp if ((len = endring - rs) >= LINESIZ) 134142407Simp memmove(text, rs, LINESIZ); 13541502Swpaul else { 136142407Simp memmove(text, rs, len); 137142407Simp memmove(text + len, ring, LINESIZ - len); 138142407Simp } 139142407Simp if (++rs == endring) 140142407Simp rs = ring; 141142407Simp text[LINESIZ] = '\r'; 142142407Simp text[LINESIZ + 1] = '\n'; 143142407Simp (void) sendto(s, text, sizeof(text), 0, (struct sockaddr *)&ss, size); 144142407Simp} 145142407Simp 146142407Simp/* ARGSUSED */ 147142407Simpvoid 148142407Simpchargen_stream(s, sep) /* Character generator */ 149142407Simp int s; 150142407Simp struct servtab *sep; 15141502Swpaul{ 152110168Ssilby int len; 153142407Simp char *rs, text[LINESIZ+2]; 154142407Simp 155110168Ssilby inetd_setproctitle(sep->se_service, s); 156142407Simp 157142407Simp if (!endring) { 158142407Simp initring(); 159142407Simp rs = ring; 160142407Simp } 16141502Swpaul 162142407Simp text[LINESIZ] = '\r'; 163142407Simp text[LINESIZ + 1] = '\n'; 164142407Simp for (rs = ring;;) { 165142407Simp if ((len = endring - rs) >= LINESIZ) 166142407Simp memmove(text, rs, LINESIZ); 16741502Swpaul else { 16849610Swpaul memmove(text, rs, len); 16949610Swpaul memmove(text + len, ring, LINESIZ - len); 17049610Swpaul } 17149610Swpaul if (++rs == endring) 17249610Swpaul rs = ring; 17349610Swpaul if (write(s, text, sizeof(text)) != sizeof(text)) 17449610Swpaul break; 17549610Swpaul } 17649610Swpaul exit(0); 17749610Swpaul} 17849610Swpaul 17949610Swpaul/* 18049610Swpaul * RFC867 Daytime Protocol. Sends the current date and time as an ascii 18149610Swpaul * character string without any regard for input. 18251432Swpaul */ 18351432Swpaul 18451432Swpaul/* ARGSUSED */ 18551432Swpaulvoid 18651432Swpauldaytime_dg(s, sep) /* Return human-readable time of day */ 18751432Swpaul int s; 18851432Swpaul struct servtab *sep; 18951432Swpaul{ 19051432Swpaul char buffer[256]; 19151432Swpaul time_t clock; 19249610Swpaul struct sockaddr_storage ss; 19349610Swpaul socklen_t size; 19449610Swpaul 19549610Swpaul clock = time((time_t *) 0); 19651455Swpaul 19749610Swpaul size = sizeof(ss); 19849610Swpaul if (recvfrom(s, buffer, sizeof(buffer), 0, 19949610Swpaul (struct sockaddr *)&ss, &size) < 0) 20049610Swpaul return; 20149610Swpaul 20249610Swpaul if (check_loop((struct sockaddr *)&ss, sep)) 203113506Smdodd return; 20451473Swpaul 20549610Swpaul (void) sprintf(buffer, "%.24s\r\n", ctime(&clock)); 20641502Swpaul (void) sendto(s, buffer, strlen(buffer), 0, 20741502Swpaul (struct sockaddr *)&ss, size); 208105221Sphk} 20941502Swpaul 21041502Swpaul/* ARGSUSED */ 21141502Swpaulvoid 212105221Sphkdaytime_stream(s, sep) /* Return human-readable time of day */ 21341502Swpaul int s; 21441502Swpaul struct servtab *sep; 21541502Swpaul{ 216105221Sphk char buffer[256]; 21741502Swpaul time_t clock; 21841502Swpaul 21941502Swpaul clock = time((time_t *) 0); 220105221Sphk 22141502Swpaul (void) sprintf(buffer, "%.24s\r\n", ctime(&clock)); 22241502Swpaul (void) send(s, buffer, strlen(buffer), MSG_EOF); 22341502Swpaul} 224105221Sphk 22541502Swpaul/* 22641502Swpaul * RFC863 Discard Protocol. Any data received is thrown away and no response 22741502Swpaul * is sent. 228105221Sphk */ 22941502Swpaul 23041502Swpaul/* ARGSUSED */ 23141502Swpaulvoid 232105221Sphkdiscard_dg(s, sep) /* Discard service -- ignore data */ 23341502Swpaul int s; 23441502Swpaul struct servtab *sep; 23541502Swpaul{ 236105221Sphk char buffer[BUFSIZE]; 23741502Swpaul 238110168Ssilby (void) read(s, buffer, sizeof(buffer)); 23941502Swpaul} 24041502Swpaul 24141502Swpaul/* ARGSUSED */ 242102336Salfredvoid 243131503Sbmsdiscard_stream(s, sep) /* Discard service -- ignore data */ 24441502Swpaul int s; 245131503Sbms struct servtab *sep; 24641502Swpaul{ 24741502Swpaul int ret; 24841502Swpaul char buffer[BUFSIZE]; 24941502Swpaul 25041502Swpaul inetd_setproctitle(sep->se_service, s); 25141502Swpaul while (1) { 25241502Swpaul while ((ret = read(s, buffer, sizeof(buffer))) > 0) 25341502Swpaul ; 25441502Swpaul if (ret == 0 || errno != EINTR) 25541502Swpaul break; 25641502Swpaul } 25741502Swpaul exit(0); 25841502Swpaul} 25941502Swpaul 260102336Salfred/* 261131503Sbms * RFC862 Echo Protocol. Any data received is sent back to the sender as 26241502Swpaul * received. 263131503Sbms */ 26441502Swpaul 26541502Swpaul/* ARGSUSED */ 26641502Swpaulvoid 26741502Swpaulecho_dg(s, sep) /* Echo service -- echo data back */ 268131503Sbms int s; 26941502Swpaul struct servtab *sep; 270131503Sbms{ 27141502Swpaul char buffer[BUFSIZE]; 272131503Sbms int i; 27341502Swpaul socklen_t size; 27441502Swpaul struct sockaddr_storage ss; 27541502Swpaul 27641502Swpaul size = sizeof(ss); 27741502Swpaul if ((i = recvfrom(s, buffer, sizeof(buffer), 0, 27841502Swpaul (struct sockaddr *)&ss, &size)) < 0) 279110168Ssilby return; 28041502Swpaul 28141502Swpaul if (check_loop((struct sockaddr *)&ss, sep)) 28241502Swpaul return; 28341502Swpaul 284102336Salfred (void) sendto(s, buffer, i, 0, (struct sockaddr *)&ss, size); 285131503Sbms} 286131503Sbms 28741502Swpaul/* ARGSUSED */ 288131503Sbmsvoid 28941502Swpaulecho_stream(s, sep) /* Echo service -- echo data back */ 290131503Sbms int s; 29141502Swpaul struct servtab *sep; 29241502Swpaul{ 29341502Swpaul char buffer[BUFSIZE]; 29441502Swpaul int i; 295131503Sbms 29641502Swpaul inetd_setproctitle(sep->se_service, s); 29741502Swpaul while ((i = read(s, buffer, sizeof(buffer))) > 0 && 29841502Swpaul write(s, buffer, i) > 0) 299131503Sbms ; 30041502Swpaul exit(0); 30141502Swpaul} 30241502Swpaul 30341502Swpaul/* 304131503Sbms * RFC1413 Identification Protocol. Given a TCP port number pair, return a 30541502Swpaul * character string which identifies the owner of that connection on the 30641502Swpaul * server's system. Extended to allow for ~/.fakeid support and ~/.noident 30741502Swpaul * support. 30841502Swpaul */ 30941502Swpaul 310131503Sbms/* ARGSUSED */ 31141502Swpaulvoid 31241502Swpauliderror(lport, fport, s, er) /* Generic ident_stream error-sending func */ 31341502Swpaul int lport, fport, s, er; 31441502Swpaul{ 31541502Swpaul char *p; 31641502Swpaul 31741502Swpaul asprintf(&p, "%d , %d : ERROR : %s\r\n", lport, fport, 31841502Swpaul er == -1 ? "HIDDEN-USER" : er ? strerror(er) : "UNKNOWN-ERROR"); 31941502Swpaul if (p == NULL) { 32041502Swpaul syslog(LOG_ERR, "asprintf: %m"); 32141502Swpaul exit(EX_OSERR); 322109058Smbr } 32341502Swpaul send(s, p, strlen(p), MSG_EOF); 32441502Swpaul free(p); 32541502Swpaul 32641502Swpaul exit(0); 32741502Swpaul} 32841502Swpaul 32941502Swpaul/* ARGSUSED */ 33041502Swpaulvoid 33141502Swpaulident_stream(s, sep) /* Ident service (AKA "auth") */ 33241502Swpaul int s; 33341502Swpaul struct servtab *sep; 33441502Swpaul{ 33541502Swpaul struct utsname un; 33641502Swpaul struct stat sb; 33741502Swpaul struct sockaddr_in sin[2]; 33841502Swpaul#ifdef INET6 33941502Swpaul struct sockaddr_in6 sin6[2]; 34041502Swpaul#endif 34141502Swpaul struct sockaddr_storage ss[2]; 34241502Swpaul struct ucred uc; 34341502Swpaul struct timeval tv = { 34441502Swpaul 10, 34541502Swpaul 0 34641502Swpaul }; 34741502Swpaul struct passwd *pw = NULL; 34841502Swpaul fd_set fdset; 34941502Swpaul char buf[BUFSIZE], *cp = NULL, *p, **av, *osname = NULL, garbage[7]; 35041502Swpaul char *fallback = NULL; 35141502Swpaul socklen_t socklen; 35241502Swpaul ssize_t ssize; 35341502Swpaul size_t size; 35441502Swpaul int c, fflag = 0, nflag = 0, rflag = 0, argc = 0, usedfallback = 0; 35541502Swpaul int gflag = 0, getcredfail = 0, onreadlen; 35641502Swpaul u_short lport, fport; 35741502Swpaul 35841502Swpaul inetd_setproctitle(sep->se_service, s); 359131503Sbms /* 360131503Sbms * Reset getopt() since we are a fork() but not an exec() from 36141502Swpaul * a parent which used getopt() already. 362110168Ssilby */ 363110168Ssilby optind = 1; 364131518Sbms optreset = 1; 36541502Swpaul /* 366131503Sbms * Take the internal argument vector and count it out to make an 367110168Ssilby * argument count for getopt. This can be used for any internal 368110168Ssilby * service to read arguments and use getopt() easily. 369110168Ssilby */ 370131503Sbms for (av = sep->se_argv; *av; av++) 371110168Ssilby argc++; 372110168Ssilby if (argc) { 373131503Sbms int sec, usec; 374110168Ssilby size_t i; 375110168Ssilby u_int32_t random; 376110168Ssilby 377110168Ssilby while ((c = getopt(argc, sep->se_argv, "d:fgno:rt:")) != -1) 378110168Ssilby switch (c) { 379110168Ssilby case 'd': 380110168Ssilby fallback = optarg; 381131503Sbms break; 382110168Ssilby case 'f': 383110168Ssilby fflag = 1; 384110168Ssilby break; 385110168Ssilby case 'g': 38641502Swpaul gflag = 1; 38741502Swpaul random = 0; /* Shush, compiler. */ 38841502Swpaul /* 389102336Salfred * The number of bits in "random" divided 390131503Sbms * by the number of bits needed per iteration 391131503Sbms * gives a more optimal way to reload the 39241502Swpaul * random number only when necessary. 39341502Swpaul * 39441502Swpaul * I'm using base-36, so I need at least 6 39541502Swpaul * bits; round it up to 8 bits to make it 396131503Sbms * easier. 39741502Swpaul */ 39841502Swpaul for (i = 0; i < sizeof(garbage) - 1; i++) { 39941502Swpaul const char *const base36 = 400131503Sbms "0123456789" 401131503Sbms "abcdefghijklmnopqrstuvwxyz"; 40241502Swpaul if (i % (sizeof(random) * 8 / 8) == 0) 40341502Swpaul random = arc4random(); 40441502Swpaul garbage[i] = 40541502Swpaul base36[(random & 0xff) % 36]; 40641502Swpaul random >>= 8; 40741502Swpaul } 40841502Swpaul garbage[i] = '\0'; 40941502Swpaul break; 41041502Swpaul case 'n': 41141502Swpaul nflag = 1; 41241502Swpaul break; 41341502Swpaul case 'o': 41441502Swpaul osname = optarg; 41541502Swpaul break; 41641502Swpaul case 'r': 41741502Swpaul rflag = 1; 41841502Swpaul break; 419131503Sbms case 't': 42041502Swpaul switch (sscanf(optarg, "%d.%d", &sec, &usec)) { 42141502Swpaul case 2: 422131503Sbms tv.tv_usec = usec; 42341502Swpaul case 1: 424110168Ssilby tv.tv_sec = sec; 425110168Ssilby break; 426131518Sbms default: 42741502Swpaul if (debug) 428131503Sbms warnx("bad -t argument"); 429110168Ssilby break; 430131503Sbms } 431110168Ssilby break; 432131503Sbms default: 433110168Ssilby break; 434110168Ssilby } 435110168Ssilby } 436110168Ssilby if (osname == NULL) { 437110168Ssilby if (uname(&un) == -1) 438110168Ssilby iderror(0, 0, s, errno); 439110168Ssilby osname = un.sysname; 440110168Ssilby } 441110168Ssilby socklen = sizeof(ss[0]); 442110168Ssilby if (getsockname(s, (struct sockaddr *)&ss[0], &socklen) == -1) 443110168Ssilby iderror(0, 0, s, errno); 444131503Sbms socklen = sizeof(ss[1]); 445110168Ssilby if (getpeername(s, (struct sockaddr *)&ss[1], &socklen) == -1) 446110168Ssilby iderror(0, 0, s, errno); 447110168Ssilby /* 448102336Salfred * We're going to prepare for and execute reception of a 449131517Sbms * packet of data from the user. The data is in the format 45051432Swpaul * "local_port , foreign_port\r\n" (with local being the 45141502Swpaul * server's port and foreign being the client's.) 452131503Sbms */ 45341502Swpaul FD_ZERO(&fdset); 454110168Ssilby FD_SET(s, &fdset); 455131503Sbms if (select(s + 1, &fdset, NULL, NULL, &tv) == -1) 456131518Sbms iderror(0, 0, s, errno); 457131518Sbms if (ioctl(s, FIONREAD, &onreadlen) == -1) 458131518Sbms iderror(0, 0, s, errno); 459131518Sbms if (onreadlen >= sizeof(buf)) 460131503Sbms onreadlen = sizeof(buf) - 1; 461131503Sbms ssize = read(s, buf, (size_t)onreadlen); 462131503Sbms if (ssize == -1) 463110168Ssilby iderror(0, 0, s, errno); 46441502Swpaul buf[ssize] = '\0'; 46551432Swpaul if (sscanf(buf, "%hu , %hu", &lport, &fport) != 2) 46641502Swpaul iderror(0, 0, s, 0); 46741502Swpaul if (gflag) { 46841502Swpaul cp = garbage; 469131518Sbms goto printit; 470131503Sbms } 47141502Swpaul 47241502Swpaul /* 473102336Salfred * If not "real" (-r), send a HIDDEN-USER error for everything. 474131503Sbms * If -d is used to set a fallback username, this is used to 47551432Swpaul * override it, and the fallback is returned instead. 47641502Swpaul */ 477131503Sbms if (!rflag) { 47841502Swpaul if (fallback == NULL) 479110168Ssilby iderror(lport, fport, s, -1); 480131503Sbms else { 481131503Sbms cp = fallback; 482131503Sbms goto printit; 483131503Sbms } 484131503Sbms } 485131503Sbms 486110168Ssilby /* 48741502Swpaul * We take the input and construct an array of two sockaddr_ins 48851432Swpaul * which contain the local address information and foreign 48941502Swpaul * address information, respectively, used to look up the 49041502Swpaul * credentials for the socket (which are returned by the 49141502Swpaul * sysctl "net.inet.tcp.getcred" when we call it.) The 49241502Swpaul * arrays have been filled in above via get{peer,sock}name(), 493131503Sbms * so right here we are only setting the ports. 49451432Swpaul */ 49551432Swpaul if (ss[0].ss_family != ss[1].ss_family) 496102336Salfred iderror(lport, fport, s, errno); 497131503Sbms size = sizeof(uc); 49851432Swpaul switch (ss[0].ss_family) { 49951432Swpaul case AF_INET: 500131503Sbms sin[0] = *(struct sockaddr_in *)&ss[0]; 50151432Swpaul sin[0].sin_port = htons(lport); 50251432Swpaul sin[1] = *(struct sockaddr_in *)&ss[1]; 50351432Swpaul sin[1].sin_port = htons(fport); 50441502Swpaul if (sysctlbyname("net.inet.tcp.getcred", &uc, &size, sin, 50541502Swpaul sizeof(sin)) == -1) 50641502Swpaul getcredfail = 1; 50741502Swpaul break; 50841502Swpaul#ifdef INET6 509102336Salfred case AF_INET6: 510131503Sbms sin6[0] = *(struct sockaddr_in6 *)&ss[0]; 51141502Swpaul sin6[0].sin6_port = htons(lport); 512147256Sbrooks sin6[1] = *(struct sockaddr_in6 *)&ss[1]; 51341502Swpaul sin6[1].sin6_port = htons(fport); 514131503Sbms if (sysctlbyname("net.inet6.tcp6.getcred", &uc, &size, sin6, 51541502Swpaul sizeof(sin6)) == -1) 516131503Sbms getcredfail = 1; 51741502Swpaul break; 51841502Swpaul#endif 519131518Sbms default: /* should not reach here */ 52041502Swpaul getcredfail = 1; 52141502Swpaul break; 52241502Swpaul } 52341502Swpaul if (getcredfail != 0) { 52441502Swpaul if (fallback == NULL) /* Use a default, if asked to */ 52541502Swpaul iderror(lport, fport, s, errno); 52641502Swpaul usedfallback = 1; 52741502Swpaul } else { 52841502Swpaul /* Look up the pw to get the username */ 52941502Swpaul pw = getpwuid(uc.cr_uid); 53041502Swpaul } 531131503Sbms if (pw == NULL && !usedfallback) /* No such user... */ 53241502Swpaul iderror(lport, fport, s, errno); 53341502Swpaul /* 53441502Swpaul * If enabled, we check for a file named ".noident" in the user's 535131503Sbms * home directory. If found, we return HIDDEN-USER. 53672084Sphk */ 53741502Swpaul if (nflag && !usedfallback) { 53841502Swpaul if (asprintf(&p, "%s/.noident", pw->pw_dir) == -1) 539130270Snaddy iderror(lport, fport, s, errno); 540130270Snaddy if (lstat(p, &sb) == 0) { 54141502Swpaul free(p); 54241502Swpaul iderror(lport, fport, s, -1); 54341502Swpaul } 54441502Swpaul free(p); 54541502Swpaul } 54641502Swpaul /* 54741502Swpaul * Here, if enabled, we read a user's ".fakeid" file in their 54841502Swpaul * home directory. It consists of a line containing the name 54941502Swpaul * they want. 55041502Swpaul */ 55141502Swpaul if (fflag && !usedfallback) { 55241502Swpaul FILE *fakeid = NULL; 55341502Swpaul 55441502Swpaul if (asprintf(&p, "%s/.fakeid", pw->pw_dir) == -1) 55541502Swpaul iderror(lport, fport, s, errno); 55641502Swpaul /* 55741502Swpaul * Here we set ourself to effectively be the user, so we don't 55841502Swpaul * open any files we have no permission to open, especially 55941502Swpaul * symbolic links to sensitive root-owned files or devices. 56041502Swpaul */ 56141502Swpaul seteuid(pw->pw_uid); 56241502Swpaul setegid(pw->pw_gid); 563102336Salfred /* 564131503Sbms * If we were to lstat() here, it would do no good, since it 56541502Swpaul * would introduce a race condition and could be defeated. 566131517Sbms * Therefore, we open the file we have permissions to open 56741502Swpaul * and if it's not a regular file, we close it and end up 568131518Sbms * returning the user's real username. 569131518Sbms */ 57041502Swpaul fakeid = fopen(p, "r"); 57141502Swpaul free(p); 57241502Swpaul if (fakeid != NULL && 57341502Swpaul fstat(fileno(fakeid), &sb) != -1 && S_ISREG(sb.st_mode)) { 57441502Swpaul buf[sizeof(buf) - 1] = '\0'; 57551432Swpaul if (fgets(buf, sizeof(buf), fakeid) == NULL) { 57641502Swpaul cp = pw->pw_name; 57741502Swpaul fclose(fakeid); 57841502Swpaul goto printit; 57941502Swpaul } 58041502Swpaul fclose(fakeid); 58141502Swpaul /* 58241502Swpaul * Usually, the file will have the desired identity 58341502Swpaul * in the form "identity\n", so we use strtok() to 584102336Salfred * end the string (which fgets() doesn't do.) 585131503Sbms */ 58641502Swpaul buf[strcspn(buf, "\r\n")] = '\0'; 587131517Sbms cp = buf; 58841502Swpaul /* Allow for beginning white space... */ 589131844Sbms while (isspace(*cp)) 590131518Sbms cp++; 59141502Swpaul /* ...and ending white space. */ 59241502Swpaul cp[strcspn(cp, " \t")] = '\0'; 59341502Swpaul /* User names of >16 characters are invalid */ 59441502Swpaul if (strlen(cp) > 16) 59541502Swpaul cp[16] = '\0'; 59641502Swpaul /* 59741502Swpaul * If the name is a zero-length string or matches 598107220Ssilby * the name of another user, it's invalid, so 599107220Ssilby * we will return their real identity instead. 600107220Ssilby */ 601107220Ssilby 602107220Ssilby if (!*cp || getpwnam(cp)) 603131503Sbms cp = getpwuid(uc.cr_uid)->pw_name; 604131503Sbms } else 605107220Ssilby cp = pw->pw_name; 606107220Ssilby } else if (!usedfallback) 607107220Ssilby cp = pw->pw_name; 60841502Swpaul else 60941502Swpaul cp = fallback; 61041502Swpaulprintit: 61141502Swpaul /* Finally, we make and send the reply. */ 61241502Swpaul if (asprintf(&p, "%d , %d : USERID : %s : %s\r\n", lport, fport, osname, 61341502Swpaul cp) == -1) { 61441502Swpaul syslog(LOG_ERR, "asprintf: %m"); 61541502Swpaul exit(EX_OSERR); 61641502Swpaul } 617102336Salfred send(s, p, strlen(p), MSG_EOF); 618131503Sbms free(p); 61941502Swpaul 620131503Sbms exit(0); 62141502Swpaul} 622131503Sbms 62349610Swpaul/* 62449610Swpaul * RFC738 Time Server. 62549610Swpaul * Return a machine readable date and time, in the form of the 626142398Simp * number of seconds since midnight, Jan 1, 1900. Since gettimeofday 62741502Swpaul * returns the number of seconds since midnight, Jan 1, 1970, 62841502Swpaul * we must add 2208988800 seconds to this figure to make up for 62941502Swpaul * some seventy years Bell Labs was asleep. 63041502Swpaul */ 631131503Sbms 63241502Swpaulunsigned long 63341502Swpaulmachtime() 63441502Swpaul{ 63541502Swpaul struct timeval tv; 63641502Swpaul 63741502Swpaul if (gettimeofday(&tv, (struct timezone *)NULL) < 0) { 638102336Salfred if (debug) 639102336Salfred warnx("unable to get time of day"); 64049610Swpaul return (0L); 64141502Swpaul } 64267087Swpaul#define OFFSET ((u_long)25567 * 24*60*60) 64341502Swpaul return (htonl((long)(tv.tv_sec + OFFSET))); 64441502Swpaul#undef OFFSET 64541502Swpaul} 64649610Swpaul 64741502Swpaul/* ARGSUSED */ 64849610Swpaulvoid 64949610Swpaulmachtime_dg(s, sep) 65041502Swpaul int s; 65193818Sjhb struct servtab *sep; 652131518Sbms{ 65341502Swpaul unsigned long result; 65441502Swpaul struct sockaddr_storage ss; 65541502Swpaul socklen_t size; 65672813Swpaul 657107220Ssilby size = sizeof(ss); 65841502Swpaul if (recvfrom(s, (char *)&result, sizeof(result), 0, 65949610Swpaul (struct sockaddr *)&ss, &size) < 0) 660127135Snjl return; 66149610Swpaul 66249610Swpaul if (check_loop((struct sockaddr *)&ss, sep)) 66349610Swpaul return; 66449610Swpaul 66541502Swpaul result = machtime(); 66641502Swpaul (void) sendto(s, (char *) &result, sizeof(result), 0, 66741502Swpaul (struct sockaddr *)&ss, size); 66849610Swpaul} 66949610Swpaul 67041502Swpaul/* ARGSUSED */ 67141502Swpaulvoid 67249610Swpaulmachtime_stream(s, sep) 673127135Snjl int s; 67449610Swpaul struct servtab *sep; 67549610Swpaul{ 67649610Swpaul unsigned long result; 67741502Swpaul 67849610Swpaul result = machtime(); 67941502Swpaul (void) send(s, (char *) &result, sizeof(result), MSG_EOF); 68041502Swpaul} 68141502Swpaul 68276586Swpaul/* 68376586Swpaul * RFC1078 TCP Port Service Multiplexer (TCPMUX). Service connections to 68476586Swpaul * services based on the service name sent. 68576586Swpaul * 68676586Swpaul * Based on TCPMUX.C by Mark K. Lottor November 1988 68776586Swpaul * sri-nic::ps:<mkl>tcpmux.c 68876586Swpaul */ 68941502Swpaul 69041502Swpaul#define MAX_SERV_LEN (256+2) /* 2 bytes for \r\n */ 69141502Swpaul#define strwrite(fd, buf) (void) write(fd, buf, sizeof(buf)-1) 692131503Sbms 693110168Ssilbystatic int /* # of characters upto \r,\n or \0 */ 694110168Ssilbygetline(fd, buf, len) 695110168Ssilby int fd; 696131503Sbms char *buf; 697110168Ssilby int len; 698110168Ssilby{ 699110168Ssilby int count = 0, n; 70041502Swpaul struct sigaction sa; 70141502Swpaul 70241502Swpaul sa.sa_flags = 0; 70341502Swpaul sigemptyset(&sa.sa_mask); 70441502Swpaul sa.sa_handler = SIG_DFL; 70541502Swpaul sigaction(SIGALRM, &sa, (struct sigaction *)0); 70641502Swpaul do { 70741502Swpaul alarm(10); 70841502Swpaul n = read(fd, buf, len-count); 70941502Swpaul alarm(0); 71041502Swpaul if (n == 0) 71141502Swpaul return (count); 71241502Swpaul if (n < 0) 71341502Swpaul return (-1); 71451432Swpaul while (--n >= 0) { 71551657Swpaul if (*buf == '\r' || *buf == '\n' || *buf == '\0') 71651432Swpaul return (count); 71751432Swpaul count++; 71841502Swpaul buf++; 71949610Swpaul } 72049610Swpaul } while (count < len); 72141502Swpaul return (count); 72241502Swpaul} 72341502Swpaul 72441502Swpaulstruct servtab * 725147256Sbrookstcpmux(s) 726147256Sbrooks int s; 727147256Sbrooks{ 728147256Sbrooks struct servtab *sep; 729147256Sbrooks char service[MAX_SERV_LEN+1]; 730147256Sbrooks int len; 73141502Swpaul 732121816Sbrooks /* Get requested service name */ 73341502Swpaul if ((len = getline(s, service, MAX_SERV_LEN)) < 0) { 73441502Swpaul strwrite(s, "-Error reading service name\r\n"); 73541502Swpaul return (NULL); 73641502Swpaul } 73741502Swpaul service[len] = '\0'; 73841502Swpaul 73941502Swpaul if (debug) 740132986Smlaier warnx("tcpmux: someone wants %s", service); 74143515Swpaul 742132986Smlaier /* 743128118Sru * Help is a required command, and lists available services, 744128118Sru * one per line. 745128118Sru */ 746128118Sru if (!strcasecmp(service, "help")) { 74741502Swpaul for (sep = servtab; sep; sep = sep->se_next) { 748131503Sbms if (!ISMUX(sep)) 74951432Swpaul continue; 75051432Swpaul (void)write(s,sep->se_service,strlen(sep->se_service)); 75141502Swpaul strwrite(s, "\r\n"); 75249610Swpaul } 75341502Swpaul return (NULL); 75441502Swpaul } 75541502Swpaul 75651432Swpaul /* Try matching a service in inetd.conf with the request */ 75741502Swpaul for (sep = servtab; sep; sep = sep->se_next) { 758131503Sbms if (!ISMUX(sep)) 759106936Ssam continue; 76041502Swpaul if (!strcasecmp(service, sep->se_service)) { 761131844Sbms if (ISMUXPLUS(sep)) { 762131844Sbms strwrite(s, "+Go\r\n"); 763113609Snjl } 764131518Sbms return (sep); 765112872Snjl } 766112872Snjl } 767112872Snjl strwrite(s, "-Service not available\r\n"); 768112872Snjl return (NULL); 769113609Snjl} 770147256Sbrooks