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