1/* 2 * Copyright (c) 1997 - 2006 Kungliga Tekniska Högskolan 3 * (Royal Institute of Technology, Stockholm, Sweden). 4 * All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 17 * 3. Neither the name of the Institute nor the names of its contributors 18 * may be used to endorse or promote products derived from this software 19 * without specific prior written permission. 20 * 21 * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND 22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24 * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE 25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 31 * SUCH DAMAGE. 32 */ 33 34#include "login_locl.h" 35#ifdef HAVE_CAPABILITY_H 36#include <capability.h> 37#endif 38#ifdef HAVE_SYS_CAPABILITY_H 39#include <sys/capability.h> 40#endif 41#ifdef HAVE_CRYPT_H 42#include <crypt.h> 43#endif 44 45RCSID("$Id$"); 46 47static int login_timeout = 60; 48 49static int 50start_login_process(void) 51{ 52 char *prog, *argv0; 53 prog = login_conf_get_string("login_program"); 54 if(prog == NULL) 55 return 0; 56 argv0 = strrchr(prog, '/'); 57 58 if(argv0) 59 argv0++; 60 else 61 argv0 = prog; 62 63 return simple_execle(prog, argv0, NULL, env); 64} 65 66static int 67start_logout_process(void) 68{ 69 char *prog, *argv0; 70 pid_t pid; 71 72 prog = login_conf_get_string("logout_program"); 73 if(prog == NULL) 74 return 0; 75 argv0 = strrchr(prog, '/'); 76 77 if(argv0) 78 argv0++; 79 else 80 argv0 = prog; 81 82 pid = fork(); 83 if(pid == 0) { 84 /* avoid getting signals sent to the shell */ 85 setpgid(0, getpid()); 86 return 0; 87 } 88 if(pid == -1) 89 err(1, "fork"); 90 /* wait for the real login process to exit */ 91#ifdef HAVE_SETPROCTITLE 92 setproctitle("waitpid %d", pid); 93#endif 94 while(1) { 95 int status; 96 int ret; 97 ret = waitpid(pid, &status, 0); 98 if(ret > 0) { 99 if(WIFEXITED(status) || WIFSIGNALED(status)) { 100 execle(prog, argv0, NULL, env); 101 err(1, "exec %s", prog); 102 } 103 } else if(ret < 0) 104 err(1, "waitpid"); 105 } 106} 107 108static void 109exec_shell(const char *shell, int fallback) 110{ 111 char *sh; 112 const char *p; 113 114 extend_env(NULL); 115 if(start_login_process() < 0) 116 warn("login process"); 117 start_logout_process(); 118 119 p = strrchr(shell, '/'); 120 if(p) 121 p++; 122 else 123 p = shell; 124 if (asprintf(&sh, "-%s", p) == -1) 125 errx(1, "Out of memory"); 126 execle(shell, sh, NULL, env); 127 if(fallback){ 128 warnx("Can't exec %s, trying %s", 129 shell, _PATH_BSHELL); 130 execle(_PATH_BSHELL, "-sh", NULL, env); 131 err(1, "%s", _PATH_BSHELL); 132 } 133 err(1, "%s", shell); 134} 135 136static enum { NONE = 0, AUTH_KRB5 = 2, AUTH_OTP = 3 } auth; 137 138#ifdef OTP 139static OtpContext otp_ctx; 140 141static int 142otp_verify(struct passwd *pwd, const char *password) 143{ 144 return (otp_verify_user (&otp_ctx, password)); 145} 146#endif /* OTP */ 147 148 149static int pag_set = 0; 150 151#ifdef KRB5 152static krb5_context context; 153static krb5_ccache id, id2; 154 155static int 156krb5_verify(struct passwd *pwd, const char *password) 157{ 158 krb5_error_code ret; 159 krb5_principal princ; 160 161 ret = krb5_parse_name(context, pwd->pw_name, &princ); 162 if(ret) 163 return 1; 164 ret = krb5_cc_new_unique(context, krb5_cc_type_memory, NULL, &id); 165 if(ret) { 166 krb5_free_principal(context, princ); 167 return 1; 168 } 169 ret = krb5_verify_user_lrealm(context, 170 princ, 171 id, 172 password, 173 1, 174 NULL); 175 krb5_free_principal(context, princ); 176 return ret; 177} 178 179static int 180krb5_start_session (const struct passwd *pwd) 181{ 182 krb5_error_code ret; 183 char residual[64]; 184 185 /* copy credentials to file cache */ 186 snprintf(residual, sizeof(residual), "FILE:/tmp/krb5cc_%u", 187 (unsigned)pwd->pw_uid); 188 krb5_cc_resolve(context, residual, &id2); 189 ret = krb5_cc_copy_cache(context, id, id2); 190 if (ret == 0) 191 add_env("KRB5CCNAME", residual); 192 else { 193 krb5_cc_destroy (context, id2); 194 return ret; 195 } 196 krb5_cc_close(context, id2); 197 krb5_cc_destroy(context, id); 198 return 0; 199} 200 201static void 202krb5_finish (void) 203{ 204 krb5_free_context(context); 205} 206 207static void 208krb5_get_afs_tokens (const struct passwd *pwd) 209{ 210 char cell[64]; 211 char *pw_dir; 212 krb5_error_code ret; 213 214 if (!k_hasafs ()) 215 return; 216 217 ret = krb5_cc_default(context, &id2); 218 219 if (ret == 0) { 220 pw_dir = pwd->pw_dir; 221 222 if (!pag_set) { 223 k_setpag(); 224 pag_set = 1; 225 } 226 227 if(k_afs_cell_of_file(pw_dir, cell, sizeof(cell)) == 0) 228 krb5_afslog_uid_home (context, id2, 229 cell, NULL, pwd->pw_uid, pwd->pw_dir); 230 krb5_afslog_uid_home (context, id2, NULL, NULL, 231 pwd->pw_uid, pwd->pw_dir); 232 krb5_cc_close (context, id2); 233 } 234} 235 236#endif /* KRB5 */ 237 238static int f_flag; 239static int p_flag; 240#if 0 241static int r_flag; 242#endif 243static int version_flag; 244static int help_flag; 245static char *remote_host; 246static char *auth_level = NULL; 247 248struct getargs args[] = { 249 { NULL, 'a', arg_string, &auth_level, "authentication mode" }, 250#if 0 251 { NULL, 'd' }, 252#endif 253 { NULL, 'f', arg_flag, &f_flag, "pre-authenticated" }, 254 { NULL, 'h', arg_string, &remote_host, "remote host", "hostname" }, 255 { NULL, 'p', arg_flag, &p_flag, "don't purge environment" }, 256#if 0 257 { NULL, 'r', arg_flag, &r_flag, "rlogin protocol" }, 258#endif 259 { "version", 0, arg_flag, &version_flag }, 260 { "help", 0, arg_flag,&help_flag, } 261}; 262 263int nargs = sizeof(args) / sizeof(args[0]); 264 265static void 266update_utmp(const char *username, const char *hostname, 267 char *tty, char *ttyn) 268{ 269 /* 270 * Update the utmp files, both BSD and SYSV style. 271 */ 272 if (utmpx_login(tty, username, hostname) != 0 && !f_flag) { 273 printf("No utmpx entry. You must exec \"login\" from the " 274 "lowest level shell.\n"); 275 exit(1); 276 } 277 utmp_login(ttyn, username, hostname); 278} 279 280static void 281checknologin(void) 282{ 283 FILE *f; 284 char buf[1024]; 285 286 f = fopen(_PATH_NOLOGIN, "r"); 287 if(f == NULL) 288 return; 289 while(fgets(buf, sizeof(buf), f)) 290 fputs(buf, stdout); 291 fclose(f); 292 exit(0); 293} 294 295/* print contents of a file */ 296static void 297show_file(const char *file) 298{ 299 FILE *f; 300 char buf[BUFSIZ]; 301 if((f = fopen(file, "r")) == NULL) 302 return; 303 while (fgets(buf, sizeof(buf), f)) 304 fputs(buf, stdout); 305 fclose(f); 306} 307 308/* 309 * Actually log in the user. `pwd' contains all the relevant 310 * information about the user. `ttyn' is the complete name of the tty 311 * and `tty' the short name. 312 */ 313 314static void 315do_login(const struct passwd *pwd, char *tty, char *ttyn) 316{ 317#ifdef HAVE_GETSPNAM 318 struct spwd *sp; 319#endif 320 int rootlogin = (pwd->pw_uid == 0); 321 gid_t tty_gid; 322 struct group *gr; 323 const char *home_dir; 324 int i; 325 326 if(!rootlogin) 327 checknologin(); 328 329#ifdef HAVE_GETSPNAM 330 sp = getspnam(pwd->pw_name); 331#endif 332 333 update_utmp(pwd->pw_name, remote_host ? remote_host : "", 334 tty, ttyn); 335 336 gr = getgrnam ("tty"); 337 if (gr != NULL) 338 tty_gid = gr->gr_gid; 339 else 340 tty_gid = pwd->pw_gid; 341 342 if (chown (ttyn, pwd->pw_uid, tty_gid) < 0) { 343 warn("chown %s", ttyn); 344 if (rootlogin == 0) 345 exit (1); 346 } 347 348 if (chmod (ttyn, S_IRUSR | S_IWUSR | S_IWGRP) < 0) { 349 warn("chmod %s", ttyn); 350 if (rootlogin == 0) 351 exit (1); 352 } 353 354#ifdef HAVE_SETLOGIN 355 if(setlogin(pwd->pw_name)){ 356 warn("setlogin(%s)", pwd->pw_name); 357 if(rootlogin == 0) 358 exit(1); 359 } 360#endif 361 if(rootlogin == 0) { 362 const char *file = login_conf_get_string("limits"); 363 if(file == NULL) 364 file = _PATH_LIMITS_CONF; 365 366 read_limits_conf(file, pwd); 367 } 368 369#ifdef HAVE_SETPCRED 370 if (setpcred (pwd->pw_name, NULL) == -1) 371 warn("setpcred(%s)", pwd->pw_name); 372#endif /* HAVE_SETPCRED */ 373#ifdef HAVE_INITGROUPS 374 if(initgroups(pwd->pw_name, pwd->pw_gid)){ 375 warn("initgroups(%s, %u)", pwd->pw_name, (unsigned)pwd->pw_gid); 376 if(rootlogin == 0) 377 exit(1); 378 } 379#endif 380 if(do_osfc2_magic(pwd->pw_uid)) 381 exit(1); 382 if(setgid(pwd->pw_gid)){ 383 warn("setgid(%u)", (unsigned)pwd->pw_gid); 384 if(rootlogin == 0) 385 exit(1); 386 } 387 if(setuid(pwd->pw_uid) || (pwd->pw_uid != 0 && setuid(0) == 0)) { 388 warn("setuid(%u)", (unsigned)pwd->pw_uid); 389 if(rootlogin == 0) 390 exit(1); 391 } 392 393 /* make sure signals are set to default actions, apparently some 394 OS:es like to ignore SIGINT, which is not very convenient */ 395 396 for (i = 1; i < NSIG; ++i) 397 signal(i, SIG_DFL); 398 399 /* all kinds of different magic */ 400 401#ifdef HAVE_GETSPNAM 402 check_shadow(pwd, sp); 403#endif 404 405#if defined(HAVE_GETUDBNAM) && defined(HAVE_SETLIM) 406 { 407 struct udb *udb; 408 long t; 409 const long maxcpu = 46116860184; /* some random constant */ 410 udb = getudbnam(pwd->pw_name); 411 if(udb == UDB_NULL) 412 errx(1, "Failed to get UDB entry."); 413 t = udb->ue_pcpulim[UDBRC_INTER]; 414 if(t == 0 || t > maxcpu) 415 t = CPUUNLIM; 416 else 417 t *= 100 * CLOCKS_PER_SEC; 418 419 if(limit(C_PROC, 0, L_CPU, t) < 0) 420 warn("limit C_PROC"); 421 422 t = udb->ue_jcpulim[UDBRC_INTER]; 423 if(t == 0 || t > maxcpu) 424 t = CPUUNLIM; 425 else 426 t *= 100 * CLOCKS_PER_SEC; 427 428 if(limit(C_JOBPROCS, 0, L_CPU, t) < 0) 429 warn("limit C_JOBPROCS"); 430 431 nice(udb->ue_nice[UDBRC_INTER]); 432 } 433#endif 434#if defined(HAVE_SGI_GETCAPABILITYBYNAME) && defined(HAVE_CAP_SET_PROC) 435 /* XXX SGI capability hack IRIX 6.x (x >= 0?) has something 436 called capabilities, that allow you to give away 437 permissions (such as chown) to specific processes. From 6.5 438 this is default on, and the default capability set seems to 439 not always be the empty set. The problem is that the 440 runtime linker refuses to do just about anything if the 441 process has *any* capabilities set, so we have to remove 442 them here (unless otherwise instructed by /etc/capability). 443 In IRIX < 6.5, these functions was called sgi_cap_setproc, 444 etc, but we ignore this fact (it works anyway). */ 445 { 446 struct user_cap *ucap = sgi_getcapabilitybyname(pwd->pw_name); 447 cap_t cap; 448 if(ucap == NULL) 449 cap = cap_from_text("all="); 450 else 451 cap = cap_from_text(ucap->ca_default); 452 if(cap == NULL) 453 err(1, "cap_from_text"); 454 if(cap_set_proc(cap) < 0) 455 err(1, "cap_set_proc"); 456 cap_free(cap); 457 free(ucap); 458 } 459#endif 460 home_dir = pwd->pw_dir; 461 if (chdir(home_dir) < 0) { 462 fprintf(stderr, "No home directory \"%s\"!\n", pwd->pw_dir); 463 if (chdir("/")) 464 exit(0); 465 home_dir = "/"; 466 fprintf(stderr, "Logging in with home = \"/\".\n"); 467 } 468#ifdef KRB5 469 if (auth == AUTH_KRB5) { 470 krb5_start_session (pwd); 471 } 472 473 krb5_get_afs_tokens (pwd); 474 475 krb5_finish (); 476#endif /* KRB5 */ 477 478 add_env("PATH", _PATH_DEFPATH); 479 480 { 481 const char *str = login_conf_get_string("environment"); 482 char buf[MAXPATHLEN]; 483 484 if(str == NULL) { 485 login_read_env(_PATH_ETC_ENVIRONMENT); 486 } else { 487 while(strsep_copy(&str, ",", buf, sizeof(buf)) != -1) { 488 if(buf[0] == '\0') 489 continue; 490 login_read_env(buf); 491 } 492 } 493 } 494 { 495 const char *str = login_conf_get_string("motd"); 496 char buf[MAXPATHLEN]; 497 498 if(str != NULL) { 499 while(strsep_copy(&str, ",", buf, sizeof(buf)) != -1) { 500 if(buf[0] == '\0') 501 continue; 502 show_file(buf); 503 } 504 } else { 505 str = login_conf_get_string("welcome"); 506 if(str != NULL) 507 show_file(str); 508 } 509 } 510 add_env("HOME", home_dir); 511 add_env("USER", pwd->pw_name); 512 add_env("LOGNAME", pwd->pw_name); 513 add_env("SHELL", pwd->pw_shell); 514 exec_shell(pwd->pw_shell, rootlogin); 515} 516 517static int 518check_password(struct passwd *pwd, const char *password) 519{ 520 if(pwd->pw_passwd == NULL) 521 return 1; 522 if(pwd->pw_passwd[0] == '\0'){ 523#ifdef ALLOW_NULL_PASSWORD 524 return password[0] != '\0'; 525#else 526 return 1; 527#endif 528 } 529 if(strcmp(pwd->pw_passwd, crypt(password, pwd->pw_passwd)) == 0) 530 return 0; 531#ifdef KRB5 532 if(krb5_verify(pwd, password) == 0) { 533 auth = AUTH_KRB5; 534 return 0; 535 } 536#endif 537#ifdef OTP 538 if (otp_verify (pwd, password) == 0) { 539 auth = AUTH_OTP; 540 return 0; 541 } 542#endif 543 return 1; 544} 545 546static void 547usage(int status) 548{ 549 arg_printusage(args, nargs, NULL, "[username]"); 550 exit(status); 551} 552 553static RETSIGTYPE 554sig_handler(int sig) 555{ 556 if (sig == SIGALRM) 557 fprintf(stderr, "Login timed out after %d seconds\n", 558 login_timeout); 559 else 560 fprintf(stderr, "Login received signal, exiting\n"); 561 exit(0); 562} 563 564int 565main(int argc, char **argv) 566{ 567 int max_tries = 5; 568 int try; 569 570 char username[32]; 571 int optidx = 0; 572 573 int ask = 1; 574 struct sigaction sa; 575 576 setprogname(argv[0]); 577 578#ifdef KRB5 579 { 580 krb5_error_code ret; 581 582 ret = krb5_init_context(&context); 583 if (ret) 584 errx (1, "krb5_init_context failed: %d", ret); 585 } 586#endif 587 588 openlog("login", LOG_ODELAY | LOG_PID, LOG_AUTH); 589 590 if (getarg (args, sizeof(args) / sizeof(args[0]), argc, argv, 591 &optidx)) 592 usage (1); 593 argc -= optidx; 594 argv += optidx; 595 596 if(help_flag) 597 usage(0); 598 if (version_flag) { 599 print_version (NULL); 600 return 0; 601 } 602 603 if (geteuid() != 0) 604 errx(1, "only root may use login, use su"); 605 606 /* Default tty settings. */ 607 stty_default(); 608 609 if(p_flag) 610 copy_env(); 611 else { 612 /* this set of variables is always preserved by BSD login */ 613 if(getenv("TERM")) 614 add_env("TERM", getenv("TERM")); 615 if(getenv("TZ")) 616 add_env("TZ", getenv("TZ")); 617 } 618 619 if(*argv){ 620 if(strchr(*argv, '=') == NULL && strcmp(*argv, "-") != 0){ 621 strlcpy (username, *argv, sizeof(username)); 622 ask = 0; 623 } 624 } 625 626#if defined(DCE) && defined(AIX) 627 esetenv("AUTHSTATE", "DCE", 1); 628#endif 629 630 /* XXX should we care about environment on the command line? */ 631 632 memset(&sa, 0, sizeof(sa)); 633 sa.sa_handler = sig_handler; 634 sigemptyset(&sa.sa_mask); 635 sa.sa_flags = 0; 636 sigaction(SIGALRM, &sa, NULL); 637 alarm(login_timeout); 638 639 for(try = 0; try < max_tries; try++){ 640 struct passwd *pwd; 641 char password[128]; 642 int ret; 643 char ttname[32]; 644 char *tty, *ttyn; 645 char prompt[128]; 646#ifdef OTP 647 char otp_str[256]; 648#endif 649 650 if(ask){ 651 f_flag = 0; 652#if 0 653 r_flag = 0; 654#endif 655 ret = read_string("login: ", username, sizeof(username), 1); 656 if(ret == -3) 657 exit(0); 658 if(ret == -2) 659 sig_handler(0); /* exit */ 660 } 661 pwd = k_getpwnam(username); 662#ifdef ALLOW_NULL_PASSWORD 663 if (pwd != NULL && (pwd->pw_passwd[0] == '\0')) { 664 strcpy(password,""); 665 } 666 else 667#endif 668 669 { 670#ifdef OTP 671 if(auth_level && strcmp(auth_level, "otp") == 0 && 672 otp_challenge(&otp_ctx, username, 673 otp_str, sizeof(otp_str)) == 0) 674 snprintf (prompt, sizeof(prompt), "%s's %s Password: ", 675 username, otp_str); 676 else 677#endif 678 strncpy(prompt, "Password: ", sizeof(prompt)); 679 680 if (f_flag == 0) { 681 ret = read_string(prompt, password, sizeof(password), 0); 682 if (ret == -3) { 683 ask = 1; 684 continue; 685 } 686 if (ret == -2) 687 sig_handler(0); 688 } 689 } 690 691 if(pwd == NULL){ 692 fprintf(stderr, "Login incorrect.\n"); 693 ask = 1; 694 continue; 695 } 696 697 if(f_flag == 0 && check_password(pwd, password)){ 698 fprintf(stderr, "Login incorrect.\n"); 699 ask = 1; 700 continue; 701 } 702 ttyn = ttyname(STDIN_FILENO); 703 if(ttyn == NULL){ 704 snprintf(ttname, sizeof(ttname), "%s??", _PATH_TTY); 705 ttyn = ttname; 706 } 707 if (strncmp (ttyn, _PATH_DEV, strlen(_PATH_DEV)) == 0) 708 tty = ttyn + strlen(_PATH_DEV); 709 else 710 tty = ttyn; 711 712 if (login_access (pwd, remote_host ? remote_host : tty) == 0) { 713 fprintf(stderr, "Permission denied\n"); 714 if (remote_host) 715 syslog(LOG_NOTICE, "%s LOGIN REFUSED FROM %s", 716 pwd->pw_name, remote_host); 717 else 718 syslog(LOG_NOTICE, "%s LOGIN REFUSED ON %s", 719 pwd->pw_name, tty); 720 exit (1); 721 } else { 722 if (remote_host) 723 syslog(LOG_NOTICE, "%s LOGIN ACCEPTED FROM %s ppid=%d", 724 pwd->pw_name, remote_host, (int) getppid()); 725 else 726 syslog(LOG_NOTICE, "%s LOGIN ACCEPTED ON %s ppid=%d", 727 pwd->pw_name, tty, (int) getppid()); 728 } 729 alarm(0); 730 do_login(pwd, tty, ttyn); 731 } 732 exit(1); 733} 734