/* * Copyright (c) 1988 The Regents of the University of California. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ /* * After a program 'su2' by Dave Serisky of Hewlett Packard. * * This re-implementation was prompted in order to: * * 1. Reduce the size and complexity of the code * 2. Require the target accounts to be specified in the * control file, so that access to non-root accounts * can be provided without giving away root as well. * * The code is based on the Berkeley su(1) implementation as * ammended and distributed with NetBSD-1.0. * * Giles Lean * January 1995 */ #ifndef lint char copyright[] = "@(#) Copyright (c) 1988 The Regents of the University of California.\n\ All rights reserved.\n"; /*static char sccsid[] = "from: @(#)su.c 5.26 (Berkeley) 7/6/91";*/ /*static char rcsid[] = "from: su.c,v 1.10 1994/05/24 06:52:23 deraadt Exp $";*/ #endif /* not lint */ #ifndef lint static char rcsid[] = "$Header: /home/giles/projects/CVS/www.nemeton.com.au/src/su2.c,v 1.1.1.1 1999/10/31 02:17:02 giles Exp $"; #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef __hpux #define _PATH_BSHELL "/bin/sh" #define _PATH_DEFPATH "/bin:/usr/bin" #else /* __hpux */ #include #endif /* __hpux */ #ifndef SU2 #define SU2 "/usr/local/etc/su2" #endif /* SU2 */ const char *su2 = SU2; extern char **environ; extern int errno; const char *ontty(void); void esetenv(const char *, const char *, int); int validate(const char *, const char *); /* * Some platforms (e.g. HP-UX) are missing setenv(). * * This function DOES NOT implement the full semantics * of setenv(); it is a minimal hack for su2. */ #ifdef NEED_SETENV static int setenv(const char *variable, const char *value, int overwrite) { char *buf; size_t n; n = strlen(variable) + strlen(value) + 2; if ((buf = (char *) malloc(n)) == (char *) 0) { fprintf(stderr, "su2: memory allocation error.\n"); exit(1); } (void) sprintf(buf, "%s=%s", variable, value); return putenv(buf); } #endif /* NEED_SETENV */ int main(int argc, char **argv) { char *avshell; char avshellbuf[MAXPATHLEN]; char *cleanenv[10]; int loginshell; /* Invoke shell as login shell */ char **np; char *p; int prio; struct passwd *pwd; uid_t ruid; char *shell; char *user; char *username; char *userpass; loginshell = 0; /* Invoke shell as a login shell */ switch(argc) { case 1: user = "root"; np = argv; break; case 2: if (strcmp(argv[1], "-")) { user = argv[1]; } else { loginshell = 1; user = "root"; } np = &argv[1]; break; default: if (strcmp(argv[1], "-")) { user = argv[1]; np = &argv[1]; } else { loginshell = 1; user = argv[2]; np = &argv[2]; } break; } errno = 0; prio = getpriority(PRIO_PROCESS, 0); if (errno) prio = 0; (void)setpriority(PRIO_PROCESS, 0, -2); openlog("su2", 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) { fprintf(stderr, "su2: who are you?\n"); exit(1); } username = strdup(pwd->pw_name); userpass = strdup(pwd->pw_passwd); if ((pwd = getpwnam(user)) == NULL) { fprintf(stderr, "su2: unknown login %s\n", user); exit(1); } if (ruid) { if (validate(username, user)) { syslog(LOG_AUTH|LOG_WARNING, "DISALLOWED SU2 %s to %s%s", username, user, ontty()); errno = EACCES; perror(user); exit(1); } p = getpass("Password:"); if (strcmp(userpass, crypt(p, userpass))) { fprintf(stderr, "Sorry\n"); syslog(LOG_AUTH|LOG_WARNING, "BAD SU2 %s to %s%s", username, user, ontty()); exit(1); } else { syslog(LOG_NOTICE|LOG_AUTH, "%s to %s%s", username, user, ontty()); } } if (pwd->pw_shell && *pwd->pw_shell) shell = pwd->pw_shell; else shell = _PATH_BSHELL; if ((p = strrchr(shell, '/')) != (char *) 0) avshell = p+1; else avshell = shell; /* set permissions */ if (setgid(pwd->pw_gid) < 0) { perror("su2: setgid"); exit(1); } if (initgroups(user, pwd->pw_gid)) { (void)fprintf(stderr, "su2: initgroups failed.\n"); exit(1); } if (setuid(pwd->pw_uid) < 0) { perror("su2: setuid"); exit(1); } if (loginshell) { p = getenv("TERM"); cleanenv[0] = NULL; environ = cleanenv; esetenv("PATH", _PATH_DEFPATH, 1); esetenv("TERM", p, 1); if (chdir(pwd->pw_dir) < 0) { fprintf(stderr, "su2: no directory\n"); exit(1); } } #ifdef BSD if (pwd->pw_uid) esetenv("USER", pwd->pw_name, 1); #endif /* BSD */ esetenv("HOME", pwd->pw_dir, 1); esetenv("SHELL", shell, 1); if (loginshell) { esetenv("LOGNAME", pwd->pw_name, 1); avshellbuf[0] = '-'; strcpy(avshellbuf+1, avshell); avshell = avshellbuf; } p = &shell[strlen(shell) - 2]; if (p < shell) p = avshellbuf; if (strcmp(p, "sh") == 0) { /* Some sort of shell ... set TMOUT in case it works. */ if ((p = getenv("TMOUT")) == (char *) 0 || *p == '\0') p = "1800"; esetenv("TMOUT", p, 1); } *np = avshell; (void)setpriority(PRIO_PROCESS, 0, prio); execv(shell, np); (void)fprintf(stderr, "su2: %s not found.\n", shell); exit(1); } const char * ontty(void) { char *p; static char buf[MAXPATHLEN + 4]; buf[0] = 0; if ((p = ttyname(STDERR_FILENO)) != (char *) 0) sprintf(buf, " on %s", p); return (buf); } /* * Quit on any failure -- machines under attack often * suffer resource exhaustion. */ void esetenv(const char *var, const char *value, int overwrite) { if (setenv(var, value, overwrite)) { fprintf(stderr, "su2: setenv failure\n"); exit(1); } } /* * Read the su2 authorisation file, bailing out if there * are any problems. */ char * read_file(const char *filename) { int fd; char *buf; struct stat statbuf; if ((fd = open(filename, O_RDONLY)) < 0) { perror(filename); syslog(LOG_AUTH|LOG_WARNING, "%s is missing", su2); exit(1); } if (fstat(fd, &statbuf) < 0) { perror("fstat"); exit(1); } if (statbuf.st_uid) { syslog(LOG_AUTH|LOG_WARNING, "%s is not owned by root", su2); errno = EACCES; /* something that will sound good */ perror("su2"); exit(1); } if (statbuf.st_mode & 0022) { syslog(LOG_AUTH|LOG_WARNING, "%s is writable by non-root users", su2); errno = EACCES; /* permission deined ... why not! */ perror("su2"); exit(1); } if ((buf = (char *) malloc(statbuf.st_size + 1)) == (char *) 0) { syslog(LOG_AUTH|LOG_WARNING, "memory allocation failure"); fprintf(stderr, "su2: memory allocation error\n"); exit(1); } if (read(fd, buf, statbuf.st_size) != statbuf.st_size) { perror("read"); exit(1); } buf[statbuf.st_size] = '\0'; close(fd); return buf; } /* * Enough of strtok(3) to read lines, since we can't use * strtok(3) for both the lines and the usernames within * lines. */ char * next_line(char *ptr) { static char *p = (char *) 0; char *oldp; if (p == (char *) 0) p = ptr; if (p == (char *) 0 || *p == '\0') return (char *) 0; oldp = p; if ((p = strchr(p, '\n')) != (char *) 0) *p++ = '\0'; return oldp; } /* * validate(from, to) * * Returns: * 0 OK * -1 error * */ int validate(const char *from_user, const char *to_user) { char *buf; char *p; if (from_user == (const char *) 0 || to_user == (const char *) 0) return -1; p = buf = next_line(read_file(su2)); for (; p; p = next_line(NULL)) { p = strtok(p, " \t"); if (strcmp(p, from_user)) continue; while ((p = strtok(NULL, " \t"))) { if (strcmp(p, to_user) == 0 || strcmp(p, "*") == 0) { free(buf); return 0; } } break; } free(buf); return -1; }