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