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