]> Pileus Git - ~andy/fetchmail/blob - rpa.c
bad0a4b9571f1b15f6fc91ed4c1069a49c0707ce
[~andy/fetchmail] / rpa.c
1 /***********************************************************************
2   module:       rpa.c
3   program:      fetchmail
4   programmer:   Michael J. Palmer <106177.1156@compuserve.com>
5   date:         29 August 1997
6   compiler:     GCC 2.7.2
7   environment:  RedHat 4.0 Linux 2.0.18
8   description:  RPA authorisation code for POP3 client
9
10   The sole entry point is POP3_auth_rpa()
11
12  ***********************************************************************/
13
14 #include  "config.h"
15
16 #if defined(POP3_ENABLE) && defined(RPA_ENABLE)
17 #include  <stdio.h>
18 #include  <unistd.h>
19 #include  <ctype.h>
20 #include  <string.h> 
21
22 #include  "socket.h"
23 #include  "fetchmail.h"
24 #include  "md5.h"
25
26 #ifdef TESTMODE
27 extern unsigned char line1[];
28 extern unsigned char line2[];
29 extern unsigned char line3[];
30
31 extern int linecount;
32 #endif
33
34 #ifndef NO_PROTO
35   /* prototypes for internal functions */
36   static int  POP3_rpa_resp(unsigned char* argbuf, int socket );
37   static void LenAppend(unsigned char** pptr, int len);
38   static int  LenSkip(unsigned char** pptr, int rxlen);
39   static int  DecBase64(unsigned char* bufp);
40   static void EncBase64(unsigned char* bufp, int len);
41   static void ToUnicode(unsigned char** pptr, unsigned char delim,
42                         unsigned char* buf, int* plen, int conv);
43   static int  SetRealmService(unsigned char* bufp);
44   static void GenChallenge(unsigned char* buf, int len);
45   static int  DigestPassphrase(unsigned char* passphrase,
46                                unsigned char* rbuf, int unicodeit);
47   static void CompUserResp();
48   static int  CheckUserAuth();
49   static void md5(unsigned char* in, int len, unsigned char* out);
50 #endif
51
52 /* RPA protocol definitions */
53
54 #define         EARLYVER        "\x01\x00"  /* Earliest supp version */
55 #define         LATEVER         "\x03\x00"  /* Latest supp version   */
56 #define         HDR             0x60        /* ASN.1 SEQUENCE        */
57 #define         MECH            "\x06\x09\x60\x86\x48\x01\x86\xF8\x73\x01\x01"
58 #define         FLAGS           "\x00\x01"  /* Mutual authentication */
59 #define         STRMAX          128         /* Bytes in Unicode      */
60 #define         Tsl             14          /* Timestamp bytelen     */
61 #define         Pul             16          /* Passphrase digest len */
62 #define         Cul             16          /* Usr challenge bytelen */
63 #define         Rul             16          /* Usr response bytelen  */
64 #define         Aul             16          /* User auth bytelen     */
65 #define         Kusl            16          /* Session key bytelen   */
66
67 #define         UNIPASS         1           /* 1=Unicode 0=iso8859   */
68 #define         PS_RPA          42          /* Return code           */
69
70 /* RPA authentication items */
71
72 unsigned char   Cs[256];                    /* Service challenge     */
73 int             Csl;                        /* Length of "    "      */
74 unsigned char   Ts[Tsl+1];                  /* Timestamp incl \0     */
75 unsigned char   Nu[STRMAX];                 /* Username in Unicode   */
76 int             Nul;                        /* Length of " in bytes  */
77 unsigned char   Ns[STRMAX];                 /* Service in Unicode    */
78 int             Nsl;                        /* Length of " in bytes  */
79 unsigned char   Nr[STRMAX];                 /* Realm in Unicode      */
80 int             Nrl;                        /* Length of " in bytes  */
81 unsigned char   Pu[Pul];                    /* Passphrase after MD5  */
82 unsigned char   Cu[Cul];                    /* User challenge        */
83 unsigned char   Ru[Rul];                    /* User response         */
84 unsigned char   Au[Aul];                    /* User auth from Deity  */
85 unsigned char   Kusu[Kusl];                 /* Obscured Session key  */
86 unsigned char   Kus[Kusl];                  /* Session key           */
87
88 /*********************************************************************
89   function:      POP3_auth_rpa
90   description:   send the AUTH RPA commands to the server, and
91                  get the server's response. Then progress through the
92                  RPA challenge/response protocol until we are
93                  (hopefully) granted authorisation.
94   arguments:
95     userid       user's id@realm e.g. myuserid@csi.com
96     passphrase   user's passphrase
97                  (upper lower or mixed case as the realm has chosen.
98                  spec allows various options :-(   )
99     socket       socket to which the server is connected.
100
101   return value:  zero if success, else non-zero.
102   calls:         SockPrintf, POP3_rpa_resp, EncBase64, DecBase64,
103                  LenAppend, GenChallenge
104   globals:       read outlevel.
105  *********************************************************************/
106
107 int POP3_auth_rpa (unsigned char *userid, unsigned char *passphrase, int socket)
108 {
109     int      ok,rxlen,verh,verl,i,rll;
110     unsigned char buf [POPBUFSIZE];
111     unsigned char *bufp;
112     int      status,aulin,kuslin;
113     char* stdec[4] = { "Success" ,
114                      "Restricted user (something wrong with account)" ,
115                      "Invalid userid or passphrase" ,
116                      "Deity error" };
117
118     /* Initiate RPA authorisation */
119
120     SockPrintf(socket,"AUTH RPA\r\n");
121
122     if (outlevel == O_VERBOSE)
123         error(0, 0, "> AUTH RPA\n");
124
125     /* Create unicode user name in Nu.              */
126     /* Create MD5 digest of user's passphrase in Pu */
127
128     bufp = userid;
129     ToUnicode(&bufp, '@', Nu, &Nul, 1);  /* User (lowercase) */
130     DigestPassphrase(passphrase, Pu, UNIPASS);
131
132     /* Get + response from server (RPA ready) */
133
134     if ((ok = POP3_rpa_resp(buf,socket)) != 0)
135     {
136         if (outlevel > O_SILENT && outlevel < O_VERBOSE)
137             error(0, 0, "%s\n",buf);
138
139         return(ok);
140     }
141
142     /* Assemble Token 1 in buf */
143
144     bufp    = buf;
145     *bufp++ = HDR;
146     LenAppend(&bufp,      17);
147     memcpy(bufp, MECH,    11); bufp += 11;
148     memcpy(bufp, EARLYVER, 2); bufp += 2;
149     memcpy(bufp, LATEVER,  2); bufp += 2;
150     memcpy(bufp, FLAGS,    2); bufp += 2;
151
152     /* Send Token 1, receive Token 2 */
153
154     EncBase64(buf, bufp-buf);
155 #ifndef TESTMODE
156     SockPrintf(socket,"%s\r\n",buf);
157 #endif
158     if (outlevel == O_VERBOSE)
159         error(0, 0, "> %s\n",buf);
160     if ((ok = POP3_rpa_resp(buf,socket)) != 0)
161     {
162         if (outlevel > O_SILENT && outlevel < O_VERBOSE)
163             error(0, 0, "%s\n",buf);
164         return(ok);
165     }
166     if ((rxlen = DecBase64(buf)) == 0)
167     {
168         if (outlevel > O_SILENT)
169             error(0, 0, "RPA token 2: Base64 decode error\n");
170         return(PS_RPA);
171     }
172     bufp = buf;
173     *(buf+rxlen) = 0;  /* Terminates realm list */
174     if (LenSkip(&bufp,rxlen) == 0) return(PS_RPA);
175
176     /* Interpret Token 2 */
177
178     verh = *(bufp++); verl = *(bufp++);
179     if (outlevel == O_VERBOSE)
180         error(0, 0, "Service chose RPA version %d.%d\n",verh,verl);
181     Csl  = *(bufp++);
182     memcpy(Cs, bufp, Csl);
183     bufp += Csl;
184     if (outlevel == O_VERBOSE)
185     {
186         error(0, 0, "Service challenge (l=%d):",Csl);
187         for (i=0; i<Csl; i++) error(0, 0, " %02X",Cs[i]);
188         error(0, 0, "\n");
189     }
190     memcpy(Ts, bufp, Tsl);
191     Ts[Tsl] = 0;
192     bufp += Tsl;
193     if (outlevel == O_VERBOSE)
194         error(0, 0, "Service timestamp %s\n",Ts);
195     rll = *(bufp++) << 8; rll = rll | *(bufp++);
196     if ((bufp-buf+rll) != rxlen)
197     {
198         if (outlevel > O_SILENT)
199             error(0, 0, "RPA token 2 length error\n");
200         return(PS_RPA);
201     }
202     if (outlevel == O_VERBOSE)
203         error(0, 0, "Realm list: %s\n",bufp);
204     if (SetRealmService(bufp) != 0)
205     {
206         if (outlevel > O_SILENT)
207             error(0, 0, "RPA error in service@realm string\n");
208         return(PS_RPA);
209     }
210
211     /* Assemble Token 3 in buf */
212
213     bufp      = buf;
214     *(bufp++) = HDR;
215     LenAppend(&bufp, 11+2+strlen(userid)+1+Cul+1+Rul );
216     memcpy(bufp, MECH, 11); bufp += 11;
217   *(bufp++) = 0;
218   *(bufp++) = strlen(userid);
219   memcpy(bufp,userid,strlen(userid)); bufp += strlen(userid);
220   GenChallenge(Cu,Cul);
221   *(bufp++) = Cul;
222   memcpy(bufp, Cu, Cul);  bufp += Cul;
223   CompUserResp();
224   *(bufp++) = Rul;
225   memcpy(bufp, Ru, Rul);  bufp += Rul;
226
227   /* Send Token 3, receive Token 4 */
228
229   EncBase64(buf,bufp-buf);
230 #ifndef TESTMODE
231   SockPrintf(socket,"%s\r\n",buf);
232 #endif
233   if (outlevel == O_VERBOSE)
234     error(0, 0, "> %s\n",buf);
235   if ((ok = POP3_rpa_resp(buf,socket)) != 0)
236     {
237     if (outlevel > O_SILENT && outlevel < O_VERBOSE)
238       error(0, 0, "%s\n",buf);
239     return(ok);
240     }
241   if ((rxlen = DecBase64(buf)) == 0)
242     {
243     if (outlevel > O_SILENT)
244       error(0, 0, "RPA token 4: Base64 decode error\n");
245     return(PS_RPA);
246     }
247   bufp = buf;
248   if (LenSkip(&bufp,rxlen) == 0) return(PS_RPA);
249
250   /* Interpret Token 4 */
251
252   aulin = *(bufp++);
253   if (outlevel == O_VERBOSE)
254     {
255     error(0, 0, "User authentication (l=%d):",aulin);
256     for (i=0; i<aulin; i++) error(0, 0, " %02X",bufp[i]);
257     error(0, 0, "\n");
258     }
259   if (aulin == Aul) memcpy(Au, bufp, Aul);
260   bufp += aulin;
261   kuslin = *(bufp++);
262   if (kuslin == Kusl) memcpy(Kusu, bufp, Kusl); /* blinded */
263   bufp += kuslin;
264   if (verh == 3)
265     {
266     status = *(bufp++);
267     if (outlevel == O_VERBOSE)
268       error(0, 0, "RPA status: %02X\n",status);
269     }
270   else status = 0;
271   if ((bufp - buf) != rxlen)
272     {
273     if (outlevel > O_SILENT)
274       error(0, 0, "RPA token 4 length error\n");
275     return(PS_RPA);
276     }
277   if (status != 0)
278     {
279     if (outlevel > O_SILENT)
280       if (status < 4)
281         error(0, 0, "RPA rejects you: %s\n",stdec[status]);
282       else
283         error(0, 0, "RPA rejects you, reason unknown\n");
284     return(PS_AUTHFAIL);
285     }
286   if (Aul != aulin)
287     {
288     error(0, 0, "RPA User Authentication length error: %d\n",aulin);
289     return(PS_RPA);
290     }
291   if (Kusl != kuslin)
292     {
293     error(0, 0, "RPA Session key length error: %d\n",kuslin);
294     return(PS_RPA);
295     }
296   if (CheckUserAuth() != 0)
297     {
298     if (outlevel > O_SILENT)
299       error(0, 0, "RPA _service_ auth fail. Spoof server?\n");
300     return(PS_AUTHFAIL);
301     }
302   if (outlevel == O_VERBOSE)
303     {
304     error(0, 0, "Session key established:");
305     for (i=0; i<Kusl; i++) error(0, 0, " %02X",Kus[i]);
306     error(0, 0, "\n");
307     }
308
309   /* Assemble Token 5 in buf and send (not in ver 2 though)  */
310   /* Version 3.0 definitely replies with +OK to this. I have */
311   /* no idea what sort of response previous versions gave.   */
312
313   if (verh != 2)
314     {
315     bufp      = buf;
316     *(bufp++) = HDR;
317     LenAppend(&bufp, 1 );
318     *(bufp++) = 0x42;
319     EncBase64(buf,bufp-buf);
320 #ifndef TESTMODE
321     SockPrintf(socket,"%s\r\n",buf);
322 #endif
323     if (outlevel == O_VERBOSE)
324       error(0, 0, "> %s\n",buf);
325     if ((ok = POP3_rpa_resp(buf,socket)) != 0)
326       {
327       if (outlevel > O_SILENT && outlevel < O_VERBOSE)
328         error(0, 0, "%s\n",buf);
329       return(ok);
330       }
331     }
332
333   if (outlevel > O_SILENT)
334     error(0, 0, "RPA authorisation complete\n");
335
336   return(PS_SUCCESS);
337 }
338
339
340 /*********************************************************************
341   function:      POP3_rpa_resp
342   description:   get the server's response to an RPA action.
343                  Return received base64 string if successful
344   arguments:
345     argbuf       buffer to receive the string.
346     socket       socket to which the server is connected.
347
348   return value:  zero if okay, else return code.
349   calls:         SockGets
350   globals:       reads outlevel.
351  *********************************************************************/
352
353 static int POP3_rpa_resp (argbuf,socket)
354 unsigned char *argbuf;
355 int socket;
356 {
357   int ok;
358   char buf [POPBUFSIZE];
359   char *bufp;
360   int sockrc;
361
362   if (outlevel == O_VERBOSE)
363     error(0, 0,  "Get response\n");
364 #ifndef TESTMODE
365   sockrc = gen_recv(socket, buf, sizeof(buf));
366 #else
367   linecount++;
368   if (linecount == 1) strcpy(buf,line1);
369   if (linecount == 2) strcpy(buf,line2);
370   if (linecount == 3) strcpy(buf,line3);
371 /*  error(0, 0, "--> "); fflush(stderr);  */
372 /*  scanf("%s",&buf)                         */
373   sockrc = PS_SUCCESS;
374 #endif
375   if (sockrc == PS_SUCCESS) {
376     bufp = buf;
377     if ((*buf) == '+')
378       {
379       bufp++;
380 /*      if (*bufp == ' ') bufp++; */
381       if (argbuf != NULL)
382         strcpy(argbuf,bufp);
383       ok=0;
384       }
385     else if (strcmp(buf,"-ERR") == 0)
386       ok = PS_ERROR;
387     else ok = PS_PROTOCOL;
388
389   }
390   else
391     ok = PS_SOCKET;
392   if (outlevel == O_VERBOSE)
393     error(0, 0,  "Get response return %d [%s]\n", ok, buf);
394   buf[sockrc] = 0;
395   return(ok);
396 }
397
398 /*********************************************************************
399   function:      LenAppend
400   description:   Store token length encoded as per ASN.1 DER rules
401                  buffer pointer stepped on appropriately.
402                  Copes with numbers up to 32767 at least.
403   arguments:
404     buf          pointer to buffer to receive result
405     len          length value to encode
406
407   return value:  none
408   calls:         none
409   globals:       none
410  *********************************************************************/
411
412 static void LenAppend(pptr,len)
413 unsigned char **pptr;
414 int  len;
415 {
416   if (len < 0x80)
417     {
418     **pptr = len; (*pptr)++;
419     }
420   else if (len < 0x100)
421     {
422     **pptr = 0x81; (*pptr)++;
423     **pptr = len;  (*pptr)++;
424     }
425   else
426     {
427     **pptr = 0x82;       (*pptr)++;
428     **pptr = len >> 8;   (*pptr)++;
429     **pptr = len & 0xFF; (*pptr)++;
430     }
431 }
432
433 /*********************************************************************
434   function:      LenSkip
435   description:   Check token header, length, and mechanism, and
436                  skip past these.
437   arguments:
438     pptr         pointer to buffer pointer
439     rxlen        number of bytes after base64 decode
440
441   return value:  0 if error, else token length value
442   calls:         none
443   globals:       reads outlevel.
444  *********************************************************************/
445
446 int LenSkip(pptr,rxlen)
447 unsigned char **pptr;
448 int rxlen;
449 {
450   int len;
451   unsigned char *save;
452   save = *pptr;
453   if (**pptr != HDR)
454     {
455     if (outlevel > O_SILENT) error(0, 0, "Hdr not 60\n");
456     return(0);
457     }
458   (*pptr)++;
459   if (((**pptr) & 0x80) == 0 )
460     {
461     len = **pptr; (*pptr)++;
462     }
463   else if ((**pptr) == 0x81)
464     {
465     len = *(*pptr+1); (*pptr) += 2;
466     }
467   else if ((**pptr) == 0x82)
468     {
469     len = ((*(*pptr+1)) << 8) | *(*pptr+2);
470     (*pptr) += 3;
471     }
472   else len = 0;
473   if (len==0)
474     {
475     if (outlevel>O_SILENT)
476       error(0, 0, "Token length error\n");
477     }
478   else if (((*pptr-save)+len) != rxlen)
479     {
480     if (outlevel>O_SILENT)
481       error(0, 0, "Token Length %d disagrees with rxlen %d\n",len,rxlen);
482     len = 0;
483     }
484   else if (memcmp(*pptr,MECH,11))
485     {
486     if (outlevel > O_SILENT)
487       error(0, 0, "Mechanism field incorrect\n");
488     len = 0;
489     }
490   else (*pptr) += 11;  /* Skip mechanism field */
491   return(len);
492 }
493
494 /*********************************************************************
495   function:      DecBase64
496   description:   Decode a Base64 string, overwriting the original.
497                  Note that result cannot be longer than input.
498
499   arguments:
500     bufp         buffer
501
502   return value:  0 if error, else number of bytes in decoded result
503   calls:         none
504   globals:       reads outlevel.
505  *********************************************************************/
506
507 static int DecBase64(bufp)
508 unsigned char *bufp;
509 {
510   unsigned int   new, bits=0, cnt=0, i, part=0;
511   unsigned char  ch;
512   unsigned char* outp=bufp;
513   unsigned char* inp=bufp;
514   while((ch=*(inp++)) != 0)
515     {
516     if ((ch != '=') && (ch != ' ') && (ch != '\n') && (ch != '\r'))
517       {
518       if      ((ch>='A') && (ch <= 'Z'))   new = ch - 'A';
519       else if ((ch>='a') && (ch <= 'z'))   new = ch - 'a' + 26;
520       else if ((ch>='0') && (ch <= '9'))   new = ch - '0' + 52;
521       else if ( ch=='+'                )   new = 62;
522       else if ( ch=='/'                )   new = 63;
523       else {
524         error(0, 0,  "dec64 error at char %d: %x\n", inp - bufp, ch);
525         return(0);
526       }
527       part=((part & 0x3F)*64) + new;
528       bits += 6;
529       if (bits >= 8)
530         {
531         bits -= 8;
532         *outp = (part >> bits);
533         cnt++; outp++;
534         }
535       }
536     }
537   if (outlevel == O_VERBOSE)
538     {
539     error(0, 0, "Inbound binary data:\n");
540     for (i=0; i<cnt; i++)
541       {
542       error(0, 0, " %02X",bufp[i]);
543       if (((i % 16)==15) || (i==(cnt-1)))
544         error(0, 0, "\n");
545       }
546     }
547   return(cnt);
548 }
549
550 /*********************************************************************
551   function:      EncBase64
552   description:   Encode into Base64 string, overwriting the original.
553                  Note that result CAN be longer than input, the buffer
554                  is assumed to be big enough. Result string is
555                  terminated with \0.
556
557   arguments:
558     bufp         buffer
559     len          number of bytes in buffer (>0)
560
561   return value:  none
562   calls:         none
563   globals:       reads outlevel;
564  *********************************************************************/
565
566 static void EncBase64(bufp,len)
567 unsigned char *bufp;
568 int  len;
569 {
570   unsigned char* outp;
571   unsigned char  c1,c2,c3;
572   char x[]="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
573   int  i;
574
575   if (outlevel == O_VERBOSE)
576     {
577     error(0, 0, "Outbound data:\n");
578     for (i=0; i<len; i++)
579       {
580       error(0, 0, " %02X",bufp[i]);
581       if (((i % 16)==15) || (i==(len-1)))
582         error(0, 0, "\n");
583       }
584     }
585   outp = bufp + (((len-1)/3)*4);
586   *(outp+4) = 0;
587   /* So we can do the update in place, start at the far end! */
588   for (i=((len-1)/3)*3; i>=0; i-=3)
589     {
590     c1 = bufp[i];
591     if ((i+1) < len) c2 = bufp[i+1]; else c2=0;
592     if ((i+2) < len) c3 = bufp[i+2]; else c3=0;
593     *(outp) = x[c1/4];
594     *(outp+1) = x[((c1 & 3)*16) + (c2/16)];
595     if ((i+1) < len) *(outp+2) = x[((c2 & 0x0F)*4) + (c3/64)];
596       else *(outp+2) = '=';
597     if ((i+2) < len) *(outp+3) = x[c3 & 0x3F];
598       else *(outp+3) = '=';
599     outp -= 4;
600     }
601 }
602
603 /*********************************************************************
604   function:      ToUnicode
605   description:   Convert ASCII (or iso-8859-1) byte string into
606                  Unicode. Ensure length isn't too long (STRMAX).
607
608   arguments:
609     pptr         pointer to input buffer
610     delim        delimiter character (in addition to \0)
611     buf          buffer where Unicode will go
612     plen         pointer to length variable (# bytes output)
613     conv         1 to convert to lowercase, 0 leaves alone
614
615   return value:  none
616   calls:         none
617   globals:       reads outlevel;
618  *********************************************************************/
619
620 static void ToUnicode(pptr,delim,buf,plen,conv)
621 unsigned char **pptr; /* input string  */
622 unsigned char delim;
623 unsigned char *buf;   /* output buffer */
624 int  *plen;
625 int conv;
626 {
627   unsigned char *p;
628   int i;
629   *plen = 0; p=buf;
630   while ( ((**pptr)!=delim) && ((**pptr)!=0) && ((*plen)<STRMAX) )
631     {
632     *(p++) = 0;
633     if (conv)
634       *(p++) = tolower(**pptr);
635     else
636       *(p++) = (**pptr);
637     (*plen) += 2;
638     (*pptr)++;
639     }
640   if ( ((**pptr)!=delim) && ((**pptr)!=0) && ((*plen)==STRMAX) )
641     {
642     if (outlevel > O_SILENT)
643       error(0, 0, "RPA String too long\n");
644     *plen = 0;
645     }
646   if (outlevel == O_VERBOSE)
647     {
648     error(0, 0, "Unicode:");
649     for (i=0; i<(*plen); i++) error(0, 0, "%02X ",buf[i]);
650     error(0, 0, "\n");
651     }
652 }
653
654 /*********************************************************************
655   function:      SetRealmService
656   description:   Select a realm from list, and store it.
657
658   arguments:
659     bufp         pointer to buffer
660
661   return value:  none
662   calls:         none
663   globals:       reads outlevel.
664                  writes Ns Nsl Nr Nrl
665  *********************************************************************/
666
667 static int SetRealmService(bufp)
668 unsigned char* bufp;
669 {
670   /* For the moment we pick the first available realm. It would */
671   /* make more sense to verify that the realm which the user    */
672   /* has given (as part of id) is in the list, and select it's  */
673   /* corresponding service name.                                */
674   ToUnicode(&bufp, '@', Ns, &Nsl, 1);  /* Service    */
675   bufp++;                              /* Skip the @ */
676   ToUnicode(&bufp, ' ', Nr, &Nrl, 1);  /* Realm name */
677   if ((Nrl == 0) || (Nsl == 0))
678     return(PS_RPA);
679   return(0);
680 }
681
682 /*********************************************************************
683   function:      GenChallenge
684   description:   Generate a random User challenge
685
686   arguments:
687     buf          pointer to buffer
688     len          length in bytes
689
690   return value:  none
691   calls:         none
692   globals:       reads outlevel.
693                  reads /dev/random
694  *********************************************************************/
695
696 static void GenChallenge(buf,len)
697 unsigned char *buf;
698 int  len;
699 {
700   int  i;
701   FILE *devrandom;
702   devrandom = fopen("/dev/urandom","rb");
703   if (devrandom == NULL)
704     {
705     if (outlevel > O_SILENT)
706       error(0, 0, "RPA Failed open of /dev/random. This shouldn't\n");
707       error(0, 0, "    prevent you logging in, but means you\n");
708       error(0, 0, "    cannot be sure you are talking to the\n");
709       error(0, 0, "    service that you think you are (replay\n");
710       error(0, 0, "    attacks by a dishonest service are possible.)\n");
711     }
712   for (i=0; i<len; i++) buf[i] = fgetc(devrandom);
713 //  for (i=0; i<len; i++) buf[i] = random();
714   fclose(devrandom);
715   if (outlevel == O_VERBOSE)
716     {
717     error(0, 0, "User challenge:");
718     for (i=0; i<len; i++) error(0, 0, " %02X",buf[i]);
719     error(0, 0, "\n");
720     }
721 }
722
723 /*********************************************************************
724   function:      DigestPassphrase
725   description:   Use MD5 to compute digest (Pu) of Passphrase
726                  Don't map to lower case. We assume the user is
727                  aware of the case requirement of the realm.
728                  (Why oh why have options in the spec?!)
729   arguments:
730     passphrase   buffer containing string, \0 terminated
731     rbuf         buffer into which digest goes
732
733   return value:  0 if ok, else error code
734   calls:         md5
735   globals:       reads authentication items listed above.
736                  writes Pu.
737  *********************************************************************/
738
739 static int DigestPassphrase(passphrase,rbuf,unicodeit)
740 unsigned char *passphrase;
741 unsigned char *rbuf;
742 int unicodeit;
743 {
744   int   len;
745   unsigned char  workarea[STRMAX];
746   unsigned char* ptr;
747
748   if (unicodeit)  /* Option in spec. Yuck. */
749     {
750     ptr = passphrase;
751     ToUnicode(&ptr, '\0', workarea, &len, 0); /* No case conv here */
752     if (len == 0)
753       return(PS_SYNTAX);
754     ptr = workarea;
755     }
756   else
757     {
758     ptr = rbuf;
759     len = strlen(passphrase);
760     }
761   md5(ptr,len,rbuf);
762   return(0);
763 }
764
765 /*********************************************************************
766   function:      CompUserResp
767   description:   Use MD5 to compute User Response (Ru) from
768                  Pu Z(48) Nu Ns Nr Cu Cs Ts Pu
769
770   arguments:     none
771
772   return value:  none
773   calls:         MD5
774   globals:       reads authentication items listed above.
775                  writes Ru.
776  *********************************************************************/
777
778 static void CompUserResp()
779 {
780   unsigned char  workarea[Pul+48+STRMAX*5+Tsl+Pul];
781   unsigned char* p;
782   p = workarea;
783   memcpy(p , Pu,  Pul); p += Pul;
784   memset(p , '\0', 48); p += 48;
785   memcpy(p , Nu,  Nul); p += Nul;
786   memcpy(p , Ns,  Nsl); p += Nsl;
787   memcpy(p , Nr,  Nrl); p += Nrl;
788   memcpy(p , Cu,  Cul); p += Cul;
789   memcpy(p , Cs,  Csl); p += Csl;
790   memcpy(p , Ts,  Tsl); p += Tsl;
791   memcpy(p , Pu,  Pul); p += Pul;
792   md5(workarea,p-workarea,Ru);
793 }
794
795 /*********************************************************************
796   function:      CheckUserAuth
797   description:   Use MD5 to verify Authentication Response to User (Au)
798                  using  Pu Z(48) Ns Nu Nr Kusu Cs Cu Ts Kus Pu
799                  Also creates unobscured session key Kus from obscured
800                  one Kusu
801
802   arguments:     none
803
804   return value:  0 if ok, PS_RPA if mismatch
805   calls:         MD5
806   globals:       reads authentication items listed above.
807                  writes Ru.
808  *********************************************************************/
809
810 static int CheckUserAuth()
811 {
812   unsigned char  workarea[Pul+48+STRMAX*7+Tsl+Pul];
813   unsigned char* p;
814   unsigned char  md5ans[16];
815   int i;
816     /* Create unobscured Kusu */
817   p = workarea;
818   memcpy(p , Pu,  Pul); p += Pul;
819   memset(p , '\0', 48); p += 48;
820   memcpy(p , Ns,  Nsl); p += Nsl;
821   memcpy(p , Nu,  Nul); p += Nul;
822   memcpy(p , Nr,  Nrl); p += Nrl;
823   memcpy(p , Cs,  Csl); p += Csl;
824   memcpy(p , Cu,  Cul); p += Cul;
825   memcpy(p , Ts,  Tsl); p += Tsl;
826   memcpy(p , Pu,  Pul); p += Pul;
827   md5(workarea,p-workarea,md5ans);
828   for (i=0; i<16; i++) Kus[i] = Kusu[i] ^ md5ans[i];
829     /* Compute Au from our information */
830   p = workarea;
831   memcpy(p , Pu,  Pul); p += Pul;
832   memset(p , '\0', 48); p += 48;
833   memcpy(p , Ns,  Nsl); p += Nsl;
834   memcpy(p , Nu,  Nul); p += Nul;
835   memcpy(p , Nr,  Nrl); p += Nrl;
836   memcpy(p , Kusu,Kusl);p += Kusl;
837   memcpy(p , Cs,  Csl); p += Csl;
838   memcpy(p , Cu,  Cul); p += Cul;
839   memcpy(p , Ts,  Tsl); p += Tsl;
840   memcpy(p , Kus, Kusl);p += Kusl;
841   memcpy(p , Pu,  Pul); p += Pul;
842   md5(workarea,p-workarea,md5ans);
843     /* Compare the two */
844   for (i=0; i<16; i++)
845     if (Au[i] != md5ans[i]) return(PS_RPA);
846   return(0);
847 }
848
849 /*********************************************************************
850   function:      md5
851   description:   Apply MD5
852   arguments:
853     in           input byte stream
854     len          length in bytes
855     out          128 bit result buffer
856   return value:  none
857   calls:         MD5 primitives
858   globals:       reads outlevel
859  *********************************************************************/
860
861 static void md5(in,len,out)
862 unsigned char*    in;
863 int      len;
864 unsigned char*    out;
865 {
866   int      i;
867   MD5_CTX  md5context;
868
869   if (outlevel == O_VERBOSE)
870     {
871     error(0, 0, "MD5 being applied to data block:\n");
872     for (i=0; i<len; i++)
873       {
874       error(0, 0, " %02X",in[i]);
875       if (((i % 16)==15) || (i==(len-1))) error(0, 0, "\n");
876       }
877     }
878   MD5Init(   &md5context );
879   MD5Update( &md5context, in, len );
880   MD5Final(  out, &md5context );
881   if (outlevel == O_VERBOSE)
882     {
883     error(0, 0, "MD5 result is: ");
884     for (i=0; i<16; i++) error(0, 0, "%02X ",out[i]);
885     error(0, 0, "\n");
886     }
887 }
888 #endif /* POP3_ENABLE && RPA_ENABLE */
889
890 /* rpa.c ends here */