COMMAND

    kerberos rsh, rcp and rlogin

SYSTEMS AFFECTED

    Systems running kerberos kth-krb4

PROBLEM

    There has been discovered  a security-hole in kerberized  rsh, rcp
    and rlogin. Every user on  a kerberized system has a  ticket-file.
    Only the owner should be able  to read this file. The name  of the
    ticketfile is stored in the environment-variable KRBTKFILE.

    The versions of  rsh, rcp and  rlogin in the  kth-krb4 package are
    setuid to work with bsd-style rshd and rlogind. When they  attempt
    to read  the ticketfile,  there is  no check  if the user starting
    the program has  read access of  the file.   Thus, a user  can use
    any other user  on the system's  ticketfile by simply  changing an
    environment variable.   To activate the  bug, it is  required that
    there  are  valid  tickets  for  the  target  user  laying  around
    somewhere on your system (usually in /tmp/).

    The hole allows any user on the system to gain privilegies of  any
    other user  including root.   Note also  that it  appears the bsdi
    version of su uses kerbose  tickets if kerbose is configured,  but
    the BSDI  kerberosIV implementation  does not  appear to  have the
    problem (the tf_init() routine which opens the ticket file  checks
    to see that  the real uid  of the process  is either root  or owns
    the ticket file).

    The  hole  has  been  successfully  tested on kth-kerberos, but is
    suspected to  exist on  any other  versions of  kerberos.  The bug
    was discovered by Mattias Amnefelt.

