]> Pileus Git - ~andy/fetchmail/blob - rpa.c
Introduced O_DEBUG.
[~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_build("%02X ",bufp[i]);
584       if (((i % 16)==15) || (i==(len-1)))
585         error_complete(0, 0, "");
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++)
653       {
654       error_build("%02X ",buf[i]);
655       if (((i % 16)==15) || (i==((*plen)-1)))
656         error_complete(0, 0, "");
657       }
658     }
659 }
660
661 /*********************************************************************
662   function:      SetRealmService
663   description:   Select a realm from list, and store it.
664
665   arguments:
666     bufp         pointer to buffer
667
668   return value:  none
669   calls:         none
670   globals:       reads outlevel.
671                  writes Ns Nsl Nr Nrl
672  *********************************************************************/
673
674 static int SetRealmService(bufp)
675 unsigned char* bufp;
676 {
677   /* For the moment we pick the first available realm. It would */
678   /* make more sense to verify that the realm which the user    */
679   /* has given (as part of id) is in the list, and select it's  */
680   /* corresponding service name.                                */
681   ToUnicode(&bufp, '@', Ns, &Nsl, 1);  /* Service    */
682   bufp++;                              /* Skip the @ */
683   ToUnicode(&bufp, ' ', Nr, &Nrl, 1);  /* Realm name */
684   if ((Nrl == 0) || (Nsl == 0))
685     return(PS_RPA);
686   return(0);
687 }
688
689 /*********************************************************************
690   function:      GenChallenge
691   description:   Generate a random User challenge
692
693   arguments:
694     buf          pointer to buffer
695     len          length in bytes
696
697   return value:  none
698   calls:         none
699   globals:       reads outlevel.
700                  reads /dev/random
701  *********************************************************************/
702
703 static void GenChallenge(buf,len)
704 unsigned char *buf;
705 int  len;
706 {
707     int  i;
708     FILE *devrandom;
709
710     devrandom = fopen("/dev/urandom","rb");
711     if (devrandom == NULL && outlevel > O_SILENT)
712     {
713         error(0, 0, "RPA Failed open of /dev/urandom. This shouldn't\n");
714         error(0, 0, "    prevent you logging in, but means you\n");
715         error(0, 0, "    cannot be sure you are talking to the\n");
716         error(0, 0, "    service that you think you are (replay\n");
717         error(0, 0, "    attacks by a dishonest service are possible.)\n");
718     }
719
720     for(i=0; i<len; i++)
721         buf[i] = devrandom ? fgetc(devrandom) : random();
722
723     if (devrandom)
724         fclose(devrandom);
725
726     if (outlevel >= O_VERBOSE)
727     {
728         error(0, 0, "User challenge:");
729         for (i=0; i<len; i++)
730           {
731           error_build("%02X ",buf[i]);
732           if (((i % 16)==15) || (i==(len-1)))
733             error_complete(0, 0, "");
734           }
735     }
736 }
737
738 /*********************************************************************
739   function:      DigestPassphrase
740   description:   Use MD5 to compute digest (Pu) of Passphrase
741                  Don't map to lower case. We assume the user is
742                  aware of the case requirement of the realm.
743                  (Why oh why have options in the spec?!)
744   arguments:
745     passphrase   buffer containing string, \0 terminated
746     rbuf         buffer into which digest goes
747
748   return value:  0 if ok, else error code
749   calls:         md5
750   globals:       reads authentication items listed above.
751                  writes Pu.
752  *********************************************************************/
753
754 static int DigestPassphrase(passphrase,rbuf,unicodeit)
755 unsigned char *passphrase;
756 unsigned char *rbuf;
757 int unicodeit;
758 {
759   int   len;
760   unsigned char  workarea[STRMAX];
761   unsigned char* ptr;
762
763   if (unicodeit)  /* Option in spec. Yuck. */
764     {
765     ptr = passphrase;
766     ToUnicode(&ptr, '\0', workarea, &len, 0); /* No case conv here */
767     if (len == 0)
768       return(PS_SYNTAX);
769     ptr = workarea;
770     }
771   else
772     {
773     ptr = rbuf;
774     len = strlen(passphrase);
775     }
776   md5(ptr,len,rbuf);
777   return(0);
778 }
779
780 /*********************************************************************
781   function:      CompUserResp
782   description:   Use MD5 to compute User Response (Ru) from
783                  Pu Z(48) Nu Ns Nr Cu Cs Ts Pu
784
785   arguments:     none
786
787   return value:  none
788   calls:         MD5
789   globals:       reads authentication items listed above.
790                  writes Ru.
791  *********************************************************************/
792
793 static void CompUserResp()
794 {
795   unsigned char  workarea[Pul+48+STRMAX*5+Tsl+Pul];
796   unsigned char* p;
797   p = workarea;
798   memcpy(p , Pu,  Pul); p += Pul;
799   memset(p , '\0', 48); p += 48;
800   memcpy(p , Nu,  Nul); p += Nul;
801   memcpy(p , Ns,  Nsl); p += Nsl;
802   memcpy(p , Nr,  Nrl); p += Nrl;
803   memcpy(p , Cu,  Cul); p += Cul;
804   memcpy(p , Cs,  Csl); p += Csl;
805   memcpy(p , Ts,  Tsl); p += Tsl;
806   memcpy(p , Pu,  Pul); p += Pul;
807   md5(workarea,p-workarea,Ru);
808 }
809
810 /*********************************************************************
811   function:      CheckUserAuth
812   description:   Use MD5 to verify Authentication Response to User (Au)
813                  using  Pu Z(48) Ns Nu Nr Kusu Cs Cu Ts Kus Pu
814                  Also creates unobscured session key Kus from obscured
815                  one Kusu
816
817   arguments:     none
818
819   return value:  0 if ok, PS_RPA if mismatch
820   calls:         MD5
821   globals:       reads authentication items listed above.
822                  writes Ru.
823  *********************************************************************/
824
825 static int CheckUserAuth()
826 {
827   unsigned char  workarea[Pul+48+STRMAX*7+Tsl+Pul];
828   unsigned char* p;
829   unsigned char  md5ans[16];
830   int i;
831     /* Create unobscured Kusu */
832   p = workarea;
833   memcpy(p , Pu,  Pul); p += Pul;
834   memset(p , '\0', 48); p += 48;
835   memcpy(p , Ns,  Nsl); p += Nsl;
836   memcpy(p , Nu,  Nul); p += Nul;
837   memcpy(p , Nr,  Nrl); p += Nrl;
838   memcpy(p , Cs,  Csl); p += Csl;
839   memcpy(p , Cu,  Cul); p += Cul;
840   memcpy(p , Ts,  Tsl); p += Tsl;
841   memcpy(p , Pu,  Pul); p += Pul;
842   md5(workarea,p-workarea,md5ans);
843   for (i=0; i<16; i++) Kus[i] = Kusu[i] ^ md5ans[i];
844     /* Compute Au from our information */
845   p = workarea;
846   memcpy(p , Pu,  Pul); p += Pul;
847   memset(p , '\0', 48); p += 48;
848   memcpy(p , Ns,  Nsl); p += Nsl;
849   memcpy(p , Nu,  Nul); p += Nul;
850   memcpy(p , Nr,  Nrl); p += Nrl;
851   memcpy(p , Kusu,Kusl);p += Kusl;
852   memcpy(p , Cs,  Csl); p += Csl;
853   memcpy(p , Cu,  Cul); p += Cul;
854   memcpy(p , Ts,  Tsl); p += Tsl;
855   memcpy(p , Kus, Kusl);p += Kusl;
856   memcpy(p , Pu,  Pul); p += Pul;
857   md5(workarea,p-workarea,md5ans);
858     /* Compare the two */
859   for (i=0; i<16; i++)
860     if (Au[i] != md5ans[i]) return(PS_RPA);
861   return(0);
862 }
863
864 /*********************************************************************
865   function:      md5
866   description:   Apply MD5
867   arguments:
868     in           input byte stream
869     len          length in bytes
870     out          128 bit result buffer
871   return value:  none
872   calls:         MD5 primitives
873   globals:       reads outlevel
874  *********************************************************************/
875
876 static void md5(in,len,out)
877 unsigned char*    in;
878 int      len;
879 unsigned char*    out;
880 {
881   int      i;
882   MD5_CTX  md5context;
883
884   if (outlevel >= O_VERBOSE)
885     {
886     error(0, 0, "MD5 being applied to data block:\n");
887     for (i=0; i<len; i++)
888       {
889       error_build("%02X ",in[i]);
890       if (((i % 16)==15) || (i==(len-1)))
891         error_complete(0, 0, "");
892       }
893     }
894   MD5Init(   &md5context );
895   MD5Update( &md5context, in, len );
896   MD5Final(  out, &md5context );
897   if (outlevel >= O_VERBOSE)
898     {
899     error(0, 0, "MD5 result is: ");
900     for (i=0; i<16; i++)
901       {
902       error_build("%02X ",out[i]);
903       }
904     error_complete(0, 0, "");
905     }
906 }
907 #endif /* POP3_ENABLE && RPA_ENABLE */
908
909 /* rpa.c ends here */