diff options
Diffstat (limited to 'accel-pppd/extra/chap-secrets.c')
-rw-r--r-- | accel-pppd/extra/chap-secrets.c | 475 |
1 files changed, 469 insertions, 6 deletions
diff --git a/accel-pppd/extra/chap-secrets.c b/accel-pppd/extra/chap-secrets.c index c0e620b1..47d59830 100644 --- a/accel-pppd/extra/chap-secrets.c +++ b/accel-pppd/extra/chap-secrets.c @@ -2,12 +2,19 @@ #include <stdlib.h> #include <errno.h> #include <string.h> +#include <byteswap.h> #include <netinet/in.h> #include <arpa/inet.h> +#ifdef CRYPTO_OPENSSL +#include "openssl/evp.h" +#include "crypto.h" +#endif + #include "pwdb.h" #include "ipdb.h" #include "ppp.h" +#include "ppp_auth.h" #include "events.h" #include "triton.h" #include "log.h" @@ -16,11 +23,18 @@ static char *def_chap_secrets = "/etc/ppp/chap-secrets"; static char *conf_chap_secrets; +static int conf_encrypted; static in_addr_t conf_gw_ip_address = 0; static void *pd_key; static struct ipdb_t ipdb; +struct hash_chain +{ + struct list_head entry; + const EVP_MD *md; +}; + struct cs_pd_t { struct ap_private pd; @@ -29,6 +43,8 @@ struct cs_pd_t char *rate; }; +static LIST_HEAD(hash_chain); + static char *skip_word(char *ptr) { char quote = 0; @@ -53,15 +69,19 @@ static char *skip_word(char *ptr) } else if (*ptr == ' ' || *ptr == '\t' || *ptr == '\n') break; } + return ptr; } + static char *skip_space(char *ptr) { for(; *ptr; ptr++) if (*ptr != ' ' && *ptr != '\t') break; + return ptr; } + static int split(char *buf, char **ptr) { int i; @@ -98,12 +118,37 @@ static struct cs_pd_t *create_pd(struct ap_session *ses, const char *username) FILE *f; char *buf; char *ptr[5]; - int n; + int n, i; struct cs_pd_t *pd; +#ifdef CRYPTO_OPENSSL + char username_hash[EVP_MAX_MD_SIZE * 2 + 1]; + uint8_t hash[EVP_MAX_MD_SIZE]; + struct hash_chain *hc; + EVP_MD_CTX md_ctx; + char c; +#endif if (!conf_chap_secrets) return NULL; +#ifdef CRYPTO_OPENSSL + if (conf_encrypted && !list_empty(&hash_chain)) { + unsigned int size = 0; + list_for_each_entry(hc, &hash_chain, entry) { + EVP_MD_CTX_init(&md_ctx); + EVP_DigestInit_ex(&md_ctx, hc->md, NULL); + EVP_DigestUpdate(&md_ctx, size == 0 ? (void *)username : (void *)hash, size == 0 ? strlen(username) : size); + EVP_DigestFinal_ex(&md_ctx, hash, &size); + EVP_MD_CTX_cleanup(&md_ctx); + } + + for (n = 0; n < size; n++) + sprintf(username_hash + n*2, "%02x", hash[n]); + + username = username_hash; + } +#endif + f = fopen(conf_chap_secrets, "r"); if (!f) { log_error("chap-secrets: open '%s': %s\n", conf_chap_secrets, strerror(errno)); @@ -138,6 +183,11 @@ out: return NULL; found: +#ifdef CRYPTO_OPENSSL + if (conf_encrypted && strlen(ptr[1]) != 32) + goto out; +#endif + pd = _malloc(sizeof(*pd)); if (!pd) { log_emerg("chap-secrets: out of memory\n"); @@ -146,11 +196,30 @@ found: memset(pd, 0, sizeof(*pd)); pd->pd.key = &pd_key; - pd->passwd = _strdup(ptr[1]); - if (!pd->passwd) { - log_emerg("chap-secrets: out of memory\n"); - _free(pd); - goto out; +#ifdef CRYPTO_OPENSSL + if (conf_encrypted) { + pd->passwd = _malloc(16); + if (!pd->passwd) { + log_emerg("chap-secrets: out of memory\n"); + _free(pd); + goto out; + } + + for (i = 0; i < 16; i++) { + c = ptr[1][i*2 + 2]; + ptr[1][i*2 + 2] = 0; + pd->passwd[i] = strtol(ptr[1] + i*2, NULL, 16); + ptr[1][i*2 + 2] = c; + } + } else +#endif + { + pd->passwd = _strdup(ptr[1]); + if (!pd->passwd) { + log_emerg("chap-secrets: out of memory\n"); + _free(pd); + goto out; + } } pd->ip.addr = conf_gw_ip_address; @@ -234,6 +303,11 @@ static char* get_passwd(struct pwdb_t *pwdb, struct ap_session *ses, const char { struct cs_pd_t *pd = find_pd(ses); +#ifdef CRYPTO_OPENSSL + if (conf_encrypted) + return NULL; +#endif + if (!pd) pd = create_pd(ses, username); @@ -243,14 +317,390 @@ static char* get_passwd(struct pwdb_t *pwdb, struct ap_session *ses, const char return _strdup(pd->passwd); } +#ifdef CRYPTO_OPENSSL +static void des_encrypt(const uint8_t *input, const uint8_t *key, uint8_t *output) +{ + int i, j, parity; + union + { + uint64_t u64; + uint8_t buf[8]; + } p_key; + DES_cblock cb; + DES_cblock res; + DES_key_schedule ks; + + memcpy(p_key.buf, key, 7); + p_key.u64 = bswap_64(p_key.u64); + + for (i = 0; i < 8; i++) { + cb[i] = (((p_key.u64 << (7 * i)) >> 56) & 0xfe); + for( j = 0, parity = 0; j < 7; j++) + if ((cb[i] >> (j + 1)) & 1) + parity++; + cb[i] |= (~parity) & 1; + } + + DES_set_key_checked(&cb, &ks); + memcpy(cb, input, 8); + DES_ecb_encrypt(&cb, &res, &ks, DES_ENCRYPT); + memcpy(output, res, 8); +} + +static int auth_pap(struct cs_pd_t *pd, const char *username, va_list args) +{ + const char *passwd = va_arg(args, const char *); + MD4_CTX md4_ctx; + unsigned char z_hash[21]; + char *u_passwd; + int i, len = strlen(passwd); + + u_passwd = _malloc(len * 2); + for (i = 0; i< len; i++) { + u_passwd[i * 2] = passwd[i]; + u_passwd[i * 2 + 1] = 0; + } + + memset(z_hash, 0, sizeof(z_hash)); + MD4_Init(&md4_ctx); + MD4_Update(&md4_ctx, u_passwd, len * 2); + MD4_Final(z_hash, &md4_ctx); + + _free(u_passwd); + + /*des_encrypt(ad->val, z_hash, nt_hash); + des_encrypt(ad->val, z_hash + 7, nt_hash + 8); + des_encrypt(ad->val, z_hash + 14, nt_hash + 16);*/ + + if (memcmp(z_hash, pd->passwd, 16)) + return PWDB_DENIED; + + return PWDB_SUCCESS; +} + +static int auth_chap_md5(struct cs_pd_t *pd, const char *username, va_list args) +{ + /*int id = va_arg(args, int); + uint8_t *challenge = va_arg(args, uint8_t *); + int challenge_len = va_arg(args, int); + uint8_t *response = va_arg(args, uint8_t *);*/ + + return PWDB_NO_IMPL; +} + +static void derive_mppe_keys_mschap_v1(struct ap_session *ses, const uint8_t *z_hash, const uint8_t *challenge, int challenge_len) +{ + MD4_CTX md4_ctx; + SHA_CTX sha_ctx; + uint8_t digest[20]; + struct ppp_t *ppp = container_of(ses, typeof(*ppp), ses); + + struct ev_mppe_keys_t ev_mppe = { + .ppp = ppp, + .policy = -1, + .recv_key = digest, + .send_key = digest, + }; + + //NtPasswordHashHash + MD4_Init(&md4_ctx); + MD4_Update(&md4_ctx, z_hash, 16); + MD4_Final(digest, &md4_ctx); + + //Get_Start_Key + SHA1_Init(&sha_ctx); + SHA1_Update(&sha_ctx, digest, 16); + SHA1_Update(&sha_ctx, digest, 16); + SHA1_Update(&sha_ctx, challenge, challenge_len); + SHA1_Final(digest, &sha_ctx); + + triton_event_fire(EV_MPPE_KEYS, &ev_mppe); +} + +int auth_mschap_v1(struct ap_session *ses, struct cs_pd_t *pd, const char *username, va_list args) +{ + int id __unused = va_arg(args, int); + const uint8_t *challenge = va_arg(args, const uint8_t *); + int challenge_len = va_arg(args, int); + const uint8_t *lm_response __unused = va_arg(args, const uint8_t *); + const uint8_t *nt_response = va_arg(args, const uint8_t *); + int flags __unused = va_arg(args, int); + uint8_t z_hash[21]; + uint8_t nt_hash[24]; + + memcpy(z_hash, pd->passwd, 16); + memset(z_hash + 16, 0, sizeof(z_hash) - 16); + + des_encrypt(challenge, z_hash, nt_hash); + des_encrypt(challenge, z_hash + 7, nt_hash + 8); + des_encrypt(challenge, z_hash + 14, nt_hash + 16); + + if (memcmp(nt_hash, nt_response, 24)) + return PWDB_DENIED; + + if (ses->ctrl->ppp) + derive_mppe_keys_mschap_v1(ses, z_hash, challenge, challenge_len); + + return PWDB_SUCCESS; +} + +static void generate_mschap_response(const uint8_t *nt_response, const uint8_t *c_hash, const uint8_t *z_hash, char *authenticator) +{ + MD4_CTX md4_ctx; + SHA_CTX sha_ctx; + uint8_t pw_hash[MD4_DIGEST_LENGTH]; + uint8_t response[SHA_DIGEST_LENGTH]; + int i; + + uint8_t 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}; + uint8_t 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}; + + + MD4_Init(&md4_ctx); + MD4_Update(&md4_ctx, z_hash, 16); + MD4_Final(pw_hash, &md4_ctx); + + SHA1_Init(&sha_ctx); + SHA1_Update(&sha_ctx, pw_hash, 16); + SHA1_Update(&sha_ctx, nt_response, 24); + SHA1_Update(&sha_ctx, magic1, 39); + SHA1_Final(response, &sha_ctx); + + SHA1_Init(&sha_ctx); + SHA1_Update(&sha_ctx, response, 20); + SHA1_Update(&sha_ctx, c_hash, 8); + SHA1_Update(&sha_ctx, magic2, 41); + SHA1_Final(response, &sha_ctx); + + for (i = 0; i < 20; i++) + sprintf(authenticator + i*2, "%02X", response[i]); +} + +static void derive_mppe_keys_mschap_v2(struct ap_session *ses, const uint8_t *z_hash, const uint8_t *nt_hash) +{ + struct ppp_t *ppp = container_of(ses, typeof(*ppp), ses); + MD4_CTX md4_ctx; + SHA_CTX sha_ctx; + uint8_t digest[20]; + uint8_t send_key[20]; + uint8_t recv_key[20]; + + uint8_t pad1[40] = + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + + uint8_t pad2[40] = + {0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, + 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, + 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, + 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2}; + + uint8_t magic1[27] = + {0x54, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, 0x74, + 0x68, 0x65, 0x20, 0x4d, 0x50, 0x50, 0x45, 0x20, 0x4d, + 0x61, 0x73, 0x74, 0x65, 0x72, 0x20, 0x4b, 0x65, 0x79}; + + uint8_t magic2[84] = + {0x4f, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x63, 0x6c, 0x69, + 0x65, 0x6e, 0x74, 0x20, 0x73, 0x69, 0x64, 0x65, 0x2c, 0x20, + 0x74, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, 0x74, 0x68, + 0x65, 0x20, 0x73, 0x65, 0x6e, 0x64, 0x20, 0x6b, 0x65, 0x79, + 0x3b, 0x20, 0x6f, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x73, + 0x65, 0x72, 0x76, 0x65, 0x72, 0x20, 0x73, 0x69, 0x64, 0x65, + 0x2c, 0x20, 0x69, 0x74, 0x20, 0x69, 0x73, 0x20, 0x74, 0x68, + 0x65, 0x20, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x20, + 0x6b, 0x65, 0x79, 0x2e}; + + uint8_t magic3[84] = + {0x4f, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x63, 0x6c, 0x69, + 0x65, 0x6e, 0x74, 0x20, 0x73, 0x69, 0x64, 0x65, 0x2c, 0x20, + 0x74, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, 0x74, 0x68, + 0x65, 0x20, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x20, + 0x6b, 0x65, 0x79, 0x3b, 0x20, 0x6f, 0x6e, 0x20, 0x74, 0x68, + 0x65, 0x20, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x20, 0x73, + 0x69, 0x64, 0x65, 0x2c, 0x20, 0x69, 0x74, 0x20, 0x69, 0x73, + 0x20, 0x74, 0x68, 0x65, 0x20, 0x73, 0x65, 0x6e, 0x64, 0x20, + 0x6b, 0x65, 0x79, 0x2e}; + + struct ev_mppe_keys_t ev_mppe = { + .ppp = ppp, + .policy = -1, + .recv_key = recv_key, + .send_key = send_key, + }; + + //NtPasswordHashHash + MD4_Init(&md4_ctx); + MD4_Update(&md4_ctx, z_hash, 16); + MD4_Final(digest, &md4_ctx); + + //GetMasterKey + SHA1_Init(&sha_ctx); + SHA1_Update(&sha_ctx, digest, 16); + SHA1_Update(&sha_ctx, nt_hash, 24); + SHA1_Update(&sha_ctx, magic1, sizeof(magic1)); + SHA1_Final(digest, &sha_ctx); + + //send key + SHA1_Init(&sha_ctx); + SHA1_Update(&sha_ctx, digest, 16); + SHA1_Update(&sha_ctx, pad1, sizeof(pad1)); + SHA1_Update(&sha_ctx, magic3, sizeof(magic2)); + SHA1_Update(&sha_ctx, pad2, sizeof(pad2)); + SHA1_Final(send_key, &sha_ctx); + + //recv key + SHA1_Init(&sha_ctx); + SHA1_Update(&sha_ctx, digest, 16); + SHA1_Update(&sha_ctx, pad1, sizeof(pad1)); + SHA1_Update(&sha_ctx, magic2, sizeof(magic3)); + SHA1_Update(&sha_ctx, pad2, sizeof(pad2)); + SHA1_Final(recv_key, &sha_ctx); + + triton_event_fire(EV_MPPE_KEYS, &ev_mppe); +} + +int auth_mschap_v2(struct ap_session *ses, struct cs_pd_t *pd, const char *username, va_list args) +{ + int id __unused = va_arg(args, int); + const uint8_t *challenge = va_arg(args, const uint8_t *); + const uint8_t *peer_challenge = va_arg(args, const uint8_t *); + const uint8_t *reserved __unused = va_arg(args, const uint8_t *); + const uint8_t *response = va_arg(args, const uint8_t *); + int flags __unused = va_arg(args, int); + char *authenticator = va_arg(args, char *); + uint8_t z_hash[21]; + uint8_t nt_hash[24]; + uint8_t c_hash[SHA_DIGEST_LENGTH]; + SHA_CTX sha_ctx; + + SHA1_Init(&sha_ctx); + SHA1_Update(&sha_ctx, peer_challenge, 16); + SHA1_Update(&sha_ctx, challenge, 16); + SHA1_Update(&sha_ctx, username, strlen(username)); + SHA1_Final(c_hash, &sha_ctx); + + memcpy(z_hash, pd->passwd, 16); + memset(z_hash + 16, 0, sizeof(z_hash) - 16); + + des_encrypt(c_hash, z_hash, nt_hash); + des_encrypt(c_hash, z_hash + 7, nt_hash + 8); + des_encrypt(c_hash, z_hash + 14, nt_hash + 16); + + if (memcmp(nt_hash, response, 24)) + return PWDB_DENIED; + + if (ses->ctrl->ppp) + derive_mppe_keys_mschap_v2(ses, z_hash, response); + + generate_mschap_response(response, c_hash, z_hash, authenticator); + + return PWDB_SUCCESS; +} + +static int check_passwd(struct pwdb_t *pwdb, struct ap_session *ses, const char *username, int type, va_list _args) +{ + va_list args; + int r = PWDB_NO_IMPL; + struct cs_pd_t *pd; + + if (!conf_encrypted) + return PWDB_NO_IMPL; + + pd = find_pd(ses); + + if (!pd) + pd = create_pd(ses, username); + + if (!pd) + return PWDB_NO_IMPL; + + va_copy(args, _args); + + switch (type) { + case PPP_PAP: + r = auth_pap(pd, username, args); + break; + case PPP_CHAP: + type = va_arg(args, int); + switch (type) { + case CHAP_MD5: + r = auth_chap_md5(pd, username, args); + break; + case MSCHAP_V1: + r = auth_mschap_v1(ses, pd, username, args); + break; + case MSCHAP_V2: + r = auth_mschap_v2(ses, pd, username, args); + break; + } + break; + } + + va_end(args); + + return r; +} +#endif + static struct ipdb_t ipdb = { .get_ipv4 = get_ip, }; static struct pwdb_t pwdb = { .get_passwd = get_passwd, +#ifdef CRYPTO_OPENSSL + .check = check_passwd, +#endif }; +#ifdef CRYPTO_OPENSSL +static void clear_hash_chain(void) +{ + struct hash_chain *hc; + + while (!list_empty(&hash_chain)) { + hc = list_entry(hash_chain.next, typeof(*hc), entry); + list_del(&hc->entry); + _free(hc); + } +} + +static void parse_hash_chain(const char *opt) +{ + char *str = _strdup(opt); + char *ptr1 = str, *ptr2; + struct hash_chain *hc; + int f = 0; + + while (!f) { + for (ptr2 = ptr1 + 1; *ptr2 && *ptr2 != ','; ptr2++); + f = *ptr2 == 0; + *ptr2 = 0; + hc = _malloc(sizeof(*hc)); + hc->md = EVP_get_digestbyname(ptr1); + if (!hc->md) { + log_error("chap-secrets: digest '%s' is unavailable\n", ptr1); + _free(hc); + return; + } + list_add_tail(&hc->entry, &hash_chain); + ptr1 = ptr2 + 1; + } +} +#endif + static void load_config(void) { const char *opt; @@ -266,6 +716,19 @@ static void load_config(void) opt = conf_get_opt("chap-secrets", "gw-ip-address"); if (opt) conf_gw_ip_address = inet_addr(opt); + + opt = conf_get_opt("chap-secrets", "encrypted"); + if (opt) + conf_encrypted = atoi(opt); + else + conf_encrypted = 0; + +#ifdef CRYPTO_OPENSSL + clear_hash_chain(); + opt = conf_get_opt("chap-secrets", "username-hash"); + if (opt) + parse_hash_chain(opt); +#endif } static void init(void) |