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