su.c revision 103423
139234Sgibbs/* 234480Sjulian * Copyright (c) 1999 - 2002 Kungliga Tekniska H�gskolan 334480Sjulian * (Royal Institute of Technology, Stockholm, Sweden). 434480Sjulian * All rights reserved. 534480Sjulian * 634480Sjulian * Redistribution and use in source and binary forms, with or without 734480Sjulian * modification, are permitted provided that the following conditions 834480Sjulian * are met: 934480Sjulian * 1034480Sjulian * 1. Redistributions of source code must retain the above copyright 1134480Sjulian * notice, this list of conditions and the following disclaimer. 1234480Sjulian * 1334480Sjulian * 2. Redistributions in binary form must reproduce the above copyright 1434480Sjulian * notice, this list of conditions and the following disclaimer in the 1534480Sjulian * documentation and/or other materials provided with the distribution. 1634480Sjulian * 1734480Sjulian * 3. Neither the name of KTH nor the names of its contributors may be 1834480Sjulian * used to endorse or promote products derived from this software without 1934480Sjulian * specific prior written permission. 2034480Sjulian * 2134480Sjulian * THIS SOFTWARE IS PROVIDED BY KTH AND ITS CONTRIBUTORS ``AS IS'' AND ANY 2234480Sjulian * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 2334480Sjulian * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 2434480Sjulian * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL KTH OR ITS CONTRIBUTORS BE 2534480Sjulian * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 2634480Sjulian * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 2734480Sjulian * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR 2834480Sjulian * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 2934480Sjulian * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 3039234Sgibbs * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 3139234Sgibbs * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ 3234480Sjulian 3334480Sjulian#include <config.h> 3434480Sjulian 3534480SjulianRCSID("$Id: su.c,v 1.25 2002/09/10 20:03:47 joda Exp $"); 3645791Speter 3734480Sjulian#include <stdio.h> 3834480Sjulian#include <stdlib.h> 3936129Sgibbs#include <string.h> 4036129Sgibbs 4134480Sjulian#include <syslog.h> 4234480Sjulian 4334480Sjulian#ifdef HAVE_PATHS_H 4434480Sjulian#include <paths.h> 4534480Sjulian#endif 4634480Sjulian 4734480Sjulian#ifdef HAVE_SHADOW_H 4834480Sjulian#include <shadow.h> 4945791Speter#endif 5045791Speter 5134480Sjulian#include <pwd.h> 5239234Sgibbs 5339234Sgibbs#include "crypto-headers.h" 5445791Speter#ifdef KRB5 5545791Speter#include <krb5.h> 5634480Sjulian#endif 5739234Sgibbs#ifdef KRB4 5839234Sgibbs#include <krb.h> 5939234Sgibbs#include <kafs.h> 6039234Sgibbs#endif 6134480Sjulian#include <err.h> 6234480Sjulian#include <roken.h> 6334480Sjulian#include <getarg.h> 6434480Sjulian 6534480Sjulian#ifndef _PATH_DEFPATH 6634480Sjulian#define _PATH_DEFPATH "/usr/bin:/bin" 6734480Sjulian#endif 6834480Sjulian 6934480Sjulian#ifndef _PATH_BSHELL 7034480Sjulian#define _PATH_BSHELL "/bin/sh" 7134480Sjulian#endif 7234480Sjulian 7334480Sjulianint kerberos_flag = 1; 7439234Sgibbsint csh_f_flag; 7545791Speterint full_login; 7634480Sjulianint env_flag; 7745791Speterchar *kerberos_instance = "root"; 7839234Sgibbsint help_flag; 7939234Sgibbsint version_flag; 8039234Sgibbschar *cmd; 8134480Sjulianchar tkfile[256]; 8245791Speter 8345791Speterstruct getargs args[] = { 8445791Speter { "kerberos", 'K', arg_negative_flag, &kerberos_flag, 8545791Speter "don't use kerberos" }, 8634480Sjulian { NULL, 'f', arg_flag, &csh_f_flag, 8745791Speter "don't read .cshrc" }, 8845791Speter { "full", 'l', arg_flag, &full_login, 8945791Speter "simulate full login" }, 9045791Speter { NULL, 'm', arg_flag, &env_flag, 9134480Sjulian "leave environment unmodified" }, 9245791Speter { "instance", 'i', arg_string, &kerberos_instance, 9334480Sjulian "root instance to use" }, 9445791Speter { "command", 'c', arg_string, &cmd, 9545791Speter "command to execute" }, 9645791Speter { "help", 'h', arg_flag, &help_flag }, 9745791Speter { "version", 0, arg_flag, &version_flag }, 9845791Speter}; 9945791Speter 10045791Speter 10145791Speterstatic void 10245791Speterusage (int ret) 10345791Speter{ 10445791Speter arg_printusage (args, 10545791Speter sizeof(args)/sizeof(*args), 10645791Speter NULL, 10745791Speter "[login [shell arguments]]"); 10845791Speter exit (ret); 10945791Speter} 11045791Speter 11145791Speterstatic void 11245791Speterfree_info(struct passwd *p) 11334480Sjulian{ 11445791Speter free (p->pw_name); 11545791Speter free (p->pw_passwd); 11645791Speter free (p->pw_dir); 11734480Sjulian free (p->pw_shell); 11834480Sjulian free (p); 11945791Speter} 12045791Speter 12134480Sjulianstatic struct passwd* 12234480Sjuliandup_info(const struct passwd *pwd) 12345791Speter{ 12445791Speter struct passwd *info; 12545791Speter 12639234Sgibbs info = malloc(sizeof(*info)); 12739234Sgibbs if(info == NULL) 12845791Speter return NULL; 12945791Speter info->pw_name = strdup(pwd->pw_name); 13034480Sjulian info->pw_passwd = strdup(pwd->pw_passwd); 13145791Speter info->pw_uid = pwd->pw_uid; 13245791Speter info->pw_gid = pwd->pw_gid; 13345791Speter info->pw_dir = strdup(pwd->pw_dir); 13445791Speter info->pw_shell = strdup(pwd->pw_shell); 13545791Speter if(info->pw_name == NULL || info->pw_passwd == NULL || 13645791Speter info->pw_dir == NULL || info->pw_shell == NULL) { 13734480Sjulian free_info (info); 13834480Sjulian return NULL; 13945791Speter } 14045791Speter return info; 14134480Sjulian} 14245791Speter 14345791Speter#if defined(KRB4) || defined(KRB5) 14439234Sgibbsstatic void 14545791Speterset_tkfile() 14634480Sjulian{ 14739234Sgibbs#ifndef TKT_ROOT 14839234Sgibbs#define TKT_ROOT "/tmp/tkt" 14939234Sgibbs#endif 15039234Sgibbs int fd; 15139234Sgibbs if(*tkfile != '\0') 15239234Sgibbs return; 15339234Sgibbs snprintf(tkfile, sizeof(tkfile), "%s_XXXXXX", TKT_ROOT); 15439234Sgibbs fd = mkstemp(tkfile); 15539234Sgibbs if(fd >= 0) 15639234Sgibbs close(fd); 15739234Sgibbs#ifdef KRB4 15845791Speter krb_set_tkt_string(tkfile); 15934480Sjulian#endif 16034480Sjulian} 16145791Speter#endif 16245791Speter 16345791Speter#ifdef KRB5 16445791Speterstatic krb5_context context; 16545791Speterstatic krb5_ccache ccache; 16645791Speter 16734480Sjulianstatic int 16834480Sjuliankrb5_verify(const struct passwd *login_info, 16939234Sgibbs const struct passwd *su_info, 17039234Sgibbs const char *kerberos_instance) 17139234Sgibbs{ 17245791Speter krb5_error_code ret; 17334480Sjulian krb5_principal p; 17434480Sjulian char *login_name = NULL; 17539234Sgibbs 17639234Sgibbs#if defined(HAVE_GETLOGIN) && !defined(POSIX_GETLOGIN) 17745791Speter login_name = getlogin(); 17845791Speter#endif 17939234Sgibbs ret = krb5_init_context (&context); 18034480Sjulian if (ret) { 18134480Sjulian#if 0 18245791Speter warnx("krb5_init_context failed: %d", ret); 18345791Speter#endif 18445791Speter return 1; 18545791Speter } 18645791Speter 18745791Speter if (login_name == NULL || strcmp (login_name, "root") == 0) 18845791Speter login_name = login_info->pw_name; 18934480Sjulian if (strcmp (su_info->pw_name, "root") == 0) 19034480Sjulian ret = krb5_make_principal(context, &p, NULL, 19134480Sjulian login_name, 19234480Sjulian kerberos_instance, 19334480Sjulian NULL); 19434480Sjulian else 19534480Sjulian ret = krb5_make_principal(context, &p, NULL, 19634480Sjulian su_info->pw_name, 19734480Sjulian NULL); 19834480Sjulian if(ret) 19934480Sjulian return 1; 20034480Sjulian 20134480Sjulian if(su_info->pw_uid != 0 || krb5_kuserok(context, p, su_info->pw_name)) { 20234480Sjulian ret = krb5_cc_gen_new(context, &krb5_mcc_ops, &ccache); 20334480Sjulian if(ret) { 20434480Sjulian#if 1 20534480Sjulian krb5_warn(context, ret, "krb5_cc_gen_new"); 20634480Sjulian#endif 20734480Sjulian krb5_free_principal (context, p); 20834480Sjulian return 1; 20934480Sjulian } 21034480Sjulian ret = krb5_verify_user_lrealm(context, p, ccache, NULL, TRUE, NULL); 21134480Sjulian krb5_free_principal (context, p); 21234480Sjulian if(ret) { 21334480Sjulian krb5_cc_destroy(context, ccache); 21434480Sjulian switch (ret) { 21534480Sjulian case KRB5_LIBOS_PWDINTR : 21634480Sjulian break; 21734480Sjulian case KRB5KRB_AP_ERR_BAD_INTEGRITY: 21834480Sjulian case KRB5KRB_AP_ERR_MODIFIED: 21934480Sjulian krb5_warnx(context, "Password incorrect"); 22034480Sjulian break; 22134480Sjulian default : 22234480Sjulian krb5_warn(context, ret, "krb5_verify_user"); 22334480Sjulian break; 22434480Sjulian } 22534480Sjulian return 1; 22634480Sjulian } 22734480Sjulian return 0; 22834480Sjulian } 22934480Sjulian krb5_free_principal (context, p); 23034480Sjulian return 1; 23134480Sjulian} 23234480Sjulian 23334480Sjulianstatic int 23434480Sjuliankrb5_start_session(void) 23534480Sjulian{ 23634480Sjulian krb5_ccache ccache2; 23734480Sjulian char *cc_name; 23834480Sjulian int ret; 23934480Sjulian 24034480Sjulian ret = krb5_cc_gen_new(context, &krb5_fcc_ops, &ccache2); 24134480Sjulian if (ret) { 24245791Speter krb5_cc_destroy(context, ccache); 24345791Speter return 1; 24445791Speter } 24545791Speter 24645791Speter ret = krb5_cc_copy_cache(context, ccache, ccache2); 24745791Speter 24845791Speter asprintf(&cc_name, "%s:%s", krb5_cc_get_type(context, ccache2), 24945791Speter krb5_cc_get_name(context, ccache2)); 25045791Speter esetenv("KRB5CCNAME", cc_name, 1); 25145791Speter 25245791Speter /* we want to export this even if we don't directly support KRB4 */ 25345791Speter set_tkfile(); 25445791Speter esetenv("KRBTKFILE", tkfile, 1); 25545791Speter 25645791Speter#ifdef KRB4 25745791Speter /* convert creds? */ 25845791Speter if(k_hasafs()) { 25945791Speter if (k_setpag() == 0) 26045791Speter krb5_afslog(context, ccache2, NULL, NULL); 26136129Sgibbs } 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 272static int 273krb_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 329static int 330krb_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 342static int 343verify_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 362int 363main(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