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.4 (Berkeley) 4/17/94"; 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 <netinet/in.h> 81 82 #include <netdb.h> 83 #include <unistd.h> 84 #include <syslog.h> 85 #include <signal.h> 86 #include <errno.h> 87 #include <fcntl.h> 88 #include <dirent.h> 89 #include <stdio.h> 90 #include <stdlib.h> 91 #include <string.h> 92 #include <ctype.h> 93 #include "lp.h" 94 #include "lp.local.h" 95 #include "pathnames.h" 96 #include "extern.h" 97 98 int lflag; /* log requests flag */ 99 int from_remote; /* from remote socket */ 100 101 static void reapchild __P((int)); 102 static void mcleanup __P((int)); 103 static void doit __P((void)); 104 static void startup __P((void)); 105 static void chkhost __P((struct sockaddr_in *)); 106 107 int 108 main(argc, argv) 109 int argc; 110 char **argv; 111 { 112 int f, funix, finet, options, fromlen; 113 fd_set defreadfds; 114 struct sockaddr_un un, fromunix; 115 struct sockaddr_in sin, frominet; 116 int omask, lfd; 117 118 options = 0; 119 gethostname(host, sizeof(host)); 120 name = argv[0]; 121 122 while (--argc > 0) { 123 argv++; 124 if (argv[0][0] == '-') 125 switch (argv[0][1]) { 126 case 'd': 127 options |= SO_DEBUG; 128 break; 129 case 'l': 130 lflag++; 131 break; 132 } 133 } 134 135 #ifndef DEBUG 136 /* 137 * Set up standard environment by detaching from the parent. 138 */ 139 daemon(0, 0); 140 #endif 141 142 openlog("lpd", LOG_PID, LOG_LPR); 143 syslog(LOG_INFO, "restarted"); 144 (void) umask(0); 145 lfd = open(_PATH_MASTERLOCK, O_WRONLY|O_CREAT, 0644); 146 if (lfd < 0) { 147 syslog(LOG_ERR, "%s: %m", _PATH_MASTERLOCK); 148 exit(1); 149 } 150 if (flock(lfd, LOCK_EX|LOCK_NB) < 0) { 151 if (errno == EWOULDBLOCK) /* active deamon present */ 152 exit(0); 153 syslog(LOG_ERR, "%s: %m", _PATH_MASTERLOCK); 154 exit(1); 155 } 156 ftruncate(lfd, 0); 157 /* 158 * write process id for others to know 159 */ 160 sprintf(line, "%u\n", getpid()); 161 f = strlen(line); 162 if (write(lfd, line, f) != f) { 163 syslog(LOG_ERR, "%s: %m", _PATH_MASTERLOCK); 164 exit(1); 165 } 166 signal(SIGCHLD, reapchild); 167 /* 168 * Restart all the printers. 169 */ 170 startup(); 171 (void) unlink(_PATH_SOCKETNAME); 172 funix = socket(AF_UNIX, SOCK_STREAM, 0); 173 if (funix < 0) { 174 syslog(LOG_ERR, "socket: %m"); 175 exit(1); 176 } 177 #define mask(s) (1 << ((s) - 1)) 178 omask = sigblock(mask(SIGHUP)|mask(SIGINT)|mask(SIGQUIT)|mask(SIGTERM)); 179 signal(SIGHUP, mcleanup); 180 signal(SIGINT, mcleanup); 181 signal(SIGQUIT, mcleanup); 182 signal(SIGTERM, mcleanup); 183 memset(&un, 0, sizeof(un)); 184 un.sun_family = AF_UNIX; 185 strcpy(un.sun_path, _PATH_SOCKETNAME); 186 #ifndef SUN_LEN 187 #define SUN_LEN(unp) (strlen((unp)->sun_path) + 2) 188 #endif 189 if (bind(funix, (struct sockaddr *)&un, SUN_LEN(&un)) < 0) { 190 syslog(LOG_ERR, "ubind: %m"); 191 exit(1); 192 } 193 sigsetmask(omask); 194 FD_ZERO(&defreadfds); 195 FD_SET(funix, &defreadfds); 196 listen(funix, 5); 197 finet = socket(AF_INET, SOCK_STREAM, 0); 198 if (finet >= 0) { 199 struct servent *sp; 200 201 if (options & SO_DEBUG) 202 if (setsockopt(finet, SOL_SOCKET, SO_DEBUG, 0, 0) < 0) { 203 syslog(LOG_ERR, "setsockopt (SO_DEBUG): %m"); 204 mcleanup(0); 205 } 206 sp = getservbyname("printer", "tcp"); 207 if (sp == NULL) { 208 syslog(LOG_ERR, "printer/tcp: unknown service"); 209 mcleanup(0); 210 } 211 memset(&sin, 0, sizeof(sin)); 212 sin.sin_family = AF_INET; 213 sin.sin_port = sp->s_port; 214 if (bind(finet, (struct sockaddr *)&sin, sizeof(sin)) < 0) { 215 syslog(LOG_ERR, "bind: %m"); 216 mcleanup(0); 217 } 218 FD_SET(finet, &defreadfds); 219 listen(finet, 5); 220 } 221 /* 222 * Main loop: accept, do a request, continue. 223 */ 224 memset(&frominet, 0, sizeof(frominet)); 225 memset(&fromunix, 0, sizeof(fromunix)); 226 for (;;) { 227 int domain, nfds, s; 228 fd_set readfds; 229 230 FD_COPY(&defreadfds, &readfds); 231 nfds = select(20, &readfds, 0, 0, 0); 232 if (nfds <= 0) { 233 if (nfds < 0 && errno != EINTR) 234 syslog(LOG_WARNING, "select: %m"); 235 continue; 236 } 237 if (FD_ISSET(funix, &readfds)) { 238 domain = AF_UNIX, fromlen = sizeof(fromunix); 239 s = accept(funix, 240 (struct sockaddr *)&fromunix, &fromlen); 241 } else /* if (FD_ISSET(finet, &readfds)) */ { 242 domain = AF_INET, fromlen = sizeof(frominet); 243 s = accept(finet, 244 (struct sockaddr *)&frominet, &fromlen); 245 } 246 if (s < 0) { 247 if (errno != EINTR) 248 syslog(LOG_WARNING, "accept: %m"); 249 continue; 250 } 251 if (fork() == 0) { 252 signal(SIGCHLD, SIG_IGN); 253 signal(SIGHUP, SIG_IGN); 254 signal(SIGINT, SIG_IGN); 255 signal(SIGQUIT, SIG_IGN); 256 signal(SIGTERM, SIG_IGN); 257 (void) close(funix); 258 (void) close(finet); 259 dup2(s, 1); 260 (void) close(s); 261 if (domain == AF_INET) { 262 from_remote = 1; 263 chkhost(&frominet); 264 } else 265 from_remote = 0; 266 doit(); 267 exit(0); 268 } 269 (void) close(s); 270 } 271 } 272 273 static void 274 reapchild(signo) 275 int signo; 276 { 277 union wait status; 278 279 while (wait3((int *)&status, WNOHANG, 0) > 0) 280 ; 281 } 282 283 static void 284 mcleanup(signo) 285 int signo; 286 { 287 if (lflag) 288 syslog(LOG_INFO, "exiting"); 289 unlink(_PATH_SOCKETNAME); 290 exit(0); 291 } 292 293 /* 294 * Stuff for handling job specifications 295 */ 296 char *user[MAXUSERS]; /* users to process */ 297 int users; /* # of users in user array */ 298 int requ[MAXREQUESTS]; /* job number of spool entries */ 299 int requests; /* # of spool requests */ 300 char *person; /* name of person doing lprm */ 301 302 char fromb[MAXHOSTNAMELEN]; /* buffer for client's machine name */ 303 char cbuf[BUFSIZ]; /* command line buffer */ 304 char *cmdnames[] = { 305 "null", 306 "printjob", 307 "recvjob", 308 "displayq short", 309 "displayq long", 310 "rmjob" 311 }; 312 313 static void 314 doit() 315 { 316 register char *cp; 317 register int n; 318 319 for (;;) { 320 cp = cbuf; 321 do { 322 if (cp >= &cbuf[sizeof(cbuf) - 1]) 323 fatal("Command line too long"); 324 if ((n = read(1, cp, 1)) != 1) { 325 if (n < 0) 326 fatal("Lost connection"); 327 return; 328 } 329 } while (*cp++ != '\n'); 330 *--cp = '\0'; 331 cp = cbuf; 332 if (lflag) { 333 if (*cp >= '\1' && *cp <= '\5') 334 syslog(LOG_INFO, "%s requests %s %s", 335 from, cmdnames[*cp], cp+1); 336 else 337 syslog(LOG_INFO, "bad request (%d) from %s", 338 *cp, from); 339 } 340 switch (*cp++) { 341 case '\1': /* check the queue and print any jobs there */ 342 printer = cp; 343 printjob(); 344 break; 345 case '\2': /* receive files to be queued */ 346 if (!from_remote) { 347 syslog(LOG_INFO, "illegal request (%d)", *cp); 348 exit(1); 349 } 350 printer = cp; 351 recvjob(); 352 break; 353 case '\3': /* display the queue (short form) */ 354 case '\4': /* display the queue (long form) */ 355 printer = cp; 356 while (*cp) { 357 if (*cp != ' ') { 358 cp++; 359 continue; 360 } 361 *cp++ = '\0'; 362 while (isspace(*cp)) 363 cp++; 364 if (*cp == '\0') 365 break; 366 if (isdigit(*cp)) { 367 if (requests >= MAXREQUESTS) 368 fatal("Too many requests"); 369 requ[requests++] = atoi(cp); 370 } else { 371 if (users >= MAXUSERS) 372 fatal("Too many users"); 373 user[users++] = cp; 374 } 375 } 376 displayq(cbuf[0] - '\3'); 377 exit(0); 378 case '\5': /* remove a job from the queue */ 379 if (!from_remote) { 380 syslog(LOG_INFO, "illegal request (%d)", *cp); 381 exit(1); 382 } 383 printer = cp; 384 while (*cp && *cp != ' ') 385 cp++; 386 if (!*cp) 387 break; 388 *cp++ = '\0'; 389 person = cp; 390 while (*cp) { 391 if (*cp != ' ') { 392 cp++; 393 continue; 394 } 395 *cp++ = '\0'; 396 while (isspace(*cp)) 397 cp++; 398 if (*cp == '\0') 399 break; 400 if (isdigit(*cp)) { 401 if (requests >= MAXREQUESTS) 402 fatal("Too many requests"); 403 requ[requests++] = atoi(cp); 404 } else { 405 if (users >= MAXUSERS) 406 fatal("Too many users"); 407 user[users++] = cp; 408 } 409 } 410 rmjob(); 411 break; 412 } 413 fatal("Illegal service request"); 414 } 415 } 416 417 /* 418 * Make a pass through the printcap database and start printing any 419 * files left from the last time the machine went down. 420 */ 421 static void 422 startup() 423 { 424 char *buf; 425 register char *cp; 426 int pid; 427 428 /* 429 * Restart the daemons. 430 */ 431 while (cgetnext(&buf, printcapdb) > 0) { 432 for (cp = buf; *cp; cp++) 433 if (*cp == '|' || *cp == ':') { 434 *cp = '\0'; 435 break; 436 } 437 if ((pid = fork()) < 0) { 438 syslog(LOG_WARNING, "startup: cannot fork"); 439 mcleanup(0); 440 } 441 if (!pid) { 442 printer = buf; 443 cgetclose(); 444 printjob(); 445 } 446 } 447 } 448 449 #define DUMMY ":nobody::" 450 451 /* 452 * Check to see if the from host has access to the line printer. 453 */ 454 static void 455 chkhost(f) 456 struct sockaddr_in *f; 457 { 458 register struct hostent *hp; 459 register FILE *hostf; 460 int first = 1; 461 extern char *inet_ntoa(); 462 463 f->sin_port = ntohs(f->sin_port); 464 if (f->sin_family != AF_INET || f->sin_port >= IPPORT_RESERVED) 465 fatal("Malformed from address"); 466 467 /* Need real hostname for temporary filenames */ 468 hp = gethostbyaddr((char *)&f->sin_addr, 469 sizeof(struct in_addr), f->sin_family); 470 if (hp == NULL) 471 fatal("Host name for your address (%s) unknown", 472 inet_ntoa(f->sin_addr)); 473 474 (void) strncpy(fromb, hp->h_name, sizeof(fromb)); 475 from[sizeof(fromb) - 1] = '\0'; 476 from = fromb; 477 478 hostf = fopen(_PATH_HOSTSEQUIV, "r"); 479 again: 480 if (hostf) { 481 if (__ivaliduser(hostf, f->sin_addr.s_addr, 482 DUMMY, DUMMY) == 0) { 483 (void) fclose(hostf); 484 return; 485 } 486 (void) fclose(hostf); 487 } 488 if (first == 1) { 489 first = 0; 490 hostf = fopen(_PATH_HOSTSLPD, "r"); 491 goto again; 492 } 493 fatal("Your host does not have line printer access"); 494 /*NOTREACHED*/ 495 } 496 497 498 499 500 501 502 503 504 505 506 507 508