aboutsummaryrefslogtreecommitdiffstats
path: root/rpa.c
diff options
context:
space:
mode:
Diffstat (limited to 'rpa.c')
-rw-r--r--rpa.c882
1 files changed, 882 insertions, 0 deletions
diff --git a/rpa.c b/rpa.c
index e69de29b..e417ad58 100644
--- 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");
+ }
+}