~ubuntu-branches/ubuntu/feisty/freeradius/feisty-security

« back to all changes in this revision

Viewing changes to src/modules/rlm_x99_token/x99_pwe.c

  • Committer: Bazaar Package Importer
  • Author(s): Mark Hymers
  • Date: 2006-12-16 20:45:11 UTC
  • mfrom: (3.1.10 feisty)
  • Revision ID: james.westby@ubuntu.com-20061216204511-3pbbsu4s8jtehsor
Tags: 1.1.3-3
Fix POSIX compliance problem in init script.  Closes: #403384. 

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
/*
2
 
 * x99_pwe.c
3
 
 * $Id: x99_pwe.c,v 1.19 2002/11/04 04:02:02 fcusack Exp $
4
 
 *
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.
9
 
 *
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.
14
 
 *
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
18
 
 *
19
 
 * Copyright 2001,2002  Google, Inc.
20
 
 */
21
 
 
22
 
/*
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.
26
 
 */
27
 
 
28
 
#ifdef FREERADIUS
29
 
#define _LRAD_MD4_H
30
 
#define _LRAD_SHA1_H
31
 
#include "libradius.h"
32
 
#include "rad_assert.h"
33
 
#endif
34
 
#include "x99.h"
35
 
#include "x99_pwe.h"
36
 
 
37
 
#include <openssl/des.h>
38
 
#include <openssl/md4.h>
39
 
#include <openssl/md5.h>
40
 
#include <openssl/sha.h>
41
 
 
42
 
#include <string.h>
43
 
 
44
 
static const char rcsid[] = "$Id: x99_pwe.c,v 1.19 2002/11/04 04:02:02 fcusack Exp $";
45
 
 
46
 
 
47
 
/* Attribute IDs for supported password encodings. */
48
 
static int pwattr[8];
49
 
 
50
 
 
51
 
/* Initialize the pwattr array for supported password encodings. */
52
 
void
53
 
x99_pwe_init(void)
54
 
{
55
 
    DICT_ATTR *da;
56
 
    int i = 0;
57
 
 
58
 
    /*
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)
62
 
     */
63
 
    (void) memset(pwattr, 0, sizeof(pwattr));
64
 
 
65
 
    /* PAP */
66
 
    if ((da = dict_attrbyname("User-Password")) != NULL) {
67
 
        pwattr[i++] = da->attr;
68
 
        pwattr[i++] = da->attr;
69
 
    }
70
 
 
71
 
    /* CHAP */
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;
76
 
        else
77
 
            pwattr[--i] = 0;
78
 
    }
79
 
 
80
 
#if 0
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;
86
 
        else
87
 
            pwattr[--i] = 0;
88
 
    }
89
 
#endif /* 0 */
90
 
 
91
 
    /* MS-CHAPv2 */
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;
96
 
        else
97
 
            pwattr[--i] = 0;
98
 
    }
99
 
}
100
 
 
101
 
 
102
 
/*
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.
106
 
 */
107
 
int
108
 
x99_pw_present(const REQUEST *request)
109
 
{
110
 
    int i;
111
 
 
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) */
118
 
        }
119
 
    }
120
 
 
121
 
    return 0;
122
 
}
123
 
 
124
 
 
125
 
/*
126
 
 * Test for password validity.  attr must be the return value from
127
 
 * x99_pw_present().
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.)
133
 
 */
134
 
int
135
 
x99_pw_valid(const REQUEST *request, x99_token_t *inst,
136
 
             int attr, const char *password, VALUE_PAIR **vps)
137
 
{
138
 
    int match = 0;
139
 
    VALUE_PAIR *chal_vp, *resp_vp;
140
 
 
141
 
    /*
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.
144
 
     */
145
 
    chal_vp = pairfind(request->packet->vps, pwattr[attr - 1]);
146
 
    resp_vp = pairfind(request->packet->vps, pwattr[attr]);
147
 
 
148
 
    /* Prepare for failure return. */
149
 
    if (vps)
150
 
        *vps = NULL;
151
 
 
152
 
    /* If modular, this would actually call the authentication function. */
153
 
    switch(pwattr[attr]) {
154
 
    case PW_PASSWORD:
155
 
        DEBUG("rlm_x99_token: pw_valid: handling PW_PASSWORD");
156
 
        match = !strcmp(password, resp_vp->strvalue);
157
 
        break;
158
 
 
159
 
    case PW_CHAP_PASSWORD:
160
 
    {
161
 
        /*
162
 
         * See RFC 1994.
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
171
 
         * handle this case.
172
 
         */
173
 
        /*                 ID       password    chal */
174
 
        unsigned char input[1 + MAX_STRING_LEN + 16];
175
 
        unsigned char output[MD5_DIGEST_LENGTH];
176
 
 
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");
180
 
            match = 0;
181
 
            break;
182
 
        }
183
 
        if (resp_vp->length != 17) {
184
 
            x99_log(X99_LOG_AUTH, "pw_valid: CHAP-Password wrong size");
185
 
            match = 0;
186
 
            break;
187
 
        }
188
 
        input[0] = *(resp_vp->strvalue);
189
 
        (void) memcpy(&input[1], password, strlen(password));
190
 
        (void) memcpy(&input[1+strlen(password)], chal_vp->strvalue,
191
 
                      chal_vp->length);
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 */
195
 
    break;
196
 
 
197
 
#if 0
198
 
    case PW_MS_CHAP_RESPONSE:
199
 
    {
200
 
        /*
201
 
         * See RFCs 2548, 2433, 3079.
202
 
         * An MS-CHAP response is (IDENT|FLAGS|LM_RESPONSE|NT_RESPONSE).
203
 
         *                 octets:   1     1       24           24
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.
211
 
         *
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.
215
 
         */
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];
219
 
        int password_len, i;
220
 
        VALUE_PAIR *vp;
221
 
 
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");
225
 
            match = 0;
226
 
            break;
227
 
        }
228
 
        if (resp_vp->length != 50) {
229
 
            x99_log(X99_LOG_AUTH, "pw_valid: MS-CHAP-Response wrong size");
230
 
            match = 0;
231
 
            break;
232
 
        }
233
 
        if ((resp_vp->strvalue)[1] != 1) {
234
 
            x99_log(X99_LOG_AUTH,
235
 
                    "pw_valid: MS-CHAP-Response bad flags (LM not supported)");
236
 
            match = 0;
237
 
            break;
238
 
        }
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");
242
 
            match = 0;
243
 
            break;
244
 
        }
