3
* $Id: x99_pwe.c,v 1.19 2002/11/04 04:02:02 fcusack Exp $
5
* This program is free software; you can redistribute it and/or modify
6
* it under the terms of the GNU General Public License as published by
7
* the Free Software Foundation; either version 2 of the License, or
8
* (at your option) any later version.
10
* This program is distributed in the hope that it will be useful,
11
* but WITHOUT ANY WARRANTY; without even the implied warranty of
12
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
* GNU General Public License for more details.
15
* You should have received a copy of the GNU General Public License
16
* along with this program; if not, write to the Free Software
17
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19
* Copyright 2001,2002 Google, Inc.
23
* This file implements passcode (password) checking functions for each
24
* supported encoding (PAP, CHAP, etc.). The current libradius interface
25
* is not sufficient for X9.9 use.
31
#include "libradius.h"
32
#include "rad_assert.h"
37
#include <openssl/des.h>
38
#include <openssl/md4.h>
39
#include <openssl/md5.h>
40
#include <openssl/sha.h>
44
static const char rcsid[] = "$Id: x99_pwe.c,v 1.19 2002/11/04 04:02:02 fcusack Exp $";
47
/* Attribute IDs for supported password encodings. */
51
/* Initialize the pwattr array for supported password encodings. */
59
* Setup known password types. These are pairs.
60
* NB: Increase pwattr array size when adding a type.
61
* It should be sized as (number of password types * 2)
63
(void) memset(pwattr, 0, sizeof(pwattr));
66
if ((da = dict_attrbyname("User-Password")) != NULL) {
67
pwattr[i++] = da->attr;
68
pwattr[i++] = da->attr;
72
if ((da = dict_attrbyname("CHAP-Challenge")) != NULL) {
73
pwattr[i++] = da->attr;
74
if ((da = dict_attrbyname("CHAP-Password")) != NULL)
75
pwattr[i++] = da->attr;
81
/* MS-CHAP (recommended not to use) */
82
if ((da = dict_attrbyname("MS-CHAP-Challenge")) != NULL) {
83
pwattr[i++] = da->attr;
84
if ((da = dict_attrbyname("MS-CHAP-Response")) != NULL)
85
pwattr[i++] = da->attr;
92
if ((da = dict_attrbyname("MS-CHAP-Challenge")) != NULL) {
93
pwattr[i++] = da->attr;
94
if ((da = dict_attrbyname("MS-CHAP2-Response")) != NULL)
95
pwattr[i++] = da->attr;
103
* Test for password presence in an Access-Request packet.
104
* Returns 0 for "no supported password present", or an non-zero
105
* opaque value that must be used when calling x99_pw_valid.
108
x99_pw_present(const REQUEST *request)
112
for (i = 0; i < sizeof(pwattr) && pwattr[i]; i += 2) {
113
if (pairfind(request->packet->vps, pwattr[i]) &&
114
pairfind(request->packet->vps, pwattr[i + 1])) {
115
DEBUG("rlm_x99_token: pw_present: found password attributes %d, %d",
116
pwattr[i], pwattr[i + 1]);
117
return i + 1; /* Can't return 0 (indicates failure) */
126
* Test for password validity. attr must be the return value from
128
* returns 1 for match, 0 for non-match.
129
* If vps is non-null, then on matches, it will point to vps that
130
* should be added to an Access-Accept packet. If access is denied,
131
* the caller is responsible for freeing any vps returned.
132
* (vps is used for MPPE atttributes.)
135
x99_pw_valid(const REQUEST *request, x99_token_t *inst,
136
int attr, const char *password, VALUE_PAIR **vps)
139
VALUE_PAIR *chal_vp, *resp_vp;
142
* A module that does this might want to verify the presence of these.
143
* This code is self contained to x99, so I know these exist.
145
chal_vp = pairfind(request->packet->vps, pwattr[attr - 1]);
146
resp_vp = pairfind(request->packet->vps, pwattr[attr]);
148
/* Prepare for failure return. */
152
/* If modular, this would actually call the authentication function. */
153
switch(pwattr[attr]) {
155
DEBUG("rlm_x99_token: pw_valid: handling PW_PASSWORD");
156
match = !strcmp(password, resp_vp->strvalue);
159
case PW_CHAP_PASSWORD:
163
* A CHAP password is MD5(CHAP_ID|SECRET|CHAP_CHALLENGE).
164
* CHAP_ID is a value set by the authenticator (the NAS), and used
165
* in the response calculation. It is available as the first byte
166
* of the CHAP-Password attribute.
167
* SECRET is the password.
168
* CHAP_CHALLENGE is the challenge given to the peer (the user).
169
* The CHAP-Challenge Attribute may be missing, in which case the
170
* challenge is taken to be the Request Authenticator. We don't
173
/* ID password chal */
174
unsigned char input[1 + MAX_STRING_LEN + 16];
175
unsigned char output[MD5_DIGEST_LENGTH];
177
DEBUG("rlm_x99_token: pw_valid: handling PW_CHAP_PASSWORD");
178
if (1 + strlen(password) + chal_vp->length > sizeof(input)) {
179
DEBUG("rlm_x99_token: pw_valid: CHAP-Challenge/password too long");
183
if (resp_vp->length != 17) {
184
x99_log(X99_LOG_AUTH, "pw_valid: CHAP-Password wrong size");
188
input[0] = *(resp_vp->strvalue);
189
(void) memcpy(&input[1], password, strlen(password));
190
(void) memcpy(&input[1+strlen(password)], chal_vp->strvalue,
192
(void) MD5(input, 1 + strlen(password) + chal_vp->length, output);
193
match = !memcmp(output, &(resp_vp->strvalue)[1], MD5_DIGEST_LENGTH);
194
} /* case PW_CHAP_PASSWORD */
198
case PW_MS_CHAP_RESPONSE:
201
* See RFCs 2548, 2433, 3079.
202
* An MS-CHAP response is (IDENT|FLAGS|LM_RESPONSE|NT_RESPONSE).
204
* IDENT is not used by RADIUS (it is the PPP MS-CHAP Identifier).
205
* FLAGS is 1 to indicate the NT_RESPONSE should be preferred.
206
* LM_RESPONSE is the LAN Manager compatible response.
207
* NT_RESPONSE is the NT compatible response.
208
* Either response may be zero-filled indicating its absence.
209
* Use of the LM response has been deprecated (RFC 2433, par. 6),
210
* so we don't handle it.
212
* The NT_RESPONSE is (DES(CHAL,K1)|DES(CHAL,K2)|DES(CHAL,K3)), where
213
* CHAL is the 8-octet challenge, and K1, K2, K3 are 7-octet pieces
214
* of MD4(unicode(password)), zero-filled to 21 octets. Sigh.
216
unsigned char nt_keys[21]; /* sized for 3 DES keys */
217
unsigned char input[MAX_STRING_LEN * 2]; /* doubled for unicode */
218
unsigned char output[24];
222
DEBUG("rlm_x99_token: pw_valid: handling PW_MS_CHAP_RESPONSE");
223
if (chal_vp->length != 8) {
224
x99_log(X99_LOG_AUTH, "pw_valid: MS-CHAP-Challenge wrong size");
228
if (resp_vp->length != 50) {
229
x99_log(X99_LOG_AUTH, "pw_valid: MS-CHAP-Response wrong size");
233
if ((resp_vp->strvalue)[1] != 1) {
234
x99_log(X99_LOG_AUTH,
235
"pw_valid: MS-CHAP-Response bad flags (LM not supported)");
239
/* This is probably overkill. */
240
if (strlen(password) > MAX_STRING_LEN) {
241
x99_log(X99_LOG_AUTH, "pw_valid: MS-CHAP password too long");
247
* Start by hashing the unicode password.
248
* This is broken because unicode chars are machine-ordered,
249
* but the spec (RFC 2433) doesn't say how to prepare
250
* the password for md4 (other than by example values).
252
password_len = strlen(password);
253
for (i = 0; i < password_len; ++i) {
254
/* Set the high order 8 bits to 0 (little-endian) */
255
input[i * 2] = *password++;
256
input[i * 2 + 1] = 0;
258
(void) memset(nt_keys, 0, sizeof(nt_keys));
259
(void) MD4(input, 2 * password_len, nt_keys);
261
/* The challenge gets encrypted. */
262
(void) memcpy(input, chal_vp->strvalue, 8);
264
/* Convert the password hash to keys, and do the encryptions. */
265
for (i = 0; i < 3; ++i) {
269
x99_key_from_hash(&key, &nt_keys[i * 7]);
270
des_set_key_unchecked(&key, ks);
271
des_ecb_encrypt((des_cblock *) input,
272
(des_cblock *) &output[i * 8],
276
match = !memcmp(output, resp_vp->strvalue + 26, 24);
281
* Generate the MS-CHAP-MPPE-Keys attribute if needed. This is not
282
* specified anywhere -- RFC 2548, par. 2.4.1 is the authority but
283
* it has typos and omissions that make this unimplementable. The
284
* code here is based on experimental results provided by
285
* Takahiro Wagatsuma <waga@sic.shibaura-it.ac.jp>.
286
* We only support 128-bit keys derived from the NT hash; 40-bit
287
* and 56-bit keys are derived from the LM hash, which besides
288
* being deprecated, has severe security problems.
291
/* First, set some related attributes. */
292
vp = pairmake("MS-MPPE-Encryption-Policy",
293
x99_mppe_policy[inst->mschap_mppe_policy], T_OP_EQ);
294
rad_assert(vp != NULL);
296
vp = pairmake("MS-MPPE-Encryption-Types",
297
x99_mppe_types[inst->mschap_mppe_types], T_OP_EQ);
298
rad_assert(vp != NULL);
301
if (inst->mschap_mppe_policy) {
302
unsigned char mppe_keys[32];
303
/* 0x ASCII(mppe_keys) '\0' */
304
char mppe_keys_string[2 + (2 * sizeof(mppe_keys)) + 1];
306
unsigned char md5_md[MD5_DIGEST_LENGTH];
307
unsigned char encode_buf[AUTH_VECTOR_LEN + MAX_STRING_LEN];
310
/* Zero the LM-Key sub-field (and padding). */
311
(void) memset(mppe_keys, 0, sizeof(mppe_keys));
312
/* The NT-Key sub-field is MD4(MD4(unicode(password))). */
313
(void) MD4(nt_keys, 16, &mppe_keys[8]);
315
#if 0 /* encoding now handled in lib/radius.c:rad_pwencode() */
316
/* Now we must encode the key as User-Password is encoded. */
317
secretlen = strlen(request->secret);
318
(void) memcpy(encode_buf, request->secret, secretlen);
319
(void) memcpy(encode_buf + secretlen, request->packet->vector,
321
(void) MD5(encode_buf, secretlen + AUTH_VECTOR_LEN, md5_md);
322
for (i = 0; i < 16; ++i)
323
mppe_keys[i] ^= md5_md[i];
324
(void) memcpy(encode_buf + secretlen, mppe_keys, MD5_DIGEST_LENGTH);
325
(void) MD5(encode_buf, secretlen + MD5_DIGEST_LENGTH, md5_md);
326
for (i = 0; i < 16; ++i)
327
mppe_keys[i + 16] ^= md5_md[i];
330
/* Whew. Now stringify it for pairmake(). */
331
mppe_keys_string[0] = '0';
332
mppe_keys_string[1] = 'x';
333
for (i = 0; i < 32; ++i)
334
(void) sprintf(&mppe_keys_string[i*2+2], "%02X", mppe_keys[i]);
335
vp = pairmake("MS-CHAP-MPPE-Keys", mppe_keys_string, T_OP_EQ);
336
rad_assert(vp != NULL);
338
} /* if (doing mppe) */
340
} /* case PW_MS_CHAP_RESPONSE */
342
#endif /* 0 (MS_CHAP) */
344
case PW_MS_CHAP2_RESPONSE:
347
* See RFCs 2548, 2759, 3079.
348
* An MS-CHAPv2 response is
349
* (IDENT|FLAGS|PEER_CHALLENGE|RESERVED|NT_RESPONSE).
350
* octets: 1 1 16 8 24
351
* IDENT is the PPP MS-CHAPv2 Identifier, used in MS-CHAP2-Success.
352
* FLAGS is currently unused.
353
* PEER_CHALLENGE is a random number, generated by the peer.
354
* NT_RESPONSE is (DES(CHAL,K1)|DES(CHAL,K2)|DES(CHAL,K3)), where
355
* K1, K2, K3 are 7-octet pieces of MD4(unicode(password)), zero-
356
* filled to 21 octets (just as in MS-CHAP); and CHAL is
357
* MSB8(SHA(PEER_CHALLENGE|MS_CHAP_CHALLENGE|USERNAME)).
359
unsigned char nt_keys[21]; /* aka "password_md", sized for 3 DES keys */
360
unsigned char password_md_md[MD4_DIGEST_LENGTH]; /* for mutual auth */
361
unsigned char input[MAX_STRING_LEN * 2]; /* doubled for unicode */
362
unsigned char output[24];
366
DEBUG("rlm_x99_token: pw_valid: handling PW_MS_CHAP2_RESPONSE");
367
if (chal_vp->length != 16) {
368
x99_log(X99_LOG_AUTH,"pw_valid: MS-CHAP-Challenge (v2) wrong size");
372
if (resp_vp->length != 50) {
373
x99_log(X99_LOG_AUTH, "pw_valid: MS-CHAP2-Response wrong size");
377
/* This is probably overkill. */
378
if (strlen(password) > MAX_STRING_LEN) {
379
x99_log(X99_LOG_AUTH, "pw_valid: MS-CHAPv2 password too long");
385
* Start by hashing the unicode password.
386
* This is broken because unicode chars are machine-ordered,
387
* but the spec (RFC 2759) doesn't say how to prepare
388
* the password for md4 (other than by example values).
390
password_len = strlen(password);
391
for (i = 0; i < password_len; ++i) {
392
/* Set the high order 8 bits to 0 (little-endian) */
393
input[i * 2] = *password++;
394
input[i * 2 + 1] = 0;
396
(void) memset(nt_keys, 0, sizeof(nt_keys));
397
(void) MD4(input, 2 * password_len, nt_keys);
399
/* Now calculate the CHAL value from our various inputs. */
402
unsigned char md[SHA_DIGEST_LENGTH];
403
char *username = request->username->strvalue;
404
int username_len = request->username->length;
407
SHA1_Update(&ctx, resp_vp->strvalue + 2, 16);
408
SHA1_Update(&ctx, chal_vp->strvalue, 16);
409
SHA1_Update(&ctx, username, username_len);
410
SHA1_Final(md, &ctx);
412
(void) memcpy(input, md, 8);
415
/* Convert the password hash to keys, and do the encryptions. */
416
for (i = 0; i < 3; ++i) {
420
x99_key_from_hash(&key, &nt_keys[i * 7]);
421
des_set_key_unchecked(&key, ks);
422
des_ecb_encrypt((des_cblock *) input,
423
(des_cblock *) &output[i * 8],
427
match = !memcmp(output, resp_vp->strvalue + 26, 24);
432
* MS-CHAPv2 requires mutual authentication; we must prove
433
* that we know the secret. This is a bit circuitous: set
434
* MD1 = SHA(MD4(MD4(unicode(password)))|NT_RESPONSE|MAGIC1),
435
* MD2 = MSB8(SHA(PEER_CHALLENGE|MS_CHAP_CHALLENGE|USERNAME)),
436
* and finally use SHA(MD1|MD2|MAGIC2) as the authenticator.
437
* The authenticator is returned as the string "S=<auth>",
438
* <auth> is the authenticator expressed as [uppercase] ASCII.
443
unsigned char md1[SHA_DIGEST_LENGTH];
444
unsigned char md2[SHA_DIGEST_LENGTH];
445
unsigned char auth_md[SHA_DIGEST_LENGTH];
446
/* S= ( ASCII(auth_md) ) \0 */
447
char auth_md_string[2 + (2 * sizeof(auth_md)) + 1];
449
* ugh. The ASCII authenticator (auth_md_string) is sent
450
* along with a single (useless) binary byte (the ID).
451
* So we must "stringify" it again (for pairmake()) since the
452
* binary byte requires the attribute to be of type "octets".
454
/* 0x (ID) ( ASCII("S="ASCII(auth_md))) */
455
char auth_octet_string[2 + 2 + (2 * sizeof(auth_md_string))];
457
char *username = request->username->strvalue;
458
int username_len = request->username->length;
460
/* "Magic server to client signing constant" */
461
unsigned char magic1[39] =
462
{ 0x4D, 0x61, 0x67, 0x69, 0x63, 0x20, 0x73, 0x65, 0x72, 0x76,
463
0x65, 0x72, 0x20, 0x74, 0x6F, 0x20, 0x63, 0x6C, 0x69, 0x65,
464
0x6E, 0x74, 0x20, 0x73, 0x69, 0x67, 0x6E, 0x69, 0x6E, 0x67,
465
0x20, 0x63, 0x6F, 0x6E, 0x73, 0x74, 0x61, 0x6E, 0x74 };
466
/* "Pad to make it do more than one iteration" */
467
unsigned char magic2[41] =
468
{ 0x50, 0x61, 0x64, 0x20, 0x74, 0x6F, 0x20, 0x6D, 0x61, 0x6B,
469
0x65, 0x20, 0x69, 0x74, 0x20, 0x64, 0x6F, 0x20, 0x6D, 0x6F,
470
0x72, 0x65, 0x20, 0x74, 0x68, 0x61, 0x6E, 0x20, 0x6F, 0x6E,
471
0x65, 0x20, 0x69, 0x74, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6F,
475
(void) MD4(nt_keys, MD4_DIGEST_LENGTH, password_md_md);
477
SHA1_Update(&ctx, password_md_md, MD4_DIGEST_LENGTH);
478
SHA1_Update(&ctx, resp_vp->strvalue + 26, 24);
479
SHA1_Update(&ctx, magic1, sizeof(magic1));
480
SHA1_Final(md1, &ctx);
484
SHA1_Update(&ctx, resp_vp->strvalue + 2, 16);
485
SHA1_Update(&ctx, chal_vp->strvalue, 16);
486
SHA1_Update(&ctx, username, username_len);
487
SHA1_Final(md2, &ctx);
489
/* The Authenticator */
491
SHA1_Update(&ctx, md1, SHA_DIGEST_LENGTH);
492
SHA1_Update(&ctx, md2, 8);
493
SHA1_Update(&ctx, magic2, sizeof(magic2));
494
SHA1_Final(auth_md, &ctx);
496
/* String conversion. */
497
auth_md_string[0] = 'S';
498
auth_md_string[1] = '=';
499
for (i = 0; i < sizeof(auth_md); ++i)
500
(void) sprintf(&auth_md_string[i * 2 + 2], "%02X", auth_md[i]);
502
/* And then octet conversion. Ugh! */
503
auth_octet_string[0] = '0';
504
auth_octet_string[1] = 'x';
505
(void) sprintf(&auth_octet_string[2], "%02X", resp_vp->strvalue[0]);
506
for (i = 0; i < sizeof(auth_md_string) - 1; ++i)
507
(void) sprintf(&auth_octet_string[i * 2 + 4], "%02X",
510
vp = pairmake("MS-CHAP2-Success", auth_octet_string, T_OP_EQ);
511
rad_assert(vp != NULL);
513
} /* Generate mutual auth info. */
516
* Generate the MPPE initial session key if needed, per RFC 3079.
517
* (Although, RFC 2548 leaves us guessing at how to generate this.)
518
* For MS-CHAPv2 we support all key lengths (40-, 56- and 128-bit),
519
* although MPPE via RADIUS supports only 40- and 128-bit keys.
520
* This is a bit more complicated than MS-CHAP. Start by generating
521
* a "master session key"
522
* MSB16(SHA(NTPasswordHashHash|NT_RESPONSE|MAGIC1)), where
523
* NTPasswordHashHash is MD4(MD4(unicode(password))), NT_RESPONSE
524
* is from the MS-CHAP2-Response attribute, and MAGIC1 is a
525
* constant from RFC 3079. Then, we derive asymmetric send/receive
526
* keys from the master session key. The "master send key" is
527
* MSBx(SHA(MASTERKEY|SHSPAD1|MAGIC3|SHSPAD2)),
528
* and the "master receive key" is
529
* MSBx(SHA(MASTERKEY|SHSPAD1|MAGIC2|SHSPAD2)), where
530
* MASTERKEY is the "master session key" generated above, and the
531
* other values are constants from RFC 3079. MSBx is the x-most
532
* significant bytes, where x is 5, 7, or 16 as appropriate for
533
* the desired key length. We always generate 16 byte (128-bit)
534
* keys, the NAS is required to truncate as needed.
537
/* First, set some related attributes. */
538
vp = pairmake("MS-MPPE-Encryption-Policy",
539
x99_mppe_policy[inst->mschapv2_mppe_policy], T_OP_EQ);
540
rad_assert(vp != NULL);
542
vp = pairmake("MS-MPPE-Encryption-Types",
543
x99_mppe_types[inst->mschapv2_mppe_types], T_OP_EQ);
544
rad_assert(vp != NULL);
547
if (inst->mschapv2_mppe_policy) {
548
/* These constants and key vars are named from RFC 3079. */
549
/* "This is the MPPE Master Key" */
550
unsigned char Magic1[27] =
551
{ 0x54, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, 0x74,
552
0x68, 0x65, 0x20, 0x4d, 0x50, 0x50, 0x45, 0x20, 0x4d,
553
0x61, 0x73, 0x74, 0x65, 0x72, 0x20, 0x4b, 0x65, 0x79 };
554
/* "On the client side, this is the send key; "
555
"on the server side, it is the receive key." */
556
unsigned char Magic2[84] =
557
{ 0x4f, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x63, 0x6c, 0x69,
558
0x65, 0x6e, 0x74, 0x20, 0x73, 0x69, 0x64, 0x65, 0x2c, 0x20,
559
0x74, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, 0x74, 0x68,
560
0x65, 0x20, 0x73, 0x65, 0x6e, 0x64, 0x20, 0x6b, 0x65, 0x79,
561
0x3b, 0x20, 0x6f, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x73,
562
0x65, 0x72, 0x76, 0x65, 0x72, 0x20, 0x73, 0x69, 0x64, 0x65,
563
0x2c, 0x20, 0x69, 0x74, 0x20, 0x69, 0x73, 0x20, 0x74, 0x68,
564
0x65, 0x20, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x20,
565
0x6b, 0x65, 0x79, 0x2e };
566
/* "On the client side, this is the receive key; "
567
"on the server side, it is the send key." */
568
unsigned char Magic3[84] =
569
{ 0x4f, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x63, 0x6c, 0x69,
570
0x65, 0x6e, 0x74, 0x20, 0x73, 0x69, 0x64, 0x65, 0x2c, 0x20,
571
0x74, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, 0x74, 0x68,
572
0x65, 0x20, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x20,
573
0x6b, 0x65, 0x79, 0x3b, 0x20, 0x6f, 0x6e, 0x20, 0x74, 0x68,
574
0x65, 0x20, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x20, 0x73,
575
0x69, 0x64, 0x65, 0x2c, 0x20, 0x69, 0x74, 0x20, 0x69, 0x73,
576
0x20, 0x74, 0x68, 0x65, 0x20, 0x73, 0x65, 0x6e, 0x64, 0x20,
577
0x6b, 0x65, 0x79, 0x2e };
578
unsigned char SHSpad1[40] =
579
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
580
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
581
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
582
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
583
unsigned char SHSpad2[40] =
584
{ 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2,
585
0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2,
586
0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2,
587
0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2 };
588
unsigned char MasterKey[16];
589
unsigned char MasterSendKey[16];
590
unsigned char MasterReceiveKey[16];
593
unsigned char sha_md[SHA_DIGEST_LENGTH];
594
#if 0 /* salting/encoding now handled in lib/radius.c:tunnel_pwencode() */
595
unsigned char md5_md[MD5_DIGEST_LENGTH];
597
/* From RFC 2548: S R A */
598
unsigned char encode_buf[MAX_STRING_LEN + AUTH_VECTOR_LEN + 2];
601
/* A useless value required by RFC 2548. */
602
unsigned char salt[2];
603
unsigned char mppe_key[32]; /* 1 + 16 + padding */
604
/* 0x ( ASCII(salt) ) */
605
unsigned char mppe_key_string[2 + (2 * sizeof(salt)) +
606
/* ( ASCII(mppe_key) ) \0 */
607
(2 * sizeof(mppe_key)) + 1];
609
/* 0x ( ASCII(mppe_key) ) \0 */
610
unsigned char mppe_key_string[2 + (2 * sizeof(MasterKey)) + 1];
613
/* Generate the master session key. */
615
SHA1_Update(&ctx, password_md_md, MD4_DIGEST_LENGTH);
616
SHA1_Update(&ctx, resp_vp->strvalue + 26, 24);
617
SHA1_Update(&ctx, Magic1, sizeof(Magic1));
618
SHA1_Final(sha_md, &ctx);
619
(void) memcpy(MasterKey, sha_md, 16);
621
/* Generate the master send key. */
623
SHA1_Update(&ctx, MasterKey, 16);
624
SHA1_Update(&ctx, SHSpad1, 40);
625
SHA1_Update(&ctx, Magic3, sizeof(Magic3));
626
SHA1_Update(&ctx, SHSpad2, 40);
627
SHA1_Final(sha_md, &ctx);
628
(void) memcpy(MasterSendKey, sha_md, 16);
630
/* Generate the master receive key. */
632
SHA1_Update(&ctx, MasterKey, 16);
633
SHA1_Update(&ctx, SHSpad1, 40);
634
SHA1_Update(&ctx, Magic2, sizeof(Magic3));
635
SHA1_Update(&ctx, SHSpad2, 40);
636
SHA1_Final(sha_md, &ctx);
637
(void) memcpy(MasterReceiveKey, sha_md, 16);
639
/* Now, generate the MS-MPPE-Send-Key attribute. */
642
/* Setup the salt value. */
646
/* Encode the key. */
647
(void) memset(mppe_key, 0, sizeof(mppe_key));
648
mppe_key[0] = 16; /* length */
649
(void) memcpy(&mppe_key[1], MasterSendKey, 16);
650
secretlen = strlen(request->secret);
651
(void) memcpy(encode_buf, request->secret, secretlen);
652
(void) memcpy(encode_buf + secretlen, request->packet->vector,
654
(void) memcpy(encode_buf + secretlen + 16, salt, 2);
655
(void) MD5(encode_buf, secretlen + AUTH_VECTOR_LEN + 2, md5_md);
656
for (i = 0; i < 16; ++i)
657
mppe_key[i] ^= md5_md[i];
658
(void) memcpy(encode_buf + secretlen, mppe_key, 16);
659
(void) MD5(encode_buf, secretlen + 16, md5_md);
660
for (i = 0; i < 16; ++i)
661
mppe_key[i + 16] ^= md5_md[i];
663
/* Whew. Now stringify it for pairmake(). */
664
mppe_key_string[0] = '0';
665
mppe_key_string[1] = 'x';
666
(void) sprintf(&mppe_key_string[2], "%02X", salt[0]);
667
(void) sprintf(&mppe_key_string[4], "%02X", salt[1]);
668
for (i = 0; i < sizeof(mppe_key); ++i)
669
(void) sprintf(&mppe_key_string[i*2+6], "%02X", mppe_key[i]);
671
mppe_key_string[0] = '0';
672
mppe_key_string[1] = 'x';
673
for (i = 0; i < sizeof(MasterSendKey); ++i)
674
(void) sprintf(&mppe_key_string[i*2+2], "%02X",
677
vp = pairmake("MS-MPPE-Send-Key", mppe_key_string, T_OP_EQ);
678
rad_assert(vp != NULL);
681
/* Generate the MS-MPPE-Recv-Key attribute. */
684
/* Setup the salt value. */
688
/* Encode the key. */
689
(void) memset(mppe_key, 0, sizeof(mppe_key));
690
mppe_key[0] = 16; /* length */
691
(void) memcpy(&mppe_key[1], MasterReceiveKey, 16);
692
secretlen = strlen(request->secret);
693
(void) memcpy(encode_buf, request->secret, secretlen);
694
(void) memcpy(encode_buf + secretlen, request->packet->vector,
696
(void) memcpy(encode_buf + secretlen + 16, salt, 2);
697
(void) MD5(encode_buf, secretlen + AUTH_VECTOR_LEN + 2, md5_md);
698
for (i = 0; i < 16; ++i)
699
mppe_key[i] ^= md5_md[i];
700
(void) memcpy(encode_buf + secretlen, mppe_key, 16);
701
(void) MD5(encode_buf, secretlen + 16, md5_md);
702
for (i = 0; i < 16; ++i)
703
mppe_key[i + 16] ^= md5_md[i];
705
/* Whew. Now stringify it for pairmake(). */
706
mppe_key_string[0] = '0';
707
mppe_key_string[1] = 'x';
708
(void) sprintf(&mppe_key_string[2], "%02X", salt[0]);
709
(void) sprintf(&mppe_key_string[4], "%02X", salt[1]);
710
for (i = 0; i < sizeof(mppe_key); ++i)
711
(void) sprintf(&mppe_key_string[i*2+6], "%02X", mppe_key[i]);
713
mppe_key_string[0] = '0';
714
mppe_key_string[1] = 'x';
715
for (i = 0; i < sizeof(MasterReceiveKey); ++i)
716
(void) sprintf(&mppe_key_string[i*2+2], "%02X",
717
MasterReceiveKey[i]);
719
vp = pairmake("MS-MPPE-Recv-Key", mppe_key_string, T_OP_EQ);
720
rad_assert(vp != NULL);
723
} /* if (doing mppe) */
725
} /* case PW_MS_CHAP2_RESPONSE */
729
DEBUG("rlm_x99_token: pw_valid: unknown password type");
733
} /* switch(pwattr[attr]) */
740
* #$!#@ have to convert 7 octet ranges into 8 octet keys.
741
* Implementation cribbed (and slightly modified) from
742
* rlm_mschap.c by Jay Miller <jaymiller@socket.net>.
743
* We don't bother checking/setting parity.
746
x99_key_from_hash(des_cblock *key, const unsigned char hashbytes[7])
749
unsigned char working;
750
unsigned char next = 0;
752
for (i = 0; i < 7; ++i) {
753
working = hashbytes[i];
754
(*key)[i] = (working >> i) | next;
755
next = (working << (7 - i));