update style
[freeradius.git] / src / modules / rlm_otp / otp_pwe.c
1 /*
2  * otp_pwe.c
3  * $Id$
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  * Copyright 2005 Frank Cusack
21  */
22
23 /*
24  * This file implements passcode (password) checking functions for each
25  * supported encoding (PAP, CHAP, etc.).  The current libradius interface
26  * is not sufficient for X9.9 use.
27  */
28
29 #ifdef FREERADIUS
30 #define _LRAD_MD4_H
31 #define _LRAD_SHA1_H
32 #include "rad_assert.h"
33 #endif
34 #include "otp.h"
35 #include "otp_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$";
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 otp_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 otp_pwe_cmp().
106  */
107 int
108 otp_pwe_present(const REQUEST *request)
109 {
110   unsigned 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_otp: pwe_present: password attributes %d, %d",
116              pwattr[i], pwattr[i + 1]);
117       return i + 1; /* Can't return 0 (indicates failure) */
118     }
119   }
120
121   DEBUG("rlm_otp: pwe_present: no password attributes present");
122   return 0;
123 }
124
125
126 /*
127  * Test for passcode (password) equality.
128  * returns 0 for match, non-zero for non-match.
129  * If data->returned_vps is non-null, then on matches, it will point to
130  * vps that should be added to an Access-Accept packet.  If access is denied,
131  * the caller is responsible for freeing any vps returned.
132  */
133 int
134 otp_pwe_cmp(struct otp_pwe_cmp_t *data, const char *password)
135 {
136   const REQUEST *request        = data->request;
137   const otp_option_t *inst      = data->inst;
138   int attr                      = data->pwattr;
139   VALUE_PAIR **vps              = data->returned_vps;
140
141   int nmatch = -1;
142   VALUE_PAIR *chal_vp, *resp_vp;
143
144   /*
145    * A module that does this might want to verify the presence of these.
146    * This code is self contained to otp, so I know these exist.
147    */
148   chal_vp = pairfind(request->packet->vps, pwattr[attr - 1]);
149   resp_vp = pairfind(request->packet->vps, pwattr[attr]);
150
151   /* Prepare for failure return. */
152   if (vps)
153     *vps = NULL;
154
155   /* If modular, this would actually call the authentication function. */
156   switch(pwattr[attr]) {
157   case PW_PASSWORD:
158     DEBUG("rlm_otp: pwe_cmp: handling PW_PASSWORD");
159     nmatch = strcmp(password, resp_vp->vp_strvalue);
160     break;
161
162   case PW_CHAP_PASSWORD:
163   {
164     /*
165      * See RFC 1994.
166      * A CHAP password is MD5(CHAP_ID|SECRET|CHAP_CHALLENGE).
167      * CHAP_ID is a value set by the authenticator (the NAS), and used
168      * in the response calculation.  It is available as the first byte
169      * of the CHAP-Password attribute.
170      * SECRET is the password.
171      * CHAP_CHALLENGE is the challenge given to the peer (the user).
172      * The CHAP-Challenge Attribute may be missing, in which case the
173      * challenge is taken to be the Request Authenticator.  We don't
174      * handle this case.
175      */
176     /*                 ID       password    chal */
177     unsigned char input[1 + MAX_STRING_LEN + 16];
178     unsigned char output[MD5_DIGEST_LENGTH];
179
180     DEBUG("rlm_otp: pwe_cmp: handling PW_CHAP_PASSWORD");
181     if (1 + strlen(password) + chal_vp->length > sizeof(input)) {
182       DEBUG("rlm_otp: pwe_cmp: CHAP-Challenge/password too long");
183       nmatch = -1;
184       break;
185     }
186     if (resp_vp->length != 17) {
187       otp_log(OTP_LOG_AUTH, "pwe_cmp: CHAP-Password wrong size");
188       nmatch = -1;
189       break;
190     }
191     input[0] = *(resp_vp->vp_strvalue);
192     (void) memcpy(&input[1], password, strlen(password));
193     (void) memcpy(&input[1+strlen(password)], chal_vp->vp_strvalue,
194                   chal_vp->length);
195     (void) MD5(input, 1 + strlen(password) + chal_vp->length, output);
196     nmatch = memcmp(output, &(resp_vp->vp_strvalue)[1], MD5_DIGEST_LENGTH);
197   } /* case PW_CHAP_PASSWORD */
198   break;
199
200 #if 0
201   case PW_MS_CHAP_RESPONSE:
202   {
203     /*
204      * See RFCs 2548, 2433, 3079.
205      * An MS-CHAP response is (IDENT|FLAGS|LM_RESPONSE|NT_RESPONSE).
206      *                 octets:   1     1       24           24
207      * IDENT is not used by RADIUS (it is the PPP MS-CHAP Identifier).
208      * FLAGS is 1 to indicate the NT_RESPONSE should be preferred.
209      * LM_RESPONSE is the LAN Manager compatible response.
210      * NT_RESPONSE is the NT compatible response.
211      * Either response may be zero-filled indicating its absence.
212      * Use of the LM response has been deprecated (RFC 2433, par. 6),
213      * so we don't handle it.
214      *
215      * The NT_RESPONSE is (DES(CHAL,K1)|DES(CHAL,K2)|DES(CHAL,K3)), where
216      * CHAL is the 8-octet challenge, and K1, K2, K3 are 7-octet pieces
217      * of MD4(unicode(password)), zero-filled to 21 octets.  Sigh.
218      */
219     unsigned char nt_keys[21]; /* sized for 3 DES keys */
220     unsigned char input[MAX_STRING_LEN * 2]; /* doubled for unicode */
221     unsigned char output[24];
222     int password_len, i;
223     VALUE_PAIR *vp;
224
225     DEBUG("rlm_otp: pwe_cmp: handling PW_MS_CHAP_RESPONSE");
226     if (chal_vp->length != 8) {
227       otp_log(OTP_LOG_AUTH, "pwe_cmp: MS-CHAP-Challenge wrong size");
228       nmatch = -1;
229       break;
230     }
231     if (resp_vp->length != 50) {
232       otp_log(OTP_LOG_AUTH, "pwe_cmp: MS-CHAP-Response wrong size");
233       nmatch = -1;
234       break;
235     }
236     if ((resp_vp->vp_strvalue)[1] != 1) {
237       otp_log(OTP_LOG_AUTH,
238               "pwe_cmp: MS-CHAP-Response bad flags (LM not supported)");
239       nmatch = -1;
240       break;
241     }
242     /* This is probably overkill. */
243     if (strlen(password) > MAX_STRING_LEN) {
244       otp_log(OTP_LOG_AUTH, "pwe_cmp: MS-CHAP password too long");
245       nmatch = -1;
246       break;
247     }
248
249     /*
250      * Start by hashing the unicode password.
251      * This is broken because unicode chars are machine-ordered,
252      * but the spec (RFC 2433) doesn't say how to prepare
253      * the password for md4 (other than by example values).
254      */
255     password_len = strlen(password);
256     for (i = 0; i < password_len; ++i) {
257       /* Set the high order 8 bits to 0 (little-endian) */
258       input[i * 2] = *password++;
259       input[i * 2 + 1] = 0;
260     }
261     (void) memset(nt_keys, 0, sizeof(nt_keys));
262     (void) MD4(input, 2 * password_len, nt_keys);
263
264     /* The challenge gets encrypted. */
265     (void) memcpy(input, chal_vp->vp_strvalue, 8);
266
267     /* Convert the password hash to keys, and do the encryptions. */
268     for (i = 0; i < 3; ++i) {
269       des_cblock key;
270       des_key_schedule ks;
271
272       otp_key_from_hash(&key, &nt_keys[i * 7]);
273       des_set_key_unchecked(&key, ks);
274       des_ecb_encrypt((des_cblock *) input,
275                       (des_cblock *) &output[i * 8],
276                       ks, DES_ENCRYPT);
277     }
278
279     nmatch = memcmp(output, resp_vp->vp_strvalue + 26, 24);
280     if (nmatch || !vps)
281       break;
282
283     /*
284      * Generate the MS-CHAP-MPPE-Keys attribute if needed.  This is not
285      * specified anywhere -- RFC 2548, par. 2.4.1 is the authority but
286      * it has typos and omissions that make this unimplementable.  The
287      * code here is based on experimental results provided by
288      * Takahiro Wagatsuma <waga@sic.shibaura-it.ac.jp>.
289      * We only support 128-bit keys derived from the NT hash; 40-bit
290      * and 56-bit keys are derived from the LM hash, which besides
291      * being deprecated, has severe security problems.
292      */
293
294     /* First, set some related attributes. */
295     vp = pairmake("MS-MPPE-Encryption-Policy",
296                   otp_mppe_policy[inst->mschap_mppe_policy], T_OP_EQ);
297     rad_assert(vp != NULL);
298     pairadd(vps, vp);
299     vp = pairmake("MS-MPPE-Encryption-Types",
300                   otp_mppe_types[inst->mschap_mppe_types], T_OP_EQ);
301     rad_assert(vp != NULL);
302     pairadd(vps, vp);
303
304     if (inst->mschap_mppe_policy) {
305       unsigned char mppe_keys[32];
306       /*                    0x    ASCII(mppe_keys)      '\0' */
307       char mppe_keys_string[2 + (2 * sizeof(mppe_keys)) + 1];
308
309       unsigned char md5_md[MD5_DIGEST_LENGTH];
310       unsigned char encode_buf[AUTH_VECTOR_LEN + MAX_STRING_LEN];
311       int secretlen;
312
313       /* Zero the LM-Key sub-field (and padding). */
314       (void) memset(mppe_keys, 0, sizeof(mppe_keys));
315       /* The NT-Key sub-field is MD4(MD4(unicode(password))). */
316       (void) MD4(nt_keys, 16, &mppe_keys[8]);
317
318 #if 0 /* encoding now handled in lib/radius.c:rad_pwencode() */
319       /* Now we must encode the key as User-Password is encoded. */
320       secretlen = strlen(request->secret);
321       (void) memcpy(encode_buf, request->secret, secretlen);
322       (void) memcpy(encode_buf + secretlen, request->packet->vector,
323                     AUTH_VECTOR_LEN);
324       (void) MD5(encode_buf, secretlen + AUTH_VECTOR_LEN, md5_md);
325       for (i = 0; i < 16; ++i)
326         mppe_keys[i] ^= md5_md[i];
327       (void) memcpy(encode_buf + secretlen, mppe_keys, MD5_DIGEST_LENGTH);
328       (void) MD5(encode_buf, secretlen + MD5_DIGEST_LENGTH, md5_md);
329       for (i = 0; i < 16; ++i)
330         mppe_keys[i + 16] ^= md5_md[i];
331 #endif /* 0 */
332
333       /* Whew.  Now stringify it for pairmake(). */
334       mppe_keys_string[0] = '0';
335       mppe_keys_string[1] = 'x';
336       for (i = 0; i < 32; ++i)
337         (void) sprintf(&mppe_keys_string[i*2+2], "%02X", mppe_keys[i]);
338       vp = pairmake("MS-CHAP-MPPE-Keys", mppe_keys_string, T_OP_EQ);
339       rad_assert(vp != NULL);
340       pairadd(vps, vp);
341     } /* if (doing mppe) */
342
343   } /* case PW_MS_CHAP_RESPONSE */
344   break;
345 #endif /* 0 (MS_CHAP) */
346
347   case PW_MS_CHAP2_RESPONSE:
348   {
349     /*
350      * See RFCs 2548, 2759, 3079.
351      * An MS-CHAPv2 response is
352      *          (IDENT|FLAGS|PEER_CHALLENGE|RESERVED|NT_RESPONSE).
353      *   octets:   1     1         16          8        24
354      * IDENT is the PPP MS-CHAPv2 Identifier, used in MS-CHAP2-Success.
355      * FLAGS is currently unused.
356      * PEER_CHALLENGE is a random number, generated by the peer.
357      * NT_RESPONSE is (DES(CHAL,K1)|DES(CHAL,K2)|DES(CHAL,K3)), where
358      * K1, K2, K3 are 7-octet pieces of MD4(unicode(password)), zero-
359      * filled to 21 octets (just as in MS-CHAP); and CHAL is
360      * MSB8(SHA(PEER_CHALLENGE|MS_CHAP_CHALLENGE|USERNAME)).
361      */
362     unsigned char nt_keys[21]; /* aka "password_md", sized for 3 DES keys */
363     unsigned char password_md_md[MD4_DIGEST_LENGTH]; /* for mutual auth */
364     unsigned char input[MAX_STRING_LEN * 2]; /* doubled for unicode */
365     unsigned char output[24];
366     unsigned password_len, i;
367     VALUE_PAIR *vp;
368
369     DEBUG("rlm_otp: pwe_cmp: handling PW_MS_CHAP2_RESPONSE");
370     if (chal_vp->length != 16) {
371       otp_log(OTP_LOG_AUTH,"pwe_cmp: MS-CHAP-Challenge (v2) wrong size");
372       nmatch = -1;
373       break;
374     }
375     if (resp_vp->length != 50) {
376       otp_log(OTP_LOG_AUTH, "pwe_cmp: MS-CHAP2-Response wrong size");
377       nmatch = -1;
378       break;
379     }
380     /* This is probably overkill. */
381     if (strlen(password) > MAX_STRING_LEN) {
382       otp_log(OTP_LOG_AUTH, "pwe_cmp: MS-CHAPv2 password too long");
383       nmatch = -1;
384       break;
385     }
386
387     /*
388      * Start by hashing the unicode password.
389      * This is broken because unicode chars are machine-ordered,
390      * but the spec (RFC 2759) doesn't say how to prepare
391      * the password for md4 (other than by example values).
392      */
393     password_len = strlen(password);
394     for (i = 0; i < password_len; ++i) {
395       /* Set the high order 8 bits to 0 (little-endian) */
396       input[i * 2] = *password++;
397       input[i * 2 + 1] = 0;
398     }
399     (void) memset(nt_keys, 0, sizeof(nt_keys));
400     (void) MD4(input, 2 * password_len, nt_keys);
401
402     /* Now calculate the CHAL value from our various inputs. */
403     {
404       SHA_CTX ctx;
405       unsigned char md[SHA_DIGEST_LENGTH];
406       char *username = request->username->vp_strvalue;
407       int username_len = request->username->length;
408
409       SHA1_Init(&ctx);
410       SHA1_Update(&ctx, resp_vp->vp_strvalue + 2, 16);
411       SHA1_Update(&ctx, chal_vp->vp_strvalue, 16);
412       SHA1_Update(&ctx, username, username_len);
413       SHA1_Final(md, &ctx);
414
415       (void) memcpy(input, md, 8);
416     }
417
418     /* Convert the password hash to keys, and do the encryptions. */
419     for (i = 0; i < 3; ++i) {
420       des_cblock key;
421       des_key_schedule ks;
422
423       otp_key_from_hash(&key, &nt_keys[i * 7]);
424       des_set_key_unchecked(&key, ks);
425       des_ecb_encrypt((des_cblock *) input,
426                       (des_cblock *) &output[i * 8],
427                       ks, DES_ENCRYPT);
428     }
429
430     nmatch = memcmp(output, resp_vp->vp_strvalue + 26, 24);
431     if (nmatch || !vps)
432       break;
433
434     /*
435      * MS-CHAPv2 requires mutual authentication; we must prove
436      * that we know the secret.  This is a bit circuitous: set
437      * MD1 = SHA(MD4(MD4(unicode(password)))|NT_RESPONSE|MAGIC1),
438      * MD2 = MSB8(SHA(PEER_CHALLENGE|MS_CHAP_CHALLENGE|USERNAME)),
439      * and finally use SHA(MD1|MD2|MAGIC2) as the authenticator.
440      * The authenticator is returned as the string "S=<auth>",
441      * <auth> is the authenticator expressed as [uppercase] ASCII.
442      * See RFC 2759.
443      */
444     {
445       SHA_CTX ctx;
446       unsigned char md1[SHA_DIGEST_LENGTH];
447       unsigned char md2[SHA_DIGEST_LENGTH];
448       unsigned char auth_md[SHA_DIGEST_LENGTH];
449       /*                  S=  (  ASCII(auth_md)   )  \0 */
450       char auth_md_string[2 + (2 * sizeof(auth_md)) + 1];
451       /*
452        * ugh.  The ASCII authenticator (auth_md_string) is sent
453        * along with a single (useless) binary byte (the ID).
454        * So we must "stringify" it again (for pairmake()) since the
455        * binary byte requires the attribute to be of type "octets".
456        */
457       /*                    0x  (ID) ( ASCII("S="ASCII(auth_md))) */
458       char auth_octet_string[2 + 2 + (2 * sizeof(auth_md_string))];
459
460       char *username = request->username->vp_strvalue;
461       int username_len = request->username->length;
462
463       /* "Magic server to client signing constant" */
464       unsigned char magic1[39] =
465         { 0x4D, 0x61, 0x67, 0x69, 0x63, 0x20, 0x73, 0x65, 0x72, 0x76,
466           0x65, 0x72, 0x20, 0x74, 0x6F, 0x20, 0x63, 0x6C, 0x69, 0x65,
467           0x6E, 0x74, 0x20, 0x73, 0x69, 0x67, 0x6E, 0x69, 0x6E, 0x67,
468           0x20, 0x63, 0x6F, 0x6E, 0x73, 0x74, 0x61, 0x6E, 0x74 };
469       /* "Pad to make it do more than one iteration" */
470       unsigned char magic2[41] =
471         { 0x50, 0x61, 0x64, 0x20, 0x74, 0x6F, 0x20, 0x6D, 0x61, 0x6B,
472           0x65, 0x20, 0x69, 0x74, 0x20, 0x64, 0x6F, 0x20, 0x6D, 0x6F,
473           0x72, 0x65, 0x20, 0x74, 0x68, 0x61, 0x6E, 0x20, 0x6F, 0x6E,
474           0x65, 0x20, 0x69, 0x74, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6F,
475           0x6E };
476
477       /* MD1 */
478       (void) MD4(nt_keys, MD4_DIGEST_LENGTH, password_md_md);
479       SHA1_Init(&ctx);
480       SHA1_Update(&ctx, password_md_md, MD4_DIGEST_LENGTH);
481       SHA1_Update(&ctx, resp_vp->vp_strvalue + 26, 24);
482       SHA1_Update(&ctx, magic1, sizeof(magic1));
483       SHA1_Final(md1, &ctx);
484
485       /* MD2 */
486       SHA1_Init(&ctx);
487       SHA1_Update(&ctx, resp_vp->vp_strvalue + 2, 16);
488       SHA1_Update(&ctx, chal_vp->vp_strvalue, 16);
489       SHA1_Update(&ctx, username, username_len);
490       SHA1_Final(md2, &ctx);
491
492       /* The Authenticator */
493       SHA1_Init(&ctx);
494       SHA1_Update(&ctx, md1, SHA_DIGEST_LENGTH);
495       SHA1_Update(&ctx, md2, 8);
496       SHA1_Update(&ctx, magic2, sizeof(magic2));
497       SHA1_Final(auth_md, &ctx);
498
499       /* String conversion. */
500       auth_md_string[0] = 'S';
501       auth_md_string[1] = '=';
502       for (i = 0; i < sizeof(auth_md); ++i)
503         (void) sprintf(&auth_md_string[i * 2 + 2], "%02X", auth_md[i]);
504
505       /* And then octet conversion.  Ugh! */
506       auth_octet_string[0] = '0';
507       auth_octet_string[1] = 'x';
508       (void) sprintf(&auth_octet_string[2], "%02X", resp_vp->vp_strvalue[0]);
509       for (i = 0; i < sizeof(auth_md_string) - 1; ++i)
510         (void) sprintf(&auth_octet_string[i * 2 +4], "%02X", auth_md_string[i]);
511
512       vp = pairmake("MS-CHAP2-Success", auth_octet_string, T_OP_EQ);
513       rad_assert(vp != NULL);
514       pairadd(vps, vp);
515     } /* Generate mutual auth info. */
516
517     /*
518      * Generate the MPPE initial session key if needed, per RFC 3079.
519      * (Although, RFC 2548 leaves us guessing at how to generate this.)
520      * For MS-CHAPv2 we support all key lengths (40-, 56- and 128-bit),
521      * although MPPE via RADIUS supports only 40- and 128-bit keys.
522      * This is a bit more complicated than MS-CHAP.  Start by generating
523      * a "master session key"
524      *    MSB16(SHA(NTPasswordHashHash|NT_RESPONSE|MAGIC1)), where
525      * NTPasswordHashHash is MD4(MD4(unicode(password))), NT_RESPONSE
526      * is from the MS-CHAP2-Response attribute, and MAGIC1 is a
527      * constant from RFC 3079.  Then, we derive asymmetric send/receive
528      * keys from the master session key.  The "master send key" is
529      *     MSBx(SHA(MASTERKEY|SHSPAD1|MAGIC3|SHSPAD2)),
530      * and the "master receive key" is
531      *     MSBx(SHA(MASTERKEY|SHSPAD1|MAGIC2|SHSPAD2)), where
532      * MASTERKEY is the "master session key" generated above, and the
533      * other values are constants from RFC 3079.  MSBx is the x-most
534      * significant bytes, where x is 5, 7, or 16 as appropriate for
535      * the desired key length.  We always generate 16 byte (128-bit)
536      * keys, the NAS is required to truncate as needed.
537      */
538
539     /* First, set some related attributes. */
540     vp = pairmake("MS-MPPE-Encryption-Policy",
541                   otp_mppe_policy[inst->mschapv2_mppe_policy], T_OP_EQ);
542     rad_assert(vp != NULL);
543     pairadd(vps, vp);
544     vp = pairmake("MS-MPPE-Encryption-Types",
545                   otp_mppe_types[inst->mschapv2_mppe_types], T_OP_EQ);
546     rad_assert(vp != NULL);
547     pairadd(vps, vp);
548
549     if (inst->mschapv2_mppe_policy) {
550       /* These constants and key vars are named from RFC 3079. */
551       /* "This is the MPPE Master Key" */
552       unsigned char Magic1[27] =
553         { 0x54, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, 0x74,
554           0x68, 0x65, 0x20, 0x4d, 0x50, 0x50, 0x45, 0x20, 0x4d,
555           0x61, 0x73, 0x74, 0x65, 0x72, 0x20, 0x4b, 0x65, 0x79 };
556       /* "On the client side, this is the send key; "
557          "on the server side, it is the receive key." */
558       unsigned char Magic2[84] =
559         { 0x4f, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x63, 0x6c, 0x69,
560           0x65, 0x6e, 0x74, 0x20, 0x73, 0x69, 0x64, 0x65, 0x2c, 0x20,
561           0x74, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, 0x74, 0x68,
562           0x65, 0x20, 0x73, 0x65, 0x6e, 0x64, 0x20, 0x6b, 0x65, 0x79,
563           0x3b, 0x20, 0x6f, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x73,
564           0x65, 0x72, 0x76, 0x65, 0x72, 0x20, 0x73, 0x69, 0x64, 0x65,
565           0x2c, 0x20, 0x69, 0x74, 0x20, 0x69, 0x73, 0x20, 0x74, 0x68,
566           0x65, 0x20, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x20,
567           0x6b, 0x65, 0x79, 0x2e };
568       /* "On the client side, this is the receive key; "
569          "on the server side, it is the send key." */
570       unsigned char Magic3[84] =
571         { 0x4f, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x63, 0x6c, 0x69,
572           0x65, 0x6e, 0x74, 0x20, 0x73, 0x69, 0x64, 0x65, 0x2c, 0x20,
573           0x74, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, 0x74, 0x68,
574           0x65, 0x20, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x20,
575           0x6b, 0x65, 0x79, 0x3b, 0x20, 0x6f, 0x6e, 0x20, 0x74, 0x68,
576           0x65, 0x20, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x20, 0x73,
577           0x69, 0x64, 0x65, 0x2c, 0x20, 0x69, 0x74, 0x20, 0x69, 0x73,
578           0x20, 0x74, 0x68, 0x65, 0x20, 0x73, 0x65, 0x6e, 0x64, 0x20,
579           0x6b, 0x65, 0x79, 0x2e };
580       unsigned char SHSpad1[40] =
581         { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
582           0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
583           0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
584           0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
585       unsigned char SHSpad2[40] =
586         { 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2,
587           0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2,
588           0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2,
589           0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2 };
590       unsigned char MasterKey[16];
591       unsigned char MasterSendKey[16];
592       unsigned char MasterReceiveKey[16];
593
594       SHA_CTX ctx;
595       unsigned char sha_md[SHA_DIGEST_LENGTH];
596 #if 0 /* salting/encoding now handled in lib/radius.c:tunnel_pwencode() */
597       unsigned char md5_md[MD5_DIGEST_LENGTH];
598
599       /*   From RFC 2548:           S                 R           A */
600       unsigned char encode_buf[MAX_STRING_LEN + AUTH_VECTOR_LEN + 2];
601       int secretlen;
602
603       /* A useless value required by RFC 2548. */
604       unsigned char salt[2];
605       unsigned char mppe_key[32]; /* 1 + 16 + padding */
606       /*                           0x   (   ASCII(salt)  ) */
607       unsigned char mppe_key_string[2 + (2 * sizeof(salt)) +
608       /*                            (   ASCII(mppe_key)  )  \0 */
609                                     (2 * sizeof(mppe_key)) + 1];
610 #else /* 0 */
611       /*                           0x   (   ASCII(mppe_key)   )  \0 */
612       unsigned char mppe_key_string[2 + (2 * sizeof(MasterKey)) + 1];
613 #endif /* 0 */
614
615       /* Generate the master session key. */
616       SHA1_Init(&ctx);
617       SHA1_Update(&ctx, password_md_md, MD4_DIGEST_LENGTH);
618       SHA1_Update(&ctx, resp_vp->vp_strvalue + 26, 24);
619       SHA1_Update(&ctx, Magic1, sizeof(Magic1));
620       SHA1_Final(sha_md, &ctx);
621       (void) memcpy(MasterKey, sha_md, 16);
622
623       /* Generate the master send key. */
624       SHA1_Init(&ctx);
625       SHA1_Update(&ctx, MasterKey, 16);
626       SHA1_Update(&ctx, SHSpad1, 40);
627       SHA1_Update(&ctx, Magic3, sizeof(Magic3));
628       SHA1_Update(&ctx, SHSpad2, 40);
629       SHA1_Final(sha_md, &ctx);
630       (void) memcpy(MasterSendKey, sha_md, 16);
631
632       /* Generate the master receive key. */
633       SHA1_Init(&ctx);
634       SHA1_Update(&ctx, MasterKey, 16);
635       SHA1_Update(&ctx, SHSpad1, 40);
636       SHA1_Update(&ctx, Magic2, sizeof(Magic3));
637       SHA1_Update(&ctx, SHSpad2, 40);
638       SHA1_Final(sha_md, &ctx);
639       (void) memcpy(MasterReceiveKey, sha_md, 16);
640
641       /* Now, generate the MS-MPPE-Send-Key attribute. */
642
643 #if 0
644       /* Setup the salt value. */
645       salt[0] = 0x80;
646       salt[1] = 0x01;
647
648       /* Encode the key. */
649       (void) memset(mppe_key, 0, sizeof(mppe_key));
650       mppe_key[0] = 16; /* length */
651       (void) memcpy(&mppe_key[1], MasterSendKey, 16);
652       secretlen = strlen(request->secret);
653       (void) memcpy(encode_buf, request->secret, secretlen);
654       (void) memcpy(encode_buf + secretlen, request->packet->vector,
655                     AUTH_VECTOR_LEN);
656       (void) memcpy(encode_buf + secretlen + 16, salt, 2);
657       (void) MD5(encode_buf, secretlen + AUTH_VECTOR_LEN + 2, md5_md);
658       for (i = 0; i < 16; ++i)
659         mppe_key[i] ^= md5_md[i];
660       (void) memcpy(encode_buf + secretlen, mppe_key, 16);
661       (void) MD5(encode_buf, secretlen + 16, md5_md);
662       for (i = 0; i < 16; ++i)
663         mppe_key[i + 16] ^= md5_md[i];
664
665       /* Whew.  Now stringify it for pairmake(). */
666       mppe_key_string[0] = '0';
667       mppe_key_string[1] = 'x';
668       (void) sprintf(&mppe_key_string[2], "%02X", salt[0]);
669       (void) sprintf(&mppe_key_string[4], "%02X", salt[1]);
670       for (i = 0; i < sizeof(mppe_key); ++i)
671         (void) sprintf(&mppe_key_string[i*2+6], "%02X", mppe_key[i]);
672 #else /* 0 */
673       mppe_key_string[0] = '0';
674       mppe_key_string[1] = 'x';
675       for (i = 0; i < sizeof(MasterSendKey); ++i)
676         (void) sprintf(&mppe_key_string[i*2+2], "%02X", MasterSendKey[i]);
677 #endif /* 0 */
678       vp = pairmake("MS-MPPE-Send-Key", mppe_key_string, T_OP_EQ);
679       rad_assert(vp != NULL);
680       pairadd(vps, vp);
681
682       /* Generate the MS-MPPE-Recv-Key attribute. */
683
684 #if 0
685       /* Setup the salt value. */
686       salt[0] = 0x80;
687       salt[1] = 0x02;
688
689       /* Encode the key. */
690       (void) memset(mppe_key, 0, sizeof(mppe_key));
691       mppe_key[0] = 16; /* length */
692       (void) memcpy(&mppe_key[1], MasterReceiveKey, 16);
693       secretlen = strlen(request->secret);
694       (void) memcpy(encode_buf, request->secret, secretlen);
695       (void) memcpy(encode_buf + secretlen, request->packet->vector,
696                     AUTH_VECTOR_LEN);
697       (void) memcpy(encode_buf + secretlen + 16, salt, 2);
698       (void) MD5(encode_buf, secretlen + AUTH_VECTOR_LEN + 2, md5_md);
699       for (i = 0; i < 16; ++i)
700         mppe_key[i] ^= md5_md[i];
701       (void) memcpy(encode_buf + secretlen, mppe_key, 16);
702       (void) MD5(encode_buf, secretlen + 16, md5_md);
703       for (i = 0; i < 16; ++i)
704         mppe_key[i + 16] ^= md5_md[i];
705
706       /* Whew.  Now stringify it for pairmake(). */
707       mppe_key_string[0] = '0';
708       mppe_key_string[1] = 'x';
709       (void) sprintf(&mppe_key_string[2], "%02X", salt[0]);
710       (void) sprintf(&mppe_key_string[4], "%02X", salt[1]);
711       for (i = 0; i < sizeof(mppe_key); ++i)
712         (void) sprintf(&mppe_key_string[i*2+6], "%02X", mppe_key[i]);
713 #else /* 0 */
714       mppe_key_string[0] = '0';
715       mppe_key_string[1] = 'x';
716       for (i = 0; i < sizeof(MasterReceiveKey); ++i)
717         (void) sprintf(&mppe_key_string[i*2+2], "%02X", 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_otp: pwe_cmp: unknown password type");
730     nmatch = -1;
731     break;
732
733   } /* switch(pwattr[attr]) */
734
735   return nmatch;
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 otp_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 }