SOLUTION

    Disable the suid-bits  on rcp, rsh  and rlogin. This  will disable
    the  program's  capabilities  to  fallback  to  the non-kerberised
    protocols if the a user fails to authenticate himself.

    Permanent fix  should be  changing the  uid of  the program to the
    user's  uid  as  early  as  possible (patches from the development
    team  are   included,  plus   two  other   security  patches   for
    kth-kerberos).  The enclosed  patch to 0.9.6 fixes  three security
    problems:

        1. the tgetent buffer overflow.   This is fixed by simply  not
           calling tgetent.
        2. vulnerability of setuid rsh, rlogin, and rcp.
        3. missing IP-nummer check in telnetd.

    This fix will of course be included in an upcoming version RSN.

    Index: appl/bsd/rcp.c
    ===================================================================
    RCS file: /afs/pdc.kth.se/src/packages/kth-krb/src/krb4/appl/bsd/rcp.c,v
    retrieving revision 1.43
    retrieving revision 1.44
    diff -u -w -r1.43 -r1.44
    --- rcp.c       1997/05/13 09:41:26     1.43
    +++ rcp.c       1997/11/03 11:18:02     1.44
    @@ -49,6 +49,9 @@
     static uid_t   userid;
     static int pflag, iamremote, iamrecursive, targetshouldbedirectory;

    +static int argc_copy;
    +static char **argv_copy;
    +
     #define        CMDNEEDS        64
     static char cmd[CMDNEEDS];             /* must hold "rcp -r -p -d\0" */

    @@ -403,8 +406,9 @@
     kerberos(char **host, char *bp, char *locuser, char *user)
     {
             int sock = -1, err;
    -again:
    +
            if (use_kerberos) {
    +               setuid(getuid());
                    rem = KSUCCESS;
                    errno = 0;
                    if (dest_realm == NULL)
    @@ -439,13 +443,11 @@
                        rem = sock;
     #endif
                    if (rem < 0) {
    -                       use_kerberos = 0;
    -                       port = get_shell_port(use_kerberos, 0);
                            if (errno == ECONNREFUSED)
                                oldw("remote host doesn't support Kerberos");
                            else if (errno == ENOENT)
                                oldw("can't provide Kerberos authentication data");
    -                       goto again;
    +                       execv(_PATH_RCP, argv_copy);
                    }
            } else {
                    if (doencrypt)
    @@ -906,8 +908,28 @@
     {
            int ch, fflag, tflag;
            char *targ;
    +       int i;

            set_progname(argv[0]);
    +
    +       /*
    +        * Prepare for execing ourselves.
    +        */
    +
    +       argc_copy = argc + 1;
    +       argv_copy = malloc((argc_copy + 1) * sizeof(*argv_copy));
    +       if (argv_copy == NULL)
    +           err(1, "malloc");
    +       argv_copy[0] = argv[0];
    +       argv_copy[1] = "-K";
    +       for(i = 1; i < argc; ++i) {
    +           argv_copy[i + 1] = strdup(argv[i]);
    +           if (argv_copy[i + 1] == NULL)
    +               errx(1, "strdup: out of memory");
    +       }
    +       argv_copy[argc + 1] = NULL;
    +
    +
            fflag = tflag = 0;
            while ((ch = getopt(argc, argv, OPTIONS)) != EOF)
                    switch(ch) {                    /* User-visible flags. */
    @@ -951,8 +973,10 @@
             * kshell service, pass 0 for no encryption */
            port = get_shell_port(use_kerberos, 0);

    +       userid = getuid();
    +
     #ifndef __CYGWIN32__
    -       if ((pwd = k_getpwuid(userid = getuid())) == NULL)
    +       if ((pwd = k_getpwuid(userid)) == NULL)
                    errx(1, "unknown user %d", (int)userid);
     #endif

    Index: appl/bsd/rlogin.c
    ===================================================================
    RCS file: /afs/pdc.kth.se/src/packages/kth-krb/src/krb4/appl/bsd/rlogin.c,v
    retrieving revision 1.61
    retrieving revision 1.62
    diff -u -w -r1.61 -r1.62
    --- rlogin.c    1997/05/25 01:14:47     1.61
    +++ rlogin.c    1997/11/03 11:18:09     1.62
    @@ -594,14 +594,12 @@
                            usage();
                    }
            optind += argoff;
    -       argc -= optind;
    -       argv += optind;

            /* if haven't gotten a host yet, do so */
    -       if (!host && !(host = *argv++))
    +       if (!host && !(host = argv[optind++]))
                    usage();

    -       if (*argv)
    +       if (argv[optind])
                    usage();

            if (!(pw = k_getpwuid(uid = getuid())))
    @@ -609,7 +607,6 @@
            if (!user)
                user = pw->pw_name;

    -
            if (user_port)
                    sv_port = user_port;
            else
    @@ -636,17 +633,8 @@

            get_window_size(0, &winsize);

    -      try_connect:
            if (use_kerberos) {
    -               struct hostent *hp;
    -
    -               /* Fully qualify hostname (needed for krb_realmofhost). */
    -               hp = gethostbyname(host);
    -               if (hp != NULL && !(host = strdup(hp->h_name))) {
    -                   errno = ENOMEM;
    -                   err(1, NULL);
    -               }
    -
    +               setuid(getuid());
                    rem = KSUCCESS;
                    errno = 0;
                    if (dest_realm == NULL)
    @@ -659,15 +647,22 @@
                            rem = krcmd(&host, sv_port, user, term, 0,
                                dest_realm);
                    if (rem < 0) {
    -                       use_kerberos = 0;
    -                       if (user_port == 0)
    -                               sv_port = get_login_port(use_kerberos,
    -                                                        doencrypt);
    +                   int i;
    +                   char **newargv;
    +
                            if (errno == ECONNREFUSED)
                                warning("remote host doesn't support Kerberos");
                            if (errno == ENOENT)
                              warning("can't provide Kerberos auth data");
    -                       goto try_connect;
    +                   newargv = malloc((argc + 2) * sizeof(*newargv));
    +                   if (newargv == NULL)
    +                       err(1, "malloc");
    +                   newargv[0] = argv[0];
    +                   newargv[1] = "-K";
    +                   for(i = 1; i < argc; ++i)
    +                       newargv[i + 1] = argv[i];
    +                   newargv[argc + 1] = NULL;
    +                   execv(_PATH_RLOGIN, newargv);
                    }
            } else {
                    if (doencrypt)
    Index: appl/bsd/rsh.c
    ===================================================================
    RCS file: /afs/pdc.kth.se/src/packages/kth-krb/src/krb4/appl/bsd/rsh.c,v
    retrieving revision 1.36
    retrieving revision 1.37
    diff -u -w -r1.36 -r1.37
    --- rsh.c       1997/06/26 13:48:35     1.36
    +++ rsh.c       1997/11/03 11:18:14     1.37
    @@ -247,9 +247,6 @@
            err(1, "can't exec %s", _PATH_RLOGIN);
         }

    -    argc -= optind;
    -    argv += optind;
    -
     #ifndef __CYGWIN32__
         if (!(pw = k_getpwuid(uid = getuid())))
            errx(1, "unknown user id.");
    @@ -266,12 +263,12 @@
         if (doencrypt)
            nfork = 0;

    -    args = copyargs(argv);
    +    args = copyargs(argv+optind);

         sv_port=get_shell_port(use_kerberos, doencrypt);

    -try_connect:
         if (use_kerberos) {
    +       setuid(getuid());
            rem = KSUCCESS;
            errno = 0;
            if (dest_realm == NULL)
    @@ -284,13 +281,22 @@
                rem = krcmd(&host, sv_port, user, args, &rfd2,
                            dest_realm);
            if (rem < 0) {
    +           int i;
    +           char **newargv;
    +
                if (errno == ECONNREFUSED)
                    warning("remote host doesn't support Kerberos");
                if (errno == ENOENT)
                    warning("can't provide Kerberos auth data");
    -           use_kerberos = 0;
    -           sv_port=get_shell_port(use_kerberos, doencrypt);
    -           goto try_connect;
    +           newargv = malloc((argc + 2) * sizeof(*newargv));
    +           if (newargv == NULL)
    +               err(1, "malloc");
    +           newargv[0] = argv[0];
    +           newargv[1] = "-K";
    +           for(i = 1; i < argc; ++i)
    +               newargv[i + 1] = argv[i];
    +           newargv[argc + 1] = NULL;
    +           execv(_PATH_RSH, newargv);
            }
         } else {
            if (doencrypt)
    Index: appl/bsd/pathnames.h
    ===================================================================
    RCS file: /afs/pdc.kth.se/src/packages/kth-krb/src/krb4/appl/bsd/pathnames.h,v
    retrieving revision 1.23
    retrieving revision 1.24
    diff -u -w -r1.23 -r1.24
    --- pathnames.h 1996/11/17 06:36:42     1.23
    +++ pathnames.h 1997/11/03 11:17:19     1.24
    @@ -65,6 +65,9 @@
     #undef _PATH_RSH               /* Redifine rsh */
     #define _PATH_RSH      BINDIR  "/rsh"

    +#undef _PATH_RCP               /* Redifine rcp */
    +#define _PATH_RCP      BINDIR  "/rcp"
    +
     #undef _PATH_LOGIN
     #define _PATH_LOGIN    BINDIR "/login"

    @@ -186,6 +189,8 @@
     #define        _PATH_RLOGIN    "/usr/athena/bin/rlogin"
     #undef  _PATH_RSH
     #define _PATH_RSH      "/usr/athena/bin/rsh"
    +#undef  _PATH_RCP
    +#define _PATH_RCP      "/usr/athena/bin/rcp"
     #undef  _PATH_LOGIN
     #define _PATH_LOGIN    "/usr/athena/bin/login"
     #endif
    Index: appl/telnet/libtelnet/kerberos.c
    ===================================================================
    RCS file: /afs/pdc.kth.se/src/packages/kth-krb/src/appl/telnet/libtelnet/kerberos.c,v
    retrieving revision 1.34
    retrieving revision 1.36
    diff -u -w -r1.34 -r1.36
    --- kerberos.c  1997/10/21 21:15:24     1.34
    +++ kerberos.c  1997/11/03 06:12:14     1.36
    @@ -265,9 +267,11 @@
     void
     kerberos4_is(Authenticator *ap, unsigned char *data, int cnt)
     {
    +    struct sockaddr_in addr;
         char realm[REALM_SZ];
         char instance[INST_SZ];
         int r;
    +    int addr_len;

         if (cnt-- < 1)
            return;
    @@ -288,8 +292,17 @@
                printf("\r\n");
            }
            k_getsockinst(0, instance, sizeof(instance));
    -       if (r = krb_rd_req(&auth, KRB_SERVICE_NAME,
    -                          instance, 0, &adat, "")) {
    +       addr_len = sizeof(addr);
    +       if(getpeername(0, (struct sockaddr *)&addr, &addr_len) < 0) {
    +           if(auth_debug_mode)
    +               printf("getpeername failed\r\n");
    +           Data(ap, KRB_REJECT, "getpeername failed", -1);
    +           auth_finished(ap, AUTH_REJECT);
    +           return;
    +       }
    +       r = krb_rd_req(&auth, KRB_SERVICE_NAME,
    +                      instance, addr.sin_addr.s_addr, &adat, "");
    +       if (r) {
                if (auth_debug_mode)
                    printf("Kerberos failed him as %s\r\n", name);
                Data(ap, KRB_REJECT, (void *)krb_get_err_text(r), -1);
    Index: appl/telnet/telnetd/telnetd.c
    ===================================================================
    RCS file: /afs/pdc.kth.se/src/packages/kth-krb/src/appl/telnet/telnetd/telnetd.c,v
    retrieving revision 1.47
    retrieving revision 1.48
    diff -u -w -r1.47 -r1.48
    --- telnetd.c   1997/10/29 01:26:58     1.47
    +++ telnetd.c   1997/11/03 06:08:26     1.48
    @@ -647,21 +647,7 @@
     int
     terminaltypeok(char *s)
     {
    -    char buf[1024];
    -
    -    if (terminaltype == NULL)
    -       return(1);
    -
    -    /*
    -     * tgetent() will return 1 if the type is known, and
    -     * 0 if it is not known.  If it returns -1, it couldn't
    -     * open the database.  But if we can't open the database,
    -     * it won't help to say we failed, because we won't be
    -     * able to verify anything else.  So, we treat -1 like 1.
    -     */
    -    if (tgetent(buf, s) == 0)
    -       return(0);
    -    return(1);
    +    return 1;
     }