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