1 /* 2 * Copyright (c) 1988 The Regents of the University of California. 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms are permitted 6 * provided that the above copyright notice and this paragraph are 7 * duplicated in all such forms and that any documentation, 8 * advertising materials, and other materials related to such 9 * distribution and use acknowledge that the software was developed 10 * by the University of California, Berkeley. The name of the 11 * University may not be used to endorse or promote products derived 12 * from this software without specific prior written permission. 13 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR 14 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED 15 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. 16 */ 17 18 #ifndef lint 19 char copyright[] = 20 "@(#) Copyright (c) 1988 The Regents of the University of California.\n\ 21 All rights reserved.\n"; 22 #endif /* not lint */ 23 24 #ifndef lint 25 static char sccsid[] = "@(#)su.c 5.19 (Berkeley) 05/31/90"; 26 #endif /* not lint */ 27 28 #include <sys/param.h> 29 #include <sys/time.h> 30 #include <sys/resource.h> 31 #include <syslog.h> 32 #include <stdio.h> 33 #include <pwd.h> 34 #include <grp.h> 35 #include <string.h> 36 #include <unistd.h> 37 #include "pathnames.h" 38 39 #ifdef KERBEROS 40 #include <kerberosIV/des.h> 41 #include <kerberosIV/krb.h> 42 #include <netdb.h> 43 44 #define ARGSTR "-Kflm" 45 46 int use_kerberos = 1; 47 #else 48 #define ARGSTR "-flm" 49 #endif 50 51 main(argc, argv) 52 int argc; 53 char **argv; 54 { 55 extern char **environ; 56 extern int errno, optind; 57 register struct passwd *pwd; 58 register char *p, **g; 59 struct group *gr; 60 uid_t ruid, getuid(); 61 int asme, ch, asthem, fastlogin, prio; 62 enum { UNSET, YES, NO } iscsh = UNSET; 63 char *user, *shell, *username, *cleanenv[2], *nargv[4], **np; 64 char shellbuf[MAXPATHLEN]; 65 char *crypt(), *getpass(), *getenv(), *getlogin(), *mytty(); 66 67 np = &nargv[3]; 68 *np-- = NULL; 69 asme = asthem = fastlogin = 0; 70 while ((ch = getopt(argc, argv, ARGSTR)) != EOF) 71 switch((char)ch) { 72 #ifdef KERBEROS 73 case 'K': 74 use_kerberos = 0; 75 break; 76 #endif 77 case 'f': 78 fastlogin = 1; 79 break; 80 case '-': 81 case 'l': 82 asme = 0; 83 asthem = 1; 84 break; 85 case 'm': 86 asme = 1; 87 asthem = 0; 88 break; 89 case '?': 90 default: 91 (void)fprintf(stderr, "usage: su [%s] [login]\n", 92 ARGSTR); 93 exit(1); 94 } 95 argv += optind; 96 97 errno = 0; 98 prio = getpriority(PRIO_PROCESS, 0); 99 if (errno) 100 prio = 0; 101 (void)setpriority(PRIO_PROCESS, 0, -2); 102 103 /* get current login name and shell */ 104 if ((pwd = getpwuid(ruid = getuid())) == NULL) { 105 fprintf(stderr, "su: who are you?\n"); 106 exit(1); 107 } 108 username = strdup(pwd->pw_name); 109 if (asme) 110 if (pwd->pw_shell && *pwd->pw_shell) 111 shell = strcpy(shellbuf, pwd->pw_shell); 112 else { 113 shell = _PATH_BSHELL; 114 iscsh = NO; 115 } 116 117 /* get target login information, default to root */ 118 user = *argv ? *argv : "root"; 119 if ((pwd = getpwnam(user)) == NULL) { 120 fprintf(stderr, "su: unknown login %s\n", user); 121 exit(1); 122 } 123 124 /* only allow those in group zero to su to root. */ 125 if (pwd->pw_uid == 0 && (gr = getgrgid((gid_t)0))) 126 for (g = gr->gr_mem;; ++g) { 127 if (!*g) { 128 (void)fprintf(stderr, 129 "su: you are not in the correct group to su %s.\n", user); 130 exit(1); 131 } 132 if (!strcmp(username, *g)) 133 break; 134 } 135 openlog("su", LOG_CONS, 0); 136 137 if (ruid) { 138 #ifdef KERBEROS 139 if (!use_kerberos || kerberos(username, user, pwd->pw_uid)) 140 #endif 141 /* if target requires a password, verify it */ 142 if (*pwd->pw_passwd) { 143 p = getpass("Password:"); 144 if (strcmp(pwd->pw_passwd, crypt(p, pwd->pw_passwd))) { 145 fprintf(stderr, "Sorry\n"); 146 if (pwd->pw_uid == 0) 147 syslog(LOG_AUTH|LOG_CRIT, 148 "BAD SU %s on %s", username, 149 mytty()); 150 exit(1); 151 } 152 } 153 } 154 155 if (asme) { 156 /* if asme and non-standard target shell, must be root */ 157 if (!chshell(pwd->pw_shell) && ruid) { 158 (void)fprintf(stderr, "su: permission denied.\n"); 159 exit(1); 160 } 161 } else if (pwd->pw_shell && *pwd->pw_shell) { 162 shell = pwd->pw_shell; 163 iscsh = UNSET; 164 } else { 165 shell = _PATH_BSHELL; 166 iscsh = NO; 167 } 168 169 /* if we're forking a csh, we want to slightly muck the args */ 170 if (iscsh == UNSET) { 171 if (p = rindex(shell, '/')) 172 ++p; 173 else 174 p = shell; 175 iscsh = strcmp(p, "csh") ? NO : YES; 176 } 177 178 /* set permissions */ 179 if (setgid(pwd->pw_gid) < 0) { 180 perror("su: setgid"); 181 exit(1); 182 } 183 if (initgroups(user, pwd->pw_gid)) { 184 (void)fprintf(stderr, "su: initgroups failed.\n"); 185 exit(1); 186 } 187 if (setuid(pwd->pw_uid) < 0) { 188 perror("su: setuid"); 189 exit(1); 190 } 191 192 if (!asme) { 193 if (asthem) { 194 p = getenv("TERM"); 195 cleanenv[0] = _PATH_SEARCHPATH; 196 cleanenv[1] = NULL; 197 environ = cleanenv; 198 (void)setenv("TERM", p, 1); 199 if (chdir(pwd->pw_dir) < 0) { 200 fprintf(stderr, "su: no directory\n"); 201 exit(1); 202 } 203 } 204 if (asthem || pwd->pw_uid) 205 (void)setenv("USER", pwd->pw_name, 1); 206 (void)setenv("HOME", pwd->pw_dir, 1); 207 (void)setenv("SHELL", shell, 1); 208 } 209 210 if (iscsh == YES) { 211 if (fastlogin) 212 *np-- = "-f"; 213 if (asme) 214 *np-- = "-m"; 215 } 216 217 /* csh strips the first character... */ 218 *np = asthem ? "-su" : iscsh == YES ? "_su" : "su"; 219 220 if (pwd->pw_uid == 0) 221 syslog(LOG_NOTICE|LOG_AUTH, "%s on %s", username, mytty()); 222 223 (void)setpriority(PRIO_PROCESS, 0, prio); 224 225 execv(shell, np); 226 (void)fprintf(stderr, "su: %s not found.\n", shell); 227 exit(1); 228 } 229 230 chshell(sh) 231 char *sh; 232 { 233 register char *cp; 234 char *getusershell(); 235 236 while ((cp = getusershell()) != NULL) 237 if (!strcmp(cp, sh)) 238 return(1); 239 return(0); 240 } 241 242 char * 243 mytty() 244 { 245 char *p, *ttyname(); 246 247 return((p = ttyname(STDERR_FILENO)) ? p : "UNKNOWN TTY"); 248 } 249 250 #ifdef KERBEROS 251 kerberos(username, user, uid) 252 char *username, *user; 253 int uid; 254 { 255 extern char *krb_err_txt[]; 256 KTEXT_ST ticket; 257 AUTH_DAT authdata; 258 struct hostent *hp; 259 register char *p; 260 int kerno; 261 u_long faddr; 262 char lrealm[REALM_SZ], krbtkfile[MAXPATHLEN], pw_buf[_PASSWORD_LEN]; 263 char hostname[MAXHOSTNAMELEN], savehost[MAXHOSTNAMELEN]; 264 char *mytty(); 265 266 if (krb_get_lrealm(lrealm, 1) != KSUCCESS) { 267 (void)fprintf(stderr, "su: couldn't get local realm.\n"); 268 return(1); 269 } 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 /* setuid(uid); */ 276 277 if (read_pw_string(pw_buf, sizeof(pw_buf) - 1, 278 "Kerberos password: ", 0)) { 279 (void)fprintf(stderr, "su: error reading password.\n"); 280 return(1); 281 } 282 283 (void)setenv("KRBTKFILE", krbtkfile, 1); 284 /* short lifetime for root tickets */ 285 if (setuid(0) < 0) { 286 perror("su: setuid"); 287 return(1); 288 } 289 (void)unlink(krbtkfile); 290 /* POLICY: short ticket lifetime for root */ 291 kerno = krb_get_pw_in_tkt(username, (uid == 0 ? "root" : ""), lrealm, 292 "krbtgt", lrealm, (uid == 0 ? 2 : DEFAULT_TKT_LIFE), pw_buf); 293 294 bzero(pw_buf, sizeof(pw_buf)); 295 296 if (kerno != KSUCCESS) { 297 if (kerno == KDC_PR_UNKNOWN) 298 return(1); 299 (void)printf("su: unable to su: %s\n", krb_err_txt[kerno]); 300 syslog(LOG_NOTICE|LOG_AUTH, 301 "su: BAD Kerberos SU: %s on %s: %s", username, mytty(), 302 krb_err_txt[kerno]); 303 return(1); 304 } 305 306 if (chown(krbtkfile, uid, -1) < 0) { 307 perror("su: 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 perror("su: hostname"); 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 (void)printf("Warning: tgt not verified.\n"); 327 syslog(LOG_NOTICE|LOG_AUTH, "su: %s on %s, tgt not verified", 328 username, mytty()); 329 } else if (kerno != KSUCCESS) { 330 (void)printf("Unable to use tgt: %s\n", krb_err_txt[kerno]); 331 syslog(LOG_NOTICE|LOG_AUTH, "su: failed su: %s on %s: %s", 332 username, mytty(), krb_err_txt[kerno]); 333 dest_tkt(); 334 return(1); 335 } else { 336 if (!(hp = gethostbyname(hostname))) { 337 (void)printf("su: can't get addr of %s\n", hostname); 338 dest_tkt(); 339 return(1); 340 } 341 (void)bcopy((char *)hp->h_addr, (char *)&faddr, sizeof(faddr)); 342 343 if ((kerno = krb_rd_req(&ticket, "rcmd", savehost, faddr, 344 &authdata, "")) != KSUCCESS) { 345 (void)printf("su: unable to verify rcmd ticket: %s\n", 346 krb_err_txt[kerno]); 347 syslog(LOG_NOTICE|LOG_AUTH, 348 "su: failed su: %s on %s: %s", username, 349 mytty(), krb_err_txt[kerno]); 350 dest_tkt(); 351 return(1); 352 } 353 } 354 return(0); 355 } 356 357 koktologin(name, realm, toname) 358 char *name, *realm, *toname; 359 { 360 register AUTH_DAT *kdata; 361 AUTH_DAT kdata_st; 362 363 kdata = &kdata_st; 364 bzero((caddr_t) kdata, sizeof(*kdata)); 365 (void)strcpy(kdata->pname, name); 366 (void)strcpy(kdata->pinst, 367 ((strcmp(toname, "root") == 0) ? "root" : "")); 368 (void)strcpy(kdata->prealm, realm); 369 return(kuserok(kdata, toname)); 370 } 371 #endif 372