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