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