1145b7b3cSmrg/*
2145b7b3cSmrg
3145b7b3cSmrgCopyright 1988, 1998  The Open Group
4145b7b3cSmrg
5145b7b3cSmrgPermission to use, copy, modify, distribute, and sell this software and its
6145b7b3cSmrgdocumentation for any purpose is hereby granted without fee, provided that
7145b7b3cSmrgthe above copyright notice appear in all copies and that both that
8145b7b3cSmrgcopyright notice and this permission notice appear in supporting
9145b7b3cSmrgdocumentation.
10145b7b3cSmrg
11145b7b3cSmrgThe above copyright notice and this permission notice shall be included
12145b7b3cSmrgin all copies or substantial portions of the Software.
13145b7b3cSmrg
14145b7b3cSmrgTHE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
15145b7b3cSmrgOR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16145b7b3cSmrgMERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
17145b7b3cSmrgIN NO EVENT SHALL THE OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR
18145b7b3cSmrgOTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
19145b7b3cSmrgARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
20145b7b3cSmrgOTHER DEALINGS IN THE SOFTWARE.
21145b7b3cSmrg
22145b7b3cSmrgExcept as contained in this notice, the name of The Open Group shall
23145b7b3cSmrgnot be used in advertising or otherwise to promote the sale, use or
24145b7b3cSmrgother dealings in this Software without prior written authorization
25145b7b3cSmrgfrom The Open Group.
26145b7b3cSmrg
27145b7b3cSmrg*/
2843b4c634Smrg/*
29c06e8ac6Smrg * Copyright (c) 2006, Oracle and/or its affiliates.
30145b7b3cSmrg *
31145b7b3cSmrg * Permission is hereby granted, free of charge, to any person obtaining a
3243b4c634Smrg * copy of this software and associated documentation files (the "Software"),
3343b4c634Smrg * to deal in the Software without restriction, including without limitation
3443b4c634Smrg * the rights to use, copy, modify, merge, publish, distribute, sublicense,
3543b4c634Smrg * and/or sell copies of the Software, and to permit persons to whom the
3643b4c634Smrg * Software is furnished to do so, subject to the following conditions:
37145b7b3cSmrg *
3843b4c634Smrg * The above copyright notice and this permission notice (including the next
3943b4c634Smrg * paragraph) shall be included in all copies or substantial portions of the
4043b4c634Smrg * Software.
41145b7b3cSmrg *
4243b4c634Smrg * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
4343b4c634Smrg * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
4443b4c634Smrg * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
4543b4c634Smrg * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
4643b4c634Smrg * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
4743b4c634Smrg * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
4843b4c634Smrg * DEALINGS IN THE SOFTWARE.
49145b7b3cSmrg */
50145b7b3cSmrg
51145b7b3cSmrg
52145b7b3cSmrg/*
53145b7b3cSmrg * xdm - display manager daemon
54145b7b3cSmrg * Author:  Keith Packard, X Consortium
55145b7b3cSmrg *
56145b7b3cSmrg * widget to get username/password
57145b7b3cSmrg *
58145b7b3cSmrg */
59145b7b3cSmrg
60145b7b3cSmrg#ifdef HAVE_CONFIG_H
61145b7b3cSmrg# include "config.h"
62145b7b3cSmrg#endif
63145b7b3cSmrg
64145b7b3cSmrg#include <X11/Intrinsic.h>
65145b7b3cSmrg#include <X11/StringDefs.h>
66145b7b3cSmrg#include <X11/Shell.h>
67145b7b3cSmrg#include <X11/XKBlib.h>
68145b7b3cSmrg
69145b7b3cSmrg#ifdef USE_XINERAMA
7039826d68Smrg# include <X11/extensions/Xinerama.h>
71145b7b3cSmrg#endif
72145b7b3cSmrg
73145b7b3cSmrg#include "dm.h"
74145b7b3cSmrg#include "dm_error.h"
75145b7b3cSmrg#include "greet.h"
7643b4c634Smrg#include "LoginP.h"
77145b7b3cSmrg
7839826d68Smrg#if defined(HAVE_OPENLOG) && defined(HAVE_SYSLOG_H)
7939826d68Smrg# define USE_SYSLOG
8039826d68Smrg# include <syslog.h>
8139826d68Smrg# ifndef LOG_AUTHPRIV
8239826d68Smrg#  define LOG_AUTHPRIV LOG_AUTH
8339826d68Smrg# endif
8439826d68Smrg# ifndef LOG_PID
8539826d68Smrg#  define LOG_PID 0
8639826d68Smrg# endif
87145b7b3cSmrg#endif
88145b7b3cSmrg
8939826d68Smrg#include <string.h>
9039826d68Smrg
91145b7b3cSmrg#if defined(SECURE_RPC) && defined(sun)
92145b7b3cSmrg/* Go figure, there's no getdomainname() prototype available */
93145b7b3cSmrgextern int getdomainname(char *name, size_t len);
94145b7b3cSmrg#endif
95145b7b3cSmrg
96145b7b3cSmrg/*
97145b7b3cSmrg * Function pointers filled in by the initial call ito the library
98145b7b3cSmrg */
99145b7b3cSmrg
100145b7b3cSmrgint     (*__xdm_PingServer)(struct display *d, Display *alternateDpy) = NULL;
101145b7b3cSmrgvoid    (*__xdm_SessionPingFailed)(struct display *d) = NULL;
10239826d68Smrgvoid    (*__xdm_Debug)(const char * fmt, ...) = NULL;
103145b7b3cSmrgvoid    (*__xdm_RegisterCloseOnFork)(int fd) = NULL;
104145b7b3cSmrgvoid    (*__xdm_SecureDisplay)(struct display *d, Display *dpy) = NULL;
105145b7b3cSmrgvoid    (*__xdm_UnsecureDisplay)(struct display *d, Display *dpy) = NULL;
106145b7b3cSmrgvoid    (*__xdm_ClearCloseOnFork)(int fd) = NULL;
107145b7b3cSmrgvoid    (*__xdm_SetupDisplay)(struct display *d) = NULL;
10839826d68Smrgvoid    (*__xdm_LogError)(const char * fmt, ...) = NULL;
109145b7b3cSmrgvoid    (*__xdm_SessionExit)(struct display *d, int status, int removeAuth) = NULL;
110145b7b3cSmrgvoid    (*__xdm_DeleteXloginResources)(struct display *d, Display *dpy) = NULL;
111145b7b3cSmrgint     (*__xdm_source)(char **environ, char *file) = NULL;
112145b7b3cSmrgchar    **(*__xdm_defaultEnv)(void) = NULL;
113dfe7682dSmrgchar    **(*__xdm_setEnv)(char **e, const char *name, const char *value) = NULL;
114145b7b3cSmrgchar    **(*__xdm_putEnv)(const char *string, char **env) = NULL;
115dfe7682dSmrgchar    **(*__xdm_parseArgs)(char **argv, const char *string) = NULL;
116145b7b3cSmrgvoid    (*__xdm_printEnv)(char **e) = NULL;
117145b7b3cSmrgchar    **(*__xdm_systemEnv)(struct display *d, char *user, char *home) = NULL;
11839826d68Smrgvoid    (*__xdm_LogOutOfMem)(const char * fmt, ...) = NULL;
119145b7b3cSmrgvoid    (*__xdm_setgrent)(void) = NULL;
120145b7b3cSmrgstruct group    *(*__xdm_getgrent)(void) = NULL;
121145b7b3cSmrgvoid    (*__xdm_endgrent)(void) = NULL;
122c9f4ce72Smrg# ifdef HAVE_GETSPNAM
123145b7b3cSmrgstruct spwd   *(*__xdm_getspnam)(GETSPNAM_ARGS) = NULL;
124145b7b3cSmrgvoid   (*__xdm_endspent)(void) = NULL;
12539826d68Smrg# endif
126145b7b3cSmrgstruct passwd   *(*__xdm_getpwnam)(GETPWNAM_ARGS) = NULL;
12739826d68Smrg# if defined(linux) || defined(__GLIBC__)
128145b7b3cSmrgvoid   (*__xdm_endpwent)(void) = NULL;
12939826d68Smrg# endif
130145b7b3cSmrgchar     *(*__xdm_crypt)(CRYPT_ARGS) = NULL;
13139826d68Smrg# ifdef USE_PAM
132145b7b3cSmrgpam_handle_t **(*__xdm_thepamhp)(void) = NULL;
13339826d68Smrg# endif
134145b7b3cSmrg
135145b7b3cSmrg#ifdef SECURE_RPC
13639826d68Smrg# include <rpc/rpc.h>
13739826d68Smrg# include <rpc/key_prot.h>
138145b7b3cSmrg#endif
139145b7b3cSmrg
140145b7b3cSmrg#ifdef K5AUTH
14139826d68Smrg# include <krb5/krb5.h>
142145b7b3cSmrg#endif
143145b7b3cSmrg
144145b7b3cSmrgstatic int	done, code;
145145b7b3cSmrg#ifndef USE_PAM
146145b7b3cSmrgstatic char	name[NAME_LEN], password[PASSWORD_LEN];
147145b7b3cSmrg#endif
148145b7b3cSmrgstatic Widget		toplevel;
149145b7b3cSmrgstatic Widget		login;
150145b7b3cSmrgstatic XtAppContext	context;
151145b7b3cSmrgstatic XtIntervalId	pingTimeout;
152145b7b3cSmrg
153145b7b3cSmrg#ifdef USE_PAM
154c06e8ac6Smrg
155c06e8ac6Smrg#ifdef __sun
156c06e8ac6Smrg/* Solaris does not const qualify arguments to pam_get_item() or the
157c06e8ac6Smrg   PAM conversation function that Linux-PAM and others do. */
158c06e8ac6Smrg# define XDM_PAM_QUAL /**/
159c06e8ac6Smrg#else
160c06e8ac6Smrg# define XDM_PAM_QUAL const
161c06e8ac6Smrg#endif
162c06e8ac6Smrg
163c06e8ac6Smrgstatic int pamconv(int num_msg, XDM_PAM_QUAL struct pam_message **msg,
164145b7b3cSmrg		   struct pam_response **response, void *appdata_ptr);
165145b7b3cSmrg
166145b7b3cSmrg# define PAM_ERROR_PRINT(pamfunc, pamh)	\
167145b7b3cSmrg	LogError("%s failure: %s\n", pamfunc, pam_strerror(pamh, pam_error))
168145b7b3cSmrg
169145b7b3cSmrg
170145b7b3cSmrgstruct myconv_data {
171145b7b3cSmrg    struct display *d;
172145b7b3cSmrg    struct greet_info *greet;
173145b7b3cSmrg    char *username_display;
174145b7b3cSmrg};
175145b7b3cSmrg#endif
176145b7b3cSmrg
177145b7b3cSmrg
178145b7b3cSmrg/*ARGSUSED*/
179145b7b3cSmrgstatic void
180145b7b3cSmrgGreetPingServer (
181145b7b3cSmrg    XtPointer	    closure,
182145b7b3cSmrg    XtIntervalId    *intervalId)
183145b7b3cSmrg{
184145b7b3cSmrg    struct display *d;
185145b7b3cSmrg
186145b7b3cSmrg    d = (struct display *) closure;
187145b7b3cSmrg    if (!PingServer (d, XtDisplay (toplevel)))
188145b7b3cSmrg	SessionPingFailed (d);
189145b7b3cSmrg    pingTimeout = XtAppAddTimeOut (context, d->pingInterval * 60 * 1000,
190145b7b3cSmrg				   GreetPingServer, (closure));
191145b7b3cSmrg}
192145b7b3cSmrg
193145b7b3cSmrg/*ARGSUSED*/
194145b7b3cSmrgstatic void
195145b7b3cSmrgGreetDone (
196145b7b3cSmrg    Widget	w,
197145b7b3cSmrg    LoginData	*data,
198145b7b3cSmrg    int		status)
199145b7b3cSmrg{
200e13a667bSwiz    Debug ("GreetDone: %s, (password is %zu long)\n",
201145b7b3cSmrg	    data->name, strlen (data->passwd));
202145b7b3cSmrg    switch (status) {
203145b7b3cSmrg    case NOTIFY_OK:
204145b7b3cSmrg#ifndef USE_PAM
205145b7b3cSmrg	strncpy (name, data->name, sizeof(name));
206145b7b3cSmrg	name[sizeof(name)-1] = '\0';
207145b7b3cSmrg	strncpy (password, data->passwd, sizeof(password));
208145b7b3cSmrg	password[sizeof(password)-1] = '\0';
209145b7b3cSmrg#endif
210145b7b3cSmrg	code = 0;
211145b7b3cSmrg	done = 1;
212145b7b3cSmrg	break;
213145b7b3cSmrg    case NOTIFY_ABORT:
214145b7b3cSmrg	Debug ("RESERVER_DISPLAY\n");
215145b7b3cSmrg	code = RESERVER_DISPLAY;
216145b7b3cSmrg	done = 1;
217145b7b3cSmrg	break;
218145b7b3cSmrg    case NOTIFY_RESTART:
219145b7b3cSmrg	Debug ("REMANAGE_DISPLAY\n");
220145b7b3cSmrg	code = REMANAGE_DISPLAY;
221145b7b3cSmrg	done = 1;
222145b7b3cSmrg	break;
223145b7b3cSmrg    case NOTIFY_ABORT_DISPLAY:
224145b7b3cSmrg	Debug ("UNMANAGE_DISPLAY\n");
225145b7b3cSmrg	code = UNMANAGE_DISPLAY;
226145b7b3cSmrg	done = 1;
227145b7b3cSmrg	break;
228145b7b3cSmrg    }
229145b7b3cSmrg#ifndef USE_PAM
230145b7b3cSmrg    if (done) {
231145b7b3cSmrg	bzero (data->name, NAME_LEN);
232145b7b3cSmrg	bzero (data->passwd, PASSWORD_LEN);
233145b7b3cSmrg    }
234145b7b3cSmrg#endif
235145b7b3cSmrg}
236145b7b3cSmrg
237145b7b3cSmrgstatic Display *
238145b7b3cSmrgInitGreet (struct display *d)
239145b7b3cSmrg{
240145b7b3cSmrg    Arg		arglist[10];
241145b7b3cSmrg    int		i;
242145b7b3cSmrg    static int	argc;
243145b7b3cSmrg    Screen		*scrn;
244145b7b3cSmrg    static char	*argv[] = { "xlogin", NULL };
245145b7b3cSmrg    Display		*dpy;
246145b7b3cSmrg#ifdef USE_XINERAMA
247145b7b3cSmrg    XineramaScreenInfo *screens;
248145b7b3cSmrg    int                 s_num;
249145b7b3cSmrg#endif
250145b7b3cSmrg
251145b7b3cSmrg    Debug ("greet %s\n", d->name);
252145b7b3cSmrg    argc = 1;
253145b7b3cSmrg    XtToolkitInitialize ();
254145b7b3cSmrg    context = XtCreateApplicationContext();
255145b7b3cSmrg    dpy = XtOpenDisplay (context, d->name, "xlogin", "Xlogin", NULL, 0,
256145b7b3cSmrg			 &argc, argv);
257145b7b3cSmrg
258145b7b3cSmrg    if (!dpy)
259145b7b3cSmrg	return NULL;
260145b7b3cSmrg
261145b7b3cSmrg#ifdef XKB
262145b7b3cSmrg    {
263145b7b3cSmrg    int opcode, evbase, errbase, majret, minret;
264145b7b3cSmrg    unsigned int value = XkbPCF_GrabsUseXKBStateMask;
265145b7b3cSmrg    if (XkbQueryExtension (dpy, &opcode, &evbase, &errbase, &majret, &minret)) {
266145b7b3cSmrg	if (!XkbSetPerClientControls (dpy, XkbPCF_GrabsUseXKBStateMask, &value))
267145b7b3cSmrg	    LogError ("%s\n", "SetPerClientControls failed");
268145b7b3cSmrg    }
269145b7b3cSmrg    }
270145b7b3cSmrg#endif
271145b7b3cSmrg    RegisterCloseOnFork (ConnectionNumber (dpy));
272145b7b3cSmrg
273145b7b3cSmrg    SecureDisplay (d, dpy);
274145b7b3cSmrg
275145b7b3cSmrg    i = 0;
276145b7b3cSmrg    scrn = XDefaultScreenOfDisplay(dpy);
277145b7b3cSmrg    XtSetArg(arglist[i], XtNscreen, scrn);	i++;
278145b7b3cSmrg    XtSetArg(arglist[i], XtNargc, argc);	i++;
279145b7b3cSmrg    XtSetArg(arglist[i], XtNargv, argv);	i++;
280145b7b3cSmrg
281145b7b3cSmrg    toplevel = XtAppCreateShell ((String) NULL, "Xlogin",
282145b7b3cSmrg		    applicationShellWidgetClass, dpy, arglist, i);
283145b7b3cSmrg
284145b7b3cSmrg    i = 0;
285145b7b3cSmrg    XtSetArg (arglist[i], XtNnotifyDone, (XtPointer)GreetDone); i++;
286145b7b3cSmrg    if (!d->authorize || d->authorizations || !d->authComplain)
287145b7b3cSmrg    {
288145b7b3cSmrg	XtSetArg (arglist[i], XtNsecureSession, True); i++;
289145b7b3cSmrg    }
290145b7b3cSmrg    login = XtCreateManagedWidget ("login", loginWidgetClass, toplevel,
291145b7b3cSmrg				    arglist, i);
292145b7b3cSmrg    XtRealizeWidget (toplevel);
293145b7b3cSmrg
294145b7b3cSmrg#ifdef USE_XINERAMA
295145b7b3cSmrg    if (
296145b7b3cSmrg	XineramaIsActive(dpy) &&
297145b7b3cSmrg	(screens = XineramaQueryScreens(dpy, &s_num)) != NULL
298145b7b3cSmrg       )
299145b7b3cSmrg    {
300145b7b3cSmrg	XWarpPointer(dpy, None, XRootWindowOfScreen (scrn),
301145b7b3cSmrg			0, 0, 0, 0,
302145b7b3cSmrg			screens[0].x_org + screens[0].width / 2,
303145b7b3cSmrg			screens[0].y_org + screens[0].height / 2);
304145b7b3cSmrg
305145b7b3cSmrg	XFree(screens);
306145b7b3cSmrg    }
307145b7b3cSmrg    else
308145b7b3cSmrg#endif
309145b7b3cSmrg    XWarpPointer(dpy, None, XRootWindowOfScreen (scrn),
310145b7b3cSmrg		    0, 0, 0, 0,
311145b7b3cSmrg		    XWidthOfScreen(scrn) / 2,
312145b7b3cSmrg		    XHeightOfScreen(scrn) / 2);
313145b7b3cSmrg
314145b7b3cSmrg    if (d->pingInterval)
315145b7b3cSmrg    {
316c9f4ce72Smrg	pingTimeout = XtAppAddTimeOut (context, d->pingInterval * 60 * 1000,
317145b7b3cSmrg				       GreetPingServer, (XtPointer) d);
318145b7b3cSmrg    }
319145b7b3cSmrg    return dpy;
320145b7b3cSmrg}
321145b7b3cSmrg
322145b7b3cSmrgstatic void
323145b7b3cSmrgCloseGreet (struct display *d)
324145b7b3cSmrg{
325145b7b3cSmrg    Boolean	    allow;
326145b7b3cSmrg    Arg	    arglist[1];
327145b7b3cSmrg    Display *dpy = XtDisplay(toplevel);
328145b7b3cSmrg
329145b7b3cSmrg    if (pingTimeout)
330145b7b3cSmrg    {
331145b7b3cSmrg	XtRemoveTimeOut (pingTimeout);
332145b7b3cSmrg	pingTimeout = 0;
333145b7b3cSmrg    }
334145b7b3cSmrg    UnsecureDisplay (d, dpy);
335145b7b3cSmrg    XtSetArg (arglist[0], XtNallowAccess, (char *) &allow);
336145b7b3cSmrg    XtGetValues (login, arglist, 1);
337145b7b3cSmrg    if (allow)
338145b7b3cSmrg    {
339145b7b3cSmrg	Debug ("Disabling access control\n");
340145b7b3cSmrg	XSetAccessControl (dpy, DisableAccess);
341145b7b3cSmrg    }
342145b7b3cSmrg    XtDestroyWidget (toplevel);
34343b4c634Smrg    toplevel = NULL;
34443b4c634Smrg    login = NULL; /* child of toplevel, which we just destroyed */
345145b7b3cSmrg    ClearCloseOnFork (XConnectionNumber (dpy));
346145b7b3cSmrg    XCloseDisplay (dpy);
347145b7b3cSmrg    Debug ("Greet connection closed\n");
348145b7b3cSmrg}
349145b7b3cSmrg
350145b7b3cSmrg#define WHITESPACE 0
351145b7b3cSmrg#define ARGUMENT 1
352145b7b3cSmrg
353145b7b3cSmrgstatic int
354145b7b3cSmrgGreet (struct display *d, struct greet_info *greet)
355145b7b3cSmrg{
356145b7b3cSmrg    XEvent		event;
357c9f4ce72Smrg    Arg		arglist[1];
358145b7b3cSmrg
359145b7b3cSmrg    XtSetArg (arglist[0], XtNallowAccess, False);
360145b7b3cSmrg    XtSetValues (login, arglist, 1);
361145b7b3cSmrg
362145b7b3cSmrg    Debug ("dispatching %s\n", d->name);
363145b7b3cSmrg    done = 0;
364145b7b3cSmrg    while (!done) {
365145b7b3cSmrg	XtAppNextEvent (context, &event);
366145b7b3cSmrg	switch (event.type) {
367145b7b3cSmrg	case MappingNotify:
368145b7b3cSmrg	    XRefreshKeyboardMapping(&event.xmapping);
369145b7b3cSmrg	    break;
370145b7b3cSmrg	default:
371145b7b3cSmrg	    XtDispatchEvent (&event);
372145b7b3cSmrg	    break;
373145b7b3cSmrg	}
374145b7b3cSmrg    }
375145b7b3cSmrg    XFlush (XtDisplay (toplevel));
376145b7b3cSmrg    Debug ("Done dispatch %s\n", d->name);
377145b7b3cSmrg    if (code == 0)
378145b7b3cSmrg    {
379145b7b3cSmrg#ifndef USE_PAM
380145b7b3cSmrg	char *ptr;
381145b7b3cSmrg	unsigned int c,state = WHITESPACE;
38239826d68Smrg
383145b7b3cSmrg	/*
384145b7b3cSmrg	 * Process the name string to get rid of white spaces.
385145b7b3cSmrg	 */
386145b7b3cSmrg	for (ptr = name; state == WHITESPACE; ptr++)
387145b7b3cSmrg	{
388145b7b3cSmrg	    c = (unsigned int)(*ptr);
389145b7b3cSmrg	    if (c == ' ')
390145b7b3cSmrg		continue;
391145b7b3cSmrg
392145b7b3cSmrg	    state = ARGUMENT;
393145b7b3cSmrg	    break;
394145b7b3cSmrg	}
395145b7b3cSmrg
396145b7b3cSmrg	greet->name = ptr;
397145b7b3cSmrg	greet->password = password;
398145b7b3cSmrg#endif  /* USE_PAM */
399145b7b3cSmrg	XtSetArg (arglist[0], XtNsessionArgument, (char *) &(greet->string));
400c9f4ce72Smrg	XtGetValues (login, arglist, 1);
401145b7b3cSmrg	Debug ("sessionArgument: %s\n", greet->string ? greet->string : "<NULL>");
402145b7b3cSmrg    }
403145b7b3cSmrg    return code;
404145b7b3cSmrg}
405145b7b3cSmrg
406145b7b3cSmrg
407145b7b3cSmrgstatic void
408c9f4ce72SmrgFailedLogin (struct display *d, const char *username)
409145b7b3cSmrg{
41039826d68Smrg#ifdef USE_SYSLOG
41143b4c634Smrg    if (username == NULL)
41243b4c634Smrg	username = "username unavailable";
41343b4c634Smrg
414145b7b3cSmrg    syslog(LOG_AUTHPRIV|LOG_NOTICE,
415145b7b3cSmrg	   "LOGIN FAILURE ON %s, %s",
41643b4c634Smrg	   d->name, username);
417145b7b3cSmrg#endif
418145b7b3cSmrg    DrawFail (login);
419145b7b3cSmrg}
420145b7b3cSmrg
421145b7b3cSmrg_X_EXPORT
422145b7b3cSmrggreet_user_rtn GreetUser(
423145b7b3cSmrg    struct display          *d,
424145b7b3cSmrg    Display                 ** dpy,
425145b7b3cSmrg    struct verify_info      *verify,
426145b7b3cSmrg    struct greet_info       *greet,
427145b7b3cSmrg    struct dlfuncs        *dlfuncs)
428145b7b3cSmrg{
429145b7b3cSmrg    int i;
430c9f4ce72Smrg    Arg		arglist[2];
431145b7b3cSmrg
432145b7b3cSmrg/*
433145b7b3cSmrg * These must be set before they are used.
434145b7b3cSmrg */
435145b7b3cSmrg    __xdm_PingServer = dlfuncs->_PingServer;
436145b7b3cSmrg    __xdm_SessionPingFailed = dlfuncs->_SessionPingFailed;
437145b7b3cSmrg    __xdm_Debug = dlfuncs->_Debug;
438145b7b3cSmrg    __xdm_RegisterCloseOnFork = dlfuncs->_RegisterCloseOnFork;
439145b7b3cSmrg    __xdm_SecureDisplay = dlfuncs->_SecureDisplay;
440145b7b3cSmrg    __xdm_UnsecureDisplay = dlfuncs->_UnsecureDisplay;
441145b7b3cSmrg    __xdm_ClearCloseOnFork = dlfuncs->_ClearCloseOnFork;
442145b7b3cSmrg    __xdm_SetupDisplay = dlfuncs->_SetupDisplay;
443145b7b3cSmrg    __xdm_LogError = dlfuncs->_LogError;
444145b7b3cSmrg    __xdm_SessionExit = dlfuncs->_SessionExit;
445145b7b3cSmrg    __xdm_DeleteXloginResources = dlfuncs->_DeleteXloginResources;
446145b7b3cSmrg    __xdm_source = dlfuncs->_source;
447145b7b3cSmrg    __xdm_defaultEnv = dlfuncs->_defaultEnv;
448145b7b3cSmrg    __xdm_setEnv = dlfuncs->_setEnv;
449145b7b3cSmrg    __xdm_putEnv = dlfuncs->_putEnv;
450145b7b3cSmrg    __xdm_parseArgs = dlfuncs->_parseArgs;
451145b7b3cSmrg    __xdm_printEnv = dlfuncs->_printEnv;
452145b7b3cSmrg    __xdm_systemEnv = dlfuncs->_systemEnv;
453145b7b3cSmrg    __xdm_LogOutOfMem = dlfuncs->_LogOutOfMem;
454145b7b3cSmrg    __xdm_setgrent = dlfuncs->_setgrent;
455145b7b3cSmrg    __xdm_getgrent = dlfuncs->_getgrent;
456145b7b3cSmrg    __xdm_endgrent = dlfuncs->_endgrent;
457c9f4ce72Smrg# ifdef HAVE_GETSPNAM
458145b7b3cSmrg    __xdm_getspnam = dlfuncs->_getspnam;
459145b7b3cSmrg    __xdm_endspent = dlfuncs->_endspent;
46039826d68Smrg# endif
461145b7b3cSmrg    __xdm_getpwnam = dlfuncs->_getpwnam;
46239826d68Smrg# if defined(linux) || defined(__GLIBC__)
463145b7b3cSmrg    __xdm_endpwent = dlfuncs->_endpwent;
46439826d68Smrg# endif
465145b7b3cSmrg    __xdm_crypt = dlfuncs->_crypt;
46639826d68Smrg# ifdef USE_PAM
467145b7b3cSmrg    __xdm_thepamhp = dlfuncs->_thepamhp;
46839826d68Smrg# endif
469145b7b3cSmrg
470145b7b3cSmrg    *dpy = InitGreet (d);
471145b7b3cSmrg    /*
472145b7b3cSmrg     * Run the setup script - note this usually will not work when
473145b7b3cSmrg     * the server is grabbed, so we don't even bother trying.
474145b7b3cSmrg     */
475145b7b3cSmrg    if (!d->grabServer)
476145b7b3cSmrg	SetupDisplay (d);
477145b7b3cSmrg    if (!*dpy) {
478145b7b3cSmrg	LogError ("Cannot reopen display %s for greet window\n", d->name);
479145b7b3cSmrg	exit (RESERVER_DISPLAY);
480145b7b3cSmrg    }
481145b7b3cSmrg
482c9f4ce72Smrg    XtSetArg (arglist[0], XtNallowNullPasswd,
483c9f4ce72Smrg	      (char *) &(greet->allow_null_passwd));
484c9f4ce72Smrg    XtSetArg (arglist[1], XtNallowRootLogin,
485c9f4ce72Smrg	      (char *) &(greet->allow_root_login));
486c9f4ce72Smrg    XtGetValues (login, arglist, 2);
487c9f4ce72Smrg
488145b7b3cSmrg    for (;;) {
489145b7b3cSmrg#ifdef USE_PAM
490145b7b3cSmrg
491145b7b3cSmrg	/* Run PAM conversation */
492145b7b3cSmrg	pam_handle_t 	**pamhp		= thepamhp();
493145b7b3cSmrg	int		  pam_error;
494145b7b3cSmrg	unsigned int	  pam_flags 	= 0;
495145b7b3cSmrg	struct myconv_data pcd		= { d, greet, NULL };
496145b7b3cSmrg	struct pam_conv   pc 		= { pamconv, &pcd };
497145b7b3cSmrg	const char *	  pam_fname;
498145b7b3cSmrg	const char *	  login_prompt;
499145b7b3cSmrg
500145b7b3cSmrg
50143b4c634Smrg	SetPrompt(login, LOGIN_PROMPT_USERNAME, NULL, LOGIN_PROMPT_NOT_SHOWN, False);
502145b7b3cSmrg	login_prompt  = GetPrompt(login, LOGIN_PROMPT_USERNAME);
50343b4c634Smrg	SetPrompt(login, LOGIN_PROMPT_PASSWORD, NULL, LOGIN_PROMPT_NOT_SHOWN, False);
50439826d68Smrg
50539826d68Smrg# define RUN_AND_CHECK_PAM_ERROR(function, args)			\
506145b7b3cSmrg	    do { 						\
507145b7b3cSmrg		pam_error = function args;			\
508145b7b3cSmrg		if (pam_error != PAM_SUCCESS) {			\
509145b7b3cSmrg		    PAM_ERROR_PRINT(#function, *pamhp);		\
510145b7b3cSmrg		    goto pam_done;				\
511145b7b3cSmrg		} 						\
51239826d68Smrg	    } while (0)
51339826d68Smrg
514145b7b3cSmrg
515145b7b3cSmrg	RUN_AND_CHECK_PAM_ERROR(pam_start,
516145b7b3cSmrg				("xdm", NULL, &pc, pamhp));
517145b7b3cSmrg
518145b7b3cSmrg	/* Set default login prompt to xdm's default from Xresources */
519145b7b3cSmrg	if (login_prompt != NULL) {
520145b7b3cSmrg	    RUN_AND_CHECK_PAM_ERROR(pam_set_item,
521145b7b3cSmrg				    (*pamhp, PAM_USER_PROMPT, login_prompt));
522145b7b3cSmrg	}
523145b7b3cSmrg
524145b7b3cSmrg	if (d->name[0] != ':') {	/* Displaying to remote host */
525145b7b3cSmrg	    char *hostname = strdup(d->name);
526145b7b3cSmrg
527145b7b3cSmrg	    if (hostname == NULL) {
528145b7b3cSmrg		LogOutOfMem("GreetUser");
529145b7b3cSmrg	    } else {
530145b7b3cSmrg		char *colon = strrchr(hostname, ':');
53139826d68Smrg
532145b7b3cSmrg		if (colon != NULL)
533145b7b3cSmrg		    *colon = '\0';
53439826d68Smrg
535145b7b3cSmrg		RUN_AND_CHECK_PAM_ERROR(pam_set_item,
536145b7b3cSmrg					(*pamhp, PAM_RHOST, hostname));
537145b7b3cSmrg		free(hostname);
538145b7b3cSmrg	    }
539c9f4ce72Smrg	} else {			/* Displaying on local host */
540c9f4ce72Smrg	    const char *ttyname = NULL;
541c9f4ce72Smrg
542c9f4ce72Smrg#ifdef __sun
543c9f4ce72Smrg	    /* Solaris PAM & auditing insist this is a device file that can
544c9f4ce72Smrg	       be found under /dev, so we can't use the display name */
545c9f4ce72Smrg	    char vtpath[16];
546c9f4ce72Smrg
547c9f4ce72Smrg	    if ((d->windowPath) && !(strchr(d->windowPath, ':'))) {
548c9f4ce72Smrg		/* if path is simply a VT, with no intermediaries, use it */
549c9f4ce72Smrg		snprintf(vtpath, sizeof(vtpath), "/dev/vt/%s", d->windowPath);
550c9f4ce72Smrg		ttyname = vtpath;
551c9f4ce72Smrg	    }
552c9f4ce72Smrg#else
553c9f4ce72Smrg	    /* On all other OS'es we just pass the display name for PAM_TTY */
554c9f4ce72Smrg	    ttyname = d->name;
555c9f4ce72Smrg#endif
556c9f4ce72Smrg	    RUN_AND_CHECK_PAM_ERROR(pam_set_item, (*pamhp, PAM_TTY, ttyname));
557c9f4ce72Smrg	}
55839826d68Smrg
559145b7b3cSmrg	if (!greet->allow_null_passwd) {
560145b7b3cSmrg	    pam_flags |= PAM_DISALLOW_NULL_AUTHTOK;
561145b7b3cSmrg	}
562145b7b3cSmrg	RUN_AND_CHECK_PAM_ERROR(pam_authenticate,
563145b7b3cSmrg				(*pamhp, pam_flags));
56439826d68Smrg
565145b7b3cSmrg	/* handle expired passwords */
566145b7b3cSmrg	pam_error = pam_acct_mgmt(*pamhp, pam_flags);
567145b7b3cSmrg	pam_fname = "pam_acct_mgmt";
568145b7b3cSmrg	if (pam_error == PAM_NEW_AUTHTOK_REQD) {
569145b7b3cSmrg	    ShowChangePasswdMessage(login);
570145b7b3cSmrg	    do {
571145b7b3cSmrg		pam_error = pam_chauthtok(*pamhp, PAM_CHANGE_EXPIRED_AUTHTOK);
572145b7b3cSmrg	    } while ((pam_error == PAM_AUTHTOK_ERR) ||
573145b7b3cSmrg		     (pam_error == PAM_TRY_AGAIN));
574145b7b3cSmrg	    pam_fname = "pam_chauthtok";
575145b7b3cSmrg	}
576145b7b3cSmrg	if (pam_error != PAM_SUCCESS) {
577145b7b3cSmrg	    PAM_ERROR_PRINT(pam_fname, *pamhp);
578145b7b3cSmrg	    goto pam_done;
579145b7b3cSmrg	}
58039826d68Smrg
581145b7b3cSmrg	RUN_AND_CHECK_PAM_ERROR(pam_setcred,
582145b7b3cSmrg				(*pamhp, 0));
583c9f4ce72Smrg	{
584c9f4ce72Smrg	    char *username	= NULL;
585c9f4ce72Smrg
586c9f4ce72Smrg	    RUN_AND_CHECK_PAM_ERROR(pam_get_item,
587c06e8ac6Smrg                                    (*pamhp, PAM_USER,
588c06e8ac6Smrg                                     (XDM_PAM_QUAL void **) &username));
589c9f4ce72Smrg	    if (username != NULL) {
590c9f4ce72Smrg		Debug("PAM_USER: %s\n", username);
591c9f4ce72Smrg		greet->name = username;
592c9f4ce72Smrg		greet->password = NULL;
593c9f4ce72Smrg	    }
594145b7b3cSmrg	}
59539826d68Smrg
596145b7b3cSmrg      pam_done:
597145b7b3cSmrg	if (code != 0)
598145b7b3cSmrg	{
599145b7b3cSmrg	    CloseGreet (d);
600145b7b3cSmrg	    SessionExit (d, code, FALSE);
601145b7b3cSmrg	}
602145b7b3cSmrg	if ((pam_error == PAM_SUCCESS) && (Verify (d, greet, verify))) {
603145b7b3cSmrg	    SetPrompt (login, 1, "Login Successful", LOGIN_TEXT_INFO, False);
604145b7b3cSmrg	    SetValue (login, 1, NULL);
605145b7b3cSmrg	    break;
606145b7b3cSmrg	} else {
60739826d68Smrg	    /* Try to fill in username for failed login error log */
608c06e8ac6Smrg	    XDM_PAM_QUAL char *username = greet->name;
609c9f4ce72Smrg
610c9f4ce72Smrg	    if (username == NULL) {
611c9f4ce72Smrg		RUN_AND_CHECK_PAM_ERROR(pam_get_item,
612c9f4ce72Smrg					(*pamhp, PAM_USER,
613c06e8ac6Smrg					 (XDM_PAM_QUAL void **) &username));
61439826d68Smrg	    }
615c9f4ce72Smrg	    FailedLogin (d, username);
616145b7b3cSmrg	    RUN_AND_CHECK_PAM_ERROR(pam_end,
617145b7b3cSmrg				    (*pamhp, pam_error));
618145b7b3cSmrg	}
619145b7b3cSmrg#else /* not PAM */
620145b7b3cSmrg	/*
621145b7b3cSmrg	 * Greet user, requesting name/password
622145b7b3cSmrg	 */
623145b7b3cSmrg	code = Greet (d, greet);
624145b7b3cSmrg	if (code != 0)
625145b7b3cSmrg	{
626145b7b3cSmrg	    CloseGreet (d);
627145b7b3cSmrg	    SessionExit (d, code, FALSE);
628145b7b3cSmrg	}
629145b7b3cSmrg	/*
630145b7b3cSmrg	 * Verify user
631145b7b3cSmrg	 */
632145b7b3cSmrg	if (Verify (d, greet, verify))
633145b7b3cSmrg	    break;
634145b7b3cSmrg	else
635c9f4ce72Smrg	{
636c9f4ce72Smrg	    FailedLogin (d, greet->name);
637c9f4ce72Smrg	    bzero (greet->name, strlen(greet->name));
638c9f4ce72Smrg	    bzero (greet->password, strlen(greet->password));
639c9f4ce72Smrg	}
640145b7b3cSmrg#endif
641145b7b3cSmrg    }
642145b7b3cSmrg    DeleteXloginResources (d, *dpy);
643145b7b3cSmrg    CloseGreet (d);
644145b7b3cSmrg    Debug ("Greet loop finished\n");
645145b7b3cSmrg    /*
646145b7b3cSmrg     * Run system-wide initialization file
647145b7b3cSmrg     */
648145b7b3cSmrg    if (source (verify->systemEnviron, d->startup) != 0)
649145b7b3cSmrg    {
650145b7b3cSmrg	Debug ("Startup program %s exited with non-zero status\n",
651145b7b3cSmrg		d->startup);
652145b7b3cSmrg	SessionExit (d, OBEYSESS_DISPLAY, FALSE);
653145b7b3cSmrg    }
654145b7b3cSmrg    /*
655145b7b3cSmrg     * for user-based authorization schemes,
656145b7b3cSmrg     * add the user to the server's allowed "hosts" list.
657145b7b3cSmrg     */
658145b7b3cSmrg    for (i = 0; i < d->authNum; i++)
659145b7b3cSmrg    {
660145b7b3cSmrg#ifdef SECURE_RPC
661145b7b3cSmrg	if (d->authorizations[i]->name_length == 9 &&
662145b7b3cSmrg	    memcmp(d->authorizations[i]->name, "SUN-DES-1", 9) == 0)
663145b7b3cSmrg	{
664145b7b3cSmrg	    XHostAddress	addr;
665145b7b3cSmrg	    char		netname[MAXNETNAMELEN+1];
666145b7b3cSmrg	    char		domainname[MAXNETNAMELEN+1];
66739826d68Smrg
668145b7b3cSmrg	    getdomainname(domainname, sizeof domainname);
669145b7b3cSmrg	    user2netname (netname, verify->uid, domainname);
670145b7b3cSmrg	    addr.family = FamilyNetname;
671145b7b3cSmrg	    addr.length = strlen (netname);
672145b7b3cSmrg	    addr.address = netname;
673145b7b3cSmrg	    XAddHost (*dpy, &addr);
674145b7b3cSmrg	}
675145b7b3cSmrg#endif
676145b7b3cSmrg#ifdef K5AUTH
677145b7b3cSmrg	if (d->authorizations[i]->name_length == 14 &&
678145b7b3cSmrg	    memcmp(d->authorizations[i]->name, "MIT-KERBEROS-5", 14) == 0)
679145b7b3cSmrg	{
680145b7b3cSmrg	    /* Update server's auth file with user-specific info.
681145b7b3cSmrg	     * Don't need to AddHost because X server will do that
682145b7b3cSmrg	     * automatically when it reads the cache we are about
683145b7b3cSmrg	     * to point it at.
684145b7b3cSmrg	     */
685145b7b3cSmrg	    extern Xauth *Krb5GetAuthFor();
686145b7b3cSmrg
687145b7b3cSmrg	    XauDisposeAuth (d->authorizations[i]);
688145b7b3cSmrg	    d->authorizations[i] =
689145b7b3cSmrg		Krb5GetAuthFor(14, "MIT-KERBEROS-5", d->name);
690145b7b3cSmrg	    SaveServerAuthorizations (d, d->authorizations, d->authNum);
69139826d68Smrg	}
692145b7b3cSmrg#endif
693145b7b3cSmrg    }
694145b7b3cSmrg
695145b7b3cSmrg    return Greet_Success;
696145b7b3cSmrg}
697145b7b3cSmrg
698145b7b3cSmrg
699145b7b3cSmrg#ifdef USE_PAM
700c06e8ac6Smrgstatic int pamconv(int num_msg, XDM_PAM_QUAL struct pam_message **msg,
701145b7b3cSmrg		   struct pam_response **response, void *appdata_ptr)
702145b7b3cSmrg{
703145b7b3cSmrg    int i;
704145b7b3cSmrg    int greetCode;
705145b7b3cSmrg    int status = PAM_SUCCESS;
706145b7b3cSmrg    const char *pam_msg_styles[5]
707145b7b3cSmrg	= { "<invalid pam msg style>",
708145b7b3cSmrg	    "PAM_PROMPT_ECHO_OFF", "PAM_PROMPT_ECHO_ON",
709145b7b3cSmrg	    "PAM_ERROR_MSG", "PAM_TEXT_INFO" } ;
71039826d68Smrg
711145b7b3cSmrg    struct pam_message      *m;
712145b7b3cSmrg    struct pam_response     *r;
713145b7b3cSmrg
714145b7b3cSmrg    struct myconv_data	    *d = (struct myconv_data *) appdata_ptr;
715145b7b3cSmrg
716145b7b3cSmrg    pam_handle_t	    **pamhp = thepamhp();
71739826d68Smrg
718145b7b3cSmrg    *response = calloc(num_msg, sizeof (struct pam_response));
719145b7b3cSmrg    if (*response == NULL)
720145b7b3cSmrg	return (PAM_BUF_ERR);
721145b7b3cSmrg
72239826d68Smrg    m = (struct pam_message *)*msg;
723145b7b3cSmrg    r = *response;
724145b7b3cSmrg
72543b4c634Smrg    if (login == NULL) {
72643b4c634Smrg	status = PAM_CONV_ERR;
72743b4c634Smrg	goto pam_error;
72843b4c634Smrg    }
72943b4c634Smrg
730145b7b3cSmrg    for (i = 0; i < num_msg; i++ , m++ , r++) {
731c06e8ac6Smrg	XDM_PAM_QUAL char *username;
732145b7b3cSmrg	int promptId = 0;
733145b7b3cSmrg	loginPromptState pStyle = LOGIN_PROMPT_ECHO_OFF;
734145b7b3cSmrg
735c06e8ac6Smrg	if ((pam_get_item(*pamhp, PAM_USER, (XDM_PAM_QUAL void **) &username)
736c06e8ac6Smrg             == PAM_SUCCESS) && (username != NULL) && (*username != '\0')) {
737145b7b3cSmrg	    SetPrompt(login, LOGIN_PROMPT_USERNAME,
738145b7b3cSmrg		      NULL, LOGIN_TEXT_INFO, False);
739145b7b3cSmrg	    SetValue(login, LOGIN_PROMPT_USERNAME, username);
740145b7b3cSmrg	    promptId = 1;
74139826d68Smrg	}
74239826d68Smrg
743145b7b3cSmrg	Debug("pam_msg: %s (%d): '%s'\n",
744145b7b3cSmrg	      ((m->msg_style > 0) && (m->msg_style <= 4)) ?
745145b7b3cSmrg	       pam_msg_styles[m->msg_style] : pam_msg_styles[0],
746145b7b3cSmrg	       m->msg_style, m->msg);
747145b7b3cSmrg
748145b7b3cSmrg	switch (m->msg_style) {
749145b7b3cSmrg	  case PAM_ERROR_MSG:
750145b7b3cSmrg	      ErrorMessage(login, m->msg, True);
751145b7b3cSmrg	      break;
752145b7b3cSmrg
753145b7b3cSmrg	  case PAM_TEXT_INFO:
754145b7b3cSmrg	      SetPrompt (login, promptId, m->msg, LOGIN_TEXT_INFO, True);
755145b7b3cSmrg	      SetValue (login, promptId, NULL);
756145b7b3cSmrg	      break;
75739826d68Smrg
758145b7b3cSmrg          case PAM_PROMPT_ECHO_ON:
759145b7b3cSmrg	      pStyle = LOGIN_PROMPT_ECHO_ON;
760145b7b3cSmrg	      /* FALLTHROUGH */
761145b7b3cSmrg          case PAM_PROMPT_ECHO_OFF:
762145b7b3cSmrg	      SetPrompt (login, promptId, m->msg, pStyle, False);
763145b7b3cSmrg	      SetValue (login, promptId, NULL);
764145b7b3cSmrg	      greetCode = Greet (d->d, d->greet);
765145b7b3cSmrg	      if (greetCode != 0) {
766145b7b3cSmrg		  status = PAM_CONV_ERR;
767145b7b3cSmrg		  goto pam_error;
768145b7b3cSmrg	      } else {
769145b7b3cSmrg		  r->resp = strdup(GetValue(login, promptId));
770145b7b3cSmrg		  SetValue(login, promptId, NULL);
771145b7b3cSmrg		  if (r->resp == NULL) {
772145b7b3cSmrg		      status = PAM_BUF_ERR;
773145b7b3cSmrg		      goto pam_error;
774145b7b3cSmrg		  }
775145b7b3cSmrg		  /* Debug("pam_resp: '%s'\n", r->resp); */
776145b7b3cSmrg	      }
777145b7b3cSmrg	      break;
778145b7b3cSmrg
779145b7b3cSmrg	  default:
780145b7b3cSmrg	      LogError("Unknown PAM msg_style: %d\n", m->msg_style);
781145b7b3cSmrg	}
782145b7b3cSmrg    }
78339826d68Smrg  pam_error:
784145b7b3cSmrg    if (status != PAM_SUCCESS) {
785145b7b3cSmrg	/* free responses */
786145b7b3cSmrg	r = *response;
787145b7b3cSmrg	for (i = 0; i < num_msg; i++, r++) {
788145b7b3cSmrg	    if (r->resp) {
789145b7b3cSmrg		bzero(r->resp, strlen(r->resp));
790145b7b3cSmrg		free(r->resp);
791145b7b3cSmrg	    }
792145b7b3cSmrg	}
793145b7b3cSmrg	free(*response);
794145b7b3cSmrg	*response = NULL;
795145b7b3cSmrg    }
796145b7b3cSmrg    return status;
797145b7b3cSmrg}
798145b7b3cSmrg#endif
799