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