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