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