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