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