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