📄 otp_mppe.c
字号:
/* * $Id: otp_mppe.c,v 1.4 2007/11/23 13:46:56 aland Exp $ * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA * * Copyright 2001,2002 Google, Inc. * Copyright 2005,2006 TRI-D Systems, Inc. */#include <freeradius-devel/ident.h>RCSID("$Id: otp_mppe.c,v 1.4 2007/11/23 13:46:56 aland Exp $")/* avoid inclusion of these FR headers which conflict w/ OpenSSL */#define _FR_MD4_H#define _FR_SHA1_H#include <freeradius-devel/rad_assert.h>#include "extern.h"#include "otp.h"#include "otp_mppe.h"#include <openssl/des.h>#include <openssl/md4.h>#include <openssl/md5.h>#include <openssl/sha.h>#include <string.h>/* * Add MPPE attributes to a request, if required. */voidotp_mppe(REQUEST *request, otp_pwe_t pwe, const otp_option_t *opt, const char *passcode){ VALUE_PAIR **avp = &request->reply->vps; VALUE_PAIR *cvp, *rvp, *vp; cvp = pairfind(request->packet->vps, pwattr[pwe - 1]); rvp = pairfind(request->packet->vps, pwattr[pwe]); switch (pwe) { case PWE_PAP: case PWE_CHAP: return; case PWE_MSCHAP: /* First, set some related attributes. */ vp = pairmake("MS-MPPE-Encryption-Policy", otp_mppe_policy[opt->mschap_mppe_policy], T_OP_EQ); rad_assert(vp != NULL); pairadd(avp, vp); vp = pairmake("MS-MPPE-Encryption-Types", otp_mppe_types[opt->mschap_mppe_types], T_OP_EQ); rad_assert(vp != NULL); pairadd(avp, vp); /* If no MPPE, we're done. */ if (!opt->mschap_mppe_policy) return; /* * Generate the MS-CHAP-MPPE-Keys attribute. This is not specified * anywhere -- RFC 2548, par. 2.4.1 is the authority but it has * typos and omissions that make this unimplementable. The * code here is based on experimental results provided by * Takahiro Wagatsuma <waga@sic.shibaura-it.ac.jp>. * We only support 128-bit keys derived from the NT hash; 40-bit * and 56-bit keys are derived from the LM hash, which besides * being deprecated, has severe security problems. */ { size_t i, passcode_len; unsigned char password_unicode[2 * OTP_MAX_PASSCODE_LEN]; unsigned char password_md[MD4_DIGEST_LENGTH]; unsigned char mppe_keys[32]; /* 0x ASCII(mppe_keys) '\0' */ char mppe_keys_string[2 + (2 * sizeof(mppe_keys)) + 1]; /* Zero the LM-Key sub-field (and padding). */ (void) memset(mppe_keys, 0, sizeof(mppe_keys)); /* * The NT-Key sub-field is MD4(MD4(unicode(password))). * Start by hashing the unicode passcode. * This is broken because unicode chars are machine-ordered, * but the spec (RFC 2433) doesn't say how to prepare * the password for md4 (other than by example values). */ passcode_len = strlen(passcode); for (i = 0; i < passcode_len; ++i) { /* Set the high order 8 bits to 0 (little-endian) */ password_unicode[i * 2] = *passcode++; password_unicode[i * 2 + 1] = 0; } /* first md4 */ (void) MD4(password_unicode, 2 * passcode_len, password_md); /* second md4 */ (void) MD4(password_md, MD4_DIGEST_LENGTH, &mppe_keys[8]);#if 0 /* encoding now handled in lib/radius.c:rad_pwencode() */ { unsigned char md5_md[MD5_DIGEST_LENGTH]; unsigned char encode_buf[AUTH_VECTOR_LEN + MAX_STRING_LEN]; int secretlen; /* Now we must encode the key as User-Password is encoded. */ secretlen = strlen(request->secret); (void) memcpy(encode_buf, request->secret, secretlen); (void) memcpy(encode_buf + secretlen, request->packet->vector, AUTH_VECTOR_LEN); (void) MD5(encode_buf, secretlen + AUTH_VECTOR_LEN, md5_md); for (i = 0; i < 16; ++i) mppe_keys[i] ^= md5_md[i]; (void) memcpy(encode_buf + secretlen, mppe_keys, MD5_DIGEST_LENGTH); (void) MD5(encode_buf, secretlen + MD5_DIGEST_LENGTH, md5_md); for (i = 0; i < 16; ++i) mppe_keys[i + 16] ^= md5_md[i]; }#endif /* 0 */ /* Whew. Now stringify it for pairmake(). */ mppe_keys_string[0] = '0'; mppe_keys_string[1] = 'x'; for (i = 0; i < 32; ++i) (void) sprintf(&mppe_keys_string[i*2+2], "%02X", mppe_keys[i]); vp = pairmake("MS-CHAP-MPPE-Keys", mppe_keys_string, T_OP_EQ); rad_assert(vp != NULL); pairadd(avp, vp); } /* (doing mppe) */ break; /* PWE_MSCHAP */ case PWE_MSCHAP2: { size_t i; unsigned char password_md_md[MD4_DIGEST_LENGTH]; /* * MS-CHAPv2 requires mutual authentication; we must prove * that we know the secret. This is a bit circuitous: set * MD1 = SHA(MD4(MD4(unicode(password)))|NT_RESPONSE|MAGIC1), * MD2 = MSB8(SHA(PEER_CHALLENGE|MS_CHAP_CHALLENGE|USERNAME)), * and finally use SHA(MD1|MD2|MAGIC2) as the authenticator. * The authenticator is returned as the string "S=<auth>", * <auth> is the authenticator expressed as [uppercase] ASCII. * See RFC 2759. */ { size_t passcode_len; unsigned char password_unicode[2 * OTP_MAX_PASSCODE_LEN]; unsigned char password_md[MD4_DIGEST_LENGTH]; SHA_CTX ctx; unsigned char md1[SHA_DIGEST_LENGTH]; unsigned char md2[SHA_DIGEST_LENGTH]; unsigned char auth_md[SHA_DIGEST_LENGTH]; /* S= ( ASCII(auth_md) ) \0 */ char auth_md_string[2 + (2 * sizeof(auth_md)) + 1]; /* * ugh. The ASCII authenticator (auth_md_string) is sent * along with a single (useless) binary byte (the ID). * So we must "stringify" it again (for pairmake()) since the * binary byte requires the attribute to be of type "octets". */ /* 0x (ID) ( ASCII("S="ASCII(auth_md))) */ char auth_octet_string[2 + 2 + (2 * sizeof(auth_md_string))]; char *username = request->username->vp_strvalue; int username_len = request->username->length; /* "Magic server to client signing constant" */ unsigned char magic1[39] = { 0x4D, 0x61, 0x67, 0x69, 0x63, 0x20, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x20, 0x74, 0x6F, 0x20, 0x63, 0x6C, 0x69, 0x65, 0x6E, 0x74, 0x20, 0x73, 0x69, 0x67, 0x6E, 0x69, 0x6E, 0x67, 0x20, 0x63, 0x6F, 0x6E, 0x73, 0x74, 0x61, 0x6E, 0x74 }; /* "Pad to make it do more than one iteration" */ unsigned char magic2[41] = { 0x50, 0x61, 0x64, 0x20, 0x74, 0x6F, 0x20, 0x6D, 0x61, 0x6B, 0x65, 0x20, 0x69, 0x74, 0x20, 0x64, 0x6F, 0x20, 0x6D, 0x6F, 0x72, 0x65, 0x20, 0x74, 0x68, 0x61, 0x6E, 0x20, 0x6F, 0x6E, 0x65, 0x20, 0x69, 0x74, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6F, 0x6E }; /* * Start by hashing the unicode passcode. * This is broken because unicode chars are machine-ordered, * but the spec (RFC 2759) doesn't say how to prepare * the password for md4 (other than by example values). */ passcode_len = strlen(passcode); for (i = 0; i < passcode_len; ++i) { /* Set the high order 8 bits to 0 (little-endian) */ password_unicode[i * 2] = *passcode++; password_unicode[i * 2 + 1] = 0; } /* first md4 */ (void) MD4(password_unicode, 2 * passcode_len, password_md); /* second md4 */ (void) MD4(password_md, MD4_DIGEST_LENGTH, password_md_md); /* MD1 */ SHA1_Init(&ctx); SHA1_Update(&ctx, password_md_md, MD4_DIGEST_LENGTH); SHA1_Update(&ctx, rvp->vp_strvalue + 26, 24); SHA1_Update(&ctx, magic1, sizeof(magic1)); SHA1_Final(md1, &ctx); /* MD2 */ SHA1_Init(&ctx); SHA1_Update(&ctx, rvp->vp_strvalue + 2, 16); SHA1_Update(&ctx, cvp->vp_strvalue, 16); SHA1_Update(&ctx, username, username_len); SHA1_Final(md2, &ctx); /* The Authenticator */ SHA1_Init(&ctx); SHA1_Update(&ctx, md1, SHA_DIGEST_LENGTH); SHA1_Update(&ctx, md2, 8); SHA1_Update(&ctx, magic2, sizeof(magic2)); SHA1_Final(auth_md, &ctx); /* String conversion. */ auth_md_string[0] = 'S'; auth_md_string[1] = '=';
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -