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 are permitted 6 * provided that this notice is preserved and that due credit is given 7 * to the University of California at Berkeley. The name of the University 8 * may not be used to endorse or promote products derived from this 9 * software without specific prior written permission. This software 10 * is provided ``as is'' without express or implied warranty. 11 */ 12 13 #ifndef lint 14 char copyright[] = 15 "@(#) Copyright (c) 1983 Regents of the University of California.\n\ 16 All rights reserved.\n"; 17 #endif /* not lint */ 18 19 #ifndef lint 20 static char sccsid[] = "@(#)lpd.c 5.5 (Berkeley) 05/05/88"; 21 #endif /* not lint */ 22 23 /* 24 * lpd -- line printer daemon. 25 * 26 * Listen for a connection and perform the requested operation. 27 * Operations are: 28 * \1printer\n 29 * check the queue for jobs and print any found. 30 * \2printer\n 31 * receive a job from another machine and queue it. 32 * \3printer [users ...] [jobs ...]\n 33 * return the current state of the queue (short form). 34 * \4printer [users ...] [jobs ...]\n 35 * return the current state of the queue (long form). 36 * \5printer person [users ...] [jobs ...]\n 37 * remove jobs from the queue. 38 * 39 * Strategy to maintain protected spooling area: 40 * 1. Spooling area is writable only by daemon and spooling group 41 * 2. lpr runs setuid root and setgrp spooling group; it uses 42 * root to access any file it wants (verifying things before 43 * with an access call) and group id to know how it should 44 * set up ownership of files in the spooling area. 45 * 3. Files in spooling area are owned by root, group spooling 46 * group, with mode 660. 47 * 4. lpd, lpq and lprm run setuid daemon and setgrp spooling group to 48 * access files and printer. Users can't get to anything 49 * w/o help of lpq and lprm programs. 50 */ 51 52 #include "lp.h" 53 54 int lflag; /* log requests flag */ 55 56 int reapchild(); 57 int mcleanup(); 58 59 main(argc, argv) 60 int argc; 61 char **argv; 62 { 63 int f, funix, finet, options, defreadfds, fromlen; 64 struct sockaddr_un sun, fromunix; 65 struct sockaddr_in sin, frominet; 66 int omask, lfd; 67 68 gethostname(host, sizeof(host)); 69 name = argv[0]; 70 71 while (--argc > 0) { 72 argv++; 73 if (argv[0][0] == '-') 74 switch (argv[0][1]) { 75 case 'd': 76 options |= SO_DEBUG; 77 break; 78 case 'l': 79 lflag++; 80 break; 81 } 82 } 83 84 #ifndef DEBUG 85 /* 86 * Set up standard environment by detaching from the parent. 87 */ 88 if (fork()) 89 exit(0); 90 for (f = 0; f < 5; f++) 91 (void) close(f); 92 (void) open("/dev/null", O_RDONLY); 93 (void) open("/dev/null", O_WRONLY); 94 (void) dup(1); 95 f = open("/dev/tty", O_RDWR); 96 if (f > 0) { 97 ioctl(f, TIOCNOTTY, 0); 98 (void) close(f); 99 } 100 #endif 101 102 openlog("lpd", LOG_PID, LOG_LPR); 103 (void) umask(0); 104 lfd = open(MASTERLOCK, O_WRONLY|O_CREAT, 0644); 105 if (lfd < 0) { 106 syslog(LOG_ERR, "%s: %m", MASTERLOCK); 107 exit(1); 108 } 109 if (flock(lfd, LOCK_EX|LOCK_NB) < 0) { 110 if (errno == EWOULDBLOCK) /* active deamon present */ 111 exit(0); 112 syslog(LOG_ERR, "%s: %m", MASTERLOCK); 113 exit(1); 114 } 115 ftruncate(lfd, 0); 116 /* 117 * write process id for others to know 118 */ 119 sprintf(line, "%u\n", getpid()); 120 f = strlen(line); 121 if (write(lfd, line, f) != f) { 122 syslog(LOG_ERR, "%s: %m", MASTERLOCK); 123 exit(1); 124 } 125 signal(SIGCHLD, reapchild); 126 /* 127 * Restart all the printers. 128 */ 129 startup(); 130 (void) unlink(SOCKETNAME); 131 funix = socket(AF_UNIX, SOCK_STREAM, 0); 132 if (funix < 0) { 133 syslog(LOG_ERR, "socket: %m"); 134 exit(1); 135 } 136 #define mask(s) (1 << ((s) - 1)) 137 omask = sigblock(mask(SIGHUP)|mask(SIGINT)|mask(SIGQUIT)|mask(SIGTERM)); 138 signal(SIGHUP, mcleanup); 139 signal(SIGINT, mcleanup); 140 signal(SIGQUIT, mcleanup); 141 signal(SIGTERM, mcleanup); 142 sun.sun_family = AF_UNIX; 143 strcpy(sun.sun_path, SOCKETNAME); 144 if (bind(funix, &sun, strlen(sun.sun_path) + 2) < 0) { 145 syslog(LOG_ERR, "ubind: %m"); 146 exit(1); 147 } 148 sigsetmask(omask); 149 defreadfds = 1 << funix; 150 listen(funix, 5); 151 finet = socket(AF_INET, SOCK_STREAM, 0); 152 if (finet >= 0) { 153 struct servent *sp; 154 155 if (options & SO_DEBUG) 156 if (setsockopt(finet, SOL_SOCKET, SO_DEBUG, 0, 0) < 0) { 157 syslog(LOG_ERR, "setsockopt (SO_DEBUG): %m"); 158 mcleanup(); 159 } 160 sp = getservbyname("printer", "tcp"); 161 if (sp == NULL) { 162 syslog(LOG_ERR, "printer/tcp: unknown service"); 163 mcleanup(); 164 } 165 sin.sin_family = AF_INET; 166 sin.sin_port = sp->s_port; 167 if (bind(finet, &sin, sizeof(sin), 0) < 0) { 168 syslog(LOG_ERR, "bind: %m"); 169 mcleanup(); 170 } 171 defreadfds |= 1 << finet; 172 listen(finet, 5); 173 } 174 /* 175 * Main loop: accept, do a request, continue. 176 */ 177 for (;;) { 178 int domain, nfds, s, readfds = defreadfds; 179 180 nfds = select(20, &readfds, 0, 0, 0); 181 if (nfds <= 0) { 182 if (nfds < 0 && errno != EINTR) 183 syslog(LOG_WARNING, "select: %m"); 184 continue; 185 } 186 if (readfds & (1 << funix)) { 187 domain = AF_UNIX, fromlen = sizeof(fromunix); 188 s = accept(funix, &fromunix, &fromlen); 189 } else if (readfds & (1 << finet)) { 190 domain = AF_INET, fromlen = sizeof(frominet); 191 s = accept(finet, &frominet, &fromlen); 192 } 193 if (s < 0) { 194 if (errno != EINTR) 195 syslog(LOG_WARNING, "accept: %m"); 196 continue; 197 } 198 if (fork() == 0) { 199 signal(SIGCHLD, SIG_IGN); 200 signal(SIGHUP, SIG_IGN); 201 signal(SIGINT, SIG_IGN); 202 signal(SIGQUIT, SIG_IGN); 203 signal(SIGTERM, SIG_IGN); 204 (void) close(funix); 205 (void) close(finet); 206 dup2(s, 1); 207 (void) close(s); 208 if (domain == AF_INET) 209 chkhost(&frominet); 210 doit(); 211 exit(0); 212 } 213 (void) close(s); 214 } 215 } 216 217 reapchild() 218 { 219 union wait status; 220 221 while (wait3(&status, WNOHANG, 0) > 0) 222 ; 223 } 224 225 mcleanup() 226 { 227 if (lflag) 228 syslog(LOG_INFO, "exiting"); 229 unlink(SOCKETNAME); 230 exit(0); 231 } 232 233 /* 234 * Stuff for handling job specifications 235 */ 236 char *user[MAXUSERS]; /* users to process */ 237 int users; /* # of users in user array */ 238 int requ[MAXREQUESTS]; /* job number of spool entries */ 239 int requests; /* # of spool requests */ 240 char *person; /* name of person doing lprm */ 241 242 char fromb[32]; /* buffer for client's machine name */ 243 char cbuf[BUFSIZ]; /* command line buffer */ 244 char *cmdnames[] = { 245 "null", 246 "printjob", 247 "recvjob", 248 "displayq short", 249 "displayq long", 250 "rmjob" 251 }; 252 253 doit() 254 { 255 register char *cp; 256 register int n; 257 258 for (;;) { 259 cp = cbuf; 260 do { 261 if (cp >= &cbuf[sizeof(cbuf) - 1]) 262 fatal("Command line too long"); 263 if ((n = read(1, cp, 1)) != 1) { 264 if (n < 0) 265 fatal("Lost connection"); 266 return; 267 } 268 } while (*cp++ != '\n'); 269 *--cp = '\0'; 270 cp = cbuf; 271 if (lflag) { 272 if (*cp >= '\1' && *cp <= '\5') 273 syslog(LOG_INFO, "%s requests %s %s", 274 from, cmdnames[*cp], cp+1); 275 else 276 syslog(LOG_INFO, "bad request (%d) from %s", 277 *cp, from); 278 } 279 switch (*cp++) { 280 case '\1': /* check the queue and print any jobs there */ 281 printer = cp; 282 printjob(); 283 break; 284 case '\2': /* receive files to be queued */ 285 printer = cp; 286 recvjob(); 287 break; 288 case '\3': /* display the queue (short form) */ 289 case '\4': /* display the queue (long form) */ 290 printer = cp; 291 while (*cp) { 292 if (*cp != ' ') { 293 cp++; 294 continue; 295 } 296 *cp++ = '\0'; 297 while (isspace(*cp)) 298 cp++; 299 if (*cp == '\0') 300 break; 301 if (isdigit(*cp)) { 302 if (requests >= MAXREQUESTS) 303 fatal("Too many requests"); 304 requ[requests++] = atoi(cp); 305 } else { 306 if (users >= MAXUSERS) 307 fatal("Too many users"); 308 user[users++] = cp; 309 } 310 } 311 displayq(cbuf[0] - '\3'); 312 exit(0); 313 case '\5': /* remove a job from the queue */ 314 printer = cp; 315 while (*cp && *cp != ' ') 316 cp++; 317 if (!*cp) 318 break; 319 *cp++ = '\0'; 320 person = cp; 321 while (*cp) { 322 if (*cp != ' ') { 323 cp++; 324 continue; 325 } 326 *cp++ = '\0'; 327 while (isspace(*cp)) 328 cp++; 329 if (*cp == '\0') 330 break; 331 if (isdigit(*cp)) { 332 if (requests >= MAXREQUESTS) 333 fatal("Too many requests"); 334 requ[requests++] = atoi(cp); 335 } else { 336 if (users >= MAXUSERS) 337 fatal("Too many users"); 338 user[users++] = cp; 339 } 340 } 341 rmjob(); 342 break; 343 } 344 fatal("Illegal service request"); 345 } 346 } 347 348 /* 349 * Make a pass through the printcap database and start printing any 350 * files left from the last time the machine went down. 351 */ 352 startup() 353 { 354 char buf[BUFSIZ]; 355 register char *cp; 356 int pid; 357 358 printer = buf; 359 360 /* 361 * Restart the daemons. 362 */ 363 while (getprent(buf) > 0) { 364 for (cp = buf; *cp; cp++) 365 if (*cp == '|' || *cp == ':') { 366 *cp = '\0'; 367 break; 368 } 369 if ((pid = fork()) < 0) { 370 syslog(LOG_WARNING, "startup: cannot fork"); 371 mcleanup(); 372 } 373 if (!pid) { 374 endprent(); 375 printjob(); 376 } 377 } 378 } 379 380 #define DUMMY ":nobody::" 381 382 /* 383 * Check to see if the from host has access to the line printer. 384 */ 385 chkhost(f) 386 struct sockaddr_in *f; 387 { 388 register struct hostent *hp; 389 register FILE *hostf; 390 register char *cp, *sp; 391 char ahost[50]; 392 int first = 1; 393 extern char *inet_ntoa(); 394 int baselen = -1; 395 396 f->sin_port = ntohs(f->sin_port); 397 if (f->sin_family != AF_INET || f->sin_port >= IPPORT_RESERVED) 398 fatal("Malformed from address"); 399 hp = gethostbyaddr(&f->sin_addr, sizeof(struct in_addr), f->sin_family); 400 if (hp == 0) 401 fatal("Host name for your address (%s) unknown", 402 inet_ntoa(f->sin_addr)); 403 404 strcpy(fromb, hp->h_name); 405 from = fromb; 406 if (!strcmp(from, host)) 407 return; 408 409 sp = fromb; 410 cp = ahost; 411 while (*sp) { 412 if (*sp == '.') { 413 if (baselen == -1) 414 baselen = sp - fromb; 415 *cp++ = *sp++; 416 } else { 417 *cp++ = isupper(*sp) ? tolower(*sp++) : *sp++; 418 } 419 } 420 *cp = '\0'; 421 hostf = fopen("/etc/hosts.equiv", "r"); 422 again: 423 if (hostf) { 424 if (!_validuser(hostf, ahost, DUMMY, DUMMY, baselen)) { 425 (void) fclose(hostf); 426 return; 427 } 428 (void) fclose(hostf); 429 } 430 if (first == 1) { 431 first = 0; 432 hostf = fopen("/etc/hosts.lpd", "r"); 433 goto again; 434 } 435 fatal("Your host does not have line printer access"); 436 } 437