/*********************************************************************** 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 The sole entry point is POP3_auth_rpa() For license terms, see the file COPYING in this directory. ***********************************************************************/ #include "config.h" #if defined(POP3_ENABLE) && defined(RPA_ENABLE) #include "fetchmail.h" #include #include #include #include #include #include #include "socket.h" #include "fm_md5.h" #include "i18n.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 */ static int POP3_rpa_resp(char* argbuf, int socket ); static void LenAppend(char** pptr, int len); static int LenSkip(char** pptr, int rxlen); static int DecBase64(char* bufp); static void EncBase64(char* bufp, int len); static void ToUnicode(char** pptr, char delim, unsigned char* buf, int* plen, int conv); static int SetRealmService(char* bufp); static void GenChallenge(unsigned char* buf, int len); static int DigestPassphrase(char* passphrase, unsigned char* rbuf, int unicodeit); static void CompUserResp(void); static int CheckUserAuth(void); static void md5(const void* 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 :-( ) socke
#!/bin/sh
#
# timeplot -- plot data on fetchmail release intervals
#
# 

# Get data from the NEWS file
timeseries | awk >/tmp/timeplot$$ '
START	{maxdiff = 0;}
/^[#%]/	{next;}
	{days[count++] = $6;}
END	{
		for (i = 0; i < count-1; i++)
		{
			diffs[i] = days[i] - days[i + 1];
			if (maxdiff < diffs[i])
				maxdiff = diffs[i];
		}
		for (i = 0; i <= maxdiff; i++)
			freq[i] = 0;
		for (i = 0; i < count - 1; i++)
		{
			freq[diffs[i]]++;
		}
		for (i = 0; i <= maxdiff; i++)
			printf("%d	%d\n", i, freq[i]);
	}
'

gnuplot >time.png - <<EOF
set xlabel "Release interval (days)"
set ylabel "Interval frequency"
plot '/tmp/timeplot$$' using 1:2 \
	title "Release interval frequency"
pause 9999
EOF

rm -f /tmp/time*

# timeplot ends here
line2); if (linecount == 3) strcpy(buf,line3); /* report(stdout, "--> "); fflush(stderr); */ /* scanf("%s",&buf) */ sockrc = PS_SUCCESS; #endif if (sockrc == PS_SUCCESS) { 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; if (outlevel >= O_DEBUG) report(stdout, GT_("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 *********************************************************************/ static void LenAppend(char **pptr_, int len) { unsigned char **pptr = (unsigned char **)pptr_; 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(char **pptr, int rxlen) { int len; char *save; save = *pptr; if ((unsigned char)**pptr != HDR) { if (outlevel > O_SILENT) report(stderr, GT_("Hdr not 60\n")); return(0); } (*pptr)++; if (((unsigned char)(**pptr) & 0x80) == 0 ) { len = (unsigned char)**pptr; (*pptr)++; } else if ((unsigned char)(**pptr) == 0x81) { len = (unsigned char)*(*pptr+1); (*pptr) += 2; } else if ((unsigned char)(**pptr) == 0x82) { len = ((unsigned char)(*(*pptr+1)) << 8) | (unsigned char)*(*pptr+2); (*pptr) += 3; } else len = 0; if (len==0) { if (outlevel>O_SILENT) report(stderr, GT_("Token length error\n")); } else if (((*pptr-save)+len) != rxlen) { if (outlevel>O_SILENT) report(stderr, GT_("Token Length %d disagrees with rxlen %d\n"),len,rxlen); len = 0; } else if (memcmp(*pptr,MECH,11)) { if (outlevel > O_SILENT) report(stderr, GT_("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. *********************************************************************/ static int DecBase64(char *bufp) { unsigned int newx, bits=0, cnt=0, i, part=0; unsigned char ch; char* outp=bufp; char* inp=bufp; while((ch=(unsigned char)*(inp++)) != 0) { if ((ch != '=') && (ch != ' ') && (ch != '\n') && (ch != '\r')) { if ((ch>='A') && (ch <= 'Z')) newx = ch - 'A'; else if ((ch>='a') && (ch <= 'z')) newx = ch - 'a' + 26; else if ((ch>='0') && (ch <= '9')) newx = ch - '0' + 52; else if ( ch=='+' ) newx = 62; else if ( ch=='/' ) newx = 63; else { report(stderr, GT_("dec64 error at char %d: %x\n"), (int)(inp - bufp), ch); return(0); } part=((part & 0x3F)*64) + newx; bits += 6; if (bits >= 8) { bits -= 8; *outp = (part >> bits); cnt++; outp++; } } } if (outlevel >= O_MONITOR) { report(stdout, GT_("Inbound binary data:\n")); for (i=0; i0) return value: none calls: none globals: reads outlevel; *********************************************************************/ static void EncBase64(char *bufp, int len) { char* outp; unsigned char c1,c2,c3; char x[]="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; int i; if (outlevel >= O_MONITOR) { report(stdout, GT_("Outbound data:\n")); for (i=0; i=0; i-=3) { c1 = (unsigned char)bufp[i]; if ((i+1) < len) c2 = (unsigned char)bufp[i+1]; else c2=0; if ((i+2) < len) c3 = (unsigned char)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; *********************************************************************/ static void ToUnicode(char **pptr /* input string*/, 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) O_SILENT) report(stderr, GT_("RPA String too long\n")); *plen = 0; } if (outlevel >= O_DEBUG) { report(stdout, GT_("Unicode:\n")); for (i=0; i<(*plen); i++) { report_build(stdout, "%02X ",buf[i]); if (((i % 16)==15) || (i==((*plen)-1))) report_complete(stdout, "\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 *********************************************************************/ static int SetRealmService(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 *********************************************************************/ static void GenChallenge(unsigned char *buf, int len) { int i; FILE *devrandom; devrandom = fopen("/dev/urandom","rb"); if (devrandom == NULL && outlevel > O_SILENT) { report(stdout, GT_("RPA Failed open of /dev/urandom. This shouldn't\n")); report(stdout, GT_(" prevent you logging in, but means you\n")); report(stdout, GT_(" cannot be sure you are talking to the\n")); report(stdout, GT_(" service that you think you are (replay\n")); report(stdout, GT_(" attacks by a dishonest service are possible.)\n")); } for(i=0; i= O_DEBUG) { report(stdout, GT_("User challenge:\n")); for (i=0; i= O_DEBUG) { report(stdout, GT_("MD5 being applied to data block:\n")); for (i=0; i= O_DEBUG) { report(stdout, GT_("MD5 result is:\n")); for (i=0; i<16; i++) { report_build(stdout, "%02X ",out[i]); } report_complete(stdout, "\n"); } } #endif /* POP3_ENABLE && RPA_ENABLE */ /* rpa.c ends here */