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.24 2002/02/19 13:01:15 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 #ifdef HAVE_OPENSSL 54 #include <openssl/des.h> 55 #else 56 #include <des.h> 57 #endif 58 #ifdef KRB5 59 #include <krb5.h> 60 #endif 61 #ifdef KRB4 62 #include <krb.h> 63 #include <kafs.h> 64 #endif 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 su_info->pw_name, 201 NULL); 202 if(ret) 203 return 1; 204 205 if(su_info->pw_uid != 0 || krb5_kuserok(context, p, su_info->pw_name)) { 206 ret = krb5_cc_gen_new(context, &krb5_mcc_ops, &ccache); 207 if(ret) { 208 #if 1 209 krb5_warn(context, ret, "krb5_cc_gen_new"); 210 #endif 211 krb5_free_principal (context, p); 212 return 1; 213 } 214 ret = krb5_verify_user_lrealm(context, p, ccache, NULL, TRUE, NULL); 215 krb5_free_principal (context, p); 216 if(ret) { 217 krb5_cc_destroy(context, ccache); 218 switch (ret) { 219 case KRB5_LIBOS_PWDINTR : 220 break; 221 case KRB5KRB_AP_ERR_BAD_INTEGRITY: 222 case KRB5KRB_AP_ERR_MODIFIED: 223 krb5_warnx(context, "Password incorrect"); 224 break; 225 default : 226 krb5_warn(context, ret, "krb5_verify_user"); 227 break; 228 } 229 return 1; 230 } 231 return 0; 232 } 233 krb5_free_principal (context, p); 234 return 1; 235 } 236 237 static int 238 krb5_start_session(void) 239 { 240 krb5_ccache ccache2; 241 char *cc_name; 242 int ret; 243 244 ret = krb5_cc_gen_new(context, &krb5_fcc_ops, &ccache2); 245 if (ret) { 246 krb5_cc_destroy(context, ccache); 247 return 1; 248 } 249 250 ret = krb5_cc_copy_cache(context, ccache, ccache2); 251 252 asprintf(&cc_name, "%s:%s", krb5_cc_get_type(context, ccache2), 253 krb5_cc_get_name(context, ccache2)); 254 esetenv("KRB5CCNAME", cc_name, 1); 255 256 /* we want to export this even if we don't directly support KRB4 */ 257 set_tkfile(); 258 esetenv("KRBTKFILE", tkfile, 1); 259 260 #ifdef KRB4 261 /* convert creds? */ 262 if(k_hasafs()) { 263 if (k_setpag() == 0) 264 krb5_afslog(context, ccache2, NULL, NULL); 265 } 266 #endif 267 268 krb5_cc_close(context, ccache2); 269 krb5_cc_destroy(context, ccache); 270 return 0; 271 } 272 #endif 273 274 #ifdef KRB4 275 276 static int 277 krb_verify(const struct passwd *login_info, 278 const struct passwd *su_info, 279 const char *kerberos_instance) 280 { 281 int ret; 282 char *login_name = NULL; 283 char *name, *instance, realm[REALM_SZ]; 284 285 #if defined(HAVE_GETLOGIN) && !defined(POSIX_GETLOGIN) 286 login_name = getlogin(); 287 #endif 288 289 ret = krb_get_lrealm(realm, 1); 290 291 if (login_name == NULL || strcmp (login_name, "root") == 0) 292 login_name = login_info->pw_name; 293 if (strcmp (su_info->pw_name, "root") == 0) { 294 name = login_name; 295 instance = (char*)kerberos_instance; 296 } else { 297 name = su_info->pw_name; 298 instance = ""; 299 } 300 301 if(su_info->pw_uid != 0 || 302 krb_kuserok(name, instance, realm, su_info->pw_name) == 0) { 303 char password[128]; 304 char *prompt; 305 asprintf (&prompt, 306 "%s's Password: ", 307 krb_unparse_name_long (name, instance, realm)); 308 if (des_read_pw_string (password, sizeof (password), prompt, 0)) { 309 memset (password, 0, sizeof (password)); 310 free(prompt); 311 return (1); 312 } 313 free(prompt); 314 if (strlen(password) == 0) 315 return (1); /* Empty passwords are not allowed */ 316 set_tkfile(); 317 setuid(geteuid()); /* need to run as root here */ 318 ret = krb_verify_user(name, instance, realm, password, 319 KRB_VERIFY_SECURE, NULL); 320 memset(password, 0, sizeof(password)); 321 322 if(ret) { 323 warnx("%s", krb_get_err_text(ret)); 324 return 1; 325 } 326 chown (tkt_string(), su_info->pw_uid, su_info->pw_gid); 327 return 0; 328 } 329 return 1; 330 } 331 332 333 static int 334 krb_start_session(void) 335 { 336 esetenv("KRBTKFILE", tkfile, 1); 337 338 /* convert creds? */ 339 if(k_hasafs() && k_setpag() == 0) 340 krb_afslog(NULL, NULL); 341 342 return 0; 343 } 344 #endif 345 346 static int 347 verify_unix(struct passwd *su) 348 { 349 char prompt[128]; 350 char pw_buf[1024]; 351 char *pw; 352 int r; 353 if(su->pw_passwd != NULL && *su->pw_passwd != '\0') { 354 snprintf(prompt, sizeof(prompt), "%s's password: ", su->pw_name); 355 r = des_read_pw_string(pw_buf, sizeof(pw_buf), prompt, 0); 356 if(r != 0) 357 exit(0); 358 pw = crypt(pw_buf, su->pw_passwd); 359 memset(pw_buf, 0, sizeof(pw_buf)); 360 if(strcmp(pw, su->pw_passwd) != 0) 361 return 1; 362 } 363 return 0; 364 } 365 366 int 367 main(int argc, char **argv) 368 { 369 int i, optind = 0; 370 char *su_user; 371 struct passwd *su_info; 372 struct passwd *login_info; 373 374 struct passwd *pwd; 375 376 char *shell; 377 378 int ok = 0; 379 int kerberos_error=1; 380 381 setprogname (argv[0]); 382 383 if(getarg(args, sizeof(args) / sizeof(args[0]), argc, argv, &optind)) 384 usage(1); 385 386 for (i=0; i < optind; i++) 387 if (strcmp(argv[i], "-") == 0) { 388 full_login = 1; 389 break; 390 } 391 392 if(help_flag) 393 usage(0); 394 if(version_flag) { 395 print_version(NULL); 396 exit(0); 397 } 398 if(optind >= argc) 399 su_user = "root"; 400 else 401 su_user = argv[optind++]; 402 403 pwd = k_getpwnam(su_user); 404 if(pwd == NULL) 405 errx (1, "unknown login %s", su_user); 406 if (pwd->pw_uid == 0 && strcmp ("root", su_user) != 0) { 407 syslog (LOG_ALERT, "NIS attack, user %s has uid 0", su_user); 408 errx (1, "unknown login %s", su_user); 409 } 410 su_info = dup_info(pwd); 411 if (su_info == NULL) 412 errx (1, "malloc: out of memory"); 413 414 pwd = getpwuid(getuid()); 415 if(pwd == NULL) 416 errx(1, "who are you?"); 417 login_info = dup_info(pwd); 418 if (login_info == NULL) 419 errx (1, "malloc: out of memory"); 420 if(env_flag) 421 shell = login_info->pw_shell; 422 else 423 shell = su_info->pw_shell; 424 if(shell == NULL || *shell == '\0') 425 shell = _PATH_BSHELL; 426 427 428 #ifdef KRB5 429 if(kerberos_flag && ok == 0 && 430 (kerberos_error=krb5_verify(login_info, su_info, kerberos_instance)) == 0) 431 ok = 5; 432 #endif 433 #ifdef KRB4 434 if(kerberos_flag && ok == 0 && 435 (kerberos_error = krb_verify(login_info, su_info, kerberos_instance)) == 0) 436 ok = 4; 437 #endif 438 439 if(ok == 0 && login_info->pw_uid && verify_unix(su_info) != 0) { 440 printf("Sorry!\n"); 441 exit(1); 442 } 443 444 #ifdef HAVE_GETSPNAM 445 { struct spwd *sp; 446 long today; 447 448 sp = getspnam(su_info->pw_name); 449 if (sp != NULL) { 450 today = time(0)/(24L * 60 * 60); 451 if (sp->sp_expire > 0) { 452 if (today >= sp->sp_expire) { 453 if (login_info->pw_uid) 454 errx(1,"Your account has expired."); 455 else 456 printf("Your account has expired."); 457 } 458 else if (sp->sp_expire - today < 14) 459 printf("Your account will expire in %d days.\n", 460 (int)(sp->sp_expire - today)); 461 } 462 if (sp->sp_max > 0) { 463 if (today >= sp->sp_lstchg + sp->sp_max) { 464 if (login_info->pw_uid) 465 errx(1,"Your password has expired. Choose a new one."); 466 else 467 printf("Your password has expired. Choose a new one."); 468 } 469 else if (today >= sp->sp_lstchg + sp->sp_max - sp->sp_warn) 470 printf("Your account will expire in %d days.\n", 471 (int)(sp->sp_lstchg + sp->sp_max -today)); 472 } 473 } 474 } 475 #endif 476 { 477 char *tty = ttyname (STDERR_FILENO); 478 syslog (LOG_NOTICE | LOG_AUTH, tty ? "%s to %s" : "%s to %s on %s", 479 login_info->pw_name, su_info->pw_name, tty); 480 } 481 482 483 if(!env_flag) { 484 if(full_login) { 485 char *t = getenv ("TERM"); 486 487 environ = malloc (10 * sizeof (char *)); 488 if (environ == NULL) 489 err (1, "malloc"); 490 environ[0] = NULL; 491 esetenv ("PATH", _PATH_DEFPATH, 1); 492 if (t) 493 esetenv ("TERM", t, 1); 494 if (chdir (su_info->pw_dir) < 0) 495 errx (1, "no directory"); 496 } 497 if (full_login || su_info->pw_uid) 498 esetenv ("USER", su_info->pw_name, 1); 499 esetenv("HOME", su_info->pw_dir, 1); 500 esetenv("SHELL", shell, 1); 501 } 502 503 { 504 int i; 505 char **args; 506 char *p; 507 508 p = strrchr(shell, '/'); 509 if(p) 510 p++; 511 else 512 p = shell; 513 514 if (strcmp(p, "csh") != 0) 515 csh_f_flag = 0; 516 517 args = malloc(((cmd ? 2 : 0) + 1 + argc - optind + 1 + csh_f_flag) * sizeof(*args)); 518 if (args == NULL) 519 err (1, "malloc"); 520 i = 0; 521 if(full_login) 522 asprintf(&args[i++], "-%s", p); 523 else 524 args[i++] = p; 525 if (cmd) { 526 args[i++] = "-c"; 527 args[i++] = cmd; 528 } 529 530 if (csh_f_flag) 531 args[i++] = "-f"; 532 533 for (argv += optind; *argv; ++argv) 534 args[i++] = *argv; 535 args[i] = NULL; 536 537 if(setgid(su_info->pw_gid) < 0) 538 err(1, "setgid"); 539 if (initgroups (su_info->pw_name, su_info->pw_gid) < 0) 540 err (1, "initgroups"); 541 if(setuid(su_info->pw_uid) < 0 542 || (su_info->pw_uid != 0 && setuid(0) == 0)) 543 err(1, "setuid"); 544 545 #ifdef KRB5 546 if (ok == 5) 547 krb5_start_session(); 548 #endif 549 #ifdef KRB4 550 if (ok == 4) 551 krb_start_session(); 552 #endif 553 { 554 char **p; 555 for(p = args; *p; p++) 556 printf("%s ", *p); 557 printf("\n"); 558 } 559 execv(shell, args); 560 } 561 562 exit(1); 563 } 564