1 /* 2 * Copyright (c) 1983, 1993, 1994 3 * The Regents of the University of California. All rights reserved. 4 * 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 3. All advertising materials mentioning features or use of this software 15 * must display the following acknowledgement: 16 * This product includes software developed by the University of 17 * California, Berkeley and its contributors. 18 * 4. Neither the name of the University nor the names of its contributors 19 * may be used to endorse or promote products derived from this software 20 * without specific prior written permission. 21 * 22 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 23 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 24 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 25 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 26 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 27 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 28 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 29 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 30 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 31 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 32 * SUCH DAMAGE. 33 */ 34 35 #ifndef lint 36 static const char copyright[] = 37 "@(#) Copyright (c) 1983, 1993, 1994\n\ 38 The Regents of the University of California. All rights reserved.\n"; 39 #endif /* not lint */ 40 41 #ifndef lint 42 #if 0 43 static char sccsid[] = "@(#)lpd.c 8.7 (Berkeley) 5/10/95"; 44 #endif 45 static const char rcsid[] = 46 "$Id$"; 47 #endif /* not lint */ 48 49 /* 50 * lpd -- line printer daemon. 51 * 52 * Listen for a connection and perform the requested operation. 53 * Operations are: 54 * \1printer\n 55 * check the queue for jobs and print any found. 56 * \2printer\n 57 * receive a job from another machine and queue it. 58 * \3printer [users ...] [jobs ...]\n 59 * return the current state of the queue (short form). 60 * \4printer [users ...] [jobs ...]\n 61 * return the current state of the queue (long form). 62 * \5printer person [users ...] [jobs ...]\n 63 * remove jobs from the queue. 64 * 65 * Strategy to maintain protected spooling area: 66 * 1. Spooling area is writable only by daemon and spooling group 67 * 2. lpr runs setuid root and setgrp spooling group; it uses 68 * root to access any file it wants (verifying things before 69 * with an access call) and group id to know how it should 70 * set up ownership of files in the spooling area. 71 * 3. Files in spooling area are owned by root, group spooling 72 * group, with mode 660. 73 * 4. lpd, lpq and lprm run setuid daemon and setgrp spooling group to 74 * access files and printer. Users can't get to anything 75 * w/o help of lpq and lprm programs. 76 */ 77 78 #include <sys/param.h> 79 #include <sys/wait.h> 80 #include <sys/types.h> 81 #include <sys/socket.h> 82 #include <sys/un.h> 83 #include <sys/stat.h> 84 #include <sys/file.h> 85 #include <netinet/in.h> 86 #include <arpa/inet.h> 87 88 #include <netdb.h> 89 #include <unistd.h> 90 #include <syslog.h> 91 #include <signal.h> 92 #include <err.h> 93 #include <errno.h> 94 #include <fcntl.h> 95 #include <dirent.h> 96 #include <stdio.h> 97 #include <stdlib.h> 98 #include <string.h> 99 #include <sysexits.h> 100 #include <ctype.h> 101 #include "lp.h" 102 #include "lp.local.h" 103 #include "pathnames.h" 104 #include "extern.h" 105 106 int lflag; /* log requests flag */ 107 int from_remote; /* from remote socket */ 108 109 static void reapchild __P((int)); 110 static void mcleanup __P((int)); 111 static void doit __P((void)); 112 static void startup __P((void)); 113 static void chkhost __P((struct sockaddr_in *)); 114 static int ckqueue __P((char *)); 115 static void usage __P((void)); 116 117 uid_t uid, euid; 118 119 int 120 main(argc, argv) 121 int argc; 122 char **argv; 123 { 124 int f, funix, finet, options, fromlen, i, errs; 125 fd_set defreadfds; 126 struct sockaddr_un un, fromunix; 127 struct sockaddr_in sin, frominet; 128 int omask, lfd; 129 struct servent *sp, serv; 130 131 euid = geteuid(); /* these shouldn't be different */ 132 uid = getuid(); 133 options = 0; 134 gethostname(host, sizeof(host)); 135 136 name = "lpd"; 137 138 if (euid != 0) 139 errx(EX_NOPERM,"must run as root"); 140 141 errs = 0; 142 while ((i = getopt(argc, argv, "dl")) != -1) 143 switch (i) { 144 case 'd': 145 options |= SO_DEBUG; 146 break; 147 case 'l': 148 lflag++; 149 break; 150 default: 151 errs++; 152 } 153 argc -= optind; 154 argv += optind; 155 if (errs) 156 usage(); 157 158 if (argc == 1) { 159 if ((i = atoi(argv[0])) == 0) 160 usage(); 161 if (i < 0 || i > USHRT_MAX) 162 errx(EX_USAGE, "port # %d is invalid", i); 163 164 serv.s_port = htons(i); 165 sp = &serv; 166 argc--; 167 } else { 168 sp = getservbyname("printer", "tcp"); 169 if (sp == NULL) 170 errx(EX_OSFILE, "printer/tcp: unknown service"); 171 } 172 173 if (argc != 0) 174 usage(); 175 176 #ifndef DEBUG 177 /* 178 * Set up standard environment by detaching from the parent. 179 */ 180 daemon(0, 0); 181 #endif 182 183 openlog("lpd", LOG_PID, LOG_LPR); 184 syslog(LOG_INFO, "restarted"); 185 (void) umask(0); 186 lfd = open(_PATH_MASTERLOCK, O_WRONLY|O_CREAT, 0644); 187 if (lfd < 0) { 188 syslog(LOG_ERR, "%s: %m", _PATH_MASTERLOCK); 189 exit(1); 190 } 191 if (flock(lfd, LOCK_EX|LOCK_NB) < 0) { 192 if (errno == EWOULDBLOCK) /* active deamon present */ 193 exit(0); 194 syslog(LOG_ERR, "%s: %m", _PATH_MASTERLOCK); 195 exit(1); 196 } 197 ftruncate(lfd, 0); 198 /* 199 * write process id for others to know 200 */ 201 sprintf(line, "%u\n", getpid()); 202 f = strlen(line); 203 if (write(lfd, line, f) != f) { 204 syslog(LOG_ERR, "%s: %m", _PATH_MASTERLOCK); 205 exit(1); 206 } 207 signal(SIGCHLD, reapchild); 208 /* 209 * Restart all the printers. 210 */ 211 startup(); 212 (void) unlink(_PATH_SOCKETNAME); 213 funix = socket(AF_UNIX, SOCK_STREAM, 0); 214 if (funix < 0) { 215 syslog(LOG_ERR, "socket: %m"); 216 exit(1); 217 } 218 #define mask(s) (1 << ((s) - 1)) 219 omask = sigblock(mask(SIGHUP)|mask(SIGINT)|mask(SIGQUIT)|mask(SIGTERM)); 220 (void) umask(07); 221 signal(SIGHUP, mcleanup); 222 signal(SIGINT, mcleanup); 223 signal(SIGQUIT, mcleanup); 224 signal(SIGTERM, mcleanup); 225 memset(&un, 0, sizeof(un)); 226 un.sun_family = AF_UNIX; 227 strcpy(un.sun_path, _PATH_SOCKETNAME); 228 #ifndef SUN_LEN 229 #define SUN_LEN(unp) (strlen((unp)->sun_path) + 2) 230 #endif 231 if (bind(funix, (struct sockaddr *)&un, SUN_LEN(&un)) < 0) { 232 syslog(LOG_ERR, "ubind: %m"); 233 exit(1); 234 } 235 (void) umask(0); 236 sigsetmask(omask); 237 FD_ZERO(&defreadfds); 238 FD_SET(funix, &defreadfds); 239 listen(funix, 5); 240 finet = socket(AF_INET, SOCK_STREAM, 0); 241 if (finet >= 0) { 242 if (options & SO_DEBUG) 243 if (setsockopt(finet, SOL_SOCKET, SO_DEBUG, 0, 0) < 0) { 244 syslog(LOG_ERR, "setsockopt (SO_DEBUG): %m"); 245 mcleanup(0); 246 } 247 memset(&sin, 0, sizeof(sin)); 248 sin.sin_family = AF_INET; 249 sin.sin_port = sp->s_port; 250 if (bind(finet, (struct sockaddr *)&sin, sizeof(sin)) < 0) { 251 syslog(LOG_ERR, "bind: %m"); 252 mcleanup(0); 253 } 254 FD_SET(finet, &defreadfds); 255 listen(finet, 5); 256 } 257 /* 258 * Main loop: accept, do a request, continue. 259 */ 260 memset(&frominet, 0, sizeof(frominet)); 261 memset(&fromunix, 0, sizeof(fromunix)); 262 for (;;) { 263 int domain, nfds, s; 264 fd_set readfds; 265 266 FD_COPY(&defreadfds, &readfds); 267 nfds = select(20, &readfds, 0, 0, 0); 268 if (nfds <= 0) { 269 if (nfds < 0 && errno != EINTR) 270 syslog(LOG_WARNING, "select: %m"); 271 continue; 272 } 273 if (FD_ISSET(funix, &readfds)) { 274 domain = AF_UNIX, fromlen = sizeof(fromunix); 275 s = accept(funix, 276 (struct sockaddr *)&fromunix, &fromlen); 277 } else /* if (FD_ISSET(finet, &readfds)) */ { 278 domain = AF_INET, fromlen = sizeof(frominet); 279 s = accept(finet, 280 (struct sockaddr *)&frominet, &fromlen); 281 if (frominet.sin_port == htons(20)) { 282 close(s); 283 continue; 284 } 285 } 286 if (s < 0) { 287 if (errno != EINTR) 288 syslog(LOG_WARNING, "accept: %m"); 289 continue; 290 } 291 if (fork() == 0) { 292 signal(SIGCHLD, SIG_IGN); 293 signal(SIGHUP, SIG_IGN); 294 signal(SIGINT, SIG_IGN); 295 signal(SIGQUIT, SIG_IGN); 296 signal(SIGTERM, SIG_IGN); 297 (void) close(funix); 298 (void) close(finet); 299 dup2(s, 1); 300 (void) close(s); 301 if (domain == AF_INET) { 302 from_remote = 1; 303 chkhost(&frominet); 304 } else 305 from_remote = 0; 306 doit(); 307 exit(0); 308 } 309 (void) close(s); 310 } 311 } 312 313 static void 314 reapchild(signo) 315 int signo; 316 { 317 union wait status; 318 319 while (wait3((int *)&status, WNOHANG, 0) > 0) 320 ; 321 } 322 323 static void 324 mcleanup(signo) 325 int signo; 326 { 327 if (lflag) 328 syslog(LOG_INFO, "exiting"); 329 unlink(_PATH_SOCKETNAME); 330 exit(0); 331 } 332 333 /* 334 * Stuff for handling job specifications 335 */ 336 char *user[MAXUSERS]; /* users to process */ 337 int users; /* # of users in user array */ 338 int requ[MAXREQUESTS]; /* job number of spool entries */ 339 int requests; /* # of spool requests */ 340 char *person; /* name of person doing lprm */ 341 342 char fromb[MAXHOSTNAMELEN]; /* buffer for client's machine name */ 343 char cbuf[BUFSIZ]; /* command line buffer */ 344 char *cmdnames[] = { 345 "null", 346 "printjob", 347 "recvjob", 348 "displayq short", 349 "displayq long", 350 "rmjob" 351 }; 352 353 static void 354 doit() 355 { 356 register char *cp; 357 register int n; 358 359 for (;;) { 360 cp = cbuf; 361 do { 362 if (cp >= &cbuf[sizeof(cbuf) - 1]) 363 fatal("Command line too long"); 364 if ((n = read(1, cp, 1)) != 1) { 365 if (n < 0) 366 fatal("Lost connection"); 367 return; 368 } 369 } while (*cp++ != '\n'); 370 *--cp = '\0'; 371 cp = cbuf; 372 if (lflag) { 373 if (*cp >= '\1' && *cp <= '\5') 374 syslog(LOG_INFO, "%s requests %s %s", 375 from, cmdnames[*cp], cp+1); 376 else 377 syslog(LOG_INFO, "bad request (%d) from %s", 378 *cp, from); 379 } 380 switch (*cp++) { 381 case '\1': /* check the queue and print any jobs there */ 382 printer = cp; 383 printjob(); 384 break; 385 case '\2': /* receive files to be queued */ 386 if (!from_remote) { 387 syslog(LOG_INFO, "illegal request (%d)", *cp); 388 exit(1); 389 } 390 printer = cp; 391 recvjob(); 392 break; 393 case '\3': /* display the queue (short form) */ 394 case '\4': /* display the queue (long form) */ 395 printer = cp; 396 while (*cp) { 397 if (*cp != ' ') { 398 cp++; 399 continue; 400 } 401 *cp++ = '\0'; 402 while (isspace(*cp)) 403 cp++; 404 if (*cp == '\0') 405 break; 406 if (isdigit(*cp)) { 407 if (requests >= MAXREQUESTS) 408 fatal("Too many requests"); 409 requ[requests++] = atoi(cp); 410 } else { 411 if (users >= MAXUSERS) 412 fatal("Too many users"); 413 user[users++] = cp; 414 } 415 } 416 displayq(cbuf[0] - '\3'); 417 exit(0); 418 case '\5': /* remove a job from the queue */ 419 if (!from_remote) { 420 syslog(LOG_INFO, "illegal request (%d)", *cp); 421 exit(1); 422 } 423 printer = cp; 424 while (*cp && *cp != ' ') 425 cp++; 426 if (!*cp) 427 break; 428 *cp++ = '\0'; 429 person = cp; 430 while (*cp) { 431 if (*cp != ' ') { 432 cp++; 433 continue; 434 } 435 *cp++ = '\0'; 436 while (isspace(*cp)) 437 cp++; 438 if (*cp == '\0') 439 break; 440 if (isdigit(*cp)) { 441 if (requests >= MAXREQUESTS) 442 fatal("Too many requests"); 443 requ[requests++] = atoi(cp); 444 } else { 445 if (users >= MAXUSERS) 446 fatal("Too many users"); 447 user[users++] = cp; 448 } 449 } 450 rmjob(); 451 break; 452 } 453 fatal("Illegal service request"); 454 } 455 } 456 457 /* 458 * Make a pass through the printcap database and start printing any 459 * files left from the last time the machine went down. 460 */ 461 static void 462 startup() 463 { 464 char *buf; 465 register char *cp; 466 int pid; 467 char *spooldirs[16]; /* Which spooldirs are active? */ 468 int i; /* Printer index presently processed */ 469 int j; /* Printer index of potential conflict */ 470 char *spooldir; /* Spooldir of present printer */ 471 int canfreespool; /* Is the spooldir malloc()ed? */ 472 473 /* 474 * Restart the daemons and test for spooldir conflict. 475 */ 476 i = 0; 477 while (cgetnext(&buf, printcapdb) > 0) { 478 479 /* Check for duplicate spooldirs */ 480 canfreespool = 1; 481 if (cgetstr(buf, "sd", &spooldir) <= 0) { 482 spooldir = _PATH_DEFSPOOL; 483 canfreespool = 0; 484 } 485 if (i < sizeof(spooldirs)/sizeof(spooldirs[0])) 486 spooldirs[i] = spooldir; 487 for (j = 0; 488 j < MIN(i,sizeof(spooldirs)/sizeof(spooldirs[0])); 489 j++) { 490 if (strcmp(spooldir, spooldirs[j]) == 0) { 491 syslog(LOG_ERR, 492 "startup: duplicate spool directories: %s", 493 spooldir); 494 mcleanup(0); 495 } 496 } 497 if (canfreespool && i >= sizeof(spooldirs)/sizeof(spooldirs[0])) 498 free(spooldir); 499 i++; 500 /* Spooldir test done */ 501 502 if (ckqueue(buf) <= 0) { 503 free(buf); 504 continue; /* no work to do for this printer */ 505 } 506 for (cp = buf; *cp; cp++) 507 if (*cp == '|' || *cp == ':') { 508 *cp = '\0'; 509 break; 510 } 511 if (lflag) 512 syslog(LOG_INFO, "work for %s", buf); 513 if ((pid = fork()) < 0) { 514 syslog(LOG_WARNING, "startup: cannot fork"); 515 mcleanup(0); 516 } 517 if (!pid) { 518 printer = buf; 519 cgetclose(); 520 printjob(); 521 /* NOTREACHED */ 522 } 523 else free(buf); 524 } 525 } 526 527 /* 528 * Make sure there's some work to do before forking off a child 529 */ 530 static int 531 ckqueue(cap) 532 char *cap; 533 { 534 register struct dirent *d; 535 DIR *dirp; 536 char *spooldir; 537 538 if (cgetstr(cap, "sd", &spooldir) == -1) 539 spooldir = _PATH_DEFSPOOL; 540 if ((dirp = opendir(spooldir)) == NULL) 541 return (-1); 542 while ((d = readdir(dirp)) != NULL) { 543 if (d->d_name[0] != 'c' || d->d_name[1] != 'f') 544 continue; /* daemon control files only */ 545 closedir(dirp); 546 return (1); /* found something */ 547 } 548 closedir(dirp); 549 return (0); 550 } 551 552 #define DUMMY ":nobody::" 553 554 /* 555 * Check to see if the from host has access to the line printer. 556 */ 557 static void 558 chkhost(f) 559 struct sockaddr_in *f; 560 { 561 register struct hostent *hp; 562 register FILE *hostf; 563 int first = 1; 564 int good = 0; 565 566 /* Need real hostname for temporary filenames */ 567 hp = gethostbyaddr((char *)&f->sin_addr, 568 sizeof(struct in_addr), f->sin_family); 569 if (hp == NULL) 570 fatal("Host name for your address (%s) unknown", 571 inet_ntoa(f->sin_addr)); 572 573 (void) strncpy(fromb, hp->h_name, sizeof(fromb) - 1); 574 from[sizeof(fromb) - 1] = '\0'; 575 from = fromb; 576 577 /* Check for spoof, ala rlogind */ 578 hp = gethostbyname(fromb); 579 if (!hp) 580 fatal("hostname for your address (%s) unknown", 581 inet_ntoa(f->sin_addr)); 582 for (; good == 0 && hp->h_addr_list[0] != NULL; hp->h_addr_list++) { 583 if (!bcmp(hp->h_addr_list[0], (caddr_t)&f->sin_addr, 584 sizeof(f->sin_addr))) 585 good = 1; 586 } 587 if (good == 0) 588 fatal("address for your hostname (%s) not matched", 589 inet_ntoa(f->sin_addr)); 590 591 hostf = fopen(_PATH_HOSTSEQUIV, "r"); 592 again: 593 if (hostf) { 594 if (__ivaliduser(hostf, f->sin_addr.s_addr, 595 DUMMY, DUMMY) == 0) { 596 (void) fclose(hostf); 597 return; 598 } 599 (void) fclose(hostf); 600 } 601 if (first == 1) { 602 first = 0; 603 hostf = fopen(_PATH_HOSTSLPD, "r"); 604 goto again; 605 } 606 fatal("Your host does not have line printer access"); 607 /*NOTREACHED*/ 608 } 609 610 static void 611 usage() 612 { 613 fprintf(stderr, "usage: lpd [-dl] [port#]\n"); 614 exit(EX_USAGE); 615 } 616