diff options
author | Theo de Raadt <deraadt@cvs.openbsd.org> | 1995-10-18 08:53:40 +0000 |
---|---|---|
committer | Theo de Raadt <deraadt@cvs.openbsd.org> | 1995-10-18 08:53:40 +0000 |
commit | d6583bb2a13f329cf0332ef2570eb8bb8fc0e39c (patch) | |
tree | ece253b876159b39c620e62b6c9b1174642e070e /usr.bin/su/su.c |
initial import of NetBSD tree
Diffstat (limited to 'usr.bin/su/su.c')
-rw-r--r-- | usr.bin/su/su.c | 443 |
1 files changed, 443 insertions, 0 deletions
diff --git a/usr.bin/su/su.c b/usr.bin/su/su.c new file mode 100644 index 00000000000..715bcb5911a --- /dev/null +++ b/usr.bin/su/su.c @@ -0,0 +1,443 @@ +/* + * 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. + */ + +#ifndef lint +char copyright[] = +"@(#) Copyright (c) 1988 The Regents of the University of California.\n\ + All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +/*static char sccsid[] = "from: @(#)su.c 5.26 (Berkeley) 7/6/91";*/ +static char rcsid[] = "$Id: su.c,v 1.1 1995/10/18 08:46:10 deraadt Exp $"; +#endif /* not lint */ + +#include <sys/param.h> +#include <sys/time.h> +#include <sys/resource.h> +#include <syslog.h> +#include <stdio.h> +#include <stdlib.h> +#include <pwd.h> +#include <grp.h> +#include <string.h> +#include <unistd.h> +#include <paths.h> + +#ifdef KERBEROS +#include <kerberosIV/des.h> +#include <kerberosIV/krb.h> +#include <netdb.h> + +#define ARGSTR "-Kflm" + +int use_kerberos = 1; +#else +#define ARGSTR "-flm" +#endif + +extern char *crypt(); +int chshell(); + +int +main(argc, argv) + int argc; + char **argv; +{ + extern char **environ; + extern int errno, optind; + register struct passwd *pwd; + register char *p, **g; + struct group *gr; + uid_t ruid, getuid(); + int asme, ch, asthem, fastlogin, prio; + enum { UNSET, YES, NO } iscsh = UNSET; + char *user, *shell, *avshell, *username, *cleanenv[10], **np; + char shellbuf[MAXPATHLEN], avshellbuf[MAXPATHLEN]; + char *getpass(), *getenv(), *getlogin(), *ontty(); + + asme = asthem = fastlogin = 0; + while ((ch = getopt(argc, argv, ARGSTR)) != EOF) + switch((char)ch) { +#ifdef KERBEROS + case 'K': + use_kerberos = 0; + break; +#endif + case 'f': + fastlogin = 1; + break; + case '-': + case 'l': + asme = 0; + asthem = 1; + break; + case 'm': + asme = 1; + asthem = 0; + break; + case '?': + default: + (void)fprintf(stderr, "usage: su [%s] [login]\n", + ARGSTR); + exit(1); + } + 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) { + fprintf(stderr, "su: who are you?\n"); + exit(1); + } + username = strdup(pwd->pw_name); + if (asme) + if (pwd->pw_shell && *pwd->pw_shell) + shell = strcpy(shellbuf, pwd->pw_shell); + else { + shell = _PATH_BSHELL; + iscsh = NO; + } + + /* get target login information, default to root */ + user = *argv ? *argv : "root"; + np = *argv ? argv : argv-1; + + if ((pwd = getpwnam(user)) == NULL) { + fprintf(stderr, "su: unknown login %s\n", user); + exit(1); + } + + if (ruid) { +#ifdef KERBEROS + if (!use_kerberos || kerberos(username, user, pwd->pw_uid)) +#endif + { + /* only allow those in group zero to su to root. */ + if (pwd->pw_uid == 0 && (gr = getgrgid((gid_t)0))) + for (g = gr->gr_mem;; ++g) { + if (!*g) { + (void)fprintf(stderr, + "su: you are not in the correct group to su %s.\n", + user); + exit(1); + } + if (!strcmp(username, *g)) + break; + } + /* if target requires a password, verify it */ + if (*pwd->pw_passwd) { + p = getpass("Password:"); +#ifdef SKEY + if (strcasecmp(p, "s/key") == 0) { + if (skey_haskey(user)) { + fprintf(stderr, "Sorry, you have no s/key.\n"); + exit(1); + } else { + if (skey_authenticate(user)) { + goto badlogin; + } + } + + } else +#endif + if (strcmp(pwd->pw_passwd, crypt(p, pwd->pw_passwd))) { +badlogin: + fprintf(stderr, "Sorry\n"); + 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) { + (void)fprintf(stderr, + "su: permission denied (shell).\n"); + exit(1); + } + } else if (pwd->pw_shell && *pwd->pw_shell) { + shell = pwd->pw_shell; + iscsh = UNSET; + } else { + shell = _PATH_BSHELL; + iscsh = NO; + } + + if (p = rindex(shell, '/')) + avshell = p+1; + else + avshell = shell; + + /* if we're forking a csh, we want to slightly muck the args */ + if (iscsh == UNSET) + iscsh = strcmp(avshell, "csh") ? NO : YES; + + /* set permissions */ + if (setgid(pwd->pw_gid) < 0) { + perror("su: setgid"); + exit(1); + } + if (initgroups(user, pwd->pw_gid)) { + (void)fprintf(stderr, "su: initgroups failed.\n"); + exit(1); + } + if (setuid(pwd->pw_uid) < 0) { + perror("su: setuid"); + exit(1); + } + + if (!asme) { + if (asthem) { + p = getenv("TERM"); + cleanenv[0] = NULL; + environ = cleanenv; + (void)setenv("PATH", _PATH_DEFPATH, 1); + (void)setenv("TERM", p, 1); + if (chdir(pwd->pw_dir) < 0) { + fprintf(stderr, "su: no directory\n"); + exit(1); + } + } + 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"; + } + + if (asthem) { + avshellbuf[0] = '-'; + strcpy(avshellbuf+1, avshell); + avshell = avshellbuf; + } else if (iscsh == YES) { + /* csh strips the first character... */ + avshellbuf[0] = '_'; + strcpy(avshellbuf+1, avshell); + avshell = avshellbuf; + } + + *np = avshell; + + if (ruid != 0) + syslog(LOG_NOTICE|LOG_AUTH, "%s to %s%s", + username, user, ontty()); + + (void)setpriority(PRIO_PROCESS, 0, prio); + + execv(shell, np); + (void)fprintf(stderr, "su: %s not found.\n", shell); + exit(1); +} + +int +chshell(sh) + char *sh; +{ + register char *cp; + char *getusershell(); + + while ((cp = getusershell()) != NULL) + if (!strcmp(cp, sh)) + return (1); + return (0); +} + +char * +ontty() +{ + char *p, *ttyname(); + static char buf[MAXPATHLEN + 4]; + + buf[0] = 0; + if (p = ttyname(STDERR_FILENO)) + sprintf(buf, " on %s", p); + return (buf); +} + +#ifdef KERBEROS +kerberos(username, user, uid) + char *username, *user; + int uid; +{ + extern char *krb_err_txt[]; + KTEXT_ST ticket; + AUTH_DAT authdata; + struct hostent *hp; + register char *p; + int kerno; + u_long faddr; + char lrealm[REALM_SZ], krbtkfile[MAXPATHLEN]; + char hostname[MAXHOSTNAMELEN], savehost[MAXHOSTNAMELEN]; + char *ontty(), *krb_get_phost(); + + if (krb_get_lrealm(lrealm, 1) != KSUCCESS) + return (1); + if (koktologin(username, lrealm, user) && !uid) { + (void)fprintf(stderr, "kerberos su: not in %s's ACL.\n", user); + return (1); + } + (void)sprintf(krbtkfile, "%s_%s_%d", TKT_ROOT, user, getuid()); + + (void)setenv("KRBTKFILE", krbtkfile, 1); + (void)krb_set_tkt_string(krbtkfile); + /* + * Set real as well as effective ID to 0 for the moment, + * to make the kerberos library do the right thing. + */ + if (setuid(0) < 0) { + perror("su: setuid"); + return (1); + } + + /* + * Little trick here -- if we are su'ing to root, + * we need to get a ticket for "xxx.root", where xxx represents + * the name of the person su'ing. Otherwise (non-root case), + * we need to get a ticket for "yyy.", where yyy represents + * the name of the person being su'd to, and the instance is null + * + * We should have a way to set the ticket lifetime, + * with a system default for root. + */ + kerno = krb_get_pw_in_tkt((uid == 0 ? username : user), + (uid == 0 ? "root" : ""), lrealm, + "krbtgt", lrealm, DEFAULT_TKT_LIFE, 0); + + if (kerno != KSUCCESS) { + if (kerno == KDC_PR_UNKNOWN) { + fprintf(stderr, "principal unknown: %s.%s@%s\n", + (uid == 0 ? username : user), + (uid == 0 ? "root" : ""), lrealm); + return (1); + } + (void)fprintf(stderr, "su: unable to su: %s\n", + krb_err_txt[kerno]); + syslog(LOG_NOTICE|LOG_AUTH, + "BAD Kerberos SU: %s to %s%s: %s", + username, user, ontty(), krb_err_txt[kerno]); + return (1); + } + + if (chown(krbtkfile, uid, -1) < 0) { + perror("su: chown:"); + (void)unlink(krbtkfile); + return (1); + } + + (void)setpriority(PRIO_PROCESS, 0, -2); + + if (gethostname(hostname, sizeof(hostname)) == -1) { + perror("su: gethostname"); + dest_tkt(); + return (1); + } + + (void)strncpy(savehost, krb_get_phost(hostname), sizeof(savehost)); + savehost[sizeof(savehost) - 1] = '\0'; + + kerno = krb_mk_req(&ticket, "rcmd", savehost, lrealm, 33); + + if (kerno == KDC_PR_UNKNOWN) { + (void)fprintf(stderr, "Warning: TGT not verified.\n"); + syslog(LOG_NOTICE|LOG_AUTH, + "%s to %s%s, TGT not verified (%s); %s.%s not registered?", + username, user, ontty(), krb_err_txt[kerno], + "rcmd", savehost); + } else if (kerno != KSUCCESS) { + (void)fprintf(stderr, "Unable to use TGT: %s\n", + krb_err_txt[kerno]); + syslog(LOG_NOTICE|LOG_AUTH, "failed su: %s to %s%s: %s", + username, user, ontty(), krb_err_txt[kerno]); + dest_tkt(); + return (1); + } else { + if (!(hp = gethostbyname(hostname))) { + (void)fprintf(stderr, "su: can't get addr of %s\n", + hostname); + dest_tkt(); + return (1); + } + (void)bcopy((char *)hp->h_addr, (char *)&faddr, sizeof(faddr)); + + if ((kerno = krb_rd_req(&ticket, "rcmd", savehost, faddr, + &authdata, "")) != KSUCCESS) { + (void)fprintf(stderr, + "su: unable to verify rcmd ticket: %s\n", + krb_err_txt[kerno]); + syslog(LOG_NOTICE|LOG_AUTH, + "failed su: %s to %s%s: %s", username, + user, ontty(), krb_err_txt[kerno]); + dest_tkt(); + return (1); + } + } + return (0); +} + +koktologin(name, realm, toname) + char *name, *realm, *toname; +{ + register AUTH_DAT *kdata; + AUTH_DAT kdata_st; + + kdata = &kdata_st; + bzero((caddr_t) kdata, sizeof(*kdata)); + (void)strcpy(kdata->pname, name); + (void)strcpy(kdata->pinst, + ((strcmp(toname, "root") == 0) ? "root" : "")); + (void)strcpy(kdata->prealm, realm); + return (kuserok(kdata, toname)); +} +#endif |