builtins.c revision 48981
1#include <sys/param.h> 2#include <sys/stat.h> 3#include <sys/socket.h> 4#include <sys/sysctl.h> 5#include <sys/ucred.h> 6#include <sys/uio.h> 7 8#include <ctype.h> 9#include <err.h> 10#include <errno.h> 11#include <limits.h> 12#include <pwd.h> 13#include <signal.h> 14#include <string.h> 15#include <unistd.h> 16 17#include "inetd.h" 18 19extern int debug; 20extern struct servtab *servtab; 21 22char ring[128]; 23char *endring; 24 25int check_loop __P((struct sockaddr_in *, struct servtab *sep)); 26void inetd_setproctitle __P((char *, int)); 27 28struct biltin biltins[] = { 29 /* Echo received data */ 30 { "echo", SOCK_STREAM, 1, -1, echo_stream }, 31 { "echo", SOCK_DGRAM, 0, 1, echo_dg }, 32 33 /* Internet /dev/null */ 34 { "discard", SOCK_STREAM, 1, -1, discard_stream }, 35 { "discard", SOCK_DGRAM, 0, 1, discard_dg }, 36 37 /* Return 32 bit time since 1970 */ 38 { "time", SOCK_STREAM, 0, -1, machtime_stream }, 39 { "time", SOCK_DGRAM, 0, 1, machtime_dg }, 40 41 /* Return human-readable time */ 42 { "daytime", SOCK_STREAM, 0, -1, daytime_stream }, 43 { "daytime", SOCK_DGRAM, 0, 1, daytime_dg }, 44 45 /* Familiar character generator */ 46 { "chargen", SOCK_STREAM, 1, -1, chargen_stream }, 47 { "chargen", SOCK_DGRAM, 0, 1, chargen_dg }, 48 49 { "tcpmux", SOCK_STREAM, 1, -1, (void (*)())tcpmux }, 50 51 { "auth", SOCK_STREAM, 1, -1, ident_stream }, 52 53 { NULL } 54}; 55 56void 57initring() 58{ 59 int i; 60 61 endring = ring; 62 63 for (i = 0; i <= 128; ++i) 64 if (isprint(i)) 65 *endring++ = i; 66} 67 68/* ARGSUSED */ 69void 70chargen_dg(s, sep) /* Character generator */ 71 int s; 72 struct servtab *sep; 73{ 74 struct sockaddr_in sin; 75 static char *rs; 76 int len, size; 77 char text[LINESIZ+2]; 78 79 if (endring == 0) { 80 initring(); 81 rs = ring; 82 } 83 84 size = sizeof(sin); 85 if (recvfrom(s, text, sizeof(text), 0, 86 (struct sockaddr *)&sin, &size) < 0) 87 return; 88 89 if (check_loop(&sin, sep)) 90 return; 91 92 if ((len = endring - rs) >= LINESIZ) 93 memmove(text, rs, LINESIZ); 94 else { 95 memmove(text, rs, len); 96 memmove(text + len, ring, LINESIZ - len); 97 } 98 if (++rs == endring) 99 rs = ring; 100 text[LINESIZ] = '\r'; 101 text[LINESIZ + 1] = '\n'; 102 (void) sendto(s, text, sizeof(text), 0, 103 (struct sockaddr *)&sin, sizeof(sin)); 104} 105 106/* ARGSUSED */ 107void 108chargen_stream(s, sep) /* Character generator */ 109 int s; 110 struct servtab *sep; 111{ 112 int len; 113 char *rs, text[LINESIZ+2]; 114 115 inetd_setproctitle(sep->se_service, s); 116 117 if (!endring) { 118 initring(); 119 rs = ring; 120 } 121 122 text[LINESIZ] = '\r'; 123 text[LINESIZ + 1] = '\n'; 124 for (rs = ring;;) { 125 if ((len = endring - rs) >= LINESIZ) 126 memmove(text, rs, LINESIZ); 127 else { 128 memmove(text, rs, len); 129 memmove(text + len, ring, LINESIZ - len); 130 } 131 if (++rs == endring) 132 rs = ring; 133 if (write(s, text, sizeof(text)) != sizeof(text)) 134 break; 135 } 136 exit(0); 137} 138 139/* ARGSUSED */ 140void 141daytime_dg(s, sep) /* Return human-readable time of day */ 142 int s; 143 struct servtab *sep; 144{ 145 char buffer[256]; 146 time_t clock; 147 struct sockaddr_in sin; 148 int size; 149 150 clock = time((time_t *) 0); 151 152 size = sizeof(sin); 153 if (recvfrom(s, buffer, sizeof(buffer), 0, 154 (struct sockaddr *)&sin, &size) < 0) 155 return; 156 157 if (check_loop(&sin, sep)) 158 return; 159 160 (void) sprintf(buffer, "%.24s\r\n", ctime(&clock)); 161 (void) sendto(s, buffer, strlen(buffer), 0, 162 (struct sockaddr *)&sin, sizeof(sin)); 163} 164 165/* ARGSUSED */ 166void 167daytime_stream(s, sep) /* Return human-readable time of day */ 168 int s; 169 struct servtab *sep; 170{ 171 char buffer[256]; 172 time_t clock; 173 174 clock = time((time_t *) 0); 175 176 (void) sprintf(buffer, "%.24s\r\n", ctime(&clock)); 177 (void) write(s, buffer, strlen(buffer)); 178} 179 180/* ARGSUSED */ 181void 182discard_dg(s, sep) /* Discard service -- ignore data */ 183 int s; 184 struct servtab *sep; 185{ 186 char buffer[BUFSIZE]; 187 188 (void) read(s, buffer, sizeof(buffer)); 189} 190 191/* ARGSUSED */ 192void 193discard_stream(s, sep) /* Discard service -- ignore data */ 194 int s; 195 struct servtab *sep; 196{ 197 int ret; 198 char buffer[BUFSIZE]; 199 200 inetd_setproctitle(sep->se_service, s); 201 while (1) { 202 while ((ret = read(s, buffer, sizeof(buffer))) > 0) 203 ; 204 if (ret == 0 || errno != EINTR) 205 break; 206 } 207 exit(0); 208} 209 210/* ARGSUSED */ 211void 212echo_dg(s, sep) /* Echo service -- echo data back */ 213 int s; 214 struct servtab *sep; 215{ 216 char buffer[BUFSIZE]; 217 int i, size; 218 struct sockaddr_in sin; 219 220 size = sizeof(sin); 221 if ((i = recvfrom(s, buffer, sizeof(buffer), 0, 222 (struct sockaddr *)&sin, &size)) < 0) 223 return; 224 225 if (check_loop(&sin, sep)) 226 return; 227 228 (void) sendto(s, buffer, i, 0, (struct sockaddr *)&sin, 229 sizeof(sin)); 230} 231 232/* ARGSUSED */ 233void 234echo_stream(s, sep) /* Echo service -- echo data back */ 235 int s; 236 struct servtab *sep; 237{ 238 char buffer[BUFSIZE]; 239 int i; 240 241 inetd_setproctitle(sep->se_service, s); 242 while ((i = read(s, buffer, sizeof(buffer))) > 0 && 243 write(s, buffer, i) > 0) 244 ; 245 exit(0); 246} 247 248/* ARGSUSED */ 249void 250iderror(lport, fport, fp, er) 251 int lport, fport, er; 252 FILE *fp; 253{ 254 fprintf(fp, "%d , %d : ERROR : %s\r\n", lport, fport, 255 er == -1 ? "HIDDEN-USER" : er ? strerror(er) : "UNKNOWN-ERROR"); 256 fflush(fp); 257 fclose(fp); 258 259 exit(0); 260} 261 262/* ARGSUSED */ 263void 264ident_stream(s, sep) /* Ident service */ 265 int s; 266 struct servtab *sep; 267{ 268 struct sockaddr_in sin[2]; 269 struct ucred uc; 270 struct passwd *pw; 271 FILE *fp; 272 char buf[BUFSIZE], *cp, **av; 273 int len, c, rflag = 0, fflag = 0, argc = 0; 274 u_short lport, fport; 275 276 inetd_setproctitle(sep->se_service, s); 277 optind = 1; 278 optreset = 1; 279 for (av = sep->se_argv; *av; av++) 280 argc++; 281 if (argc) { 282 while ((c = getopt(argc, sep->se_argv, "fr")) != -1) 283 switch (c) { 284 case 'f': 285 fflag = 1; 286 break; 287 case 'r': 288 rflag = 1; 289 break; 290 default: 291 break; 292 } 293 } 294 fp = fdopen(s, "r+"); 295 len = sizeof(sin[0]); 296 if (getsockname(s, (struct sockaddr *)&sin[0], &len) == -1) 297 iderror(0, 0, fp, errno); 298 len = sizeof(sin[1]); 299 if (getpeername(s, (struct sockaddr *)&sin[1], &len) == -1) 300 iderror(0, 0, fp, errno); 301 errno = 0; 302 if (fgets(buf, sizeof(buf), fp) == NULL) 303 iderror(0, 0, fp, errno); 304 buf[BUFSIZE - 1] = '\0'; 305 strtok(buf, "\r\n"); 306 cp = strtok(buf, ","); 307 if (cp == NULL || sscanf(cp, "%hu", &lport) != 1) 308 iderror(0, 0, fp, 0); 309 cp = strtok(NULL, ","); 310 if (cp == NULL || sscanf(cp, "%hu", &fport) != 1) 311 iderror(0, 0, fp, 0); 312 if (!rflag) 313 iderror(lport, fport, fp, -1); 314 sin[0].sin_port = htons(lport); 315 sin[1].sin_port = htons(fport); 316 len = sizeof(uc); 317 if (sysctlbyname("net.inet.tcp.getcred", &uc, &len, sin, 318 sizeof(sin)) == -1) 319 iderror(lport, fport, fp, errno); 320 pw = getpwuid(uc.cr_uid); 321 if (pw == NULL) 322 iderror(lport, fport, fp, errno); 323 if (fflag) { 324 FILE *fakeid = NULL; 325 char fakeid_path[PATH_MAX]; 326 struct stat sb; 327 seteuid(pw->pw_uid); 328 setegid(pw->pw_gid); 329 snprintf(fakeid_path, sizeof(fakeid_path), "%s/.fakeid", 330 pw->pw_dir); 331 if ((fakeid = fopen(fakeid_path, "r")) != NULL && 332 fstat(fileno(fakeid), &sb) != -1 && S_ISREG(sb.st_mode)) { 333 buf[sizeof(buf) - 1] = '\0'; 334 if (fgets(buf, sizeof(buf), fakeid) == NULL) { 335 cp = pw->pw_name; 336 fclose(fakeid); 337 goto printit; 338 } 339 fclose(fakeid); 340 strtok(buf, "\r\n"); 341 if (strlen(buf) > 16) 342 buf[16] = '\0'; 343 cp = buf; 344 while (isspace(*cp)) 345 cp++; 346 strtok(cp, " \t"); 347 if (!*cp || getpwnam(cp)) 348 cp = getpwuid(uc.cr_uid)->pw_name; 349 } else 350 cp = pw->pw_name; 351 } else 352 cp = pw->pw_name; 353printit: 354 fprintf(fp, "%d , %d : USERID : FreeBSD :%s\r\n", lport, fport, 355 cp); 356 fflush(fp); 357 fclose(fp); 358 359 exit(0); 360} 361 362/* 363 * Return a machine readable date and time, in the form of the 364 * number of seconds since midnight, Jan 1, 1900. Since gettimeofday 365 * returns the number of seconds since midnight, Jan 1, 1970, 366 * we must add 2208988800 seconds to this figure to make up for 367 * some seventy years Bell Labs was asleep. 368 */ 369 370unsigned long 371machtime() 372{ 373 struct timeval tv; 374 375 if (gettimeofday(&tv, (struct timezone *)NULL) < 0) { 376 if (debug) 377 warnx("unable to get time of day"); 378 return (0L); 379 } 380#define OFFSET ((u_long)25567 * 24*60*60) 381 return (htonl((long)(tv.tv_sec + OFFSET))); 382#undef OFFSET 383} 384 385/* ARGSUSED */ 386void 387machtime_dg(s, sep) 388 int s; 389 struct servtab *sep; 390{ 391 unsigned long result; 392 struct sockaddr_in sin; 393 int size; 394 395 size = sizeof(sin); 396 if (recvfrom(s, (char *)&result, sizeof(result), 0, 397 (struct sockaddr *)&sin, &size) < 0) 398 return; 399 400 if (check_loop(&sin, sep)) 401 return; 402 403 result = machtime(); 404 (void) sendto(s, (char *) &result, sizeof(result), 0, 405 (struct sockaddr *)&sin, sizeof(sin)); 406} 407 408/* ARGSUSED */ 409void 410machtime_stream(s, sep) 411 int s; 412 struct servtab *sep; 413{ 414 unsigned long result; 415 416 result = machtime(); 417 (void) write(s, (char *) &result, sizeof(result)); 418} 419 420/* 421 * Based on TCPMUX.C by Mark K. Lottor November 1988 422 * sri-nic::ps:<mkl>tcpmux.c 423 */ 424 425#define MAX_SERV_LEN (256+2) /* 2 bytes for \r\n */ 426#define strwrite(fd, buf) (void) write(fd, buf, sizeof(buf)-1) 427 428static int /* # of characters upto \r,\n or \0 */ 429getline(fd, buf, len) 430 int fd; 431 char *buf; 432 int len; 433{ 434 int count = 0, n; 435 struct sigaction sa; 436 437 sa.sa_flags = 0; 438 sigemptyset(&sa.sa_mask); 439 sa.sa_handler = SIG_DFL; 440 sigaction(SIGALRM, &sa, (struct sigaction *)0); 441 do { 442 alarm(10); 443 n = read(fd, buf, len-count); 444 alarm(0); 445 if (n == 0) 446 return (count); 447 if (n < 0) 448 return (-1); 449 while (--n >= 0) { 450 if (*buf == '\r' || *buf == '\n' || *buf == '\0') 451 return (count); 452 count++; 453 buf++; 454 } 455 } while (count < len); 456 return (count); 457} 458 459struct servtab * 460tcpmux(s) 461 int s; 462{ 463 struct servtab *sep; 464 char service[MAX_SERV_LEN+1]; 465 int len; 466 467 /* Get requested service name */ 468 if ((len = getline(s, service, MAX_SERV_LEN)) < 0) { 469 strwrite(s, "-Error reading service name\r\n"); 470 return (NULL); 471 } 472 service[len] = '\0'; 473 474 if (debug) 475 warnx("tcpmux: someone wants %s", service); 476 477 /* 478 * Help is a required command, and lists available services, 479 * one per line. 480 */ 481 if (!strcasecmp(service, "help")) { 482 for (sep = servtab; sep; sep = sep->se_next) { 483 if (!ISMUX(sep)) 484 continue; 485 (void)write(s,sep->se_service,strlen(sep->se_service)); 486 strwrite(s, "\r\n"); 487 } 488 return (NULL); 489 } 490 491 /* Try matching a service in inetd.conf with the request */ 492 for (sep = servtab; sep; sep = sep->se_next) { 493 if (!ISMUX(sep)) 494 continue; 495 if (!strcasecmp(service, sep->se_service)) { 496 if (ISMUXPLUS(sep)) { 497 strwrite(s, "+Go\r\n"); 498 } 499 return (sep); 500 } 501 } 502 strwrite(s, "-Service not available\r\n"); 503 return (NULL); 504} 505 506