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