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