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