1 /* 2 * Copyright (c) 1999 - 2003 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,v 1.26.2.1 2003/05/06 12:06:44 joda Exp $"); 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 57 #include "crypto-headers.h" 58 #ifdef KRB5 59 #include <krb5.h> 60 #endif 61 #ifdef KRB4 62 #include <krb.h> 63 #endif 64 #include <kafs.h> 65 #include <err.h> 66 #include <roken.h> 67 #include <getarg.h> 68 69 #ifndef _PATH_DEFPATH 70 #define _PATH_DEFPATH "/usr/bin:/bin" 71 #endif 72 73 #ifndef _PATH_BSHELL 74 #define _PATH_BSHELL "/bin/sh" 75 #endif 76 77 int kerberos_flag = 1; 78 int csh_f_flag; 79 int full_login; 80 int env_flag; 81 char *kerberos_instance = "root"; 82 int help_flag; 83 int version_flag; 84 char *cmd; 85 char tkfile[256]; 86 87 struct getargs args[] = { 88 { "kerberos", 'K', arg_negative_flag, &kerberos_flag, 89 "don't use kerberos" }, 90 { NULL, 'f', arg_flag, &csh_f_flag, 91 "don't read .cshrc" }, 92 { "full", 'l', arg_flag, &full_login, 93 "simulate full login" }, 94 { NULL, 'm', arg_flag, &env_flag, 95 "leave environment unmodified" }, 96 { "instance", 'i', arg_string, &kerberos_instance, 97 "root instance to use" }, 98 { "command", 'c', arg_string, &cmd, 99 "command to execute" }, 100 { "help", 'h', arg_flag, &help_flag }, 101 { "version", 0, arg_flag, &version_flag }, 102 }; 103 104 105 static void 106 usage (int ret) 107 { 108 arg_printusage (args, 109 sizeof(args)/sizeof(*args), 110 NULL, 111 "[login [shell arguments]]"); 112 exit (ret); 113 } 114 115 static void 116 free_info(struct passwd *p) 117 { 118 free (p->pw_name); 119 free (p->pw_passwd); 120 free (p->pw_dir); 121 free (p->pw_shell); 122 free (p); 123 } 124 125 static struct passwd* 126 dup_info(const struct passwd *pwd) 127 { 128 struct passwd *info; 129 130 info = malloc(sizeof(*info)); 131 if(info == NULL) 132 return NULL; 133 info->pw_name = strdup(pwd->pw_name); 134 info->pw_passwd = strdup(pwd->pw_passwd); 135 info->pw_uid = pwd->pw_uid; 136 info->pw_gid = pwd->pw_gid; 137 info->pw_dir = strdup(pwd->pw_dir); 138 info->pw_shell = strdup(pwd->pw_shell); 139 if(info->pw_name == NULL || info->pw_passwd == NULL || 140 info->pw_dir == NULL || info->pw_shell == NULL) { 141 free_info (info); 142 return NULL; 143 } 144 return info; 145 } 146 147 #if defined(KRB4) || defined(KRB5) 148 static void 149 set_tkfile() 150 { 151 #ifndef TKT_ROOT 152 #define TKT_ROOT "/tmp/tkt" 153 #endif 154 int fd; 155 if(*tkfile != '\0') 156 return; 157 snprintf(tkfile, sizeof(tkfile), "%s_XXXXXX", TKT_ROOT); 158 fd = mkstemp(tkfile); 159 if(fd >= 0) 160 close(fd); 161 #ifdef KRB4 162 krb_set_tkt_string(tkfile); 163 #endif 164 } 165 #endif 166 167 #ifdef KRB5 168 static krb5_context context; 169 static krb5_ccache ccache; 170 171 static int 172 krb5_verify(const struct passwd *login_info, 173 const struct passwd *su_info, 174 const char *kerberos_instance) 175 { 176 krb5_error_code ret; 177 krb5_principal p; 178 char *login_name = NULL; 179 180 #if defined(HAVE_GETLOGIN) && !defined(POSIX_GETLOGIN) 181 login_name = getlogin(); 182 #endif 183 ret = krb5_init_context (&context); 184 if (ret) { 185 #if 0 186 warnx("krb5_init_context failed: %d", ret); 187 #endif 188 return 1; 189 } 190 191 if (login_name == NULL || strcmp (login_name, "root") == 0) 192 login_name = login_info->pw_name; 193 if (strcmp (su_info->pw_name, "root") == 0) 194 ret = krb5_make_principal(context, &p, NULL, 195 login_name, 196 kerberos_instance, 197 NULL); 198 else 199 ret = krb5_make_principal(context, &p, NULL, 200 #ifdef _FREEFALL_CONFIG 201 login_name, 202 #else 203 su_info->pw_name, 204 #endif 205 NULL); 206 if(ret) 207 return 1; 208 209 if( 210 #ifndef _FREEFALL_CONFIG 211 su_info->pw_uid != 0 || 212 #endif 213 krb5_kuserok(context, p, su_info->pw_name)) { 214 ret = krb5_cc_gen_new(context, &krb5_mcc_ops, &ccache); 215 if(ret) { 216 #if 1 217 krb5_warn(context, ret, "krb5_cc_gen_new"); 218 #endif 219 krb5_free_principal (context, p); 220 return 1; 221 } 222 ret = krb5_verify_user_lrealm(context, p, ccache, NULL, TRUE, NULL); 223 krb5_free_principal (context, p); 224 if(ret) { 225 krb5_cc_destroy(context, ccache); 226 switch (ret) { 227 case KRB5_LIBOS_PWDINTR : 228 break; 229 case KRB5KRB_AP_ERR_BAD_INTEGRITY: 230 case KRB5KRB_AP_ERR_MODIFIED: 231 krb5_warnx(context, "Password incorrect"); 232 break; 233 default : 234 krb5_warn(context, ret, "krb5_verify_user"); 235 break; 236 } 237 return 1; 238 } 239 return 0; 240 } 241 krb5_free_principal (context, p); 242 return 1; 243 } 244 245 static int 246 krb5_start_session(void) 247 { 248 krb5_ccache ccache2; 249 char *cc_name; 250 int ret; 251 252 ret = krb5_cc_gen_new(context, &krb5_fcc_ops, &ccache2); 253 if (ret) { 254 krb5_cc_destroy(context, ccache); 255 return 1; 256 } 257 258 ret = krb5_cc_copy_cache(context, ccache, ccache2); 259 260 asprintf(&cc_name, "%s:%s", krb5_cc_get_type(context, ccache2), 261 krb5_cc_get_name(context, ccache2)); 262 esetenv("KRB5CCNAME", cc_name, 1); 263 264 /* we want to export this even if we don't directly support KRB4 */ 265 set_tkfile(); 266 esetenv("KRBTKFILE", tkfile, 1); 267 268 /* convert creds? */ 269 if(k_hasafs()) { 270 if (k_setpag() == 0) 271 krb5_afslog(context, ccache2, NULL, NULL); 272 } 273 274 krb5_cc_close(context, ccache2); 275 krb5_cc_destroy(context, ccache); 276 return 0; 277 } 278 #endif 279 280 #ifdef KRB4 281 282 static int 283 krb_verify(const struct passwd *login_info, 284 const struct passwd *su_info, 285 const char *kerberos_instance) 286 { 287 int ret; 288 char *login_name = NULL; 289 char *name, *instance, realm[REALM_SZ]; 290 291 #if defined(HAVE_GETLOGIN) && !defined(POSIX_GETLOGIN) 292 login_name = getlogin(); 293 #endif 294 295 ret = krb_get_lrealm(realm, 1); 296 297 if (login_name == NULL || strcmp (login_name, "root") == 0) 298 login_name = login_info->pw_name; 299 if (strcmp (su_info->pw_name, "root") == 0) { 300 name = login_name; 301 instance = (char*)kerberos_instance; 302 } else { 303 name = su_info->pw_name; 304 instance = ""; 305 } 306 307 if(su_info->pw_uid != 0 || 308 krb_kuserok(name, instance, realm, su_info->pw_name) == 0) { 309 char password[128]; 310 char *prompt; 311 asprintf (&prompt, 312 "%s's Password: ", 313 krb_unparse_name_long (name, instance, realm)); 314 if (des_read_pw_string (password, sizeof (password), prompt, 0)) { 315 memset (password, 0, sizeof (password)); 316 free(prompt); 317 return (1); 318 } 319 free(prompt); 320 if (strlen(password) == 0) 321 return (1); /* Empty passwords are not allowed */ 322 set_tkfile(); 323 setuid(geteuid()); /* need to run as root here */ 324 ret = krb_verify_user(name, instance, realm, password, 325 KRB_VERIFY_SECURE, NULL); 326 memset(password, 0, sizeof(password)); 327 328 if(ret) { 329 warnx("%s", krb_get_err_text(ret)); 330 return 1; 331 } 332 chown (tkt_string(), su_info->pw_uid, su_info->pw_gid); 333 return 0; 334 } 335 return 1; 336 } 337 338 339 static int 340 krb_start_session(void) 341 { 342 esetenv("KRBTKFILE", tkfile, 1); 343 344 /* convert creds? */ 345 if(k_hasafs() && k_setpag() == 0) 346 krb_afslog(NULL, NULL); 347 348 return 0; 349 } 350 #endif 351 352 static int 353 verify_unix(struct passwd *su) 354 { 355 char prompt[128]; 356 char pw_buf[1024]; 357 char *pw; 358 int r; 359 if(su->pw_passwd != NULL && *su->pw_passwd != '\0') { 360 snprintf(prompt, sizeof(prompt), "%s's password: ", su->pw_name); 361 r = des_read_pw_string(pw_buf, sizeof(pw_buf), prompt, 0); 362 if(r != 0) 363 exit(0); 364 pw = crypt(pw_buf, su->pw_passwd); 365 memset(pw_buf, 0, sizeof(pw_buf)); 366 if(strcmp(pw, su->pw_passwd) != 0) 367 return 1; 368 } 369 return 0; 370 } 371 372 int 373 main(int argc, char **argv) 374 { 375 int i, optind = 0; 376 char *su_user; 377 struct passwd *su_info; 378 struct passwd *login_info; 379 380 struct passwd *pwd; 381 382 char *shell; 383 384 int ok = 0; 385 int kerberos_error=1; 386 387 setprogname (argv[0]); 388 389 if(getarg(args, sizeof(args) / sizeof(args[0]), argc, argv, &optind)) 390 usage(1); 391 392 for (i=0; i < optind; i++) 393 if (strcmp(argv[i], "-") == 0) { 394 full_login = 1; 395 break; 396 } 397 398 if(help_flag) 399 usage(0); 400 if(version_flag) { 401 print_version(NULL); 402 exit(0); 403 } 404 if(optind >= argc) 405 su_user = "root"; 406 else 407 su_user = argv[optind++]; 408 409 pwd = k_getpwnam(su_user); 410 if(pwd == NULL) 411 errx (1, "unknown login %s", su_user); 412 if (pwd->pw_uid == 0 && strcmp ("root", su_user) != 0) { 413 syslog (LOG_ALERT, "NIS attack, user %s has uid 0", su_user); 414 errx (1, "unknown login %s", su_user); 415 } 416 su_info = dup_info(pwd); 417 if (su_info == NULL) 418 errx (1, "malloc: out of memory"); 419 420 pwd = getpwuid(getuid()); 421 if(pwd == NULL) 422 errx(1, "who are you?"); 423 login_info = dup_info(pwd); 424 if (login_info == NULL) 425 errx (1, "malloc: out of memory"); 426 if(env_flag) 427 shell = login_info->pw_shell; 428 else 429 shell = su_info->pw_shell; 430 if(shell == NULL || *shell == '\0') 431 shell = _PATH_BSHELL; 432 433 434 #ifdef KRB5 435 if(kerberos_flag && ok == 0 && 436 (kerberos_error=krb5_verify(login_info, su_info, kerberos_instance)) == 0) 437 ok = 5; 438 #endif 439 #ifdef KRB4 440 if(kerberos_flag && ok == 0 && 441 (kerberos_error = krb_verify(login_info, su_info, kerberos_instance)) == 0) 442 ok = 4; 443 #endif 444 445 if(ok == 0 && login_info->pw_uid 446 #ifndef _FREEFALL_CONFIG 447 && verify_unix(su_info) != 0 448 #endif 449 ) { 450 printf("Sorry!\n"); 451 exit(1); 452 } 453 454 #ifdef HAVE_GETSPNAM 455 { struct spwd *sp; 456 long today; 457 458 sp = getspnam(su_info->pw_name); 459 if (sp != NULL) { 460 today = time(0)/(24L * 60 * 60); 461 if (sp->sp_expire > 0) { 462 if (today >= sp->sp_expire) { 463 if (login_info->pw_uid) 464 errx(1,"Your account has expired."); 465 else 466 printf("Your account has expired."); 467 } 468 else if (sp->sp_expire - today < 14) 469 printf("Your account will expire in %d days.\n", 470 (int)(sp->sp_expire - today)); 471 } 472 if (sp->sp_max > 0) { 473 if (today >= sp->sp_lstchg + sp->sp_max) { 474 if (login_info->pw_uid) 475 errx(1,"Your password has expired. Choose a new one."); 476 else 477 printf("Your password has expired. Choose a new one."); 478 } 479 else if (today >= sp->sp_lstchg + sp->sp_max - sp->sp_warn) 480 printf("Your account will expire in %d days.\n", 481 (int)(sp->sp_lstchg + sp->sp_max -today)); 482 } 483 } 484 } 485 #endif 486 { 487 char *tty = ttyname (STDERR_FILENO); 488 syslog (LOG_NOTICE | LOG_AUTH, tty ? "%s to %s" : "%s to %s on %s", 489 login_info->pw_name, su_info->pw_name, tty); 490 } 491 492 493 if(!env_flag) { 494 if(full_login) { 495 char *t = getenv ("TERM"); 496 497 environ = malloc (10 * sizeof (char *)); 498 if (environ == NULL) 499 err (1, "malloc"); 500 environ[0] = NULL; 501 esetenv ("PATH", _PATH_DEFPATH, 1); 502 if (t) 503 esetenv ("TERM", t, 1); 504 if (chdir (su_info->pw_dir) < 0) 505 errx (1, "no directory"); 506 } 507 if (full_login || su_info->pw_uid) 508 esetenv ("USER", su_info->pw_name, 1); 509 esetenv("HOME", su_info->pw_dir, 1); 510 esetenv("SHELL", shell, 1); 511 } 512 513 { 514 int i; 515 char **args; 516 char *p; 517 518 p = strrchr(shell, '/'); 519 if(p) 520 p++; 521 else 522 p = shell; 523 524 if (strcmp(p, "csh") != 0) 525 csh_f_flag = 0; 526 527 args = malloc(((cmd ? 2 : 0) + 1 + argc - optind + 1 + csh_f_flag) * sizeof(*args)); 528 if (args == NULL) 529 err (1, "malloc"); 530 i = 0; 531 if(full_login) 532 asprintf(&args[i++], "-%s", p); 533 else 534 args[i++] = p; 535 if (cmd) { 536 args[i++] = "-c"; 537 args[i++] = cmd; 538 } 539 540 if (csh_f_flag) 541 args[i++] = "-f"; 542 543 for (argv += optind; *argv; ++argv) 544 args[i++] = *argv; 545 args[i] = NULL; 546 547 if(setgid(su_info->pw_gid) < 0) 548 err(1, "setgid"); 549 if (initgroups (su_info->pw_name, su_info->pw_gid) < 0) 550 err (1, "initgroups"); 551 if(setuid(su_info->pw_uid) < 0 552 || (su_info->pw_uid != 0 && setuid(0) == 0)) 553 err(1, "setuid"); 554 555 #ifdef KRB5 556 if (ok == 5) 557 krb5_start_session(); 558 #endif 559 #ifdef KRB4 560 if (ok == 4) 561 krb_start_session(); 562 #endif 563 execv(shell, args); 564 } 565 566 exit(1); 567 } 568