245
 
 
246
 
        /*
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).
251
 
         */
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;
257
 
        }
258
 
        (void) memset(nt_keys, 0, sizeof(nt_keys));
259
 
        (void) MD4(input, 2 * password_len, nt_keys);
260
 
 
261
 
        /* The challenge gets encrypted. */
262
 
        (void) memcpy(input, chal_vp->strvalue, 8);
263
 
 
264
 
        /* Convert the password hash to keys, and do the encryptions. */
265
 
        for (i = 0; i < 3; ++i) {
266
 
            des_cblock key;
267
 
            des_key_schedule ks;
268
 
 
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],
273
 
                            ks, DES_ENCRYPT);
274
 
        }
275
 
 
276
 
        match = !memcmp(output, resp_vp->strvalue + 26, 24);
277
 
        if (!match || !vps)
278
 
            break;
279
 
 
280
 
        /*
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.
289
 
         */
290
 
 
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);
295
 
        pairadd(vps, vp);
296
 
        vp = pairmake("MS-MPPE-Encryption-Types",
297
 
                      x99_mppe_types[inst->mschap_mppe_types], T_OP_EQ);
298
 
        rad_assert(vp != NULL);
299
 
        pairadd(vps, vp);
300
 
 
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];
305
 
 
306
 
            unsigned char md5_md[MD5_DIGEST_LENGTH];
307
 
            unsigned char encode_buf[AUTH_VECTOR_LEN + MAX_STRING_LEN];
308
 
            int secretlen;
309
 
 
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]);
314
 
 
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,
320
 
                          AUTH_VECTOR_LEN);
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];
328
 
#endif /* 0 */
329
 
 
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);
337
 
            pairadd(vps, vp);
338
 
        } /* if (doing mppe) */
339
 
 
340
 
    } /* case PW_MS_CHAP_RESPONSE */
341
 
    break;
342
 
#endif /* 0 (MS_CHAP) */
343
 
 
344
 
    case PW_MS_CHAP2_RESPONSE:
345
 
    {
346
 
        /*
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)).
358
 
         */
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];
363
 
        int password_len, i;
364
 
        VALUE_PAIR *vp;
365
 
 
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");
369
 
            match = 0;
370
 
            break;
371
 
        }
372
 
        if (resp_vp->length != 50) {
373
 
            x99_log(X99_LOG_AUTH, "pw_valid: MS-CHAP2-Response wrong size");
374
 
            match = 0;
375
 
            break;
376
 
        }
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");
380
 
            match = 0;
381
 
            break;
382
 
        }
383
 
 
384
 
        /*
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).
389
 
         */
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;
395
 
        }
396
 
        (void) memset(nt_keys, 0, sizeof(nt_keys));
397
 
        (void) MD4(input, 2 * password_len, nt_keys);
398
 
 
399
 
        /* Now calculate the CHAL value from our various inputs. */
400
 
        {
401
 
            SHA_CTX ctx;
402
 
            unsigned char md[SHA_DIGEST_LENGTH];
403
 
            char *username = request->username->strvalue;
404
 
            int username_len = request->username->length;
405
 
 
406
 
            SHA1_Init(&ctx);
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);
411
 
 
412
 
            (void) memcpy(input, md, 8);
413
 
        }
414
 
 
415
 
        /* Convert the password hash to keys, and do the encryptions. */
416
 
        for (i = 0; i < 3; ++i) {
417
 
            des_cblock key;
418
 
            des_key_schedule ks;
419
 
 
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],
424
 
                            ks, DES_ENCRYPT);
425
 
        }
426
 
 
427
 
        match = !memcmp(output, resp_vp->strvalue + 26, 24);
