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