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.4 (Berkeley) 04/17/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 memset(&frominet, 0, sizeof(frominet)); 199 memset(&fromunix, 0, sizeof(fromunix)); 200 for (;;) { 201 int domain, nfds, s; 202 fd_set readfds; 203 204 FD_COPY(&defreadfds, &readfds); 205 nfds = select(20, &readfds, 0, 0, 0); 206 if (nfds <= 0) { 207 if (nfds < 0 && errno != EINTR) 208 syslog(LOG_WARNING, "select: %m"); 209 continue; 210 } 211 if (FD_ISSET(funix, &readfds)) { 212 domain = AF_UNIX, fromlen = sizeof(fromunix); 213 s = accept(funix, 214 (struct sockaddr *)&fromunix, &fromlen); 215 } else /* if (FD_ISSET(finet, &readfds)) */ { 216 domain = AF_INET, fromlen = sizeof(frominet); 217 s = accept(finet, 218 (struct sockaddr *)&frominet, &fromlen); 219 } 220 if (s < 0) { 221 if (errno != EINTR) 222 syslog(LOG_WARNING, "accept: %m"); 223 continue; 224 } 225 if (fork() == 0) { 226 signal(SIGCHLD, SIG_IGN); 227 signal(SIGHUP, SIG_IGN); 228 signal(SIGINT, SIG_IGN); 229 signal(SIGQUIT, SIG_IGN); 230 signal(SIGTERM, SIG_IGN); 231 (void) close(funix); 232 (void) close(finet); 233 dup2(s, 1); 234 (void) close(s); 235 if (domain == AF_INET) { 236 from_remote = 1; 237 chkhost(&frominet); 238 } else 239 from_remote = 0; 240 doit(); 241 exit(0); 242 } 243 (void) close(s); 244 } 245 } 246 247 static void 248 reapchild(signo) 249 int signo; 250 { 251 union wait status; 252 253 while (wait3((int *)&status, WNOHANG, 0) > 0) 254 ; 255 } 256 257 static void 258 mcleanup(signo) 259 int signo; 260 { 261 if (lflag) 262 syslog(LOG_INFO, "exiting"); 263 unlink(_PATH_SOCKETNAME); 264 exit(0); 265 } 266 267 /* 268 * Stuff for handling job specifications 269 */ 270 char *user[MAXUSERS]; /* users to process */ 271 int users; /* # of users in user array */ 272 int requ[MAXREQUESTS]; /* job number of spool entries */ 273 int requests; /* # of spool requests */ 274 char *person; /* name of person doing lprm */ 275 276 char fromb[MAXHOSTNAMELEN]; /* buffer for client's machine name */ 277 char cbuf[BUFSIZ]; /* command line buffer */ 278 char *cmdnames[] = { 279 "null", 280 "printjob", 281 "recvjob", 282 "displayq short", 283 "displayq long", 284 "rmjob" 285 }; 286 287 static void 288 doit() 289 { 290 register char *cp; 291 register int n; 292 293 for (;;) { 294 cp = cbuf; 295 do { 296 if (cp >= &cbuf[sizeof(cbuf) - 1]) 297 fatal("Command line too long"); 298 if ((n = read(1, cp, 1)) != 1) { 299 if (n < 0) 300 fatal("Lost connection"); 301 return; 302 } 303 } while (*cp++ != '\n'); 304 *--cp = '\0'; 305 cp = cbuf; 306 if (lflag) { 307 if (*cp >= '\1' && *cp <= '\5') 308 syslog(LOG_INFO, "%s requests %s %s", 309 from, cmdnames[*cp], cp+1); 310 else 311 syslog(LOG_INFO, "bad request (%d) from %s", 312 *cp, from); 313 } 314 switch (*cp++) { 315 case '\1': /* check the queue and print any jobs there */ 316 printer = cp; 317 printjob(); 318 break; 319 case '\2': /* receive files to be queued */ 320 if (!from_remote) { 321 syslog(LOG_INFO, "illegal request (%d)", *cp); 322 exit(1); 323 } 324 printer = cp; 325 recvjob(); 326 break; 327 case '\3': /* display the queue (short form) */ 328 case '\4': /* display the queue (long form) */ 329 printer = cp; 330 while (*cp) { 331 if (*cp != ' ') { 332 cp++; 333 continue; 334 } 335 *cp++ = '\0'; 336 while (isspace(*cp)) 337 cp++; 338 if (*cp == '\0') 339 break; 340 if (isdigit(*cp)) { 341 if (requests >= MAXREQUESTS) 342 fatal("Too many requests"); 343 requ[requests++] = atoi(cp); 344 } else { 345 if (users >= MAXUSERS) 346 fatal("Too many users"); 347 user[users++] = cp; 348 } 349 } 350 displayq(cbuf[0] - '\3'); 351 exit(0); 352 case '\5': /* remove a job from the queue */ 353 if (!from_remote) { 354 syslog(LOG_INFO, "illegal request (%d)", *cp); 355 exit(1); 356 } 357 printer = cp; 358 while (*cp && *cp != ' ') 359 cp++; 360 if (!*cp) 361 break; 362 *cp++ = '\0'; 363 person = cp; 364 while (*cp) { 365 if (*cp != ' ') { 366 cp++; 367 continue; 368 } 369 *cp++ = '\0'; 370 while (isspace(*cp)) 371 cp++; 372 if (*cp == '\0') 373 break; 374 if (isdigit(*cp)) { 375 if (requests >= MAXREQUESTS) 376 fatal("Too many requests"); 377 requ[requests++] = atoi(cp); 378 } else { 379 if (users >= MAXUSERS) 380 fatal("Too many users"); 381 user[users++] = cp; 382 } 383 } 384 rmjob(); 385 break; 386 } 387 fatal("Illegal service request"); 388 } 389 } 390 391 /* 392 * Make a pass through the printcap database and start printing any 393 * files left from the last time the machine went down. 394 */ 395 static void 396 startup() 397 { 398 char *buf; 399 register char *cp; 400 int pid; 401 402 /* 403 * Restart the daemons. 404 */ 405 while (cgetnext(&buf, printcapdb) > 0) { 406 for (cp = buf; *cp; cp++) 407 if (*cp == '|' || *cp == ':') { 408 *cp = '\0'; 409 break; 410 } 411 if ((pid = fork()) < 0) { 412 syslog(LOG_WARNING, "startup: cannot fork"); 413 mcleanup(0); 414 } 415 if (!pid) { 416 printer = buf; 417 cgetclose(); 418 printjob(); 419 } 420 } 421 } 422 423 #define DUMMY ":nobody::" 424 425 /* 426 * Check to see if the from host has access to the line printer. 427 */ 428 static void 429 chkhost(f) 430 struct sockaddr_in *f; 431 { 432 register struct hostent *hp; 433 register FILE *hostf; 434 int first = 1; 435 extern char *inet_ntoa(); 436 437 f->sin_port = ntohs(f->sin_port); 438 if (f->sin_family != AF_INET || f->sin_port >= IPPORT_RESERVED) 439 fatal("Malformed from address"); 440 441 /* Need real hostname for temporary filenames */ 442 hp = gethostbyaddr((char *)&f->sin_addr, 443 sizeof(struct in_addr), f->sin_family); 444 if (hp == NULL) 445 fatal("Host name for your address (%s) unknown", 446 inet_ntoa(f->sin_addr)); 447 448 (void) strncpy(fromb, hp->h_name, sizeof(fromb)); 449 from[sizeof(fromb) - 1] = '\0'; 450 from = fromb; 451 452 hostf = fopen(_PATH_HOSTSEQUIV, "r"); 453 again: 454 if (hostf) { 455 if (__ivaliduser(hostf, f->sin_addr.s_addr, 456 DUMMY, DUMMY) == 0) { 457 (void) fclose(hostf); 458 return; 459 } 460 (void) fclose(hostf); 461 } 462 if (first == 1) { 463 first = 0; 464 hostf = fopen(_PATH_HOSTSLPD, "r"); 465 goto again; 466 } 467 fatal("Your host does not have line printer access"); 468 /*NOTREACHED*/ 469 } 470 471 472 473 474 475 476 477 478 479 480 481 482