1 /* 2 * Copyright (c) 1988 The Regents of the University of California. 3 * All rights reserved. 4 * 5 * %sccs.include.redist.c% 6 */ 7 8 #ifndef lint 9 char copyright[] = 10 "@(#) Copyright (c) 1988 The Regents of the University of California.\n\ 11 All rights reserved.\n"; 12 #endif /* not lint */ 13 14 #ifndef lint 15 static char sccsid[] = "@(#)su.c 5.21 (Berkeley) 06/20/90"; 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 "pathnames.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(), *mytty(); 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 93 /* get current login name and shell */ 94 if ((pwd = getpwuid(ruid = getuid())) == NULL) { 95 fprintf(stderr, "su: who are you?\n"); 96 exit(1); 97 } 98 username = strdup(pwd->pw_name); 99 if (asme) 100 if (pwd->pw_shell && *pwd->pw_shell) 101 shell = strcpy(shellbuf, pwd->pw_shell); 102 else { 103 shell = _PATH_BSHELL; 104 iscsh = NO; 105 } 106 107 /* get target login information, default to root */ 108 user = *argv ? *argv : "root"; 109 if ((pwd = getpwnam(user)) == NULL) { 110 fprintf(stderr, "su: unknown login %s\n", user); 111 exit(1); 112 } 113 114 /* only allow those in group zero to su to root. */ 115 if (pwd->pw_uid == 0 && (gr = getgrgid((gid_t)0))) 116 for (g = gr->gr_mem;; ++g) { 117 if (!*g) { 118 (void)fprintf(stderr, 119 "su: you are not in the correct group to su %s.\n", user); 120 exit(1); 121 } 122 if (!strcmp(username, *g)) 123 break; 124 } 125 openlog("su", LOG_CONS, 0); 126 127 if (ruid) { 128 #ifdef KERBEROS 129 if (!use_kerberos || kerberos(username, user, pwd->pw_uid)) 130 #endif 131 /* if target requires a password, verify it */ 132 if (*pwd->pw_passwd) { 133 p = getpass("Password:"); 134 if (strcmp(pwd->pw_passwd, crypt(p, pwd->pw_passwd))) { 135 fprintf(stderr, "Sorry\n"); 136 syslog(LOG_AUTH|LOG_CRIT, 137 "BAD SU %s on %s to %s", username, 138 mytty(), user); 139 exit(1); 140 } 141 } 142 } 143 144 if (asme) { 145 /* if asme and non-standard target shell, must be root */ 146 if (!chshell(pwd->pw_shell) && ruid) { 147 (void)fprintf(stderr, 148 "su: permission denied (shell).\n"); 149 exit(1); 150 } 151 } else if (pwd->pw_shell && *pwd->pw_shell) { 152 shell = pwd->pw_shell; 153 iscsh = UNSET; 154 } else { 155 shell = _PATH_BSHELL; 156 iscsh = NO; 157 } 158 159 /* if we're forking a csh, we want to slightly muck the args */ 160 if (iscsh == UNSET) { 161 if (p = rindex(shell, '/')) 162 ++p; 163 else 164 p = shell; 165 iscsh = strcmp(p, "csh") ? NO : YES; 166 } 167 168 /* set permissions */ 169 if (setgid(pwd->pw_gid) < 0) { 170 perror("su: setgid"); 171 exit(1); 172 } 173 if (initgroups(user, pwd->pw_gid)) { 174 (void)fprintf(stderr, "su: initgroups failed.\n"); 175 exit(1); 176 } 177 if (setuid(pwd->pw_uid) < 0) { 178 perror("su: setuid"); 179 exit(1); 180 } 181 182 if (!asme) { 183 if (asthem) { 184 p = getenv("TERM"); 185 cleanenv[0] = _PATH_SEARCHPATH; 186 cleanenv[1] = NULL; 187 environ = cleanenv; 188 (void)setenv("TERM", p, 1); 189 if (chdir(pwd->pw_dir) < 0) { 190 fprintf(stderr, "su: no directory\n"); 191 exit(1); 192 } 193 } 194 if (asthem || pwd->pw_uid) 195 (void)setenv("USER", pwd->pw_name, 1); 196 (void)setenv("HOME", pwd->pw_dir, 1); 197 (void)setenv("SHELL", shell, 1); 198 } 199 200 if (iscsh == YES) { 201 if (fastlogin) 202 *np-- = "-f"; 203 if (asme) 204 *np-- = "-m"; 205 } 206 207 /* csh strips the first character... */ 208 *np = asthem ? "-su" : iscsh == YES ? "_su" : "su"; 209 210 syslog(LOG_NOTICE|LOG_AUTH, "%s on %s to %s", username, mytty(), user); 211 212 (void)setpriority(PRIO_PROCESS, 0, prio); 213 214 execv(shell, np); 215 (void)fprintf(stderr, "su: %s not found.\n", shell); 216 exit(1); 217 } 218 219 chshell(sh) 220 char *sh; 221 { 222 register char *cp; 223 char *getusershell(); 224 225 while ((cp = getusershell()) != NULL) 226 if (!strcmp(cp, sh)) 227 return(1); 228 return(0); 229 } 230 231 char * 232 mytty() 233 { 234 char *p, *ttyname(); 235 236 return((p = ttyname(STDERR_FILENO)) ? p : "UNKNOWN TTY"); 237 } 238 239 #ifdef KERBEROS 240 kerberos(username, user, uid) 241 char *username, *user; 242 int uid; 243 { 244 extern char *krb_err_txt[]; 245 KTEXT_ST ticket; 246 AUTH_DAT authdata; 247 struct hostent *hp; 248 register char *p; 249 int kerno; 250 u_long faddr; 251 char lrealm[REALM_SZ], krbtkfile[MAXPATHLEN]; 252 char hostname[MAXHOSTNAMELEN], savehost[MAXHOSTNAMELEN]; 253 char *mytty(); 254 255 if (krb_get_lrealm(lrealm, 1) != KSUCCESS) { 256 (void)fprintf(stderr, "su: couldn't get local realm.\n"); 257 return(1); 258 } 259 if (koktologin(username, lrealm, user) && !uid) { 260 (void)fprintf(stderr, "kerberos su: not in %s's ACL.\n", user); 261 return(1); 262 } 263 (void)sprintf(krbtkfile, "%s_%s_%d", TKT_ROOT, user, getuid()); 264 265 (void)setenv("KRBTKFILE", krbtkfile, 1); 266 if (setuid(0) < 0) { 267 perror("su: setuid"); 268 return(1); 269 } 270 (void)unlink(krbtkfile); 271 272 /* 273 * Little trick here -- if we are su'ing to root, 274 * we need to get a ticket for "xxx.root", where xxx represents 275 * the name of the person su'ing. Otherwise (non-root case), 276 * we need to get a ticket for "yyy.", where yyy represents 277 * the name of the person being su'd to, and the instance is null 278 * 279 * Also: POLICY: short ticket lifetime for root 280 */ 281 kerno = krb_get_pw_in_tkt((uid == 0 ? username : user), 282 (uid == 0 ? "root" : ""), lrealm, 283 "krbtgt", lrealm, (uid == 0 ? 2 : DEFAULT_TKT_LIFE), 0); 284 285 if (kerno != KSUCCESS) { 286 if (kerno == KDC_PR_UNKNOWN) { 287 fprintf(stderr, "principal unknown: %s.%s@%s\n", 288 (uid == 0 ? username : user), 289 (uid == 0 ? "root" : ""), lrealm); 290 return(1); 291 } 292 (void)printf("su: unable to su: %s\n", krb_err_txt[kerno]); 293 syslog(LOG_NOTICE|LOG_AUTH, 294 "su: BAD Kerberos SU: %s on %s to %s: %s", 295 username, mytty(), user, krb_err_txt[kerno]); 296 return(1); 297 } 298 299 if (chown(krbtkfile, uid, -1) < 0) { 300 perror("su: chown:"); 301 (void)unlink(krbtkfile); 302 return(1); 303 } 304 305 (void)setpriority(PRIO_PROCESS, 0, -2); 306 307 if (gethostname(hostname, sizeof(hostname)) == -1) { 308 perror("su: hostname"); 309 dest_tkt(); 310 return(1); 311 } 312 313 (void)strncpy(savehost, krb_get_phost(hostname), sizeof(savehost)); 314 savehost[sizeof(savehost) - 1] = '\0'; 315 316 kerno = krb_mk_req(&ticket, "rcmd", savehost, lrealm, 33); 317 318 if (kerno == KDC_PR_UNKNOWN) { 319 (void)printf("Warning: tgt not verified.\n"); 320 syslog(LOG_NOTICE|LOG_AUTH, 321 "su: %s on %s to %s, TGT not verified", 322 username, mytty(), user); 323 } else if (kerno != KSUCCESS) { 324 (void)printf("Unable to use TGT: %s\n", krb_err_txt[kerno]); 325 syslog(LOG_NOTICE|LOG_AUTH, "su: failed su: %s on %s to %s: %s", 326 username, mytty(), user, krb_err_txt[kerno]); 327 dest_tkt(); 328 return(1); 329 } else { 330 if (!(hp = gethostbyname(hostname))) { 331 (void)printf("su: can't get addr of %s\n", hostname); 332 dest_tkt(); 333 return(1); 334 } 335 (void)bcopy((char *)hp->h_addr, (char *)&faddr, sizeof(faddr)); 336 337 if ((kerno = krb_rd_req(&ticket, "rcmd", savehost, faddr, 338 &authdata, "")) != KSUCCESS) { 339 (void)printf("su: unable to verify rcmd ticket: %s\n", 340 krb_err_txt[kerno]); 341 syslog(LOG_NOTICE|LOG_AUTH, 342 "su: failed su: %s on %s to %s: %s", username, 343 mytty(), user, krb_err_txt[kerno]); 344 dest_tkt(); 345 return(1); 346 } 347 } 348 return(0); 349 } 350 351 koktologin(name, realm, toname) 352 char *name, *realm, *toname; 353 { 354 register AUTH_DAT *kdata; 355 AUTH_DAT kdata_st; 356 357 kdata = &kdata_st; 358 bzero((caddr_t) kdata, sizeof(*kdata)); 359 (void)strcpy(kdata->pname, name); 360 (void)strcpy(kdata->pinst, 361 ((strcmp(toname, "root") == 0) ? "root" : "")); 362 (void)strcpy(kdata->prealm, realm); 363 return(kuserok(kdata, toname)); 364 } 365 #endif 366