1 /* 2 * Copyright (c) 1999 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.10 1999/09/28 02:34:17 assar 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 <krb5.h> 54 #include <kafs.h> 55 #include <err.h> 56 #include <roken.h> 57 #include <getarg.h> 58 #include <kafs.h> 59 60 #ifndef _PATH_DEFPATH 61 #define _PATH_DEFPATH "/usr/bin:/bin" 62 #endif 63 64 #ifndef _PATH_BSHELL 65 #define _PATH_BSHELL "/bin/sh" 66 #endif 67 68 int kerberos_flag = 1; 69 int csh_f_flag; 70 int full_login; 71 int env_flag; 72 char *kerberos_instance = "root"; 73 int help_flag; 74 int version_flag; 75 char *cmd; 76 77 struct getargs args[] = { 78 { "kerberos", 'K', arg_negative_flag, &kerberos_flag, 79 "don't use kerberos" }, 80 { NULL, 'f', arg_flag, &csh_f_flag, 81 "don't read .cshrc" }, 82 { "full", 'l', arg_flag, &full_login, 83 "simulate full login" }, 84 { NULL, 'm', arg_flag, &env_flag, 85 "leave environment unmodified" }, 86 { "instance", 'i', arg_string, &kerberos_instance, 87 "root instance to use" }, 88 { "command", 'c', arg_string, &cmd, 89 "command to execute" }, 90 { "help", 'h', arg_flag, &help_flag }, 91 { "version", 0, arg_flag, &version_flag }, 92 }; 93 94 95 static void 96 usage (int ret) 97 { 98 arg_printusage (args, 99 sizeof(args)/sizeof(*args), 100 NULL, 101 "[login [shell arguments]]"); 102 exit (ret); 103 } 104 105 static struct passwd* 106 make_info(struct passwd *pwd) 107 { 108 struct passwd *info; 109 info = malloc(sizeof(*info)); 110 if(info == NULL) 111 return NULL; 112 info->pw_name = strdup(pwd->pw_name); 113 info->pw_passwd = strdup(pwd->pw_passwd); 114 info->pw_uid = pwd->pw_uid; 115 info->pw_gid = pwd->pw_gid; 116 info->pw_dir = strdup(pwd->pw_dir); 117 info->pw_shell = strdup(pwd->pw_shell); 118 if(info->pw_name == NULL || info->pw_passwd == NULL || 119 info->pw_dir == NULL || info->pw_shell == NULL) 120 return NULL; 121 return info; 122 } 123 124 #ifdef KRB5 125 static krb5_context context; 126 static krb5_ccache ccache; 127 #endif 128 129 static int 130 krb5_verify(struct passwd *login_info, struct passwd *su_info, 131 const char *kerberos_instance) 132 { 133 #ifdef KRB5 134 krb5_error_code ret; 135 krb5_principal p; 136 137 ret = krb5_init_context (&context); 138 if (ret) { 139 #if 0 140 warnx("krb5_init_context failed: %u", ret); 141 #endif 142 return 1; 143 } 144 145 if (strcmp (su_info->pw_name, "root") == 0) 146 ret = krb5_make_principal(context, &p, NULL, 147 login_info->pw_name, 148 kerberos_instance, 149 NULL); 150 else 151 ret = krb5_make_principal(context, &p, NULL, 152 su_info->pw_name, 153 NULL); 154 if(ret) 155 return 1; 156 157 if(su_info->pw_uid != 0 || krb5_kuserok(context, p, su_info->pw_name)) { 158 ret = krb5_cc_gen_new(context, &krb5_mcc_ops, &ccache); 159 if(ret) { 160 #if 1 161 krb5_warn(context, ret, "krb5_cc_gen_new"); 162 #endif 163 return 1; 164 } 165 ret = krb5_verify_user_lrealm(context, p, ccache, NULL, TRUE, NULL); 166 if(ret) { 167 krb5_free_principal (context, p); 168 krb5_cc_destroy(context, ccache); 169 switch (ret) { 170 case KRB5KRB_AP_ERR_BAD_INTEGRITY: 171 case KRB5KRB_AP_ERR_MODIFIED: 172 krb5_warnx(context, "Password incorrect"); 173 break; 174 default : 175 krb5_warn(context, ret, "krb5_verify_user"); 176 break; 177 } 178 return 1; 179 } 180 return 0; 181 } 182 #endif 183 return 1; 184 } 185 186 #ifdef KRB5 187 static int 188 krb5_start_session(void) 189 { 190 krb5_ccache ccache2; 191 char *cc_name; 192 int ret; 193 194 ret = krb5_cc_gen_new(context, &krb5_fcc_ops, &ccache2); 195 if (ret) { 196 krb5_cc_destroy(context, ccache); 197 return 1; 198 } 199 200 ret = krb5_cc_copy_cache(context, ccache, ccache2); 201 202 asprintf(&cc_name, "%s:%s", krb5_cc_get_type(context, ccache2), 203 krb5_cc_get_name(context, ccache2)); 204 setenv("KRB5CCNAME", cc_name, 1); 205 206 #ifdef KRB4 207 if(k_hasafs()) { 208 if (k_setpag() == 0) 209 krb5_afslog(context, ccache2, NULL, NULL); 210 } 211 #endif 212 213 krb5_cc_close(context, ccache2); 214 krb5_cc_destroy(context, ccache); 215 return 0; 216 } 217 #endif 218 219 static int 220 verify_unix(struct passwd *su) 221 { 222 char prompt[128]; 223 char pw_buf[1024]; 224 char *pw; 225 int r; 226 if(su->pw_passwd != NULL && *su->pw_passwd != '\0') { 227 sprintf(prompt, "%s's password: ", su->pw_name); 228 r = des_read_pw_string(pw_buf, sizeof(pw_buf), prompt, 0); 229 if(r != 0) 230 exit(0); 231 pw = crypt(pw_buf, su->pw_passwd); 232 memset(pw_buf, 0, sizeof(pw_buf)); 233 if(strcmp(pw, su->pw_passwd) != 0) 234 return 1; 235 } 236 return 0; 237 } 238 239 int 240 main(int argc, char **argv) 241 { 242 int i, optind = 0; 243 char *su_user; 244 struct passwd *su_info; 245 char *login_user = NULL; 246 struct passwd *login_info; 247 248 struct passwd *pwd; 249 250 char *shell; 251 252 int ok = 0; 253 int kerberos_error=1; 254 255 set_progname (argv[0]); 256 257 if(getarg(args, sizeof(args) / sizeof(args[0]), argc, argv, &optind)) 258 usage(1); 259 260 for (i=0; i < optind; i++) 261 if (strcmp(argv[i], "-") == 0) { 262 full_login = 1; 263 break; 264 } 265 266 if(help_flag) 267 usage(0); 268 if(version_flag) { 269 print_version(NULL); 270 exit(0); 271 } 272 if(optind >= argc) 273 su_user = "root"; 274 else 275 su_user = argv[optind++]; 276 277 pwd = k_getpwnam(su_user); 278 if(pwd == NULL) 279 errx (1, "unknown login %s", su_user); 280 if (pwd->pw_uid == 0 && strcmp ("root", su_user) != 0) { 281 syslog (LOG_ALERT, "NIS attack, user %s has uid 0", su_user); 282 errx (1, "unknown login %s", su_user); 283 } 284 su_info = make_info(pwd); 285 286 #if defined(HAVE_GETLOGIN) && !defined(POSIX_GETLOGIN) 287 login_user = getlogin(); 288 #endif 289 if(login_user == NULL || (pwd = getpwnam(login_user)) == NULL) 290 pwd = getpwuid(getuid()); 291 if(pwd == NULL) 292 errx(1, "who are you?"); 293 login_info = make_info(pwd); 294 if(env_flag) 295 shell = login_info->pw_shell; 296 else 297 shell = su_info->pw_shell; 298 if(shell == NULL || *shell == '\0') 299 shell = _PATH_BSHELL; 300 301 if(kerberos_flag && ok == 0 && 302 (kerberos_error=krb5_verify(login_info, su_info, kerberos_instance)) == 0) 303 ok++; 304 305 if(ok == 0 && login_info->pw_uid && verify_unix(su_info) != 0) { 306 printf("Sorry!\n"); 307 exit(1); 308 } 309 310 #ifdef HAVE_GETSPNAM 311 { struct spwd *sp; 312 long today; 313 314 sp=getspnam(su_info->pw_name); 315 if (sp==NULL) 316 errx(1,"Have not rights to read shadow passwords!"); 317 today = time(0)/(24L * 60 * 60); 318 if (sp->sp_expire > 0) { 319 if (today >= sp->sp_expire) { 320 if (login_info->pw_uid) 321 errx(1,"Your account has expired."); 322 else 323 printf("Your account has expired."); 324 } 325 else if (sp->sp_expire - today < 14) 326 printf("Your account will expire in %d days.\n", 327 (int)(sp->sp_expire - today)); 328 } 329 if (sp->sp_max > 0) { 330 if (today >= sp->sp_lstchg + sp->sp_max) { 331 if (login_info->pw_uid) 332 errx(1,"Your password has expired. Choose a new one."); 333 else 334 printf("Your password has expired. Choose a new one."); 335 } 336 else if (today >= sp->sp_lstchg + sp->sp_max - sp->sp_warn) 337 printf("Your account will expire in %d days.\n", 338 (int)(sp->sp_lstchg + sp->sp_max -today)); 339 } 340 } 341 #endif 342 { 343 char *tty = ttyname (STDERR_FILENO); 344 syslog (LOG_NOTICE | LOG_AUTH, tty ? "%s to %s" : "%s to %s on %s", 345 login_info->pw_name, su_info->pw_name, tty); 346 } 347 348 349 if(!env_flag) { 350 if(full_login) { 351 char *t = getenv ("TERM"); 352 353 environ = malloc (10 * sizeof (char *)); 354 if (environ == NULL) 355 err (1, "malloc"); 356 environ[0] = NULL; 357 setenv ("PATH", _PATH_DEFPATH, 1); 358 if (t) 359 setenv ("TERM", t, 1); 360 if (chdir (su_info->pw_dir) < 0) 361 errx (1, "no directory"); 362 } 363 if (full_login || su_info->pw_uid) 364 setenv ("USER", su_info->pw_name, 1); 365 setenv("HOME", su_info->pw_dir, 1); 366 setenv("SHELL", shell, 1); 367 } 368 369 { 370 int i; 371 char **args; 372 char *p; 373 374 p = strrchr(shell, '/'); 375 if(p) 376 p++; 377 else 378 p = shell; 379 380 if (strcmp(p, "csh") != 0) 381 csh_f_flag = 0; 382 383 args = malloc(((cmd ? 2 : 0) + 1 + argc - optind + 1 + csh_f_flag) * sizeof(*args)); 384 if (args == NULL) 385 err (1, "malloc"); 386 i = 0; 387 if(full_login) 388 asprintf(&args[i++], "-%s", p); 389 else 390 args[i++] = p; 391 if (cmd) { 392 args[i++] = "-c"; 393 args[i++] = cmd; 394 } 395 396 if (csh_f_flag) 397 args[i++] = "-f"; 398 399 for (argv += optind; *argv; ++argv) 400 args[i++] = *argv; 401 args[i] = NULL; 402 403 if(setgid(su_info->pw_gid) < 0) 404 err(1, "setgid"); 405 if (initgroups (su_info->pw_name, su_info->pw_gid) < 0) 406 err (1, "initgroups"); 407 if(setuid(su_info->pw_uid) < 0) 408 err(1, "setuid"); 409 410 #ifdef KRB5 411 if (!kerberos_error) 412 krb5_start_session(); 413 #endif 414 execv(shell, args); 415 } 416 417 exit(1); 418 } 419