source: trunk/athena/etc/xdm/xdm/session.c @ 6052

Revision 6052, 13.9 KB checked in by lwvanels, 33 years ago (diff)
Initial revision
Line 
1/*
2 * xdm - display manager daemon
3 *
4 * $XConsortium: session.c,v 1.55 91/09/19 16:25:56 keith Exp $
5 *
6 * Copyright 1988 Massachusetts Institute of Technology
7 *
8 * Permission to use, copy, modify, and distribute this software and its
9 * documentation for any purpose and without fee is hereby granted, provided
10 * that the above copyright notice appear in all copies and that both that
11 * copyright notice and this permission notice appear in supporting
12 * documentation, and that the name of M.I.T. not be used in advertising or
13 * publicity pertaining to distribution of the software without specific,
14 * written prior permission.  M.I.T. makes no representations about the
15 * suitability of this software for any purpose.  It is provided "as is"
16 * without express or implied warranty.
17 *
18 * Author:  Keith Packard, MIT X Consortium
19 */
20
21/*
22 * session.c
23 */
24
25# include "dm.h"
26# include <X11/Xlib.h>
27# include <signal.h>
28# include <X11/Xatom.h>
29# include <errno.h>
30# include <stdio.h>
31# include <ctype.h>
32#ifdef SECURE_RPC
33# include <rpc/rpc.h>
34# include <rpc/key_prot.h>
35#endif
36#if defined(_AIX) && defined(_IBMR2)
37#include <sys/id.h>
38#endif
39
40extern int  errno;
41
42static int                      clientPid;
43static struct greet_info        greet;
44static struct verify_info       verify;
45
46static Jmp_buf  abortSession;
47
48/* ARGSUSED */
49static SIGVAL
50catchTerm (n)
51    int n;
52{
53    Longjmp (abortSession, 1);
54}
55
56static Jmp_buf  pingTime;
57
58/* ARGSUSED */
59static SIGVAL
60catchAlrm (n)
61    int n;
62{
63    Longjmp (pingTime, 1);
64}
65
66SessionPingFailed (d)
67    struct display  *d;
68{
69    if (clientPid > 1)
70    {
71        AbortClient (clientPid);
72        source (verify.systemEnviron, d->reset);
73    }
74    SessionExit (d, RESERVER_DISPLAY, TRUE);
75}
76
77/*
78 * We need our own error handlers because we can't be sure what exit code Xlib
79 * will use, and our Xlib does exit(1) which matches REMANAGE_DISPLAY, which
80 * can cause a race condition leaving the display wedged.  We need to use
81 * RESERVER_DISPLAY for IO errors, to ensure that the manager waits for the
82 * server to terminate.  For other X errors, we should give up.
83 */
84
85/*ARGSUSED*/
86static
87IOErrorHandler (dpy)
88    Display *dpy;
89{
90    extern char *sys_errlist[];
91    extern int sys_nerr;
92    char *s = ((errno >= 0 && errno < sys_nerr) ? sys_errlist[errno]
93                                                : "unknown error");
94
95    LogError("fatal IO error %d (%s)\n", errno, s);
96    UnVerify();
97    exit(RESERVER_DISPLAY);
98}
99
100static int
101ErrorHandler(dpy, event)
102    Display *dpy;
103    XErrorEvent *event;
104{
105    LogError("X error\n");
106    if (XmuPrintDefaultErrorMessage (dpy, event, stderr) == 0) return 0;
107    UnVerify();
108    exit(UNMANAGE_DISPLAY);
109    /*NOTREACHED*/
110}
111
112ManageSession (d)
113struct display  *d;
114{
115    int                 pid, code, i;
116    Display             *dpy, *InitGreet ();
117
118    Debug ("ManageSession %s\n", d->name);
119    (void)XSetIOErrorHandler(IOErrorHandler);
120    (void)XSetErrorHandler(ErrorHandler);
121    SetTitle(d->name, (char *) 0);
122    /*
123     * Load system default Resources
124     */
125    LoadXloginResources (d);
126    dpy = InitGreet (d);
127    /*
128     * Run the setup script - note this usually will not work when
129     * the server is grabbed, so we don't even bother trying.
130     */
131    if (!d->grabServer)
132        SetupDisplay (d);
133    if (!dpy) {
134        LogError ("Cannot reopen display %s for greet window\n", d->name);
135        UnVerify();
136        exit (RESERVER_DISPLAY);
137    }
138    greet.string = "";
139    for (;;) {
140        /*
141         * Greet user, requesting name/password
142         */
143        code = Greet (d, &greet);
144        if (code != 0)
145        {
146            CloseGreet (d);
147            SessionExit (d, code, FALSE);
148        }
149        /*
150         * Verify user
151         */
152        if (Verify (d, &greet, &verify))
153            break;
154        else
155            FailedLogin (d, &greet);
156    }
157    DeleteXloginResources (d, dpy);
158#ifdef SECURE_RPC
159    for (i = 0; i < d->authNum; i++)
160    {
161        if (d->authorizations[i]->name_length == 9 &&
162            bcmp (d->authorizations[i]->name, "SUN-DES-1", 9) == 0)
163        {
164            XHostAddress        addr;
165            char                netname[MAXNETNAMELEN+1];
166            char                domainname[MAXNETNAMELEN+1];
167   
168            getdomainname(domainname, sizeof domainname);
169            user2netname (netname, verify.uid, domainname);
170            addr.family = FamilyNetname;
171            addr.length = strlen (netname);
172            addr.address = netname;
173            XAddHost (dpy, &addr);
174            break;
175        }
176    }
177#endif
178    CloseGreet (d);
179    Debug ("Greet loop finished\n");
180    /*
181     * Run system-wide initialization file
182     */
183    if (source (verify.systemEnviron, d->startup) != 0)
184    {
185        Debug ("Startup program %s exited with non-zero status\n",
186                d->startup);
187        SessionExit (d, OBEYSESS_DISPLAY, FALSE);
188    }
189    clientPid = 0;
190    if (!Setjmp (abortSession)) {
191        (void) Signal (SIGTERM, catchTerm);
192        /*
193         * Start the clients, changing uid/groups
194         *         setting up environment and running the session
195         */
196        if (StartClient (&verify, d, &clientPid, greet.password)) {
197            Debug ("Client Started\n");
198            /*
199             * Wait for session to end,
200             */
201            for (;;) {
202                if (d->pingInterval)
203                {
204                    if (!Setjmp (pingTime))
205                    {
206                        (void) Signal (SIGALRM, catchAlrm);
207                        (void) alarm (d->pingInterval * 60);
208                        pid = wait ((waitType *) 0);
209                        (void) alarm (0);
210                    }
211                    else
212                    {
213                        (void) alarm (0);
214                        if (!PingServer (d, (Display *) NULL))
215                            SessionPingFailed (d);
216                    }
217                }
218                else
219                {
220                    pid = wait ((waitType *) 0);
221                }
222                if (pid == clientPid)
223                    break;
224            }
225        } else {
226            LogError ("session start failed\n");
227        }
228    } else {
229        /*
230         * when terminating the session, nuke
231         * the child and then run the reset script
232         */
233        AbortClient (clientPid);
234    }
235    /*
236     * run system-wide reset file
237     */
238    Debug ("Source reset program %s\n", d->reset);
239    source (verify.systemEnviron, d->reset);
240    SessionExit (d, OBEYSESS_DISPLAY, TRUE);
241}
242
243LoadXloginResources (d)
244struct display  *d;
245{
246    char        **args, **parseArgs();
247    char        **env = 0, **setEnv(), **systemEnv();
248
249    if (d->resources[0] && access (d->resources, 4) == 0) {
250        env = systemEnv (d, (char *) 0, (char *) 0);
251        args = parseArgs ((char **) 0, d->xrdb);
252        args = parseArgs (args, d->resources);
253        Debug ("Loading resource file: %s\n", d->resources);
254        (void) runAndWait (args, env);
255        freeArgs (args);
256        freeEnv (env);
257    }
258}
259
260SetupDisplay (d)
261struct display  *d;
262{
263    char        **env = 0, **setEnv(), **systemEnv();
264
265    if (d->setup && d->setup[0])
266    {
267        env = systemEnv (d, (char *) 0, (char *) 0);
268        (void) source (env, d->setup);
269        freeEnv (env);
270    }
271}
272
273/*ARGSUSED*/
274DeleteXloginResources (d, dpy)
275struct display  *d;
276Display         *dpy;
277{
278    int i;
279    Atom prop = XInternAtom(dpy, "SCREEN_RESOURCES", True);
280
281    XDeleteProperty(dpy, RootWindow (dpy, 0), XA_RESOURCE_MANAGER);
282    if (prop) {
283        for (i = ScreenCount(dpy); --i >= 0; )
284            XDeleteProperty(dpy, RootWindow (dpy, i), prop);
285    }
286}
287
288static Jmp_buf syncJump;
289
290/* ARGSUSED */
291static SIGVAL
292syncTimeout (n)
293    int n;
294{
295    Longjmp (syncJump, 1);
296}
297
298SecureDisplay (d, dpy)
299struct display  *d;
300Display         *dpy;
301{
302    Debug ("SecureDisplay %s\n", d->name);
303    (void) Signal (SIGALRM, syncTimeout);
304    if (Setjmp (syncJump)) {
305        LogError ("WARNING: display %s could not be secured\n",
306                   d->name);
307        SessionExit (d, RESERVER_DISPLAY, FALSE);
308    }
309    (void) alarm ((unsigned) d->grabTimeout);
310    Debug ("Before XGrabServer %s\n", d->name);
311    XGrabServer (dpy);
312    if (XGrabKeyboard (dpy, DefaultRootWindow (dpy), True, GrabModeAsync,
313                       GrabModeAsync, CurrentTime) != GrabSuccess)
314    {
315        (void) alarm (0);
316        (void) Signal (SIGALRM, SIG_DFL);
317        LogError ("WARNING: keyboard on display %s could not be secured\n",
318                  d->name);
319        SessionExit (d, RESERVER_DISPLAY, FALSE);
320    }
321    Debug ("XGrabKeyboard succeeded %s\n", d->name);
322    (void) alarm (0);
323    (void) Signal (SIGALRM, SIG_DFL);
324    pseudoReset (dpy);
325    if (!d->grabServer)
326    {
327        XUngrabServer (dpy);
328        XSync (dpy, 0);
329    }
330    Debug ("done secure %s\n", d->name);
331}
332
333UnsecureDisplay (d, dpy)
334struct display  *d;
335Display         *dpy;
336{
337    Debug ("Unsecure display %s\n", d->name);
338    if (d->grabServer)
339    {
340        XUngrabServer (dpy);
341        XSync (dpy, 0);
342    }
343}
344
345SessionExit (d, status, removeAuth)
346    struct display  *d;
347{
348    UnVerify();
349    /* make sure the server gets reset after the session is over */
350    if (d->serverPid >= 2 && d->resetSignal)
351        kill (d->serverPid, d->resetSignal);
352    else
353        ResetServer (d);
354    if (removeAuth)
355    {
356#ifdef NGROUPS_MAX
357        setgid (verify.groups[0]);
358#else
359        setgid (verify.gid);
360#endif
361        setuid (verify.uid);
362        RemoveUserAuthorization (d, &verify);
363    }
364    Debug ("Display %s exiting with status %d\n", d->name, status);
365    exit (status);
366}
367
368StartClient (verify, d, pidp, passwd)
369struct verify_info      *verify;
370struct display          *d;
371int                     *pidp;
372char                    *passwd;
373{
374    char        **f, *home, *getEnv ();
375    char        *failsafeArgv[2];
376    int pid;
377
378    if (verify->argv) {
379        Debug ("StartSession %s: ", verify->argv[0]);
380        for (f = verify->argv; *f; f++)
381                Debug ("%s ", *f);
382        Debug ("; ");
383    }
384    if (verify->userEnviron) {
385        for (f = verify->userEnviron; *f; f++)
386                Debug ("%s ", *f);
387        Debug ("\n");
388    }
389    switch (pid = fork ()) {
390    case 0:
391        CleanUpChild ();
392#ifdef NGROUPS_MAX
393
394#if defined(_AIX) && defined(_IBMR2)
395        setgidx(ID_SAVED|ID_REAL|ID_EFFECTIVE, verify->groups[0]);
396#else
397        setgid (verify->groups[0]);
398#endif
399        setgroups (verify->ngroups, verify->groups);
400#else
401        setgid (verify->gid);
402#endif
403#if defined(_AIX) && defined(_IBMR2)
404        setuidx(ID_LOGIN|ID_SAVED|ID_REAL|ID_EFFECTIVE, verify->uid);
405#else
406        setuid (verify->uid);
407#endif
408#ifdef SECURE_RPC
409        {
410            char    netname[MAXNETNAMELEN+1], secretkey[HEXKEYBYTES+1];
411            int     ret;
412            int     len;
413
414            getnetname (netname);
415            Debug ("User netname: %s\n", netname);
416            len = strlen (passwd);
417            if (len > 8)
418                bzero (passwd + 8, len - 8);
419            ret = getsecretkey(netname,secretkey,passwd);
420            Debug ("getsecretkey returns %d, key length %d\n",
421                    ret, strlen (secretkey));
422            ret = key_setsecret(secretkey);
423            Debug ("key_setsecret returns %d\n", ret);
424        }
425#endif
426        bzero(passwd, strlen(passwd));
427        SetUserAuthorization (d, verify);
428        home = getEnv (verify->userEnviron, "HOME");
429        if (home)
430                if (chdir (home) == -1) {
431                        LogError ("No home directory %s for user %s, using /\n",
432                                  home, getEnv (verify->userEnviron, "USER"));
433                        chdir ("/");
434                }
435        if (verify->argv) {
436                Debug ("executing session %s\n", verify->argv[0]);
437                execute (verify->argv, verify->userEnviron);
438                LogError ("Session execution failed %s\n", verify->argv[0]);
439        } else {
440                LogError ("Session has no command/arguments\n");
441        }
442        failsafeArgv[0] = d->failsafeClient;
443        failsafeArgv[1] = 0;
444        execute (failsafeArgv, verify->userEnviron);
445        exit (1);
446    case -1:
447        bzero(passwd, strlen(passwd));
448        Debug ("StartSession, fork failed\n");
449        LogError ("can't start session for %d, fork failed\n", d->name);
450        return 0;
451    default:
452        bzero(passwd, strlen(passwd));
453        Debug ("StartSession, fork suceeded %d\n", pid);
454        *pidp = pid;
455        return 1;
456    }
457}
458
459static Jmp_buf  tenaciousClient;
460
461/* ARGSUSED */
462static SIGVAL
463waitAbort (n)
464    int n;
465{
466        Longjmp (tenaciousClient, 1);
467}
468
469#if defined(_POSIX_SOURCE) || defined(SYSV) || defined(SVR4)
470#define killpg(pgrp, sig) kill(-(pgrp), sig)
471#endif
472
473AbortClient (pid)
474int     pid;
475{
476    int sig = SIGTERM;
477#if __STDC__
478    volatile int        i;
479#else
480    int i;
481#endif
482    int retId;
483    for (i = 0; i < 4; i++) {
484        if (killpg (pid, sig) == -1) {
485            switch (errno) {
486            case EPERM:
487                LogError ("xdm can't kill client\n");
488            case EINVAL:
489            case ESRCH:
490                return;
491            }
492        }
493        if (!Setjmp (tenaciousClient)) {
494            (void) Signal (SIGALRM, waitAbort);
495            (void) alarm ((unsigned) 10);
496            retId = wait ((waitType *) 0);
497            (void) alarm ((unsigned) 0);
498            (void) Signal (SIGALRM, SIG_DFL);
499            if (retId == pid)
500                break;
501        } else
502            (void) Signal (SIGALRM, SIG_DFL);
503        sig = SIGKILL;
504    }
505}
506
507int
508source (environ, file)
509char                    **environ;
510char                    *file;
511{
512    char        **args, *args_safe[2];
513    extern char **parseArgs ();
514    int         ret;
515
516    if (file && file[0]) {
517        Debug ("source %s\n", file);
518        args = parseArgs ((char **) 0, file);
519        if (!args)
520        {
521            args = args_safe;
522            args[0] = file;
523            args[1] = NULL;
524        }
525        ret = runAndWait (args, environ);
526        freeArgs (args);
527        return ret;
528    }
529    return 0;
530}
531
532int
533runAndWait (args, environ)
534    char        **args;
535    char        **environ;
536{
537    int pid;
538    extern int  errno;
539    waitType    result;
540
541    switch (pid = fork ()) {
542    case 0:
543        CleanUpChild ();
544        execute (args, environ);
545        LogError ("can't execute %s\n", args[0]);
546        exit (1);
547    case -1:
548        Debug ("fork failed\n");
549        LogError ("can't fork to execute %s\n", args[0]);
550        return 1;
551    default:
552        while (wait (&result) != pid)
553                /* SUPPRESS 530 */
554                ;
555        break;
556    }
557    return waitVal (result);
558}
559
560execute (argv, environ)
561char    **argv;
562char    **environ;
563{
564    /* make stdout follow stderr to the log file */
565    dup2 (2,1);
566    execve (argv[0], argv, environ);
567    /*
568     * In case this is a shell script which hasn't been
569     * made executable (or this is a SYSV box), do
570     * a reasonable thing
571     */
572    if (errno != ENOENT) {
573        char    program[1024], *e, *p, *optarg;
574        FILE    *f;
575        char    **newargv, **av;
576        int     argc;
577
578        /*
579         * emulate BSD kernel behaviour -- read
580         * the first line; check if it starts
581         * with "#!", in which case it uses
582         * the rest of the line as the name of
583         * program to run.  Else use "/bin/sh".
584         */
585        f = fopen (argv[0], "r");
586        if (!f)
587            return;
588        if (fgets (program, sizeof (program) - 1, f) == NULL)
589        {
590            fclose (f);
591            return;
592        }
593        fclose (f);
594        e = program + strlen (program) - 1;
595        if (*e == '\n')
596            *e = '\0';
597        if (!strncmp (program, "#!", 2)) {
598            p = program + 2;
599            while (*p && isspace (*p))
600                ++p;
601            optarg = p;
602            while (*optarg && !isspace (*optarg))
603                ++optarg;
604            if (*optarg) {
605                *optarg = '\0';
606                do
607                    ++optarg;
608                while (*optarg && isspace (*optarg));
609            } else
610                optarg = 0;
611        } else {
612            p = "/bin/sh";
613            optarg = 0;
614        }
615        Debug ("Shell script execution: %s (optarg %s)\n",
616                p, optarg ? optarg : "(null)");
617        for (av = argv, argc = 0; *av; av++, argc++)
618            /* SUPPRESS 530 */
619            ;
620        newargv = (char **) malloc ((argc + (optarg ? 3 : 2)) * sizeof (char *));
621        if (!newargv)
622            return;
623        av = newargv;
624        *av++ = p;
625        if (optarg)
626            *av++ = optarg;
627        /* SUPPRESS 560 */
628        while (*av++ = *argv++)
629            /* SUPPRESS 530 */
630            ;
631        execve (newargv[0], newargv, environ);
632    }
633}
Note: See TracBrowser for help on using the repository browser.