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