builtins.c revision 49030
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.5 1999/07/23 15:00:07 sheldonh 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 FILE *fakeid = NULL; 309 struct stat sb; 310 struct utsname un; 311 struct sockaddr_in sin[2]; 312 struct ucred uc; 313 struct passwd *pw; 314 struct timeval tv = { 315 10, 316 0 317 }; 318 fd_set fdset; 319 char fakeid_path[PATH_MAX]; 320 char buf[BUFSIZE], *cp = NULL, *p, **av, *osname = NULL; 321 int sec, usec; 322 int len, c, rflag = 0, fflag = 0, argc = 0; 323 u_short lport, fport; 324 325 inetd_setproctitle(sep->se_service, s); 326 optind = 1; 327 optreset = 1; 328 for (av = sep->se_argv; *av; av++) 329 argc++; 330 if (argc) { 331 while ((c = getopt(argc, sep->se_argv, "fro:t:")) != -1) 332 switch (c) { 333 case 'f': 334 fflag = 1; 335 break; 336 case 'r': 337 rflag = 1; 338 break; 339 case 'o': 340 osname = optarg; 341 break; 342 case 't': 343 switch (sscanf(optarg, "%d.%d", &sec, &usec)) { 344 case 2: 345 tv.tv_usec = usec; 346 case 1: 347 tv.tv_sec = sec; 348 break; 349 default: 350 if (debug) 351 warnx("bad -t argument"); 352 break; 353 } 354 default: 355 break; 356 } 357 } 358 if (osname == NULL) { 359 if (uname(&un)) 360 iderror(0, 0, s, errno); 361 osname = un.sysname; 362 } 363 len = sizeof(sin[0]); 364 if (getsockname(s, (struct sockaddr *)&sin[0], &len) == -1) 365 iderror(0, 0, s, errno); 366 len = sizeof(sin[1]); 367 if (getpeername(s, (struct sockaddr *)&sin[1], &len) == -1) 368 iderror(0, 0, s, errno); 369 FD_ZERO(&fdset); 370 FD_SET(s, &fdset); 371 if (select(s + 1, &fdset, NULL, NULL, &tv) == -1) 372 iderror(0, 0, s, errno); 373 if (ioctl(s, FIONREAD, &len) == -1) 374 iderror(0, 0, s, errno); 375 if (len >= sizeof(buf)) 376 len = sizeof(buf) - 1; 377 len = read(s, buf, len); 378 if (len == -1) 379 iderror(0, 0, s, errno); 380 buf[len] = '\0'; 381 if (sscanf(buf, "%hu , %hu", &lport, &fport) != 2) 382 iderror(0, 0, s, 0); 383 if (!rflag) 384 iderror(lport, fport, s, -1); 385 sin[0].sin_port = htons(lport); 386 sin[1].sin_port = htons(fport); 387 len = sizeof(uc); 388 if (sysctlbyname("net.inet.tcp.getcred", &uc, &len, sin, 389 sizeof(sin)) == -1) 390 iderror(lport, fport, s, errno); 391 pw = getpwuid(uc.cr_uid); 392 if (pw == NULL) 393 iderror(lport, fport, s, errno); 394 if (fflag) { 395 seteuid(pw->pw_uid); 396 setegid(pw->pw_gid); 397 snprintf(fakeid_path, sizeof(fakeid_path), "%s/.fakeid", 398 pw->pw_dir); 399 if ((fakeid = fopen(fakeid_path, "r")) != NULL && 400 fstat(fileno(fakeid), &sb) != -1 && S_ISREG(sb.st_mode)) { 401 buf[sizeof(buf) - 1] = '\0'; 402 if (fgets(buf, sizeof(buf), fakeid) == NULL) { 403 cp = pw->pw_name; 404 fclose(fakeid); 405 goto printit; 406 } 407 fclose(fakeid); 408 strtok(buf, "\r\n"); 409 if (strlen(buf) > 16) 410 buf[16] = '\0'; 411 cp = buf; 412 while (isspace(*cp)) 413 cp++; 414 strtok(cp, " \t"); 415 if (!*cp || getpwnam(cp)) 416 cp = getpwuid(uc.cr_uid)->pw_name; 417 } else 418 cp = pw->pw_name; 419 } else 420 cp = pw->pw_name; 421printit: 422 if (asprintf(&p, "%d , %d : USERID : %s : %s\r\n", lport, fport, osname, 423 cp) == -1) 424 iderror(0, 0, s, errno); 425 write(s, p, strlen(p)); 426 free(p); 427 428 exit(0); 429} 430 431/* 432 * Return a machine readable date and time, in the form of the 433 * number of seconds since midnight, Jan 1, 1900. Since gettimeofday 434 * returns the number of seconds since midnight, Jan 1, 1970, 435 * we must add 2208988800 seconds to this figure to make up for 436 * some seventy years Bell Labs was asleep. 437 */ 438 439unsigned long 440machtime() 441{ 442 struct timeval tv; 443 444 if (gettimeofday(&tv, (struct timezone *)NULL) < 0) { 445 if (debug) 446 warnx("unable to get time of day"); 447 return (0L); 448 } 449#define OFFSET ((u_long)25567 * 24*60*60) 450 return (htonl((long)(tv.tv_sec + OFFSET))); 451#undef OFFSET 452} 453 454/* ARGSUSED */ 455void 456machtime_dg(s, sep) 457 int s; 458 struct servtab *sep; 459{ 460 unsigned long result; 461 struct sockaddr_in sin; 462 int size; 463 464 size = sizeof(sin); 465 if (recvfrom(s, (char *)&result, sizeof(result), 0, 466 (struct sockaddr *)&sin, &size) < 0) 467 return; 468 469 if (check_loop(&sin, sep)) 470 return; 471 472 result = machtime(); 473 (void) sendto(s, (char *) &result, sizeof(result), 0, 474 (struct sockaddr *)&sin, sizeof(sin)); 475} 476 477/* ARGSUSED */ 478void 479machtime_stream(s, sep) 480 int s; 481 struct servtab *sep; 482{ 483 unsigned long result; 484 485 result = machtime(); 486 (void) write(s, (char *) &result, sizeof(result)); 487} 488 489/* 490 * Based on TCPMUX.C by Mark K. Lottor November 1988 491 * sri-nic::ps:<mkl>tcpmux.c 492 */ 493 494#define MAX_SERV_LEN (256+2) /* 2 bytes for \r\n */ 495#define strwrite(fd, buf) (void) write(fd, buf, sizeof(buf)-1) 496 497static int /* # of characters upto \r,\n or \0 */ 498getline(fd, buf, len) 499 int fd; 500 char *buf; 501 int len; 502{ 503 int count = 0, n; 504 struct sigaction sa; 505 506 sa.sa_flags = 0; 507 sigemptyset(&sa.sa_mask); 508 sa.sa_handler = SIG_DFL; 509 sigaction(SIGALRM, &sa, (struct sigaction *)0); 510 do { 511 alarm(10); 512 n = read(fd, buf, len-count); 513 alarm(0); 514 if (n == 0) 515 return (count); 516 if (n < 0) 517 return (-1); 518 while (--n >= 0) { 519 if (*buf == '\r' || *buf == '\n' || *buf == '\0') 520 return (count); 521 count++; 522 buf++; 523 } 524 } while (count < len); 525 return (count); 526} 527 528struct servtab * 529tcpmux(s) 530 int s; 531{ 532 struct servtab *sep; 533 char service[MAX_SERV_LEN+1]; 534 int len; 535 536 /* Get requested service name */ 537 if ((len = getline(s, service, MAX_SERV_LEN)) < 0) { 538 strwrite(s, "-Error reading service name\r\n"); 539 return (NULL); 540 } 541 service[len] = '\0'; 542 543 if (debug) 544 warnx("tcpmux: someone wants %s", service); 545 546 /* 547 * Help is a required command, and lists available services, 548 * one per line. 549 */ 550 if (!strcasecmp(service, "help")) { 551 for (sep = servtab; sep; sep = sep->se_next) { 552 if (!ISMUX(sep)) 553 continue; 554 (void)write(s,sep->se_service,strlen(sep->se_service)); 555 strwrite(s, "\r\n"); 556 } 557 return (NULL); 558 } 559 560 /* Try matching a service in inetd.conf with the request */ 561 for (sep = servtab; sep; sep = sep->se_next) { 562 if (!ISMUX(sep)) 563 continue; 564 if (!strcasecmp(service, sep->se_service)) { 565 if (ISMUXPLUS(sep)) { 566 strwrite(s, "+Go\r\n"); 567 } 568 return (sep); 569 } 570 } 571 strwrite(s, "-Service not available\r\n"); 572 return (NULL); 573} 574 575