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 signal(SIGHUP, mcleanup); 183 signal(SIGINT, mcleanup); 184 signal(SIGQUIT, mcleanup); 185 signal(SIGTERM, mcleanup); 186 memset(&un, 0, sizeof(un)); 187 un.sun_family = AF_UNIX; 188 strcpy(un.sun_path, _PATH_SOCKETNAME); 189 #ifndef SUN_LEN 190 #define SUN_LEN(unp) (strlen((unp)->sun_path) + 2) 191 #endif 192 if (bind(funix, (struct sockaddr *)&un, SUN_LEN(&un)) < 0) { 193 syslog(LOG_ERR, "ubind: %m"); 194 exit(1); 195 } 196 sigsetmask(omask); 197 FD_ZERO(&defreadfds); 198 FD_SET(funix, &defreadfds); 199 listen(funix, 5); 200 finet = socket(AF_INET, SOCK_STREAM, 0); 201 if (finet >= 0) { 202 struct servent *sp; 203 204 if (options & SO_DEBUG) 205 if (setsockopt(finet, SOL_SOCKET, SO_DEBUG, 0, 0) < 0) { 206 syslog(LOG_ERR, "setsockopt (SO_DEBUG): %m"); 207 mcleanup(0); 208 } 209 sp = getservbyname("printer", "tcp"); 210 if (sp == NULL) { 211 syslog(LOG_ERR, "printer/tcp: unknown service"); 212 mcleanup(0); 213 } 214 memset(&sin, 0, sizeof(sin)); 215 sin.sin_family = AF_INET; 216 sin.sin_port = sp->s_port; 217 if (bind(finet, (struct sockaddr *)&sin, sizeof(sin)) < 0) { 218 syslog(LOG_ERR, "bind: %m"); 219 mcleanup(0); 220 } 221 FD_SET(finet, &defreadfds); 222 listen(finet, 5); 223 } 224 /* 225 * Main loop: accept, do a request, continue. 226 */ 227 memset(&frominet, 0, sizeof(frominet)); 228 memset(&fromunix, 0, sizeof(fromunix)); 229 for (;;) { 230 int domain, nfds, s; 231 fd_set readfds; 232 233 FD_COPY(&defreadfds, &readfds); 234 nfds = select(20, &readfds, 0, 0, 0); 235 if (nfds <= 0) { 236 if (nfds < 0 && errno != EINTR) 237 syslog(LOG_WARNING, "select: %m"); 238 continue; 239 } 240 if (FD_ISSET(funix, &readfds)) { 241 domain = AF_UNIX, fromlen = sizeof(fromunix); 242 s = accept(funix, 243 (struct sockaddr *)&fromunix, &fromlen); 244 } else /* if (FD_ISSET(finet, &readfds)) */ { 245 domain = AF_INET, fromlen = sizeof(frominet); 246 s = accept(finet, 247 (struct sockaddr *)&frominet, &fromlen); 248 } 249 if (s < 0) { 250 if (errno != EINTR) 251 syslog(LOG_WARNING, "accept: %m"); 252 continue; 253 } 254 if (fork() == 0) { 255 signal(SIGCHLD, SIG_IGN); 256 signal(SIGHUP, SIG_IGN); 257 signal(SIGINT, SIG_IGN); 258 signal(SIGQUIT, SIG_IGN); 259 signal(SIGTERM, SIG_IGN); 260 (void) close(funix); 261 (void) close(finet); 262 dup2(s, 1); 263 (void) close(s); 264 if (domain == AF_INET) { 265 from_remote = 1; 266 chkhost(&frominet); 267 } else 268 from_remote = 0; 269 doit(); 270 exit(0); 271 } 272 (void) close(s); 273 } 274 } 275 276 static void 277 reapchild(signo) 278 int signo; 279 { 280 union wait status; 281 282 while (wait3((int *)&status, WNOHANG, 0) > 0) 283 ; 284 } 285 286 static void 287 mcleanup(signo) 288 int signo; 289 { 290 if (lflag) 291 syslog(LOG_INFO, "exiting"); 292 unlink(_PATH_SOCKETNAME); 293 exit(0); 294 } 295 296 /* 297 * Stuff for handling job specifications 298 */ 299 char *user[MAXUSERS]; /* users to process */ 300 int users; /* # of users in user array */ 301 int requ[MAXREQUESTS]; /* job number of spool entries */ 302 int requests; /* # of spool requests */ 303 char *person; /* name of person doing lprm */ 304 305 char fromb[MAXHOSTNAMELEN]; /* buffer for client's machine name */ 306 char cbuf[BUFSIZ]; /* command line buffer */ 307 char *cmdnames[] = { 308 "null", 309 "printjob", 310 "recvjob", 311 "displayq short", 312 "displayq long", 313 "rmjob" 314 }; 315 316 static void 317 doit() 318 { 319 register char *cp; 320 register int n; 321 322 for (;;) { 323 cp = cbuf; 324 do { 325 if (cp >= &cbuf[sizeof(cbuf) - 1]) 326 fatal("Command line too long"); 327 if ((n = read(1, cp, 1)) != 1) { 328 if (n < 0) 329 fatal("Lost connection"); 330 return; 331 } 332 } while (*cp++ != '\n'); 333 *--cp = '\0'; 334 cp = cbuf; 335 if (lflag) { 336 if (*cp >= '\1' && *cp <= '\5') 337 syslog(LOG_INFO, "%s requests %s %s", 338 from, cmdnames[*cp], cp+1); 339 else 340 syslog(LOG_INFO, "bad request (%d) from %s", 341 *cp, from); 342 } 343 switch (*cp++) { 344 case '\1': /* check the queue and print any jobs there */ 345 printer = cp; 346 printjob(); 347 break; 348 case '\2': /* receive files to be queued */ 349 if (!from_remote) { 350 syslog(LOG_INFO, "illegal request (%d)", *cp); 351 exit(1); 352 } 353 printer = cp; 354 recvjob(); 355 break; 356 case '\3': /* display the queue (short form) */ 357 case '\4': /* display the queue (long form) */ 358 printer = cp; 359 while (*cp) { 360 if (*cp != ' ') { 361 cp++; 362 continue; 363 } 364 *cp++ = '\0'; 365 while (isspace(*cp)) 366 cp++; 367 if (*cp == '\0') 368 break; 369 if (isdigit(*cp)) { 370 if (requests >= MAXREQUESTS) 371 fatal("Too many requests"); 372 requ[requests++] = atoi(cp); 373 } else { 374 if (users >= MAXUSERS) 375 fatal("Too many users"); 376 user[users++] = cp; 377 } 378 } 379 displayq(cbuf[0] - '\3'); 380 exit(0); 381 case '\5': /* remove a job from the queue */ 382 if (!from_remote) { 383 syslog(LOG_INFO, "illegal request (%d)", *cp); 384 exit(1); 385 } 386 printer = cp; 387 while (*cp && *cp != ' ') 388 cp++; 389 if (!*cp) 390 break; 391 *cp++ = '\0'; 392 person = cp; 393 while (*cp) { 394 if (*cp != ' ') { 395 cp++; 396 continue; 397 } 398 *cp++ = '\0'; 399 while (isspace(*cp)) 400 cp++; 401 if (*cp == '\0') 402 break; 403 if (isdigit(*cp)) { 404 if (requests >= MAXREQUESTS) 405 fatal("Too many requests"); 406 requ[requests++] = atoi(cp); 407 } else { 408 if (users >= MAXUSERS) 409 fatal("Too many users"); 410 user[users++] = cp; 411 } 412 } 413 rmjob(); 414 break; 415 } 416 fatal("Illegal service request"); 417 } 418 } 419 420 /* 421 * Make a pass through the printcap database and start printing any 422 * files left from the last time the machine went down. 423 */ 424 static void 425 startup() 426 { 427 char *buf; 428 register char *cp; 429 int pid; 430 431 /* 432 * Restart the daemons. 433 */ 434 while (cgetnext(&buf, printcapdb) > 0) { 435 if (ckqueue(buf) <= 0) { 436 free(buf); 437 continue; /* no work to do for this printer */ 438 } 439 for (cp = buf; *cp; cp++) 440 if (*cp == '|' || *cp == ':') { 441 *cp = '\0'; 442 break; 443 } 444 if (lflag) 445 syslog(LOG_INFO, "work for %s", buf); 446 if ((pid = fork()) < 0) { 447 syslog(LOG_WARNING, "startup: cannot fork"); 448 mcleanup(0); 449 } 450 if (!pid) { 451 printer = buf; 452 cgetclose(); 453 printjob(); 454 /* NOTREACHED */ 455 } 456 else free(buf); 457 } 458 } 459 460 /* 461 * Make sure there's some work to do before forking off a child 462 */ 463 static int 464 ckqueue(cap) 465 char *cap; 466 { 467 register struct dirent *d; 468 DIR *dirp; 469 char *spooldir; 470 471 if (cgetstr(cap, "sd", &spooldir) == -1) 472 spooldir = _PATH_DEFSPOOL; 473 if ((dirp = opendir(spooldir)) == NULL) 474 return (-1); 475 while ((d = readdir(dirp)) != NULL) { 476 if (d->d_name[0] != 'c' || d->d_name[1] != 'f') 477 continue; /* daemon control files only */ 478 closedir(dirp); 479 return (1); /* found something */ 480 } 481 closedir(dirp); 482 return (0); 483 } 484 485 #define DUMMY ":nobody::" 486 487 /* 488 * Check to see if the from host has access to the line printer. 489 */ 490 static void 491 chkhost(f) 492 struct sockaddr_in *f; 493 { 494 register struct hostent *hp; 495 register FILE *hostf; 496 int first = 1; 497 498 f->sin_port = ntohs(f->sin_port); 499 if (f->sin_family != AF_INET || f->sin_port >= IPPORT_RESERVED) 500 fatal("Malformed from address"); 501 502 /* Need real hostname for temporary filenames */ 503 hp = gethostbyaddr((char *)&f->sin_addr, 504 sizeof(struct in_addr), f->sin_family); 505 if (hp == NULL) 506 fatal("Host name for your address (%s) unknown", 507 inet_ntoa(f->sin_addr)); 508 509 (void) strncpy(fromb, hp->h_name, sizeof(fromb)); 510 from[sizeof(fromb) - 1] = '\0'; 511 from = fromb; 512 513 hostf = fopen(_PATH_HOSTSEQUIV, "r"); 514 again: 515 if (hostf) { 516 if (__ivaliduser(hostf, f->sin_addr.s_addr, 517 DUMMY, DUMMY) == 0) { 518 (void) fclose(hostf); 519 return; 520 } 521 (void) fclose(hostf); 522 } 523 if (first == 1) { 524 first = 0; 525 hostf = fopen(_PATH_HOSTSLPD, "r"); 526 goto again; 527 } 528 fatal("Your host does not have line printer access"); 529 /*NOTREACHED*/ 530 } 531 532 533 534 535 536 537 538 539 540 541 542 543