verify.c revision 629baa8c
1/*
2
3Copyright 1988, 1998  The Open Group
4
5Permission to use, copy, modify, distribute, and sell this software and its
6documentation for any purpose is hereby granted without fee, provided that
7the above copyright notice appear in all copies and that both that
8copyright notice and this permission notice appear in supporting
9documentation.
10
11The above copyright notice and this permission notice shall be included
12in all copies or substantial portions of the Software.
13
14THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
15OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
17IN NO EVENT SHALL THE OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR
18OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
19ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
20OTHER DEALINGS IN THE SOFTWARE.
21
22Except as contained in this notice, the name of The Open Group shall
23not be used in advertising or otherwise to promote the sale, use or
24other dealings in this Software without prior written authorization
25from The Open Group.
26
27*/
28
29/*
30 * xdm - display manager daemon
31 * Author:  Keith Packard, MIT X Consortium
32 *
33 * verify.c
34 *
35 * typical unix verification routine.
36 */
37
38#include	"dm.h"
39#include	"dm_error.h"
40
41#include	<pwd.h>
42
43#if defined(USE_PAM)
44# include	<security/pam_appl.h>
45# include	<stdlib.h>
46#elif defined(HAVE_GETSPNAM)
47# include	<shadow.h>
48# include	<errno.h>
49#elif defined(USE_BSDAUTH)
50# include	<login_cap.h>
51# include	<varargs.h>
52# include	<bsd_auth.h>
53#elif defined(USESECUREWARE)
54# include       <sys/types.h>
55# include       <prot.h>
56#endif
57
58#include	"greet.h"
59
60#ifdef QNX4
61extern char *crypt(const char *, const char *);
62#endif
63
64static char *envvars[] = {
65    "TZ",			/* SYSV and SVR4, but never hurts */
66#if defined(sony) && !defined(SYSTYPE_SYSV) && !defined(_SYSTYPE_SYSV)
67    "bootdev",
68    "boothowto",
69    "cputype",
70    "ioptype",
71    "machine",
72    "model",
73    "CONSDEVTYPE",
74    "SYS_LANGUAGE",
75    "SYS_CODE",
76#endif
77#if (defined(SVR4) || defined(SYSV)) && defined(i386) && !defined(sun)
78    "XLOCAL",
79#endif
80    NULL
81};
82
83#ifdef KERBEROS
84# include <sys/param.h>
85# include <kerberosIV/krb.h>
86/* OpenBSD 2.8 needs this. */
87# if defined(OpenBSD) && (OpenBSD <= 200012)
88#  include <kerberosIV/kafs.h>
89# endif
90static char krbtkfile[MAXPATHLEN];
91#endif
92
93static char **
94userEnv (struct display *d, int useSystemPath, char *user, char *home, char *shell)
95{
96    char	**env;
97    char	**envvar;
98    char	*str;
99
100    env = defaultEnv ();
101    env = setEnv (env, "DISPLAY", d->name);
102    env = setEnv (env, "HOME", home);
103    env = setEnv (env, "LOGNAME", user); /* POSIX, System V */
104    env = setEnv (env, "USER", user);    /* BSD */
105    env = setEnv (env, "PATH", useSystemPath ? d->systemPath : d->userPath);
106    env = setEnv (env, "SHELL", shell);
107#ifdef KERBEROS
108    if (krbtkfile[0] != '\0')
109        env = setEnv (env, "KRBTKFILE", krbtkfile);
110#endif
111    for (envvar = envvars; *envvar; envvar++)
112    {
113	str = getenv(*envvar);
114	if (str)
115	    env = setEnv (env, *envvar, str);
116    }
117    return env;
118}
119
120#ifdef USE_BSDAUTH
121_X_INTERNAL
122int
123Verify (struct display *d, struct greet_info *greet, struct verify_info *verify)
124{
125	struct passwd	*p;
126	login_cap_t	*lc;
127	auth_session_t	*as;
128	char		*style, *shell, *home, *s, **argv;
129	char		path[MAXPATHLEN];
130	int		authok;
131
132	/* User may have specified an authentication style. */
133	if ((style = strchr(greet->name, ':')) != NULL)
134		*style++ = '\0';
135
136	Debug ("Verify %s, style %s ...\n", greet->name,
137	    style ? style : "default");
138
139	p = getpwnam (greet->name);
140	endpwent();
141
142	if (!p || strlen (greet->name) == 0) {
143		Debug("getpwnam() failed.\n");
144		bzero(greet->password, strlen(greet->password));
145		return 0;
146	}
147
148	if ((lc = login_getclass(p->pw_class)) == NULL) {
149		Debug("login_getclass() failed.\n");
150		bzero(greet->password, strlen(greet->password));
151		return 0;
152	}
153	if ((style = login_getstyle(lc, style, "xdm")) == NULL) {
154		Debug("login_getstyle() failed.\n");
155		bzero(greet->password, strlen(greet->password));
156		return 0;
157	}
158	if ((as = auth_open()) == NULL) {
159		Debug("auth_open() failed.\n");
160		login_close(lc);
161		bzero(greet->password, strlen(greet->password));
162		return 0;
163	}
164	if (auth_setoption(as, "login", "yes") == -1) {
165		Debug("auth_setoption() failed.\n");
166		login_close(lc);
167		bzero(greet->password, strlen(greet->password));
168		return 0;
169	}
170
171	/* Set up state for no challenge, just check a response. */
172	auth_setstate(as, 0);
173	auth_setdata(as, "", 1);
174	auth_setdata(as, greet->password, strlen(greet->password) + 1);
175
176	/* Build path of the auth script and call it */
177	snprintf(path, sizeof(path), _PATH_AUTHPROG "%s", style);
178	auth_call(as, path, style, "-s", "response", greet->name,
179		  lc->lc_class, (void *)NULL);
180	authok = auth_getstate(as);
181
182	if ((authok & AUTH_ALLOW) == 0) {
183		Debug("password verify failed\n");
184		bzero(greet->password, strlen(greet->password));
185		auth_close(as);
186		login_close(lc);
187		return 0;
188	}
189	/* Run the approval script */
190	if (!auth_approval(as, lc, greet->name, "auth-xdm")) {
191		Debug("login not approved\n");
192		bzero(greet->password, strlen(greet->password));
193		auth_close(as);
194		login_close(lc);
195		return 0;
196	}
197	auth_close(as);
198	login_close(lc);
199	/* Check empty passwords against allowNullPasswd */
200	if (!greet->allow_null_passwd && strlen(greet->password) == 0) {
201		Debug("empty password not allowed\n");
202		return 0;
203	}
204	/* Only accept root logins if allowRootLogin resource is set */
205	if (p->pw_uid == 0 && !greet->allow_root_login) {
206		Debug("root logins not allowed\n");
207		bzero(greet->password, strlen(greet->password));
208		return 0;
209	}
210
211	/*
212	 * Shell must be in /etc/shells
213	 */
214	for (;;) {
215		s = getusershell();
216		if (s == NULL) {
217			/* did not found the shell in /etc/shells
218			   -> failure */
219			Debug("shell not in /etc/shells\n");
220			bzero(greet->password, strlen(greet->password));
221			endusershell();
222			return 0;
223		}
224		if (strcmp(s, p->pw_shell) == 0) {
225			/* found the shell in /etc/shells */
226			endusershell();
227			break;
228		}
229	}
230#elif defined(USESECUREWARE) /* !USE_BSDAUTH */
231/*
232 * This is a global variable and will be referenced in at least session.c
233 */
234struct smp_user_info *userp = 0;
235
236_X_INTERNAL
237int
238Verify (struct display *d, struct greet_info *greet, struct verify_info *verify)
239{
240  int ret, pwtries = 0, nis, delay;
241  char *reason = 0;
242  struct passwd	*p;
243  char *shell, *home, **argv;
244
245  Debug ("Verify %s ...\n", greet->name);
246
247  p = getpwnam (greet->name);
248  endpwent();
249
250  if (!p || strlen (greet->name) == 0) {
251    LogError ("getpwnam() failed.\n");
252    bzero(greet->password, strlen(greet->password));
253    return 0;
254  }
255
256  ret = smp_check_user (SMP_LOGIN, greet->name, 0, 0, &userp, &pwtries,
257    &reason, &nis, &delay);
258  if (ret != SMP_RETIRED && userp->retired)
259    ret = userp->result = SMP_RETIRED;
260  Debug ("smp_check_user returns %d\n", ret);
261
262  switch (ret) {
263    case SMP_FAIL:
264      Debug ("Out of memory in smp_check_user\n");
265      goto smp_fail;
266    case SMP_EXTFAIL:
267      Debug ("SMP_EXTFAIL: %s", reason);
268      goto smp_fail;
269    case SMP_NOTAUTH:
270      Debug ("Not authorized\n");
271      goto smp_fail;
272    case SMP_TERMLOCK:
273      Debug ("Terminal is locked!\n");
274      goto smp_fail;
275    case SMP_ACCTLOCK:
276      Debug ("Account is locked\n");
277      goto smp_fail;
278    case SMP_RETIRED:
279      Debug ("Account is retired\n");
280      goto smp_fail;
281    case SMP_OVERRIDE:
282      Debug ("On override device ... proceeding\n");
283      break;
284    case SMP_NULLPW:
285      Debug ("NULL password entry\n");
286      if (!greet->allow_null_passwd) {
287        goto smp_fail;
288      }
289      break;
290    case SMP_BADUSER:
291      Debug ("User not found in protected password database\n");
292      goto smp_fail;
293    case SMP_PWREQ:
294      Debug ("Password change required\n");
295      goto smp_fail;
296    case SMP_HASPW:
297      break;
298    default:
299      Debug ("Unhandled smp_check_user return %d\n", ret);
300smp_fail:
301      sleep(delay);
302      smp_audit_fail (userp, 0);
303      bzero(greet->password, strlen(greet->password));
304      return 0;
305      break;
306  }
307
308  if (ret != SMP_NULLPW) {
309    /*
310     * If we require a password, check it.
311     */
312    ret = smp_check_pw (greet->password, userp, &reason);
313    switch (ret) {
314      case SMP_CANCHANGE:
315      case SMP_CANTCHANGE:
316      case SMP_OVERRIDE:
317        break;
318      default:
319        goto smp_fail;
320    }
321  }
322#else /* !USE_BSDAUTH && !USESECUREWARE */
323_X_INTERNAL
324int
325Verify (struct display *d, struct greet_info *greet, struct verify_info *verify)
326{
327	struct passwd	*p;
328# ifndef USE_PAM
329#  ifdef HAVE_GETSPNAM
330	struct spwd	*sp;
331#  endif
332	char		*user_pass = NULL;
333# endif
334# ifdef __OpenBSD__
335	char            *s;
336	struct timeval  tp;
337# endif
338	char		*shell, *home;
339	char		**argv;
340
341	Debug ("Verify %s ...\n", greet->name);
342
343	p = getpwnam (greet->name);
344	endpwent();
345
346	if (!p || strlen (greet->name) == 0) {
347		Debug ("getpwnam() failed.\n");
348		if (greet->password != NULL)
349		    bzero(greet->password, strlen(greet->password));
350		return 0;
351	}
352
353	/*
354	 * Only accept root logins if allowRootLogin resource is not false
355	 */
356	if ((p->pw_uid == 0) && !greet->allow_root_login) {
357		Debug("root logins not allowed\n");
358		if (greet->password != NULL)
359		    bzero(greet->password, strlen(greet->password));
360		return 0;
361	}
362
363# if defined(sun) && defined(SVR4)
364	/* Solaris: If CONSOLE is set to /dev/console in /etc/default/login,
365	   then root can only login on system console */
366
367#  define SOLARIS_LOGIN_DEFAULTS "/etc/default/login"
368
369	if (p->pw_uid == 0) {
370	    char *console = NULL, *tmp = NULL;
371	    FILE *fs;
372
373	    if ((fs= fopen(SOLARIS_LOGIN_DEFAULTS, "r")) != NULL)
374	    {
375		char str[120];
376		while (!feof(fs))
377		{
378		    fgets(str, 120, fs);
379		    if(str[0] == '#' || strlen(str) < 8)
380			continue;
381		    if((tmp = strstr(str, "CONSOLE=")) != NULL)
382			console = strdup((tmp+8));
383		}
384		fclose(fs);
385                if ( console != NULL &&
386		  (strncmp(console, "/dev/console", 12) == 0) &&
387		  (strncmp(d->name,":0",2) != 0) )
388		{
389                        Debug("Not on system console\n");
390			if (greet->password != NULL)
391			    bzero(greet->password, strlen(greet->password));
392			free(console);
393	                return 0;
394                }
395		free(console);
396	    }
397	    else
398	    {
399		Debug("Could not open %s\n", SOLARIS_LOGIN_DEFAULTS);
400	    }
401	}
402# endif
403
404# ifndef USE_PAM /* PAM authentication happened in GreetUser already */
405#  ifdef linux
406	if (!strcmp(p->pw_passwd, "!") || !strcmp(p->pw_passwd, "*")) {
407	    Debug ("The account is locked, no login allowed.\n");
408	    bzero(greet->password, strlen(greet->password));
409	    return 0;
410	}
411#  endif
412	user_pass = p->pw_passwd;
413#  ifdef KERBEROS
414	if(strcmp(greet->name, "root") != 0){
415		char name[ANAME_SZ];
416		char realm[REALM_SZ];
417		char *q;
418		int ret;
419
420		if(krb_get_lrealm(realm, 1)){
421			Debug ("Can't get Kerberos realm.\n");
422		} else {
423
424		    snprintf(krbtkfile, sizeof(krbktfile), "%s.%s",
425			     TKT_ROOT, d->name);
426		    krb_set_tkt_string(krbtkfile);
427		    unlink(krbtkfile);
428
429		    ret = krb_verify_user(greet->name, "", realm,
430				      greet->password, 1, "rcmd");
431
432		    if(ret == KSUCCESS){
433			    chown(krbtkfile, p->pw_uid, p->pw_gid);
434			    Debug("kerberos verify succeeded\n");
435			    if (k_hasafs()) {
436				    if (k_setpag() == -1)
437					    LogError ("setpag() failed for %s\n",
438						      greet->name);
439
440				    if((ret = k_afsklog(NULL, NULL)) != KSUCCESS)
441					    LogError("Warning %s\n",
442						     krb_get_err_text(ret));
443			    }
444			    goto done;
445		    } else if(ret != KDC_PR_UNKNOWN && ret != SKDC_CANT){
446			    /* failure */
447			    Debug("kerberos verify failure %d\n", ret);
448			    krbtkfile[0] = '\0';
449		    }
450		}
451	}
452#  endif
453#  ifdef HAVE_GETSPNAM
454	errno = 0;
455	sp = getspnam(greet->name);
456	if (sp == NULL) {
457	    Debug ("getspnam() failed: %s\n", _SysErrorMsg (errno));
458	} else {
459	    user_pass = sp->sp_pwdp;
460	}
461#   ifndef QNX4
462	endspent();
463#   endif  /* QNX4 doesn't need endspent() to end shadow passwd ops */
464#  endif /* HAVE_GETSPNAM */
465#  if defined(ultrix) || defined(__ultrix__)
466	if (authenticate_user(p, greet->password, NULL) < 0)
467#  else
468	if (strcmp (crypt (greet->password, user_pass), user_pass))
469#  endif
470	{
471		if(!greet->allow_null_passwd || strlen(p->pw_passwd) > 0) {
472			Debug ("password verify failed\n");
473			bzero(greet->password, strlen(greet->password));
474			return 0;
475		} /* else: null passwd okay */
476	}
477#  ifdef KERBEROS
478done:
479#  endif
480	/*
481	 * Only accept root logins if allowRootLogin resource is set
482	 */
483	if ((p->pw_uid == 0) && !greet->allow_root_login) {
484		Debug("root logins not allowed\n");
485		bzero(greet->password, strlen(greet->password));
486		return 0;
487	}
488#  ifdef __OpenBSD__
489	/*
490	 * Shell must be in /etc/shells
491	 */
492	for (;;) {
493		s = getusershell();
494		if (s == NULL) {
495			/* did not found the shell in /etc/shells
496			   -> failure */
497			Debug("shell not in /etc/shells\n");
498			bzero(greet->password, strlen(greet->password));
499			endusershell();
500			return 0;
501		}
502		if (strcmp(s, p->pw_shell) == 0) {
503			/* found the shell in /etc/shells */
504			endusershell();
505			break;
506		}
507	}
508	/*
509	 * Test for expired password
510	 */
511	if (p->pw_change || p->pw_expire)
512		(void)gettimeofday(&tp, (struct timezone *)NULL);
513	if (p->pw_change) {
514		if (tp.tv_sec >= p->pw_change) {
515			Debug("Password has expired.\n");
516			bzero(greet->password, strlen(greet->password));
517			return 0;
518		}
519	}
520	if (p->pw_expire) {
521		if (tp.tv_sec >= p->pw_expire) {
522			Debug("account has expired.\n");
523			bzero(greet->password, strlen(greet->password));
524			return 0;
525		}
526	}
527#  endif /* __OpenBSD__ */
528	bzero(user_pass, strlen(user_pass)); /* in case shadow password */
529
530# endif /* USE_PAM */
531#endif /* USE_BSDAUTH */
532
533	Debug ("verify succeeded\n");
534	/* The password is passed to StartClient() for use by user-based
535	   authorization schemes.  It is zeroed there. */
536	verify->uid = p->pw_uid;
537	verify->gid = p->pw_gid;
538	home = p->pw_dir;
539	shell = p->pw_shell;
540	argv = NULL;
541	if (d->session)
542		argv = parseArgs (argv, d->session);
543	if (greet->string)
544		argv = parseArgs (argv, greet->string);
545	if (!argv)
546		argv = parseArgs (argv, "xsession");
547	verify->argv = argv;
548	verify->userEnviron = userEnv (d, p->pw_uid == 0,
549				       greet->name, home, shell);
550	Debug ("user environment:\n");
551	printEnv (verify->userEnviron);
552	verify->systemEnviron = systemEnv (d, greet->name, home);
553	Debug ("system environment:\n");
554	printEnv (verify->systemEnviron);
555	Debug ("end of environments\n");
556	return 1;
557}
558