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