1 /* 2 * Copyright (c) 1988, 1993, 1994 3 * The Regents of the University of California. All rights reserved. 4 * 5 * %sccs.include.redist.c% 6 */ 7 8 #ifndef lint 9 static char copyright[] = 10 "@(#) Copyright (c) 1988, 1993, 1994\n\ 11 The Regents of the University of California. All rights reserved.\n"; 12 #endif /* not lint */ 13 14 #ifndef lint 15 static char sccsid[] = "@(#)su.c 8.3 (Berkeley) 04/02/94"; 16 #endif /* not lint */ 17 18 #include <sys/param.h> 19 #include <sys/time.h> 20 #include <sys/resource.h> 21 22 #include <err.h> 23 #include <errno.h> 24 #include <grp.h> 25 #include <paths.h> 26 #include <pwd.h> 27 #include <stdio.h> 28 #include <stdlib.h> 29 #include <string.h> 30 #include <syslog.h> 31 #include <unistd.h> 32 33 #ifdef KERBEROS 34 #include <kerberosIV/des.h> 35 #include <kerberosIV/krb.h> 36 #include <netdb.h> 37 38 #define ARGSTR "-Kflm" 39 40 int use_kerberos = 1; 41 #else 42 #define ARGSTR "-flm" 43 #endif 44 45 char *ontty __P((void)); 46 int chshell __P((char *)); 47 48 int 49 main(argc, argv) 50 int argc; 51 char **argv; 52 { 53 extern char **environ; 54 struct passwd *pwd; 55 char *p, **g, *user, *shell, *username, *cleanenv[2], *nargv[4], **np; 56 struct group *gr; 57 uid_t ruid; 58 int asme, ch, asthem, fastlogin, prio; 59 enum { UNSET, YES, NO } iscsh = UNSET; 60 char shellbuf[MAXPATHLEN]; 61 62 np = &nargv[3]; 63 *np-- = NULL; 64 asme = asthem = fastlogin = 0; 65 while ((ch = getopt(argc, argv, ARGSTR)) != EOF) 66 switch((char)ch) { 67 #ifdef KERBEROS 68 case 'K': 69 use_kerberos = 0; 70 break; 71 #endif 72 case 'f': 73 fastlogin = 1; 74 break; 75 case '-': 76 case 'l': 77 asme = 0; 78 asthem = 1; 79 break; 80 case 'm': 81 asme = 1; 82 asthem = 0; 83 break; 84 case '?': 85 default: 86 (void)fprintf(stderr, "usage: su [%s] [login]\n", 87 ARGSTR); 88 exit(1); 89 } 90 argv += optind; 91 92 errno = 0; 93 prio = getpriority(PRIO_PROCESS, 0); 94 if (errno) 95 prio = 0; 96 (void)setpriority(PRIO_PROCESS, 0, -2); 97 openlog("su", LOG_CONS, 0); 98 99 /* get current login name and shell */ 100 ruid = getuid(); 101 username = getlogin(); 102 if (username == NULL || (pwd = getpwnam(username)) == NULL || 103 pwd->pw_uid != ruid) 104 pwd = getpwuid(ruid); 105 if (pwd == NULL) 106 errx(1, "who are you?"); 107 username = strdup(pwd->pw_name); 108 if (username == NULL) 109 err(1, NULL); 110 if (asme) 111 if (pwd->pw_shell && *pwd->pw_shell) 112 shell = strcpy(shellbuf, pwd->pw_shell); 113 else { 114 shell = _PATH_BSHELL; 115 iscsh = NO; 116 } 117 118 /* get target login information, default to root */ 119 user = *argv ? *argv : "root"; 120 if ((pwd = getpwnam(user)) == NULL) { 121 fprintf(stderr, "su: unknown login %s\n", user); 122 exit(1); 123 } 124 125 if (ruid) { 126 #ifdef KERBEROS 127 if (!use_kerberos || kerberos(username, user, pwd->pw_uid)) 128 #endif 129 { 130 /* only allow those in group zero to su to root. */ 131 if (pwd->pw_uid == 0 && (gr = getgrgid((gid_t)0))) 132 for (g = gr->gr_mem;; ++g) { 133 if (!*g) 134 errx(1, 135 "you are not in the correct group to su %s.", 136 user); 137 if (strcmp(username, *g) == 0) 138 break; 139 } 140 /* if target requires a password, verify it */ 141 if (*pwd->pw_passwd) { 142 p = getpass("Password:"); 143 if (strcmp(pwd->pw_passwd, crypt(p, pwd->pw_passwd))) { 144 fprintf(stderr, "Sorry\n"); 145 syslog(LOG_AUTH|LOG_WARNING, 146 "BAD SU %s to %s%s", username, 147 user, ontty()); 148 exit(1); 149 } 150 } 151 } 152 } 153 154 if (asme) { 155 /* if asme and non-standard target shell, must be root */ 156 if (!chshell(pwd->pw_shell) && ruid) 157 errx(1, "permission denied (shell)."); 158 } else if (pwd->pw_shell && *pwd->pw_shell) { 159 shell = pwd->pw_shell; 160 iscsh = UNSET; 161 } else { 162 shell = _PATH_BSHELL; 163 iscsh = NO; 164 } 165 166 /* if we're forking a csh, we want to slightly muck the args */ 167 if (iscsh == UNSET) { 168 if (p = strrchr(shell, '/')) 169 ++p; 170 else 171 p = shell; 172 iscsh = strcmp(p, "csh") ? NO : YES; 173 } 174 175 /* set permissions */ 176 if (setgid(pwd->pw_gid) < 0) 177 err(1, "setgid"); 178 if (initgroups(user, pwd->pw_gid)) 179 errx(1, "initgroups failed"); 180 if (setuid(pwd->pw_uid) < 0) 181 err(1, "setuid"); 182 183 if (!asme) { 184 if (asthem) { 185 p = getenv("TERM"); 186 cleanenv[0] = _PATH_DEFPATH; 187 cleanenv[1] = NULL; 188 environ = cleanenv; 189 (void)setenv("TERM", p, 1); 190 if (chdir(pwd->pw_dir) < 0) 191 errx(1, "no directory"); 192 } 193 if (asthem || pwd->pw_uid) 194 (void)setenv("USER", pwd->pw_name, 1); 195 (void)setenv("HOME", pwd->pw_dir, 1); 196 (void)setenv("SHELL", shell, 1); 197 } 198 199 if (iscsh == YES) { 200 if (fastlogin) 201 *np-- = "-f"; 202 if (asme) 203 *np-- = "-m"; 204 } 205 206 /* csh strips the first character... */ 207 *np = asthem ? "-su" : iscsh == YES ? "_su" : "su"; 208 209 if (ruid != 0) 210 syslog(LOG_NOTICE|LOG_AUTH, "%s to %s%s", 211 username, user, ontty()); 212 213 (void)setpriority(PRIO_PROCESS, 0, prio); 214 215 execv(shell, np); 216 err(1, "%s", shell); 217 } 218 219 int 220 chshell(sh) 221 char *sh; 222 { 223 char *cp; 224 225 while ((cp = getusershell()) != NULL) 226 if (strcmp(cp, sh) == 0) 227 return (1); 228 return (0); 229 } 230 231 char * 232 ontty() 233 { 234 char *p; 235 static char buf[MAXPATHLEN + 4]; 236 237 buf[0] = 0; 238 if (p = ttyname(STDERR_FILENO)) 239 snprintf(buf, sizeof(buf), " on %s", p); 240 return (buf); 241 } 242 243 #ifdef KERBEROS 244 kerberos(username, user, uid) 245 char *username, *user; 246 int uid; 247 { 248 extern char *krb_err_txt[]; 249 KTEXT_ST ticket; 250 AUTH_DAT authdata; 251 struct hostent *hp; 252 char *p; 253 int kerno; 254 u_long faddr; 255 char lrealm[REALM_SZ], krbtkfile[MAXPATHLEN]; 256 char hostname[MAXHOSTNAMELEN], savehost[MAXHOSTNAMELEN]; 257 char *krb_get_phost(); 258 259 if (krb_get_lrealm(lrealm, 1) != KSUCCESS) 260 return (1); 261 if (koktologin(username, lrealm, user) && !uid) { 262 warnx("kerberos: not in %s's ACL.", user); 263 return (1); 264 } 265 (void)sprintf(krbtkfile, "%s_%s_%d", TKT_ROOT, user, getuid()); 266 267 (void)setenv("KRBTKFILE", krbtkfile, 1); 268 (void)krb_set_tkt_string(krbtkfile); 269 /* 270 * Set real as well as effective ID to 0 for the moment, 271 * to make the kerberos library do the right thing. 272 */ 273 if (setuid(0) < 0) { 274 warn("setuid"); 275 return (1); 276 } 277 278 /* 279 * Little trick here -- if we are su'ing to root, 280 * we need to get a ticket for "xxx.root", where xxx represents 281 * the name of the person su'ing. Otherwise (non-root case), 282 * we need to get a ticket for "yyy.", where yyy represents 283 * the name of the person being su'd to, and the instance is null 284 * 285 * We should have a way to set the ticket lifetime, 286 * with a system default for root. 287 */ 288 kerno = krb_get_pw_in_tkt((uid == 0 ? username : user), 289 (uid == 0 ? "root" : ""), lrealm, 290 "krbtgt", lrealm, DEFAULT_TKT_LIFE, 0); 291 292 if (kerno != KSUCCESS) { 293 if (kerno == KDC_PR_UNKNOWN) { 294 warnx("kerberos: principal unknown: %s.%s@%s", 295 (uid == 0 ? username : user), 296 (uid == 0 ? "root" : ""), lrealm); 297 return (1); 298 } 299 warnx("kerberos: unable to su: %s", krb_err_txt[kerno]); 300 syslog(LOG_NOTICE|LOG_AUTH, 301 "BAD Kerberos SU: %s to %s%s: %s", 302 username, user, ontty(), krb_err_txt[kerno]); 303 return (1); 304 } 305 306 if (chown(krbtkfile, uid, -1) < 0) { 307 warn("chown"); 308 (void)unlink(krbtkfile); 309 return (1); 310 } 311 312 (void)setpriority(PRIO_PROCESS, 0, -2); 313 314 if (gethostname(hostname, sizeof(hostname)) == -1) { 315 warn("gethostname"); 316 dest_tkt(); 317 return (1); 318 } 319 320 (void)strncpy(savehost, krb_get_phost(hostname), sizeof(savehost)); 321 savehost[sizeof(savehost) - 1] = '\0'; 322 323 kerno = krb_mk_req(&ticket, "rcmd", savehost, lrealm, 33); 324 325 if (kerno == KDC_PR_UNKNOWN) { 326 warnx("Warning: TGT not verified."); 327 syslog(LOG_NOTICE|LOG_AUTH, 328 "%s to %s%s, TGT not verified (%s); %s.%s not registered?", 329 username, user, ontty(), krb_err_txt[kerno], 330 "rcmd", savehost); 331 } else if (kerno != KSUCCESS) { 332 warnx("Unable to use TGT: %s", krb_err_txt[kerno]); 333 syslog(LOG_NOTICE|LOG_AUTH, "failed su: %s to %s%s: %s", 334 username, user, ontty(), krb_err_txt[kerno]); 335 dest_tkt(); 336 return (1); 337 } else { 338 if (!(hp = gethostbyname(hostname))) { 339 warnx("can't get addr of %s", hostname); 340 dest_tkt(); 341 return (1); 342 } 343 memmove((char *)&faddr, (char *)hp->h_addr, sizeof(faddr)); 344 345 if ((kerno = krb_rd_req(&ticket, "rcmd", savehost, faddr, 346 &authdata, "")) != KSUCCESS) { 347 warnx("kerberos: unable to verify rcmd ticket: %s\n", 348 krb_err_txt[kerno]); 349 syslog(LOG_NOTICE|LOG_AUTH, 350 "failed su: %s to %s%s: %s", username, 351 user, ontty(), krb_err_txt[kerno]); 352 dest_tkt(); 353 return (1); 354 } 355 } 356 return (0); 357 } 358 359 koktologin(name, realm, toname) 360 char *name, *realm, *toname; 361 { 362 AUTH_DAT *kdata; 363 AUTH_DAT kdata_st; 364 365 kdata = &kdata_st; 366 memset((char *)kdata, 0, sizeof(*kdata)); 367 (void)strcpy(kdata->pname, name); 368 (void)strcpy(kdata->pinst, 369 ((strcmp(toname, "root") == 0) ? "root" : "")); 370 (void)strcpy(kdata->prealm, realm); 371 return (kuserok(kdata, toname)); 372 } 373 #endif 374