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