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