]> Pileus Git - ~andy/fetchmail/commitdiff
The code as I received it from Michael Palmer.
authorEric S. Raymond <esr@thyrsus.com>
Tue, 30 Sep 1997 20:43:30 +0000 (20:43 -0000)
committerEric S. Raymond <esr@thyrsus.com>
Tue, 30 Sep 1997 20:43:30 +0000 (20:43 -0000)
svn path=/trunk/; revision=1442

rpa.c

diff --git a/rpa.c b/rpa.c
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..e417ad581457810ff5ba3bf0ed612bdcf7552f20 100644 (file)
--- a/rpa.c
+++ b/rpa.c
@@ -0,0 +1,882 @@
+/***********************************************************************
+  module:       rpa.c
+  program:      fetchmail
+  programmer:   Michael J. Palmer <106177.1156@compuserve.com>
+  date:         29 August 1997
+  compiler:     GCC 2.7.2
+  environment:  RedHat 4.0 Linux 2.0.18
+  description:  RPA authorisation code for POP3 client
+ ***********************************************************************/
+
+#include  <stdio.h>
+#include  <unistd.h>
+#include  <ctype.h>
+
+#include  "socket.h"
+#include  "fetchmail.h"
+#include  "md5.h"
+
+
+
+#ifdef TESTMODE
+extern unsigned char line1[];
+extern unsigned char line2[];
+extern unsigned char line3[];
+
+extern int linecount;
+#endif
+
+#ifndef NO_PROTO
+  /* prototypes for internal functions */
+  int  POP3_rpa_resp(unsigned char* argbuf, int socket );
+  void LenAppend(unsigned char** pptr, int len);
+  int  LenSkip(unsigned char** pptr, int rxlen);
+  int  DecBase64(unsigned char* bufp);
+  void EncBase64(unsigned char* bufp, int len);
+  void ToUnicode(unsigned char** pptr, unsigned char delim,
+                 unsigned char* buf, int* plen, int conv);
+  int  SetRealmService(unsigned char* bufp);
+  void GenChallenge(unsigned char* buf, int len);
+  int  DigestPassphrase(unsigned char* passphrase,unsigned char* rbuf, int unicodeit);
+  void CompUserResp();
+  int  CheckUserAuth();
+  void md5(unsigned char* in, int len, unsigned char* out);
+#endif
+
+/* RPA protocol definitions */
+
+#define         EARLYVER        "\x01\x00"  /* Earliest supp version */
+#define         LATEVER         "\x03\x00"  /* Latest supp version   */
+#define         HDR             0x60        /* ASN.1 SEQUENCE        */
+#define         MECH            "\x06\x09\x60\x86\x48\x01\x86\xF8\x73\x01\x01"
+#define         FLAGS           "\x00\x01"  /* Mutual authentication */
+#define         STRMAX          128         /* Bytes in Unicode      */
+#define         Tsl             14          /* Timestamp bytelen     */
+#define         Pul             16          /* Passphrase digest len */
+#define         Cul             16          /* Usr challenge bytelen */
+#define         Rul             16          /* Usr response bytelen  */
+#define         Aul             16          /* User auth bytelen     */
+#define         Kusl            16          /* Session key bytelen   */
+
+#define         UNIPASS         1           /* 1=Unicode 0=iso8859   */
+#define         PS_RPA          42          /* Return code           */
+
+/* RPA authentication items */
+
+unsigned char   Cs[256];                    /* Service challenge     */
+int             Csl;                        /* Length of "    "      */
+unsigned char   Ts[Tsl+1];                  /* Timestamp incl \0     */
+unsigned char   Nu[STRMAX];                 /* Username in Unicode   */
+int             Nul;                        /* Length of " in bytes  */
+unsigned char   Ns[STRMAX];                 /* Service in Unicode    */
+int             Nsl;                        /* Length of " in bytes  */
+unsigned char   Nr[STRMAX];                 /* Realm in Unicode      */
+int             Nrl;                        /* Length of " in bytes  */
+unsigned char   Pu[Pul];                    /* Passphrase after MD5  */
+unsigned char   Cu[Cul];                    /* User challenge        */
+unsigned char   Ru[Rul];                    /* User response         */
+unsigned char   Au[Aul];                    /* User auth from Deity  */
+unsigned char   Kusu[Kusl];                 /* Obscured Session key  */
+unsigned char   Kus[Kusl];                  /* Session key           */
+
+/*********************************************************************
+  function:      POP3_auth_rpa
+  description:   send the AUTH RPA commands to the server, and
+                 get the server's response. Then progress through the
+                 RPA challenge/response protocol until we are
+                 (hopefully) granted authorisation.
+  arguments:
+    userid       user's id@realm e.g. myuserid@csi.com
+    passphrase   user's passphrase
+                 (upper lower or mixed case as the realm has chosen.
+                 spec allows various options :-(   )
+    socket       socket to which the server is connected.
+
+  return value:  zero if success, else non-zero.
+  calls:         SockPrintf, POP3_rpa_resp, EncBase64, DecBase64,
+                 LenAppend, GenChallenge
+  globals:       read outlevel.
+ *********************************************************************/
+
+int POP3_auth_rpa (unsigned char *userid, unsigned char *passphrase, int socket)
+{
+    int      ok,rxlen,verh,verl,i,rll;
+    unsigned char buf [POPBUFSIZE];
+    unsigned char *bufp;
+    int      status,aulin,kuslin;
+    char* stdec[4] = { "Success" ,
+                     "Restricted user (something wrong with account)" ,
+                     "Invalid userid or passphrase" ,
+                     "Deity error" };
+
+    /* Initiate RPA authorisation */
+
+    SockPrintf(socket,"AUTH RPA\r\n");
+
+    if (outlevel == O_VERBOSE)
+       fprintf(stderr,"> AUTH RPA\n");
+
+    /* Create unicode user name in Nu.              */
+    /* Create MD5 digest of user's passphrase in Pu */
+
+    bufp = userid;
+    ToUnicode(&bufp, '@', Nu, &Nul, 1);  /* User (lowercase) */
+    DigestPassphrase(passphrase, Pu, UNIPASS);
+
+    /* Get + response from server (RPA ready) */
+
+    if ((ok = POP3_rpa_resp(buf,socket)) != 0)
+    {
+       if (outlevel > O_SILENT && outlevel < O_VERBOSE)
+           fprintf(stderr,"%s\n",buf);
+
+       return(ok);
+    }
+
+    /* Assemble Token 1 in buf */
+
+    bufp    = buf;
+    *bufp++ = HDR;
+    LenAppend(&bufp,      17);
+    memcpy(bufp, MECH,    11); bufp += 11;
+    memcpy(bufp, EARLYVER, 2); bufp += 2;
+    memcpy(bufp, LATEVER,  2); bufp += 2;
+    memcpy(bufp, FLAGS,    2); bufp += 2;
+
+    /* Send Token 1, receive Token 2 */
+
+    EncBase64(buf, bufp-buf);
+#ifndef TESTMODE
+    SockPrintf(socket,"%s\r\n",buf);
+#endif
+    if (outlevel == O_VERBOSE)
+       fprintf(stderr,"> %s\n",buf);
+    if ((ok = POP3_rpa_resp(buf,socket)) != 0)
+    {
+       if (outlevel > O_SILENT && outlevel < O_VERBOSE)
+           fprintf(stderr,"%s\n",buf);
+       return(ok);
+    }
+    if ((rxlen = DecBase64(buf)) == 0)
+    {
+       if (outlevel > O_SILENT)
+           fprintf(stderr,"RPA token 2: Base64 decode error\n");
+       return(PS_RPA);
+    }
+    bufp = buf;
+    *(buf+rxlen) = 0;  /* Terminates realm list */
+    if (LenSkip(&bufp,rxlen) == 0) return(PS_RPA);
+
+    /* Interpret Token 2 */
+
+    verh = *(bufp++); verl = *(bufp++);
+    if (outlevel == O_VERBOSE)
+       fprintf(stderr,"Service chose RPA version %d.%d\n",verh,verl);
+    Csl  = *(bufp++);
+    memcpy(Cs, bufp, Csl);
+    bufp += Csl;
+    if (outlevel == O_VERBOSE)
+    {
+       fprintf(stderr,"Service challenge (l=%d):",Csl);
+       for (i=0; i<Csl; i++) fprintf(stderr," %02X",Cs[i]);
+       fprintf(stderr,"\n");
+    }
+    memcpy(Ts, bufp, Tsl);
+    Ts[Tsl] = 0;
+    bufp += Tsl;
+    if (outlevel == O_VERBOSE)
+       fprintf(stderr,"Service timestamp %s\n",Ts);
+    rll = *(bufp++) << 8; rll = rll | *(bufp++);
+    if ((bufp-buf+rll) != rxlen)
+    {
+       if (outlevel > O_SILENT)
+           fprintf(stderr,"RPA token 2 length error\n");
+       return(PS_RPA);
+    }
+    if (outlevel == O_VERBOSE)
+       fprintf(stderr,"Realm list: %s\n",bufp);
+    if (SetRealmService(bufp) != 0)
+    {
+       if (outlevel > O_SILENT)
+           fprintf(stderr,"RPA error in service@realm string\n");
+       return(PS_RPA);
+    }
+
+    /* Assemble Token 3 in buf */
+
+    bufp      = buf;
+    *(bufp++) = HDR;
+    LenAppend(&bufp, 11+2+strlen(userid)+1+Cul+1+Rul );
+    memcpy(bufp, MECH, 11); bufp += 11;
+  *(bufp++) = 0;
+  *(bufp++) = strlen(userid);
+  memcpy(bufp,userid,strlen(userid)); bufp += strlen(userid);
+  GenChallenge(Cu,Cul);
+  *(bufp++) = Cul;
+  memcpy(bufp, Cu, Cul);  bufp += Cul;
+  CompUserResp();
+  *(bufp++) = Rul;
+  memcpy(bufp, Ru, Rul);  bufp += Rul;
+
+  /* Send Token 3, receive Token 4 */
+
+  EncBase64(buf,bufp-buf);
+#ifndef TESTMODE
+  SockPrintf(socket,"%s\r\n",buf);
+#endif
+  if (outlevel == O_VERBOSE)
+    fprintf(stderr,"> %s\n",buf);
+  if ((ok = POP3_rpa_resp(buf,socket)) != 0)
+    {
+    if (outlevel > O_SILENT && outlevel < O_VERBOSE)
+      fprintf(stderr,"%s\n",buf);
+    return(ok);
+    }
+  if ((rxlen = DecBase64(buf)) == 0)
+    {
+    if (outlevel > O_SILENT)
+      fprintf(stderr,"RPA token 4: Base64 decode error\n");
+    return(PS_RPA);
+    }
+  bufp = buf;
+  if (LenSkip(&bufp,rxlen) == 0) return(PS_RPA);
+
+  /* Interpret Token 4 */
+
+  aulin = *(bufp++);
+  if (outlevel == O_VERBOSE)
+    {
+    fprintf(stderr,"User authentication (l=%d):",aulin);
+    for (i=0; i<aulin; i++) fprintf(stderr," %02X",bufp[i]);
+    fprintf(stderr,"\n");
+    }
+  if (aulin == Aul) memcpy(Au, bufp, Aul);
+  bufp += aulin;
+  kuslin = *(bufp++);
+  if (kuslin == Kusl) memcpy(Kusu, bufp, Kusl); /* blinded */
+  bufp += kuslin;
+  if (verh == 3)
+    {
+    status = *(bufp++);
+    if (outlevel == O_VERBOSE)
+      fprintf(stderr,"RPA status: %02X\n",status);
+    }
+  else status = 0;
+  if ((bufp - buf) != rxlen)
+    {
+    if (outlevel > O_SILENT)
+      fprintf(stderr,"RPA token 4 length error\n");
+    return(PS_RPA);
+    }
+  if (status != 0)
+    {
+    if (outlevel > O_SILENT)
+      if (status < 4)
+        fprintf(stderr,"RPA rejects you: %s\n",stdec[status]);
+      else
+        fprintf(stderr,"RPA rejects you, reason unknown\n");
+    return(PS_AUTHFAIL);
+    }
+  if (Aul != aulin)
+    {
+    fprintf(stderr,"RPA User Authentication length error: %d\n",aulin);
+    return(PS_RPA);
+    }
+  if (Kusl != kuslin)
+    {
+    fprintf(stderr,"RPA Session key length error: %d\n",kuslin);
+    return(PS_RPA);
+    }
+  if (CheckUserAuth() != 0)
+    {
+    if (outlevel > O_SILENT)
+      fprintf(stderr,"RPA _service_ auth fail. Spoof server?\n");
+    return(PS_AUTHFAIL);
+    }
+  if (outlevel == O_VERBOSE)
+    {
+    fprintf(stderr,"Session key established:");
+    for (i=0; i<Kusl; i++) fprintf(stderr," %02X",Kus[i]);
+    fprintf(stderr,"\n");
+    }
+
+  /* Assemble Token 5 in buf and send (not in ver 2 though)  */
+  /* Version 3.0 definitely replies with +OK to this. I have */
+  /* no idea what sort of response previous versions gave.   */
+
+  if (verh != 2)
+    {
+    bufp      = buf;
+    *(bufp++) = HDR;
+    LenAppend(&bufp, 1 );
+    *(bufp++) = 0x42;
+    EncBase64(buf,bufp-buf);
+#ifndef TESTMODE
+    SockPrintf(socket,"%s\r\n",buf);
+#endif
+    if (outlevel == O_VERBOSE)
+      fprintf(stderr,"> %s\n",buf);
+    if ((ok = POP3_rpa_resp(buf,socket)) != 0)
+      {
+      if (outlevel > O_SILENT && outlevel < O_VERBOSE)
+        fprintf(stderr,"%s\n",buf);
+      return(ok);
+      }
+    }
+
+  if (outlevel > O_SILENT)
+    fprintf(stderr,"RPA authorisation complete\n");
+
+  return(PS_SUCCESS);
+}
+
+
+/*********************************************************************
+  function:      POP3_rpa_resp
+  description:   get the server's response to an RPA action.
+                 Return received base64 string if successful
+  arguments:
+    argbuf       buffer to receive the string.
+    socket       socket to which the server is connected.
+
+  return value:  zero if okay, else return code.
+  calls:         SockGets
+  globals:       reads outlevel.
+ *********************************************************************/
+
+int POP3_rpa_resp (argbuf,socket)
+unsigned char *argbuf;
+int socket;
+{
+  int ok;
+  char buf [POPBUFSIZE];
+  char *bufp;
+  int sockrc;
+  fprintf(stderr, "Get response\n");
+#ifndef TESTMODE
+  sockrc = SockRead(socket, buf, sizeof(buf));
+#else
+  linecount++;
+  if (linecount == 1) strcpy(buf,line1);
+  if (linecount == 2) strcpy(buf,line2);
+  if (linecount == 3) strcpy(buf,line3);
+/*  fprintf(stderr,"--> "); fflush(stderr);  */
+/*  scanf("%s",&buf)                         */
+  sockrc = 0;
+#endif
+  if (sockrc > 0) {
+    buf[sockrc] = 0;
+    if (outlevel == O_VERBOSE)
+      fprintf(stderr,"%s\n",buf);
+
+    bufp = buf;
+    if ((*buf) == '+')
+      {
+      bufp++;
+/*      if (*bufp == ' ') bufp++; */
+      if (argbuf != NULL)
+        strcpy(argbuf,bufp);
+      ok=0;
+      }
+    else if (strcmp(buf,"-ERR") == 0)
+      ok = PS_ERROR;
+    else ok = PS_PROTOCOL;
+
+  }
+  else
+    ok = PS_SOCKET;
+  fprintf(stderr, "Get response return %d [%s]\n", ok, buf);
+  buf[sockrc] = 0;
+  return(ok);
+}
+
+/*********************************************************************
+  function:      LenAppend
+  description:   Store token length encoded as per ASN.1 DER rules
+                 buffer pointer stepped on appropriately.
+                 Copes with numbers up to 32767 at least.
+  arguments:
+    buf          pointer to buffer to receive result
+    len          length value to encode
+
+  return value:  none
+  calls:         none
+  globals:       none
+ *********************************************************************/
+
+void LenAppend(pptr,len)
+unsigned char **pptr;
+int  len;
+{
+  if (len < 0x80)
+    {
+    **pptr = len; (*pptr)++;
+    }
+  else if (len < 0x100)
+    {
+    **pptr = 0x81; (*pptr)++;
+    **pptr = len;  (*pptr)++;
+    }
+  else
+    {
+    **pptr = 0x82;       (*pptr)++;
+    **pptr = len >> 8;   (*pptr)++;
+    **pptr = len & 0xFF; (*pptr)++;
+    }
+}
+
+/*********************************************************************
+  function:      LenSkip
+  description:   Check token header, length, and mechanism, and
+                 skip past these.
+  arguments:
+    pptr         pointer to buffer pointer
+    rxlen        number of bytes after base64 decode
+
+  return value:  0 if error, else token length value
+  calls:         none
+  globals:       reads outlevel.
+ *********************************************************************/
+
+int LenSkip(pptr,rxlen)
+unsigned char **pptr;
+int rxlen;
+{
+  int len;
+  unsigned char *save;
+  save = *pptr;
+  if (**pptr != HDR)
+    {
+    if (outlevel > O_SILENT) fprintf(stderr,"Hdr not 60\n");
+    return(0);
+    }
+  (*pptr)++;
+  if (((**pptr) & 0x80) == 0 )
+    {
+    len = **pptr; (*pptr)++;
+    }
+  else if ((**pptr) == 0x81)
+    {
+    len = *(*pptr+1); (*pptr) += 2;
+    }
+  else if ((**pptr) == 0x82)
+    {
+    len = ((*(*pptr+1)) << 8) | *(*pptr+2);
+    (*pptr) += 3;
+    }
+  else len = 0;
+  if (len==0)
+    {
+    if (outlevel>O_SILENT)
+      fprintf(stderr,"Token length error\n");
+    }
+  else if (((*pptr-save)+len) != rxlen)
+    {
+    if (outlevel>O_SILENT)
+      fprintf(stderr,"Token Length %d disagrees with rxlen %d\n",len,rxlen);
+    len = 0;
+    }
+  else if (memcmp(*pptr,MECH,11))
+    {
+    if (outlevel > O_SILENT)
+      fprintf(stderr,"Mechanism field incorrect\n");
+    len = 0;
+    }
+  else (*pptr) += 11;  /* Skip mechanism field */
+  return(len);
+}
+
+/*********************************************************************
+  function:      DecBase64
+  description:   Decode a Base64 string, overwriting the original.
+                 Note that result cannot be longer than input.
+
+  arguments:
+    bufp         buffer
+
+  return value:  0 if error, else number of bytes in decoded result
+  calls:         none
+  globals:       reads outlevel.
+ *********************************************************************/
+
+int DecBase64(bufp)
+unsigned char *bufp;
+{
+  unsigned int   new, bits=0, cnt=0, i, part=0;
+  unsigned char  ch;
+  unsigned char* outp=bufp;
+  unsigned char* inp=bufp;
+  while((ch=*(inp++)) != 0)
+    {
+    if ((ch != '=') && (ch != ' ') && (ch != '\n') && (ch != '\r'))
+      {
+      if      ((ch>='A') && (ch <= 'Z'))   new = ch - 'A';
+      else if ((ch>='a') && (ch <= 'z'))   new = ch - 'a' + 26;
+      else if ((ch>='0') && (ch <= '9'))   new = ch - '0' + 52;
+      else if ( ch=='+'                )   new = 62;
+      else if ( ch=='/'                )   new = 63;
+      else {
+       fprintf(stderr, "dec64 error at char %d: %x\n", inp - bufp, ch);
+       return(0);
+      }
+      part=((part & 0x3F)*64) + new;
+      bits += 6;
+      if (bits >= 8)
+        {
+        bits -= 8;
+        *outp = (part >> bits);
+        cnt++; outp++;
+        }
+      }
+    }
+  if (outlevel == O_VERBOSE)
+    {
+    fprintf(stderr,"Inbound binary data:\n");
+    for (i=0; i<cnt; i++)
+      {
+      fprintf(stderr," %02X",bufp[i]);
+      if (((i % 16)==15) || (i==(cnt-1)))
+        fprintf(stderr,"\n");
+      }
+    }
+  return(cnt);
+}
+
+/*********************************************************************
+  function:      EncBase64
+  description:   Encode into Base64 string, overwriting the original.
+                 Note that result CAN be longer than input, the buffer
+                 is assumed to be big enough. Result string is
+                 terminated with \0.
+
+  arguments:
+    bufp         buffer
+    len          number of bytes in buffer (>0)
+
+  return value:  none
+  calls:         none
+  globals:       reads outlevel;
+ *********************************************************************/
+
+void EncBase64(bufp,len)
+unsigned char *bufp;
+int  len;
+{
+  unsigned char* outp;
+  unsigned char  c1,c2,c3;
+  char x[]="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+  int  i;
+
+  if (outlevel == O_VERBOSE)
+    {
+    fprintf(stderr,"Outbound data:\n");
+    for (i=0; i<len; i++)
+      {
+      fprintf(stderr," %02X",bufp[i]);
+      if (((i % 16)==15) || (i==(len-1)))
+        fprintf(stderr,"\n");
+      }
+    }
+  outp = bufp + (((len-1)/3)*4);
+  *(outp+4) = 0;
+  /* So we can do the update in place, start at the far end! */
+  for (i=((len-1)/3)*3; i>=0; i-=3)
+    {
+    c1 = bufp[i];
+    if ((i+1) < len) c2 = bufp[i+1]; else c2=0;
+    if ((i+2) < len) c3 = bufp[i+2]; else c3=0;
+    *(outp) = x[c1/4];
+    *(outp+1) = x[((c1 & 3)*16) + (c2/16)];
+    if ((i+1) < len) *(outp+2) = x[((c2 & 0x0F)*4) + (c3/64)];
+      else *(outp+2) = '=';
+    if ((i+2) < len) *(outp+3) = x[c3 & 0x3F];
+      else *(outp+3) = '=';
+    outp -= 4;
+    }
+}
+
+/*********************************************************************
+  function:      ToUnicode
+  description:   Convert ASCII (or iso-8859-1) byte string into
+                 Unicode. Ensure length isn't too long (STRMAX).
+
+  arguments:
+    pptr         pointer to input buffer
+    delim        delimiter character (in addition to \0)
+    buf          buffer where Unicode will go
+    plen         pointer to length variable (# bytes output)
+    conv         1 to convert to lowercase, 0 leaves alone
+
+  return value:  none
+  calls:         none
+  globals:       reads outlevel;
+ *********************************************************************/
+
+void ToUnicode(pptr,delim,buf,plen,conv)
+unsigned char **pptr; /* input string  */
+unsigned char delim;
+unsigned char *buf;   /* output buffer */
+int  *plen;
+int conv;
+{
+  unsigned char *p;
+  int i;
+  *plen = 0; p=buf;
+  while ( ((**pptr)!=delim) && ((**pptr)!=0) && ((*plen)<STRMAX) )
+    {
+    *(p++) = 0;
+    if (conv)
+      *(p++) = tolower(**pptr);
+    else
+      *(p++) = (**pptr);
+    (*plen) += 2;
+    (*pptr)++;
+    }
+  if ( ((**pptr)!=delim) && ((**pptr)!=0) && ((*plen)==STRMAX) )
+    {
+    if (outlevel > O_SILENT)
+      fprintf(stderr,"RPA String too long\n");
+    *plen = 0;
+    }
+  if (outlevel == O_VERBOSE)
+    {
+    fprintf(stderr,"Unicode:");
+    for (i=0; i<(*plen); i++) fprintf(stderr,"%02X ",buf[i]);
+    fprintf(stderr,"\n");
+    }
+}
+
+/*********************************************************************
+  function:      SetRealmService
+  description:   Select a realm from list, and store it.
+
+  arguments:
+    bufp         pointer to buffer
+
+  return value:  none
+  calls:         none
+  globals:       reads outlevel.
+                 writes Ns Nsl Nr Nrl
+ *********************************************************************/
+
+int SetRealmService(bufp)
+unsigned char* bufp;
+{
+  /* For the moment we pick the first available realm. It would */
+  /* make more sense to verify that the realm which the user    */
+  /* has given (as part of id) is in the list, and select it's  */
+  /* corresponding service name.                                */
+  ToUnicode(&bufp, '@', Ns, &Nsl, 1);  /* Service    */
+  bufp++;                              /* Skip the @ */
+  ToUnicode(&bufp, ' ', Nr, &Nrl, 1);  /* Realm name */
+  if ((Nrl == 0) || (Nsl == 0))
+    return(PS_RPA);
+  return(0);
+}
+
+/*********************************************************************
+  function:      GenChallenge
+  description:   Generate a random User challenge
+
+  arguments:
+    buf          pointer to buffer
+    len          length in bytes
+
+  return value:  none
+  calls:         none
+  globals:       reads outlevel.
+                 reads /dev/random
+ *********************************************************************/
+
+void GenChallenge(buf,len)
+unsigned char *buf;
+int  len;
+{
+  int  i;
+  FILE *devrandom;
+  devrandom = fopen("/dev/urandom","rb");
+  if (devrandom == NULL)
+    {
+    if (outlevel > O_SILENT)
+      fprintf(stderr,"RPA Failed open of /dev/random. This shouldn't\n");
+      fprintf(stderr,"    prevent you logging in, but means you\n");
+      fprintf(stderr,"    cannot be sure you are talking to the\n");
+      fprintf(stderr,"    service that you think you are (replay\n");
+      fprintf(stderr,"    attacks by a dishonest service are possible.)\n");
+    }
+  for (i=0; i<len; i++) buf[i] = fgetc(devrandom);
+//  for (i=0; i<len; i++) buf[i] = random();
+  fclose(devrandom);
+  if (outlevel == O_VERBOSE)
+    {
+    fprintf(stderr,"User challenge:");
+    for (i=0; i<len; i++) fprintf(stderr," %02X",buf[i]);
+    fprintf(stderr,"\n");
+    }
+}
+
+/*********************************************************************
+  function:      DigestPassphrase
+  description:   Use MD5 to compute digest (Pu) of Passphrase
+                 Don't map to lower case. We assume the user is
+                 aware of the case requirement of the realm.
+                 (Why oh why have options in the spec?!)
+  arguments:
+    passphrase   buffer containing string, \0 terminated
+    rbuf         buffer into which digest goes
+
+  return value:  0 if ok, else error code
+  calls:         md5
+  globals:       reads authentication items listed above.
+                 writes Pu.
+ *********************************************************************/
+
+int DigestPassphrase(passphrase,rbuf,unicodeit)
+unsigned char *passphrase;
+unsigned char *rbuf;
+int unicodeit;
+{
+  int   len;
+  unsigned char  workarea[STRMAX];
+  unsigned char* ptr;
+
+  if (unicodeit)  /* Option in spec. Yuck. */
+    {
+    ptr = passphrase;
+    ToUnicode(&ptr, '\0', workarea, &len, 0); /* No case conv here */
+    if (len == 0)
+      return(PS_SYNTAX);
+    ptr = workarea;
+    }
+  else
+    {
+    ptr = rbuf;
+    len = strlen(passphrase);
+    }
+  md5(ptr,len,rbuf);
+  return(0);
+}
+
+/*********************************************************************
+  function:      CompUserResp
+  description:   Use MD5 to compute User Response (Ru) from
+                 Pu Z(48) Nu Ns Nr Cu Cs Ts Pu
+
+  arguments:     none
+
+  return value:  none
+  calls:         MD5
+  globals:       reads authentication items listed above.
+                 writes Ru.
+ *********************************************************************/
+
+void CompUserResp()
+{
+  unsigned char  workarea[Pul+48+STRMAX*5+Tsl+Pul];
+  unsigned char* p;
+  p = workarea;
+  memcpy(p , Pu,  Pul); p += Pul;
+  memset(p , '\0', 48); p += 48;
+  memcpy(p , Nu,  Nul); p += Nul;
+  memcpy(p , Ns,  Nsl); p += Nsl;
+  memcpy(p , Nr,  Nrl); p += Nrl;
+  memcpy(p , Cu,  Cul); p += Cul;
+  memcpy(p , Cs,  Csl); p += Csl;
+  memcpy(p , Ts,  Tsl); p += Tsl;
+  memcpy(p , Pu,  Pul); p += Pul;
+  md5(workarea,p-workarea,Ru);
+}
+
+/*********************************************************************
+  function:      CheckUserAuth
+  description:   Use MD5 to verify Authentication Response to User (Au)
+                 using  Pu Z(48) Ns Nu Nr Kusu Cs Cu Ts Kus Pu
+                 Also creates unobscured session key Kus from obscured
+                 one Kusu
+
+  arguments:     none
+
+  return value:  0 if ok, PS_RPA if mismatch
+  calls:         MD5
+  globals:       reads authentication items listed above.
+                 writes Ru.
+ *********************************************************************/
+
+int CheckUserAuth()
+{
+  unsigned char  workarea[Pul+48+STRMAX*7+Tsl+Pul];
+  unsigned char* p;
+  unsigned char  md5ans[16];
+  int i;
+    /* Create unobscured Kusu */
+  p = workarea;
+  memcpy(p , Pu,  Pul); p += Pul;
+  memset(p , '\0', 48); p += 48;
+  memcpy(p , Ns,  Nsl); p += Nsl;
+  memcpy(p , Nu,  Nul); p += Nul;
+  memcpy(p , Nr,  Nrl); p += Nrl;
+  memcpy(p , Cs,  Csl); p += Csl;
+  memcpy(p , Cu,  Cul); p += Cul;
+  memcpy(p , Ts,  Tsl); p += Tsl;
+  memcpy(p , Pu,  Pul); p += Pul;
+  md5(workarea,p-workarea,md5ans);
+  for (i=0; i<16; i++) Kus[i] = Kusu[i] ^ md5ans[i];
+    /* Compute Au from our information */
+  p = workarea;
+  memcpy(p , Pu,  Pul); p += Pul;
+  memset(p , '\0', 48); p += 48;
+  memcpy(p , Ns,  Nsl); p += Nsl;
+  memcpy(p , Nu,  Nul); p += Nul;
+  memcpy(p , Nr,  Nrl); p += Nrl;
+  memcpy(p , Kusu,Kusl);p += Kusl;
+  memcpy(p , Cs,  Csl); p += Csl;
+  memcpy(p , Cu,  Cul); p += Cul;
+  memcpy(p , Ts,  Tsl); p += Tsl;
+  memcpy(p , Kus, Kusl);p += Kusl;
+  memcpy(p , Pu,  Pul); p += Pul;
+  md5(workarea,p-workarea,md5ans);
+    /* Compare the two */
+  for (i=0; i<16; i++)
+    if (Au[i] != md5ans[i]) return(PS_RPA);
+  return(0);
+}
+
+/*********************************************************************
+  function:      md5
+  description:   Apply MD5
+  arguments:
+    in           input byte stream
+    len          length in bytes
+    out          128 bit result buffer
+  return value:  none
+  calls:         MD5 primitives
+  globals:       reads outlevel
+ *********************************************************************/
+
+void md5(in,len,out)
+unsigned char*    in;
+int      len;
+unsigned char*    out;
+{
+  int      i;
+  MD5_CTX  md5context;
+
+  if (outlevel == O_VERBOSE)
+    {
+    fprintf(stderr,"MD5 being applied to data block:\n");
+    for (i=0; i<len; i++)
+      {
+      fprintf(stderr," %02X",in[i]);
+      if (((i % 16)==15) || (i==(len-1))) fprintf(stderr,"\n");
+      }
+    }
+  MD5Init(   &md5context );
+  MD5Update( &md5context, in, len );
+  MD5Final(  out, &md5context );
+  if (outlevel == O_VERBOSE)
+    {
+    fprintf(stderr,"MD5 result is: ");
+    for (i=0; i<16; i++) fprintf(stderr,"%02X ",out[i]);
+    fprintf(stderr,"\n");
+    }
+}