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