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