428
 
        if (!match || !vps)
429
 
            break;
430
 
 
431
 
        /*
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.
439
 
         * See RFC 2759.
440
 
         */
441
 
        {
442
 
            SHA_CTX ctx;
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];
448
 
            /*
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".
453
 
             */
454
 
            /*                    0x  (ID) ( ASCII("S="ASCII(auth_md))) */
455
 
            char auth_octet_string[2 + 2 + (2 * sizeof(auth_md_string))];
456
 
 
457
 
            char *username = request->username->strvalue;
458
 
            int username_len = request->username->length;
459
 
 
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,
472
 
              0x6E };
473
 
 
474
 
            /* MD1 */
475
 
            (void) MD4(nt_keys, MD4_DIGEST_LENGTH, password_md_md);
476
 
            SHA1_Init(&ctx);
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);
481
 
 
482
 
            /* MD2 */
483
 
            SHA1_Init(&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);
488
 
 
489
 
            /* The Authenticator */
490
 
            SHA1_Init(&ctx);
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);
495
 
 
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]);
501
 
 
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",
508
 
                               auth_md_string[i]);
509
 
 
510
 
            vp = pairmake("MS-CHAP2-Success", auth_octet_string, T_OP_EQ);
511
 
            rad_assert(vp != NULL);
512
 
            pairadd(vps, vp);
513
 
        } /* Generate mutual auth info. */
514
 
 
515
 
        /*
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.
535
 
         */
536
 
 
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);
541
 
        pairadd(vps, vp);
542
 
        vp = pairmake("MS-MPPE-Encryption-Types",
543
 
                      x99_mppe_types[inst->mschapv2_mppe_types], T_OP_EQ);
544
 
        rad_assert(vp != NULL);
545
 
        pairadd(vps, vp);
546
 
 
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];
591
 
 
592
 
            SHA_CTX ctx;
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];
596
 
 
597
 
            /*   From RFC 2548:           S                 R           A */
598
 
            unsigned char encode_buf[MAX_STRING_LEN + AUTH_VECTOR_LEN + 2];
599
 
            int secretlen;
600
 
 
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];
608
 
#else /* 0 */
609
 
            /*                           0x   (   ASCII(mppe_key)   )  \0 */
610
 
            unsigned char mppe_key_string[2 + (2 * sizeof(MasterKey)) + 1];
611
 
#endif /* 0 */
612
 
 
613
 
            /* Generate the master session key. */
614
 
            SHA1_Init(&ctx);
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);
620
 
 
621
 
            /* Generate the master send key. */
622
 
            SHA1_Init(&ctx);
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);
629
 
 
630
 
            /* Generate the master receive key. */
631
 
            SHA1_Init(&ctx);
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);
638
 
 
639
 
            /* Now, generate the MS-MPPE-Send-Key attribute. */
640
 
 
641
 
#if 0
642
 
            /* Setup the salt value. */
643
 
            salt[0] = 0x80;
644
 
            salt[1] = 0x01;
645
 
 
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,
653
 
                          AUTH_VECTOR_LEN);
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];
662
 
 
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]);
670
 
#else /* 0 */
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",
675
 
                               MasterSendKey[i]);
676
 
#endif /* 0 */
677
 
            vp = pairmake("MS-MPPE-Send-Key", mppe_key_string, T_OP_EQ);
678
 
            rad_assert(vp != NULL);
679
 
            pairadd(vps, vp);
680
 
 
681
 
            /* Generate the MS-MPPE-Recv-Key attribute. */
682
 
 
683
 
#if 0
684
 
            /* Setup the salt value. */
685
 
            salt[0] = 0x80;
686
 
            salt[1] = 0x02;
687
 
 
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,
695
 
                          AUTH_VECTOR_LEN);
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];
704
 
 
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]);
712
 
#else /* 0 */
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]);
718
 
#endif /* 0 */
719
 
            vp = pairmake("MS-MPPE-Recv-Key", mppe_key_string, T_OP_EQ);
720
 
            rad_assert(vp != NULL);
721
 
            pairadd(vps, vp);
722
 
 
723
 
        } /* if (doing mppe) */
724
 
 
725
 
    } /* case PW_MS_CHAP2_RESPONSE */
726
 
    break;
727
 
 
728
 
    default:
729
 
        DEBUG("rlm_x99_token: pw_valid: unknown password type");
730
 
        match = 0;
731
 
        break;
732
 
 
733
 
    } /* switch(pwattr[attr]) */
734
 
 
735
 
    return match;
736
 
}
737
 
 
738
 
 
739
 
/*
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.
744
 
 */
745
 
static void
746
 
x99_key_from_hash(des_cblock *key, const unsigned char hashbytes[7])
747
 
{
748
 
    int i;
749
 
    unsigned char working;
750
 
    unsigned char next = 0;
751
 
 
752
 
    for (i = 0; i < 7; ++i) {
753
 
        working = hashbytes[i];
754
 
        (*key)[i] = (working >> i) | next;
755
 
        next = (working << (7 - i));
756
 
    }
757
 
    (*key)[i] = next;
758
 
}
759