builtins.c revision 49051
1/*- 2 * Copyright (c) 1983, 1991, 1993, 1994 3 * The Regents of the University of California. All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 * SUCH DAMAGE. 25 * 26 * $Id: builtins.c,v 1.7 1999/07/23 15:49:14 green Exp $ 27 * 28 */ 29 30#include <sys/filio.h> 31#include <sys/ioccom.h> 32#include <sys/param.h> 33#include <sys/stat.h> 34#include <sys/socket.h> 35#include <sys/sysctl.h> 36#include <sys/ucred.h> 37#include <sys/uio.h> 38#include <sys/utsname.h> 39 40#include <ctype.h> 41#include <err.h> 42#include <errno.h> 43#include <limits.h> 44#include <pwd.h> 45#include <signal.h> 46#include <stdlib.h> 47#include <string.h> 48#include <sysexits.h> 49#include <syslog.h> 50#include <unistd.h> 51 52#include "inetd.h" 53 54extern int debug; 55extern struct servtab *servtab; 56 57char ring[128]; 58char *endring; 59 60int check_loop __P((struct sockaddr_in *, struct servtab *sep)); 61void inetd_setproctitle __P((char *, int)); 62 63struct biltin biltins[] = { 64 /* Echo received data */ 65 { "echo", SOCK_STREAM, 1, -1, echo_stream }, 66 { "echo", SOCK_DGRAM, 0, 1, echo_dg }, 67 68 /* Internet /dev/null */ 69 { "discard", SOCK_STREAM, 1, -1, discard_stream }, 70 { "discard", SOCK_DGRAM, 0, 1, discard_dg }, 71 72 /* Return 32 bit time since 1970 */ 73 { "time", SOCK_STREAM, 0, -1, machtime_stream }, 74 { "time", SOCK_DGRAM, 0, 1, machtime_dg }, 75 76 /* Return human-readable time */ 77 { "daytime", SOCK_STREAM, 0, -1, daytime_stream }, 78 { "daytime", SOCK_DGRAM, 0, 1, daytime_dg }, 79 80 /* Familiar character generator */ 81 { "chargen", SOCK_STREAM, 1, -1, chargen_stream }, 82 { "chargen", SOCK_DGRAM, 0, 1, chargen_dg }, 83 84 { "tcpmux", SOCK_STREAM, 1, -1, (void (*)())tcpmux }, 85 86 { "auth", SOCK_STREAM, 1, -1, ident_stream }, 87 88 { NULL } 89}; 90 91void 92initring() 93{ 94 int i; 95 96 endring = ring; 97 98 for (i = 0; i <= 128; ++i) 99 if (isprint(i)) 100 *endring++ = i; 101} 102 103/* ARGSUSED */ 104void 105chargen_dg(s, sep) /* Character generator */ 106 int s; 107 struct servtab *sep; 108{ 109 struct sockaddr_in sin; 110 static char *rs; 111 int len, size; 112 char text[LINESIZ+2]; 113 114 if (endring == 0) { 115 initring(); 116 rs = ring; 117 } 118 119 size = sizeof(sin); 120 if (recvfrom(s, text, sizeof(text), 0, 121 (struct sockaddr *)&sin, &size) < 0) 122 return; 123 124 if (check_loop(&sin, sep)) 125 return; 126 127 if ((len = endring - rs) >= LINESIZ) 128 memmove(text, rs, LINESIZ); 129 else { 130 memmove(text, rs, len); 131 memmove(text + len, ring, LINESIZ - len); 132 } 133 if (++rs == endring) 134 rs = ring; 135 text[LINESIZ] = '\r'; 136 text[LINESIZ + 1] = '\n'; 137 (void) sendto(s, text, sizeof(text), 0, 138 (struct sockaddr *)&sin, sizeof(sin)); 139} 140 141/* ARGSUSED */ 142void 143chargen_stream(s, sep) /* Character generator */ 144 int s; 145 struct servtab *sep; 146{ 147 int len; 148 char *rs, text[LINESIZ+2]; 149 150 inetd_setproctitle(sep->se_service, s); 151 152 if (!endring) { 153 initring(); 154 rs = ring; 155 } 156 157 text[LINESIZ] = '\r'; 158 text[LINESIZ + 1] = '\n'; 159 for (rs = ring;;) { 160 if ((len = endring - rs) >= LINESIZ) 161 memmove(text, rs, LINESIZ); 162 else { 163 memmove(text, rs, len); 164 memmove(text + len, ring, LINESIZ - len); 165 } 166 if (++rs == endring) 167 rs = ring; 168 if (write(s, text, sizeof(text)) != sizeof(text)) 169 break; 170 } 171 exit(0); 172} 173 174/* ARGSUSED */ 175void 176daytime_dg(s, sep) /* Return human-readable time of day */ 177 int s; 178 struct servtab *sep; 179{ 180 char buffer[256]; 181 time_t clock; 182 struct sockaddr_in sin; 183 int size; 184 185 clock = time((time_t *) 0); 186 187 size = sizeof(sin); 188 if (recvfrom(s, buffer, sizeof(buffer), 0, 189 (struct sockaddr *)&sin, &size) < 0) 190 return; 191 192 if (check_loop(&sin, sep)) 193 return; 194 195 (void) sprintf(buffer, "%.24s\r\n", ctime(&clock)); 196 (void) sendto(s, buffer, strlen(buffer), 0, 197 (struct sockaddr *)&sin, sizeof(sin)); 198} 199 200/* ARGSUSED */ 201void 202daytime_stream(s, sep) /* Return human-readable time of day */ 203 int s; 204 struct servtab *sep; 205{ 206 char buffer[256]; 207 time_t clock; 208 209 clock = time((time_t *) 0); 210 211 (void) sprintf(buffer, "%.24s\r\n", ctime(&clock)); 212 (void) write(s, buffer, strlen(buffer)); 213} 214 215/* ARGSUSED */ 216void 217discard_dg(s, sep) /* Discard service -- ignore data */ 218 int s; 219 struct servtab *sep; 220{ 221 char buffer[BUFSIZE]; 222 223 (void) read(s, buffer, sizeof(buffer)); 224} 225 226/* ARGSUSED */ 227void 228discard_stream(s, sep) /* Discard service -- ignore data */ 229 int s; 230 struct servtab *sep; 231{ 232 int ret; 233 char buffer[BUFSIZE]; 234 235 inetd_setproctitle(sep->se_service, s); 236 while (1) { 237 while ((ret = read(s, buffer, sizeof(buffer))) > 0) 238 ; 239 if (ret == 0 || errno != EINTR) 240 break; 241 } 242 exit(0); 243} 244 245/* ARGSUSED */ 246void 247echo_dg(s, sep) /* Echo service -- echo data back */ 248 int s; 249 struct servtab *sep; 250{ 251 char buffer[BUFSIZE]; 252 int i, size; 253 struct sockaddr_in sin; 254 255 size = sizeof(sin); 256 if ((i = recvfrom(s, buffer, sizeof(buffer), 0, 257 (struct sockaddr *)&sin, &size)) < 0) 258 return; 259 260 if (check_loop(&sin, sep)) 261 return; 262 263 (void) sendto(s, buffer, i, 0, (struct sockaddr *)&sin, 264 sizeof(sin)); 265} 266 267/* ARGSUSED */ 268void 269echo_stream(s, sep) /* Echo service -- echo data back */ 270 int s; 271 struct servtab *sep; 272{ 273 char buffer[BUFSIZE]; 274 int i; 275 276 inetd_setproctitle(sep->se_service, s); 277 while ((i = read(s, buffer, sizeof(buffer))) > 0 && 278 write(s, buffer, i) > 0) 279 ; 280 exit(0); 281} 282 283/* ARGSUSED */ 284void 285iderror(lport, fport, s, er) 286 int lport, fport, s, er; 287{ 288 char *p; 289 290 asprintf(&p, "%d , %d : ERROR : %s\r\n", lport, fport, 291 er == -1 ? "HIDDEN-USER" : er ? strerror(er) : "UNKNOWN-ERROR"); 292 if (p == NULL) { 293 syslog(LOG_ERR, "Out of memory."); 294 exit(EX_OSERR); 295 } 296 write(s, p, strlen(p)); 297 free(p); 298 299 exit(0); 300} 301 302/* ARGSUSED */ 303void 304ident_stream(s, sep) /* Ident service */ 305 int s; 306 struct servtab *sep; 307{ 308 struct sockaddr_in sin[2]; 309 struct ucred uc; 310 struct passwd *pw; 311 struct timeval tv = { 312 10, 313 0 314 }; 315 fd_set fdset; 316 char buf[BUFSIZE], *cp = NULL, *p, **av, *osname = NULL; 317 int len, c, rflag = 0, fflag = 0, argc = 0; 318 u_short lport, fport; 319 320 inetd_setproctitle(sep->se_service, s); 321 optind = 1; 322 optreset = 1; 323 for (av = sep->se_argv; *av; av++) 324 argc++; 325 if (argc) { 326 while ((c = getopt(argc, sep->se_argv, "fro:t:")) != -1) 327 switch (c) { 328 case 'f': 329 fflag = 1; 330 break; 331 case 'r': 332 rflag = 1; 333 break; 334 case 'o': 335 osname = optarg; 336 break; 337 case 't': 338 { 339 int sec, usec; 340 341 switch (sscanf(optarg, "%d.%d", &sec, &usec)) { 342 case 2: 343 tv.tv_usec = usec; 344 case 1: 345 tv.tv_sec = sec; 346 break; 347 default: 348 if (debug) 349 warnx("bad -t argument"); 350 break; 351 } 352 } 353 default: 354 break; 355 } 356 } 357 if (osname == NULL) { 358 struct utsname un; 359 360 if (uname(&un) == -1) 361 iderror(0, 0, s, errno); 362 osname = un.sysname; 363 } 364 len = sizeof(sin[0]); 365 if (getsockname(s, (struct sockaddr *)&sin[0], &len) == -1) 366 iderror(0, 0, s, errno); 367 len = sizeof(sin[1]); 368 if (getpeername(s, (struct sockaddr *)&sin[1], &len) == -1) 369 iderror(0, 0, s, errno); 370 FD_ZERO(&fdset); 371 FD_SET(s, &fdset); 372 if (select(s + 1, &fdset, NULL, NULL, &tv) == -1) 373 iderror(0, 0, s, errno); 374 if (ioctl(s, FIONREAD, &len) == -1) 375 iderror(0, 0, s, errno); 376 if (len >= sizeof(buf)) 377 len = sizeof(buf) - 1; 378 len = read(s, buf, len); 379 if (len == -1) 380 iderror(0, 0, s, errno); 381 buf[len] = '\0'; 382 if (sscanf(buf, "%hu , %hu", &lport, &fport) != 2) 383 iderror(0, 0, s, 0); 384 if (!rflag) 385 iderror(lport, fport, s, -1); 386 sin[0].sin_port = htons(lport); 387 sin[1].sin_port = htons(fport); 388 len = sizeof(uc); 389 if (sysctlbyname("net.inet.tcp.getcred", &uc, &len, sin, 390 sizeof(sin)) == -1) 391 iderror(lport, fport, s, errno); 392 pw = getpwuid(uc.cr_uid); 393 if (pw == NULL) 394 iderror(lport, fport, s, errno); 395 if (fflag) { 396 FILE *fakeid = NULL; 397 char fakeid_path[PATH_MAX]; 398 struct stat sb; 399 400 seteuid(pw->pw_uid); 401 setegid(pw->pw_gid); 402 snprintf(fakeid_path, sizeof(fakeid_path), "%s/.fakeid", 403 pw->pw_dir); 404 if ((fakeid = fopen(fakeid_path, "r")) != NULL && 405 fstat(fileno(fakeid), &sb) != -1 && S_ISREG(sb.st_mode)) { 406 buf[sizeof(buf) - 1] = '\0'; 407 if (fgets(buf, sizeof(buf), fakeid) == NULL) { 408 cp = pw->pw_name; 409 fclose(fakeid); 410 goto printit; 411 } 412 fclose(fakeid); 413 strtok(buf, "\r\n"); 414 if (strlen(buf) > 16) 415 buf[16] = '\0'; 416 cp = buf; 417 while (isspace(*cp)) 418 cp++; 419 strtok(cp, " \t"); 420 if (!*cp || getpwnam(cp)) 421 cp = getpwuid(uc.cr_uid)->pw_name; 422 } else 423 cp = pw->pw_name; 424 } else 425 cp = pw->pw_name; 426printit: 427 if (asprintf(&p, "%d , %d : USERID : %s : %s\r\n", lport, fport, osname, 428 cp) == -1) { 429 syslog(LOG_ERR, "Out of memory."); 430 exit(EX_OSERR); 431 } 432 write(s, p, strlen(p)); 433 free(p); 434 435 exit(0); 436} 437 438/* 439 * Return a machine readable date and time, in the form of the 440 * number of seconds since midnight, Jan 1, 1900. Since gettimeofday 441 * returns the number of seconds since midnight, Jan 1, 1970, 442 * we must add 2208988800 seconds to this figure to make up for 443 * some seventy years Bell Labs was asleep. 444 */ 445 446unsigned long 447machtime() 448{ 449 struct timeval tv; 450 451 if (gettimeofday(&tv, (struct timezone *)NULL) < 0) { 452 if (debug) 453 warnx("unable to get time of day"); 454 return (0L); 455 } 456#define OFFSET ((u_long)25567 * 24*60*60) 457 return (htonl((long)(tv.tv_sec + OFFSET))); 458#undef OFFSET 459} 460 461/* ARGSUSED */ 462void 463machtime_dg(s, sep) 464 int s; 465 struct servtab *sep; 466{ 467 unsigned long result; 468 struct sockaddr_in sin; 469 int size; 470 471 size = sizeof(sin); 472 if (recvfrom(s, (char *)&result, sizeof(result), 0, 473 (struct sockaddr *)&sin, &size) < 0) 474 return; 475 476 if (check_loop(&sin, sep)) 477 return; 478 479 result = machtime(); 480 (void) sendto(s, (char *) &result, sizeof(result), 0, 481 (struct sockaddr *)&sin, sizeof(sin)); 482} 483 484/* ARGSUSED */ 485void 486machtime_stream(s, sep) 487 int s; 488 struct servtab *sep; 489{ 490 unsigned long result; 491 492 result = machtime(); 493 (void) write(s, (char *) &result, sizeof(result)); 494} 495 496/* 497 * Based on TCPMUX.C by Mark K. Lottor November 1988 498 * sri-nic::ps:<mkl>tcpmux.c 499 */ 500 501#define MAX_SERV_LEN (256+2) /* 2 bytes for \r\n */ 502#define strwrite(fd, buf) (void) write(fd, buf, sizeof(buf)-1) 503 504static int /* # of characters upto \r,\n or \0 */ 505getline(fd, buf, len) 506 int fd; 507 char *buf; 508 int len; 509{ 510 int count = 0, n; 511 struct sigaction sa; 512 513 sa.sa_flags = 0; 514 sigemptyset(&sa.sa_mask); 515 sa.sa_handler = SIG_DFL; 516 sigaction(SIGALRM, &sa, (struct sigaction *)0); 517 do { 518 alarm(10); 519 n = read(fd, buf, len-count); 520 alarm(0); 521 if (n == 0) 522 return (count); 523 if (n < 0) 524 return (-1); 525 while (--n >= 0) { 526 if (*buf == '\r' || *buf == '\n' || *buf == '\0') 527 return (count); 528 count++; 529 buf++; 530 } 531 } while (count < len); 532 return (count); 533} 534 535struct servtab * 536tcpmux(s) 537 int s; 538{ 539 struct servtab *sep; 540 char service[MAX_SERV_LEN+1]; 541 int len; 542 543 /* Get requested service name */ 544 if ((len = getline(s, service, MAX_SERV_LEN)) < 0) { 545 strwrite(s, "-Error reading service name\r\n"); 546 return (NULL); 547 } 548 service[len] = '\0'; 549 550 if (debug) 551 warnx("tcpmux: someone wants %s", service); 552 553 /* 554 * Help is a required command, and lists available services, 555 * one per line. 556 */ 557 if (!strcasecmp(service, "help")) { 558 for (sep = servtab; sep; sep = sep->se_next) { 559 if (!ISMUX(sep)) 560 continue; 561 (void)write(s,sep->se_service,strlen(sep->se_service)); 562 strwrite(s, "\r\n"); 563 } 564 return (NULL); 565 } 566 567 /* Try matching a service in inetd.conf with the request */ 568 for (sep = servtab; sep; sep = sep->se_next) { 569 if (!ISMUX(sep)) 570 continue; 571 if (!strcasecmp(service, sep->se_service)) { 572 if (ISMUXPLUS(sep)) { 573 strwrite(s, "+Go\r\n"); 574 } 575 return (sep); 576 } 577 } 578 strwrite(s, "-Service not available\r\n"); 579 return (NULL); 580} 581 582