source: trunk/athena/bin/lpr/lpd.c @ 6619

Revision 6619, 16.5 KB checked in by vrt, 31 years ago (diff)
Solaris Port
Line 
1/*
2 *      $Source: /afs/dev.mit.edu/source/repository/athena/bin/lpr/lpd.c,v $
3 *      $Author: vrt $
4 *      $Locker:  $
5 *      $Header: /afs/dev.mit.edu/source/repository/athena/bin/lpr/lpd.c,v 1.17 1993-05-10 13:36:33 vrt Exp $
6 */
7
8/*
9 * Copyright (c) 1983 Regents of the University of California.
10 * All rights reserved.  The Berkeley software License Agreement
11 * specifies the terms and conditions for redistribution.
12 */
13
14#ifndef lint
15char copyright[] =
16"@(#) Copyright (c) 1983 Regents of the University of California.\n\
17 All rights reserved.\n";
18
19static char sccsid[] = "@(#)lpd.c       5.4 (Berkeley) 5/6/86";
20static char *rcsid_lpd_c = "$Id: lpd.c,v 1.17 1993-05-10 13:36:33 vrt Exp $";
21#endif
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 *      kprinter\nkerberos credentials
39 *              Uses kerberos authentication
40 *
41 * Strategy to maintain protected spooling area:
42 *      1. Spooling area is writable only by daemon and spooling group
43 *      2. lpr runs setuid root and setgrp spooling group; it uses
44 *         root to access any file it wants (verifying things before
45 *         with an access call) and group id to know how it should
46 *         set up ownership of files in the spooling area.
47 *      3. Files in spooling area are owned by root, group spooling
48 *         group, with mode 660.
49 *      4. lpd, lpq and lprm run setuid daemon and setgrp spooling group to
50 *         access files and printer.  Users can't get to anything
51 *         w/o help of lpq and lprm programs.
52 */
53
54#include "lp.h"
55
56#ifdef ZEPHYR
57#undef STAT
58#include <zephyr/zephyr.h>
59#endif
60
61int     lflag;                          /* log requests flag */
62
63int     reapchild();
64int     mcleanup();
65
66#ifdef KERBEROS
67KTEXT_ST kticket;
68AUTH_DAT kdata;
69int kauth;
70int sin_len;
71struct sockaddr_in faddr;
72char krbname[ANAME_SZ + INST_SZ + REALM_SZ + 3];
73char kprincipal[ANAME_SZ];
74char kinstance[INST_SZ];
75char krealm[REALM_SZ];
76char local_realm[REALM_SZ];
77char kversion[9];
78int kflag;                     /* Is the current job authentc */
79int kerror;                    /* They tried sending auth, but it failed */
80int kerberos_override = -1;    /* Does command option override KA in printcap? */
81int kerberos_cf = 0;           /* Are we using a kerberized cf file? */
82int use_kerberos;
83#endif KERBEROS
84
85#ifdef LACL
86char from_host[MAXHOSTNAMELEN];
87#endif
88
89main(argc, argv)
90        int argc;
91        char **argv;
92{
93        int f, funix, finet, options=0, defreadfds, fromlen;
94        struct sockaddr_un sockun, fromunix;
95        struct sockaddr_in sin, frominet;
96        struct hostent *hp;
97        int omask, lfd;
98
99#ifdef ZEPHYR
100        ZInitialize();
101#endif ZEPHYR
102
103        gethostname(host, sizeof(host));
104        if(hp = gethostbyname(host)) strcpy(host, hp -> h_name);
105
106        name = argv[0];
107
108        while (--argc > 0) {
109                argv++;
110                if (argv[0][0] == '-')
111                        switch (argv[0][1]) {
112                        case 'd':
113                                options |= SO_DEBUG;
114                                break;
115                        case 'l':
116                                lflag++;
117                                break;
118#ifdef KERBEROS
119                        case 'u':
120                                kerberos_override = 0;
121                                break;
122                        case 'k':
123                                kerberos_override = 1;
124                                break;
125#endif KERBEROS
126                        }
127        }
128
129#ifndef DEBUG
130        /*
131         * Set up standard environment by detaching from the parent.
132         */
133        if (fork())
134                exit(0);
135        for (f = 0; f < 5; f++)
136                (void) close(f);
137        (void) open("/dev/null", O_RDONLY);
138        (void) open("/dev/null", O_WRONLY);
139        (void) dup(1);
140        f = open("/dev/tty", O_RDWR);
141        if (f > 0) {
142                ioctl(f, TIOCNOTTY, 0);
143                (void) close(f);
144        }
145#endif
146
147#ifdef LOG_LPR
148        openlog("lpd", LOG_PID, LOG_LPR);
149#else
150        openlog("lpd", LOG_PID);
151#endif
152        if (lflag) syslog(LOG_INFO, "daemon started");
153        (void) umask(0);
154        lfd = open(MASTERLOCK, O_WRONLY|O_CREAT, 0644);
155        if (lfd < 0) {
156                syslog(LOG_ERR, "%s: %m", MASTERLOCK);
157                exit(1);
158        }
159        if (flock(lfd, LOCK_EX|LOCK_NB) < 0) {
160                if (errno == EWOULDBLOCK)       /* active deamon present */
161                        exit(0);
162                syslog(LOG_ERR, "%s: %m", MASTERLOCK);
163                exit(1);
164        }
165        ftruncate(lfd, 0);
166        /*
167         * write process id for others to know
168         */
169        sprintf(line, "%u\n", getpid());
170        f = strlen(line);
171        if (write(lfd, line, f) != f) {
172                syslog(LOG_ERR, "%s: %m", MASTERLOCK);
173                exit(1);
174        }
175        signal(SIGCHLD, reapchild);
176        /*
177         * Restart all the printers.
178         */
179        startup();
180        (void) UNLINK(SOCKETNAME);
181        funix = socket(AF_UNIX, SOCK_STREAM, 0);
182        if (funix < 0) {
183                syslog(LOG_ERR, "socket: %m");
184                exit(1);
185        }
186#define mask(s) (1 << ((s) - 1))
187        omask = sigblock(mask(SIGHUP)|mask(SIGINT)|mask(SIGQUIT)|mask(SIGTERM));
188        signal(SIGHUP, mcleanup);
189        signal(SIGINT, mcleanup);
190        signal(SIGQUIT, mcleanup);
191        signal(SIGTERM, mcleanup);
192        sockun.sun_family = AF_UNIX;
193        strcpy(sockun.sun_path, SOCKETNAME);
194        if (bind(funix, &sockun, strlen(sockun.sun_path) + 2) < 0) {
195                syslog(LOG_ERR, "ubind: %m");
196                exit(1);
197        }
198        sigsetmask(omask);
199        defreadfds = 1 << funix;
200        listen(funix, 5);
201        finet = socket(AF_INET, SOCK_STREAM, 0);
202        if (finet >= 0) {
203                struct servent *sp;
204
205                if (options & SO_DEBUG)
206                        if (setsockopt(finet, SOL_SOCKET, SO_DEBUG, 0, 0) < 0) {
207                                syslog(LOG_ERR, "setsockopt (SO_DEBUG): %m");
208                                mcleanup();
209                        }
210                sp = getservbyname("printer", "tcp");
211                if (sp == NULL) {
212                        syslog(LOG_ERR, "printer/tcp: unknown service");
213                        mcleanup();
214                }
215                sin.sin_family = AF_INET;
216                sin.sin_port = sp->s_port;
217                if (bind(finet, &sin, sizeof(sin)) < 0) {
218                        syslog(LOG_ERR, "bind: %m");
219                        mcleanup();
220                }
221                defreadfds |= 1 << finet;
222                listen(finet, 5);
223        }
224        /*
225         * Main loop: accept, do a request, continue.
226         */
227        for (;;) {
228                int domain, nfds, s, readfds = defreadfds;
229
230                nfds = select(20, &readfds, 0, 0, 0);
231                if (nfds <= 0) {
232                        if (nfds < 0 && errno != EINTR)
233                                syslog(LOG_WARNING, "select: %m");
234                        continue;
235                }
236                if (readfds & (1 << funix)) {
237                        domain = AF_UNIX, fromlen = sizeof(fromunix);
238                        s = accept(funix, &fromunix, &fromlen);
239                } else if (readfds & (1 << finet)) {
240                        domain = AF_INET, fromlen = sizeof(frominet);
241                        s = accept(finet, &frominet, &fromlen);
242                }
243                if (s < 0) {
244                        if (errno != EINTR)
245                                syslog(LOG_WARNING, "accept: %m");
246                        continue;
247                }
248                if (fork() == 0) {
249                        signal(SIGCHLD, SIG_IGN);
250                        signal(SIGHUP, SIG_IGN);
251                        signal(SIGINT, SIG_IGN);
252                        signal(SIGQUIT, SIG_IGN);
253                        signal(SIGTERM, SIG_IGN);
254                        (void) close(funix);
255                        (void) close(finet);
256                        dup2(s, 1);
257                        (void) close(s);
258                        if (domain == AF_INET)
259                                chkhost(&frominet);
260                        doit();
261                        exit(0);
262                }
263                (void) close(s);
264        }
265}
266
267reapchild()
268{
269#if !defined(POSIX)
270        union wait status;
271#else
272        int status;
273#endif
274
275#ifdef POSIX
276        while (waitpid(-1,&status,WNOHANG) >0)
277#else
278        while (wait3(&status, WNOHANG, 0) > 0)
279#endif
280                ;
281}
282
283mcleanup()
284{
285        if (lflag)
286                syslog(LOG_INFO, "exiting");
287        UNLINK(SOCKETNAME);
288        exit(0);
289}
290
291/*
292 * Stuff for handling job specifications
293 */
294char    *user[MAXUSERS];        /* users to process */
295int     users;                  /* # of users in user array */
296int     requ[MAXREQUESTS];      /* job number of spool entries */
297int     requests;               /* # of spool requests */
298char    *person;                /* name of person doing lprm */
299
300char    fromb[32];      /* buffer for client's machine name */
301char    cbuf[BUFSIZ];   /* command line buffer */
302char    *cmdnames[] = {
303        "null",
304        "printjob",
305        "recvjob",
306        "displayq short",
307        "displayq long",
308        "rmjob"
309};
310
311
312#ifdef KERBEROS
313require_kerberos(printer)
314char *printer;
315{
316        int status;
317        short KA;
318        int use_kerberos;
319       
320#ifdef HESIOD
321        if ((status = pgetent(line, printer)) <= 0) {
322                if (pralias(alibuf, printer))
323                        printer = alibuf;
324                if ((status = hpgetent(line, printer)) < 1)
325                        fatal("unknown printer");
326        }
327#else
328        if ((status = pgetent(line, printer)) < 0) {
329                fatal("can't open printer description file");
330        } else if (status == 0)
331                fatal("unknown printer");
332#endif HESIOD                   
333        KA = pgetnum("ka");
334        if (KA > 0)
335                use_kerberos = 1;
336        else
337                use_kerberos = 0;
338        if (kerberos_override > -1)
339                use_kerberos = kerberos_override;
340       
341        return(use_kerberos);
342}
343#endif KERBEROS
344
345
346doit()
347{
348        register char *cp;
349        register int n;
350       
351#ifdef KERBEROS
352        kflag = 0;
353        kerberos_cf = 0;
354        kerror = 0;
355#endif KERBEROS
356       
357        for (;;) {
358                cp = cbuf;
359                do {
360                        if (cp >= &cbuf[sizeof(cbuf) - 1])
361                                fatal("Command line too long");
362                        if ((n = read(1, cp, 1)) != 1) {
363                                if (n < 0)
364                                        fatal("Lost connection");
365                                return;
366                        }
367                } while (*cp++ != '\n');
368                *--cp = '\0';
369                cp = cbuf;
370                if (lflag) {
371                        if (*cp >= '\1' && *cp <= '\5')
372                                syslog(LOG_INFO, "%s requests %s %s",
373                                        from, cmdnames[*cp], cp+1);
374#ifdef KERBEROS
375                        else if (*cp == 'k')
376                                syslog(LOG_INFO, "%s sent kerberos credentials",
377                                       from);
378#endif KERBEROS
379                        else
380                                syslog(LOG_INFO, "bad request (%d) from %s",
381                                        *cp, from);
382                }
383                switch (*cp++) {
384                case '\1':      /* check the queue and print any jobs there */
385                        printer = cp;
386                        printjob();
387                        break;
388                case '\2':      /* receive files to be queued */
389                        printer = cp;
390#ifdef KERBEROS
391                        if (require_kerberos(printer)) {
392                            if (kflag)
393                                kerberos_cf = 1;
394                            else {
395                                /* Return an error and abort */
396                                syslog(LOG_DEBUG,"%s: Cannot receive job before authentication",printer);
397                                putchar('\2');
398                                exit(1);
399                            }
400                        }
401#endif KERBEROS
402                        recvjob();
403                        break;
404                case '\3':      /* display the queue (short form) */
405                case '\4':      /* display the queue (long form) */
406                        printer = cp;
407                        while (*cp) {
408                                if (*cp != ' ') {
409                                        cp++;
410                                        continue;
411                                }
412                                *cp++ = '\0';
413                                while (isspace(*cp))
414                                        cp++;
415                                if (*cp == '\0')
416                                        break;
417                                if (isdigit(*cp)) {
418                                        if (requests >= MAXREQUESTS)
419                                                fatal("Too many requests");
420                                        requ[requests++] = atoi(cp);
421                                } else {
422                                        if (users >= MAXUSERS)
423                                                fatal("Too many users");
424                                        user[users++] = cp;
425                                }
426                        }
427                        displayq(cbuf[0] - '\3');
428                        exit(0);
429                case '\5':      /* remove a job from the queue */
430                        printer = cp;
431                        while (*cp && *cp != ' ')
432                                cp++;
433                        if (!*cp)
434                                break;
435                        *cp++ = '\0';
436                        person = cp;
437                        while (*cp) {
438                                if (*cp != ' ') {
439                                        cp++;
440                                        continue;
441                                }
442                                *cp++ = '\0';
443                                while (isspace(*cp))
444                                        cp++;
445                                if (*cp == '\0')
446                                        break;
447                                if (isdigit(*cp)) {
448                                        if (requests >= MAXREQUESTS)
449                                                fatal("Too many requests");
450                                        requ[requests++] = atoi(cp);
451                                } else {
452                                        if (users >= MAXUSERS)
453                                                fatal("Too many users");
454                                        user[users++] = cp;
455                                }
456                        }
457#ifdef KERBEROS
458                        if (require_kerberos(printer)) {
459                            if (kflag) {
460                                kerberos_cf = 1;
461                                make_kname(kprincipal, kinstance,
462                                           krealm, krbname);
463                                person = krbname;
464                            }
465                            else
466                                /* This message gets sent to the user */
467                                    {
468                                        printf("Kerberos authentication required to remove job.\n");
469                                        exit(1);
470                                    }
471                        }
472#endif KERBEROS
473                        rmjob();
474                        break;
475#ifdef KERBEROS
476                case 'k':       /* Parse kerberos credentials */
477                        printer = cp;
478                        kprincipal[0] = krealm[0] = '\0';
479                        bzero(&kticket, sizeof(KTEXT_ST));
480                        bzero(&kdata,   sizeof(AUTH_DAT));
481                        sin_len = sizeof (struct sockaddr_in);
482                        if (getpeername(1, &faddr, &sin_len) < 0) {
483                                /* return error and exit */
484                                fatal("Could not get peername");
485                        }
486                        /* Tell remote side that kerberos is accepted here! */
487                        putchar('\0');
488                        fflush(stdout);
489                        strcpy(kinstance, "*");
490                        kauth = krb_recvauth(0L, 1, &kticket, KLPR_SERVICE,
491                                             kinstance,
492                                             &faddr,
493                                             (struct sockaddr_in *)NULL,
494                                             &kdata, "", NULL,
495                                             kversion);
496                        if (kauth != KSUCCESS) {
497                                /* return error and exit */
498                                /* We cannot call fatal - not really
499                                   in protocol yet. We will set error
500                                   for return later. */
501                            putchar('\3');
502                            syslog(LOG_DEBUG,"%s: Sending back auth failed", printer);
503                            exit(1);
504                            break;
505                        }
506                        strncpy(kprincipal, kdata.pname,  ANAME_SZ);
507                        strncpy(kinstance,  kdata.pinst,  INST_SZ);
508                        krb_get_lrealm(local_realm, 1);
509                        if (strncmp(kdata.prealm, local_realm, REALM_SZ))
510                            strncpy(krealm, kdata.prealm, REALM_SZ);
511#ifdef DEBUG
512                        if (krealm[0] == '\0')
513                                syslog(LOG_DEBUG,"Authentication for %s.%s",
514                                       kprincipal, kinstance);
515                        else
516                                syslog(LOG_DEBUG,"Authentication for %s.%s@%s",
517                                       kprincipal, kinstance, krealm);
518#endif DEBUG
519                        /* Ackknowledge accepted */
520                        kflag = 1;
521                        putchar('\0');
522                        fflush(stdout);
523                        break;
524#endif KERBEROS
525                default:
526                        fatal("Illegal service request");
527                        break;
528                }
529        }
530}
531
532/*
533 * Make a pass through the printcap database and start printing any
534 * files left from the last time the machine went down.
535 */
536startup()
537{
538        char buf[BUFSIZ];
539        register char *cp;
540        int pid;
541
542        printer = buf;
543
544        /*
545         * Restart the daemons.
546         */
547        while (getprent(buf) > 0) {
548                for (cp = buf; *cp; cp++)
549                        if (*cp == '|' || *cp == ':') {
550                                *cp = '\0';
551                                break;
552                        }
553                if ((pid = fork()) < 0) {
554                        syslog(LOG_WARNING, "startup: cannot fork");
555                        mcleanup();
556                }
557                if (!pid) {
558                        endprent();
559                        printjob();
560                }
561        }
562}
563
564#define DUMMY ":nobody::"
565
566/*
567 * Check to see if the from host has access to the line printer.
568 */
569chkhost(f)
570        struct sockaddr_in *f;
571{
572  /* The following definitions define what consititutes an "athena machine":
573   */
574#ifdef WS
575#ifdef NET
576#undef NET
577#endif
578#define NET(x)  (((x) >> 24) & 0xff)
579#define SUBNET(x) (((x) >> 16) & 0xff)
580#define HHOST(x) (((x) >> 8) & 0xff)
581#define LHOST ((x) & 0xff)
582#define ATHENA_NETWORK 18
583#endif
584
585        register struct hostent *hp;
586        register FILE *hostf;
587        register char *cp, *sp;
588        unsigned long hold_net;
589        char ahost[50];
590        int first = 1;
591        extern char *inet_ntoa();
592        int baselen = -1;
593
594        f->sin_port = ntohs(f->sin_port);
595        if (f->sin_family != AF_INET || f->sin_port >= IPPORT_RESERVED)
596                fatal("Malformed from address");
597        hp = gethostbyaddr(&f->sin_addr, sizeof(struct in_addr), f->sin_family);
598        if (hp == 0)
599                fatal("Host name for your address (%s) unknown",
600                        inet_ntoa(f->sin_addr));
601
602        strcpy(fromb, hp->h_name);
603        from = fromb;
604#ifdef LACL
605        strcpy(from_host, fromb);
606#endif
607        if (!strcasecmp(from, host))
608                return;
609#ifdef WS
610        /* Code for workstation printing only which permits any machine on the
611           Athena network, and in the namespace, to print, even if not
612           in /etc/hosts.equiv or /etc/hosts.lpd */
613
614        hold_net = ntohl(f->sin_addr.s_addr);
615        if (NET(hold_net) == ATHENA_NETWORK)
616            return;
617#endif
618        sp = fromb;
619        cp = ahost;
620        while (*sp) {
621                if (*sp == '.') {
622                        if (baselen == -1)
623                                baselen = sp - fromb;
624                        *cp++ = *sp++;
625                } else {
626                        *cp++ = isupper(*sp) ? tolower(*sp++) : *sp++;
627                }
628        }
629        *cp = '\0';
630
631        hostf = fopen("/etc/hosts.equiv", "r");
632again:
633        if (hostf) {
634                if (!_validuser(hostf, ahost, DUMMY, DUMMY, baselen)) {
635                        (void) fclose(hostf);
636                        return;
637                }
638                (void) fclose(hostf);
639        }
640        if (first == 1) {
641                first = 0;
642                hostf = fopen("/etc/hosts.lpd", "r");
643                goto again;
644        }
645        printer = (char *) NULL;
646        fatal("Your host does not have line printer access");
647}
648
649/*
650 * A version of startdaemon for routines within lpd.
651 * We're already here.... why open a connection to ourselves?
652 */
653
654startdaemon(pr)
655        char    *pr;
656{
657        int     pid;
658       
659        if ((pid = fork()) == 0) {
660                printer = malloc(strlen(pr) + 1);
661                strcpy(printer, pr);
662                if (lflag)
663                        syslog(LOG_INFO, "startdaemon(%s) succeeded", printer);
664                printjob();
665        } else if (pid < 0) {
666                perr("fork");
667                return(0);
668        } else
669                return(1);
670}
671
672static
673perr(msg)
674        char *msg;
675{
676        extern char *name;
677        extern int sys_nerr;
678        extern char *sys_errlist[];
679        extern int errno;
680
681        if (lflag)
682                syslog(LOG_INFO, "%s: %s: %m", name, msg);
683        printf("%s: %s: ", name, msg);
684        fputs(errno < sys_nerr ? sys_errlist[errno] : "Unknown error" , stdout);
685        putchar('\n');
686}
687
688#if defined(_AUX_SOURCE)
689_validuser(hostf, rhost, luser, ruser, baselen)
690char *rhost, *luser, *ruser;
691FILE *hostf;
692int baselen;
693{
694        char *user;
695        char ahost[MAXHOSTNAMELEN];
696        register char *p;
697
698        while (fgets(ahost, sizeof (ahost), hostf)) {
699                p = ahost;
700                while (*p != '\n' && *p != ' ' && *p != '\t' && *p != '\0') {
701                        *p = isupper(*p) ? tolower(*p) : *p;
702                        p++;
703                }
704                if (*p == ' ' || *p == '\t') {
705                        *p++ = '\0';
706                        while (*p == ' ' || *p == '\t')
707                                p++;
708                        user = p;
709                        while (*p != '\n' && *p != ' ' && *p != '\t' && *p != '\0')
710                                p++;
711                } else
712                        user = p;
713                *p = '\0';
714                if (_checkhost(rhost, ahost, baselen) &&
715                    !strcmp(ruser, *user ? user : luser)) {
716                        return (0);
717                }
718        }
719        return (-1);
720}
721
722_checkhost(rhost, lhost, len)
723char *rhost, *lhost;
724int len;
725{
726        static char ldomain[MAXHOSTNAMELEN + 1];
727        static char *domainp = NULL;
728        static int nodomain = 0;
729        register char *cp;
730
731        if (len == -1)
732                return(!strcmp(rhost, lhost));
733        if (strncmp(rhost, lhost, len))
734                return(0);
735        if (!strcmp(rhost, lhost))
736                return(1);
737        if (*(lhost + len) != '\0')
738                return(0);
739        if (nodomain)
740                return(0);
741        if (!domainp) {
742                if (gethostname(ldomain, sizeof(ldomain)) == -1) {
743                        nodomain = 1;
744                        return(0);
745                }
746                ldomain[MAXHOSTNAMELEN] = NULL;
747                if ((domainp = index(ldomain, '.')) == (char *)NULL) {
748                        nodomain = 1;
749                        return(0);
750                }
751                for (cp = ++domainp; *cp; ++cp)
752                        if (islower(*cp))
753                                *cp = toupper(*cp);
754        }
755        return(!strcmp(domainp, rhost + len +1));
756
757}
758#endif
Note: See TracBrowser for help on using the repository browser.