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