1 /* 2 * Copyright (c) 1999 - 2007 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 KTH nor the names of its contributors may be 18 * used to endorse or promote products derived from this software without 19 * specific prior written permission. 20 * 21 * THIS SOFTWARE IS PROVIDED BY KTH AND ITS CONTRIBUTORS ``AS IS'' AND ANY 22 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 24 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL KTH OR ITS CONTRIBUTORS BE 25 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 26 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 27 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR 28 * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 29 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 30 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 31 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ 32 33 /* 34 * $FreeBSD$ 35 */ 36 37 #include <config.h> 38 39 RCSID("$Id: su.c 21988 2007-10-19 05:36:54Z lha $"); 40 41 #include <stdio.h> 42 #include <stdlib.h> 43 #include <string.h> 44 45 #include <syslog.h> 46 47 #ifdef HAVE_PATHS_H 48 #include <paths.h> 49 #endif 50 51 #ifdef HAVE_SHADOW_H 52 #include <shadow.h> 53 #endif 54 55 #include <pwd.h> 56 #ifdef HAVE_CRYPT_H 57 #include <crypt.h> 58 #endif 59 60 #include "crypto-headers.h" 61 #ifdef KRB5 62 #include <krb5.h> 63 #endif 64 #ifdef KRB4 65 #include <krb.h> 66 #endif 67 #include <kafs.h> 68 #include <err.h> 69 #include <roken.h> 70 #include <getarg.h> 71 72 #include "supaths.h" 73 74 int kerberos_flag = 1; 75 int csh_f_flag; 76 int full_login; 77 int env_flag; 78 char *kerberos_instance = "root"; 79 int help_flag; 80 int version_flag; 81 char *cmd; 82 char tkfile[256]; 83 84 struct getargs args[] = { 85 { "kerberos", 'K', arg_negative_flag, &kerberos_flag, 86 "don't use kerberos" }, 87 { NULL, 'f', arg_flag, &csh_f_flag, 88 "don't read .cshrc" }, 89 { "full", 'l', arg_flag, &full_login, 90 "simulate full login" }, 91 { NULL, 'm', arg_flag, &env_flag, 92 "leave environment unmodified" }, 93 { "instance", 'i', arg_string, &kerberos_instance, 94 "root instance to use" }, 95 { "command", 'c', arg_string, &cmd, 96 "command to execute" }, 97 { "help", 'h', arg_flag, &help_flag }, 98 { "version", 0, arg_flag, &version_flag }, 99 }; 100 101 102 static void 103 usage (int ret) 104 { 105 arg_printusage (args, 106 sizeof(args)/sizeof(*args), 107 NULL, 108 "[login [shell arguments]]"); 109 exit (ret); 110 } 111 112 static void 113 free_info(struct passwd *p) 114 { 115 free (p->pw_name); 116 free (p->pw_passwd); 117 free (p->pw_dir); 118 free (p->pw_shell); 119 free (p); 120 } 121 122 static struct passwd* 123 dup_info(const struct passwd *pwd) 124 { 125 struct passwd *info; 126 127 info = malloc(sizeof(*info)); 128 if(info == NULL) 129 return NULL; 130 info->pw_name = strdup(pwd->pw_name); 131 info->pw_passwd = strdup(pwd->pw_passwd); 132 info->pw_uid = pwd->pw_uid; 133 info->pw_gid = pwd->pw_gid; 134 info->pw_dir = strdup(pwd->pw_dir); 135 info->pw_shell = strdup(pwd->pw_shell); 136 if(info->pw_name == NULL || info->pw_passwd == NULL || 137 info->pw_dir == NULL || info->pw_shell == NULL) { 138 free_info (info); 139 return NULL; 140 } 141 return info; 142 } 143 144 #if defined(KRB4) || defined(KRB5) 145 static void 146 set_tkfile() 147 { 148 #ifndef TKT_ROOT 149 #define TKT_ROOT "/tmp/tkt" 150 #endif 151 int fd; 152 if(*tkfile != '\0') 153 return; 154 snprintf(tkfile, sizeof(tkfile), "%s_XXXXXX", TKT_ROOT); 155 fd = mkstemp(tkfile); 156 if(fd >= 0) 157 close(fd); 158 #ifdef KRB4 159 krb_set_tkt_string(tkfile); 160 #endif 161 } 162 #endif 163 164 #ifdef KRB5 165 static krb5_context context; 166 static krb5_ccache ccache; 167 168 static int 169 krb5_verify(const struct passwd *login_info, 170 const struct passwd *su_info, 171 const char *kerberos_instance) 172 { 173 krb5_error_code ret; 174 krb5_principal p; 175 krb5_realm *realms, *r; 176 char *login_name = NULL; 177 int user_ok = 0; 178 179 #if defined(HAVE_GETLOGIN) && !defined(POSIX_GETLOGIN) 180 login_name = getlogin(); 181 #endif 182 ret = krb5_init_context (&context); 183 if (ret) { 184 #if 0 185 warnx("krb5_init_context failed: %d", ret); 186 #endif 187 return 1; 188 } 189 190 ret = krb5_get_default_realms(context, &realms); 191 if (ret) 192 return 1; 193 194 /* Check all local realms */ 195 for (r = realms; *r != NULL && !user_ok; r++) { 196 197 if (login_name == NULL || strcmp (login_name, "root") == 0) 198 login_name = login_info->pw_name; 199 if (strcmp (su_info->pw_name, "root") == 0) 200 ret = krb5_make_principal(context, &p, *r, 201 login_name, 202 kerberos_instance, 203 NULL); 204 else 205 ret = krb5_make_principal(context, &p, *r, 206 su_info->pw_name, 207 NULL); 208 if (ret) { 209 krb5_free_host_realm(context, realms); 210 return 1; 211 } 212 213 /* if we are su-ing too root, check with krb5_kuserok */ 214 if (su_info->pw_uid == 0 && !krb5_kuserok(context, p, su_info->pw_name)) 215 continue; 216 217 ret = krb5_cc_gen_new(context, &krb5_mcc_ops, &ccache); 218 if(ret) { 219 krb5_free_host_realm(context, realms); 220 krb5_free_principal (context, p); 221 return 1; 222 } 223 ret = krb5_verify_user(context, p, ccache, NULL, TRUE, NULL); 224 krb5_free_principal (context, p); 225 switch (ret) { 226 case 0: 227 user_ok = 1; 228 break; 229 case KRB5_LIBOS_PWDINTR : 230 krb5_cc_destroy(context, ccache); 231 break; 232 case KRB5KRB_AP_ERR_BAD_INTEGRITY: 233 case KRB5KRB_AP_ERR_MODIFIED: 234 krb5_cc_destroy(context, ccache); 235 krb5_warnx(context, "Password incorrect"); 236 break; 237 default : 238 krb5_cc_destroy(context, ccache); 239 krb5_warn(context, ret, "krb5_verify_user"); 240 break; 241 } 242 } 243 krb5_free_host_realm(context, realms); 244 if (!user_ok) 245 return 1; 246 return 0; 247 } 248 249 static int 250 krb5_start_session(void) 251 { 252 krb5_ccache ccache2; 253 char *cc_name; 254 int ret; 255 256 ret = krb5_cc_gen_new(context, &krb5_fcc_ops, &ccache2); 257 if (ret) { 258 krb5_cc_destroy(context, ccache); 259 return 1; 260 } 261 262 ret = krb5_cc_copy_cache(context, ccache, ccache2); 263 264 ret = asprintf(&cc_name, "%s:%s", krb5_cc_get_type(context, ccache2), 265 krb5_cc_get_name(context, ccache2)); 266 if (ret == -1) 267 errx(1, "malloc - out of memory"); 268 esetenv("KRB5CCNAME", cc_name, 1); 269 270 /* we want to export this even if we don't directly support KRB4 */ 271 set_tkfile(); 272 esetenv("KRBTKFILE", tkfile, 1); 273 274 /* convert creds? */ 275 if(k_hasafs()) { 276 if (k_setpag() == 0) 277 krb5_afslog(context, ccache2, NULL, NULL); 278 } 279 280 krb5_cc_close(context, ccache2); 281 krb5_cc_destroy(context, ccache); 282 return 0; 283 } 284 #endif 285 286 #ifdef KRB4 287 288 static int 289 krb_verify(const struct passwd *login_info, 290 const struct passwd *su_info, 291 const char *kerberos_instance) 292 { 293 int ret; 294 char *login_name = NULL; 295 char *name, *instance, realm[REALM_SZ]; 296 297 #if defined(HAVE_GETLOGIN) && !defined(POSIX_GETLOGIN) 298 login_name = getlogin(); 299 #endif 300 301 ret = krb_get_lrealm(realm, 1); 302 303 if (login_name == NULL || strcmp (login_name, "root") == 0) 304 login_name = login_info->pw_name; 305 if (strcmp (su_info->pw_name, "root") == 0) { 306 name = login_name; 307 instance = (char*)kerberos_instance; 308 } else { 309 name = su_info->pw_name; 310 instance = ""; 311 } 312 313 if(su_info->pw_uid != 0 || 314 krb_kuserok(name, instance, realm, su_info->pw_name) == 0) { 315 char password[128]; 316 char *prompt; 317 ret = asprintf (&prompt, 318 "%s's Password: ", 319 krb_unparse_name_long (name, instance, realm)); 320 if (ret == -1) 321 return (1); 322 if (UI_UTIL_read_pw_string (password, sizeof (password), prompt, 0)) { 323 memset (password, 0, sizeof (password)); 324 free(prompt); 325 return (1); 326 } 327 free(prompt); 328 if (strlen(password) == 0) 329 return (1); /* Empty passwords are not allowed */ 330 set_tkfile(); 331 setuid(geteuid()); /* need to run as root here */ 332 ret = krb_verify_user(name, instance, realm, password, 333 KRB_VERIFY_SECURE, NULL); 334 memset(password, 0, sizeof(password)); 335 336 if(ret) { 337 warnx("%s", krb_get_err_text(ret)); 338 return 1; 339 } 340 chown (tkt_string(), su_info->pw_uid, su_info->pw_gid); 341 return 0; 342 } 343 return 1; 344 } 345 346 347 static int 348 krb_start_session(void) 349 { 350 esetenv("KRBTKFILE", tkfile, 1); 351 352 /* convert creds? */ 353 if(k_hasafs() && k_setpag() == 0) 354 krb_afslog(NULL, NULL); 355 356 return 0; 357 } 358 #endif 359 360 #define GROUP_MEMBER 0 361 #define GROUP_MISSING 1 362 #define GROUP_EMPTY 2 363 #define GROUP_NOT_MEMBER 3 364 365 static int 366 group_member_p(const char *group, const char *user) 367 { 368 struct group *g; 369 int i; 370 g = getgrnam(group); 371 if(g == NULL) 372 return GROUP_MISSING; 373 if(g->gr_mem[0] == NULL) 374 return GROUP_EMPTY; 375 for(i = 0; g->gr_mem[i] != NULL; i++) 376 if(strcmp(user, g->gr_mem[i]) == 0) 377 return GROUP_MEMBER; 378 return GROUP_NOT_MEMBER; 379 } 380 381 static int 382 verify_unix(struct passwd *login, struct passwd *su) 383 { 384 char prompt[128]; 385 char pw_buf[1024]; 386 char *pw; 387 int r; 388 if(su->pw_passwd != NULL && *su->pw_passwd != '\0') { 389 snprintf(prompt, sizeof(prompt), "%s's password: ", su->pw_name); 390 r = UI_UTIL_read_pw_string(pw_buf, sizeof(pw_buf), prompt, 0); 391 if(r != 0) 392 exit(0); 393 pw = crypt(pw_buf, su->pw_passwd); 394 memset(pw_buf, 0, sizeof(pw_buf)); 395 if(strcmp(pw, su->pw_passwd) != 0) { 396 syslog (LOG_ERR | LOG_AUTH, "%s to %s: incorrect password", 397 login->pw_name, su->pw_name); 398 return 1; 399 } 400 } 401 /* if su:ing to root, check membership of group wheel or root; if 402 that group doesn't exist, or is empty, allow anyone to su 403 root */ 404 if(su->pw_uid == 0) { 405 #ifndef ROOT_GROUP 406 #define ROOT_GROUP "wheel" 407 #endif 408 int gs = group_member_p(ROOT_GROUP, login->pw_name); 409 if(gs == GROUP_NOT_MEMBER) { 410 syslog (LOG_ERR | LOG_AUTH, "%s to %s: not in group %s", 411 login->pw_name, su->pw_name, ROOT_GROUP); 412 return 1; 413 } 414 return 0; 415 } 416 return 0; 417 } 418 419 int 420 main(int argc, char **argv) 421 { 422 int i, optind = 0; 423 char *su_user; 424 struct passwd *su_info; 425 struct passwd *login_info; 426 427 struct passwd *pwd; 428 429 char *shell; 430 431 int ok = 0; 432 int kerberos_error=1; 433 434 setprogname (argv[0]); 435 436 if(getarg(args, sizeof(args) / sizeof(args[0]), argc, argv, &optind)) 437 usage(1); 438 439 for (i=0; i < optind; i++) 440 if (strcmp(argv[i], "-") == 0) { 441 full_login = 1; 442 break; 443 } 444 445 if(help_flag) 446 usage(0); 447 if(version_flag) { 448 print_version(NULL); 449 exit(0); 450 } 451 if(optind >= argc) 452 su_user = "root"; 453 else 454 su_user = argv[optind++]; 455 456 if (!issuid() && getuid() != 0) 457 warnx("Not setuid and you are root, expect this to fail"); 458 459 pwd = k_getpwnam(su_user); 460 if(pwd == NULL) 461 errx (1, "unknown login %s", su_user); 462 if (pwd->pw_uid == 0 && strcmp ("root", su_user) != 0) { 463 syslog (LOG_ALERT, "NIS attack, user %s has uid 0", su_user); 464 errx (1, "unknown login %s", su_user); 465 } 466 su_info = dup_info(pwd); 467 if (su_info == NULL) 468 errx (1, "malloc: out of memory"); 469 470 pwd = getpwuid(getuid()); 471 if(pwd == NULL) 472 errx(1, "who are you?"); 473 login_info = dup_info(pwd); 474 if (login_info == NULL) 475 errx (1, "malloc: out of memory"); 476 if(env_flag) 477 shell = login_info->pw_shell; 478 else 479 shell = su_info->pw_shell; 480 if(shell == NULL || *shell == '\0') 481 shell = _PATH_BSHELL; 482 483 484 #ifdef KRB5 485 if(kerberos_flag && ok == 0 && 486 (kerberos_error=krb5_verify(login_info, su_info, kerberos_instance)) == 0) 487 ok = 5; 488 #endif 489 #ifdef KRB4 490 if(kerberos_flag && ok == 0 && 491 (kerberos_error = krb_verify(login_info, su_info, kerberos_instance)) == 0) 492 ok = 4; 493 #endif 494 495 if(ok == 0 && login_info->pw_uid && verify_unix(login_info, su_info) != 0) { 496 printf("Sorry!\n"); 497 exit(1); 498 } 499 500 #ifdef HAVE_GETSPNAM 501 { struct spwd *sp; 502 long today; 503 504 sp = getspnam(su_info->pw_name); 505 if (sp != NULL) { 506 today = time(0)/(24L * 60 * 60); 507 if (sp->sp_expire > 0) { 508 if (today >= sp->sp_expire) { 509 if (login_info->pw_uid) 510 errx(1,"Your account has expired."); 511 else 512 printf("Your account has expired."); 513 } 514 else if (sp->sp_expire - today < 14) 515 printf("Your account will expire in %d days.\n", 516 (int)(sp->sp_expire - today)); 517 } 518 if (sp->sp_max > 0) { 519 if (today >= sp->sp_lstchg + sp->sp_max) { 520 if (login_info->pw_uid) 521 errx(1,"Your password has expired. Choose a new one."); 522 else 523 printf("Your password has expired. Choose a new one."); 524 } 525 else if (today >= sp->sp_lstchg + sp->sp_max - sp->sp_warn) 526 printf("Your account will expire in %d days.\n", 527 (int)(sp->sp_lstchg + sp->sp_max -today)); 528 } 529 } 530 } 531 #endif 532 { 533 char *tty = ttyname (STDERR_FILENO); 534 syslog (LOG_NOTICE | LOG_AUTH, tty ? "%s to %s on %s" : "%s to %s", 535 login_info->pw_name, su_info->pw_name, tty); 536 } 537 538 539 if(!env_flag) { 540 if(full_login) { 541 char *t = getenv ("TERM"); 542 char **newenv = NULL; 543 int i, j; 544 545 i = read_environment(_PATH_ETC_ENVIRONMENT, &newenv); 546 547 environ = malloc ((10 + i) * sizeof (char *)); 548 if (environ == NULL) 549 err (1, "malloc"); 550 environ[0] = NULL; 551 552 for (j = 0; j < i; j++) { 553 char *p = strchr(newenv[j], '='); 554 *p++ = 0; 555 esetenv (newenv[j], p, 1); 556 } 557 free(newenv); 558 559 esetenv ("PATH", _PATH_DEFPATH, 1); 560 if (t) 561 esetenv ("TERM", t, 1); 562 if (chdir (su_info->pw_dir) < 0) 563 errx (1, "no directory"); 564 } 565 if (full_login || su_info->pw_uid) 566 esetenv ("USER", su_info->pw_name, 1); 567 esetenv("HOME", su_info->pw_dir, 1); 568 esetenv("SHELL", shell, 1); 569 } 570 571 { 572 int i; 573 char **args; 574 char *p; 575 576 p = strrchr(shell, '/'); 577 if(p) 578 p++; 579 else 580 p = shell; 581 582 if (strcmp(p, "csh") != 0) 583 csh_f_flag = 0; 584 585 args = malloc(((cmd ? 2 : 0) + 1 + argc - optind + 1 + csh_f_flag) * sizeof(*args)); 586 if (args == NULL) 587 err (1, "malloc"); 588 i = 0; 589 if(full_login) { 590 if (asprintf(&args[i++], "-%s", p) == -1) 591 errx (1, "malloc"); 592 } else 593 args[i++] = p; 594 if (cmd) { 595 args[i++] = "-c"; 596 args[i++] = cmd; 597 } 598 599 if (csh_f_flag) 600 args[i++] = "-f"; 601 602 for (argv += optind; *argv; ++argv) 603 args[i++] = *argv; 604 args[i] = NULL; 605 606 if(setgid(su_info->pw_gid) < 0) 607 err(1, "setgid"); 608 if (initgroups (su_info->pw_name, su_info->pw_gid) < 0) 609 err (1, "initgroups"); 610 if(setuid(su_info->pw_uid) < 0 611 || (su_info->pw_uid != 0 && setuid(0) == 0)) 612 err(1, "setuid"); 613 614 #ifdef KRB5 615 if (ok == 5) 616 krb5_start_session(); 617 #endif 618 #ifdef KRB4 619 if (ok == 4) 620 krb_start_session(); 621 #endif 622 execv(shell, args); 623 } 624 625 exit(1); 626 } 627