📄 digest.c
字号:
hex_encode(buf.data, buf.length, &r.u.initReply.opaque); free(buf.data); if (r.u.initReply.opaque == NULL) { krb5_clear_error_string(context); ret = ENOMEM; goto out; } kdc_log(context, config, 0, "Digest %s init request successful from %s", ireq.u.init.type, from); break; } case choice_DigestReqInner_digestRequest: { sp = krb5_storage_emem(); if (sp == NULL) { ret = ENOMEM; krb5_set_error_string(context, "out of memory"); goto out; } ret = krb5_store_stringz(sp, ireq.u.digestRequest.type); if (ret) { krb5_clear_error_string(context); goto out; } krb5_store_stringz(sp, ireq.u.digestRequest.serverNonce); if (ireq.u.digestRequest.hostname) { ret = krb5_store_stringz(sp, *ireq.u.digestRequest.hostname); if (ret) { krb5_clear_error_string(context); goto out; } } buf.length = strlen(ireq.u.digestRequest.opaque); buf.data = malloc(buf.length); if (buf.data == NULL) { krb5_set_error_string(context, "out of memory"); ret = ENOMEM; goto out; } ret = hex_decode(ireq.u.digestRequest.opaque, buf.data, buf.length); if (ret <= 0) { krb5_set_error_string(context, "Failed to decode opaque"); ret = ENOMEM; goto out; } buf.length = ret; ret = decode_Checksum(buf.data, buf.length, &res, NULL); free(buf.data); if (ret) { krb5_set_error_string(context, "Failed to decode digest Checksum"); goto out; } ret = krb5_storage_to_data(sp, &buf); if (ret) { krb5_clear_error_string(context); goto out; } serverNonce.length = strlen(ireq.u.digestRequest.serverNonce); serverNonce.data = malloc(serverNonce.length); if (serverNonce.data == NULL) { krb5_set_error_string(context, "out of memory"); ret = ENOMEM; goto out; } /* * CHAP does the checksum of the raw nonce, but do it for all * types, since we need to check the timestamp. */ { ssize_t ssize; ssize = hex_decode(ireq.u.digestRequest.serverNonce, serverNonce.data, serverNonce.length); if (ssize <= 0) { krb5_set_error_string(context, "Failed to decode serverNonce"); ret = ENOMEM; goto out; } serverNonce.length = ssize; } ret = get_digest_key(context, config, server, &crypto); if (ret) goto out; ret = krb5_verify_checksum(context, crypto, KRB5_KU_DIGEST_OPAQUE, buf.data, buf.length, &res); krb5_crypto_destroy(context, crypto); crypto = NULL; if (ret) goto out; /* verify time */ { unsigned char *p = serverNonce.data; uint32_t t; if (serverNonce.length < 4) { krb5_set_error_string(context, "server nonce too short"); ret = EINVAL; goto out; } t = p[0] | (p[1] << 8) | (p[2] << 16) | (p[3] << 24); if (abs((kdc_time & 0xffffffff) - t) > context->max_skew) { krb5_set_error_string(context, "time screw in server nonce "); ret = EINVAL; goto out; } } if (strcasecmp(ireq.u.digestRequest.type, "CHAP") == 0) { MD5_CTX ctx; unsigned char md[MD5_DIGEST_LENGTH]; char *mdx; char id; if ((config->digests_allowed & CHAP_MD5) == 0) { kdc_log(context, config, 0, "Digest CHAP MD5 not allowed"); goto out; } if (ireq.u.digestRequest.identifier == NULL) { krb5_set_error_string(context, "Identifier missing " "from CHAP request"); ret = EINVAL; goto out; } if (hex_decode(*ireq.u.digestRequest.identifier, &id, 1) != 1) { krb5_set_error_string(context, "failed to decode identifier"); ret = EINVAL; goto out; } ret = get_password_entry(context, config, ireq.u.digestRequest.username, &password); if (ret) goto out; MD5_Init(&ctx); MD5_Update(&ctx, &id, 1); MD5_Update(&ctx, password, strlen(password)); MD5_Update(&ctx, serverNonce.data, serverNonce.length); MD5_Final(md, &ctx); hex_encode(md, sizeof(md), &mdx); if (mdx == NULL) { krb5_clear_error_string(context); ret = ENOMEM; goto out; } r.element = choice_DigestRepInner_response; ret = strcasecmp(mdx, ireq.u.digestRequest.responseData); free(mdx); if (ret == 0) { r.u.response.success = TRUE; } else { kdc_log(context, config, 0, "CHAP reply mismatch for %s", ireq.u.digestRequest.username); r.u.response.success = FALSE; } } else if (strcasecmp(ireq.u.digestRequest.type, "SASL-DIGEST-MD5") == 0) { MD5_CTX ctx; unsigned char md[MD5_DIGEST_LENGTH]; char *mdx; char *A1, *A2; if ((config->digests_allowed & DIGEST_MD5) == 0) { kdc_log(context, config, 0, "Digest SASL MD5 not allowed"); goto out; } if (ireq.u.digestRequest.nonceCount == NULL) goto out; if (ireq.u.digestRequest.clientNonce == NULL) goto out; if (ireq.u.digestRequest.qop == NULL) goto out; if (ireq.u.digestRequest.realm == NULL) goto out; ret = get_password_entry(context, config, ireq.u.digestRequest.username, &password); if (ret) goto failed; MD5_Init(&ctx); MD5_Update(&ctx, ireq.u.digestRequest.username, strlen(ireq.u.digestRequest.username)); MD5_Update(&ctx, ":", 1); MD5_Update(&ctx, *ireq.u.digestRequest.realm, strlen(*ireq.u.digestRequest.realm)); MD5_Update(&ctx, ":", 1); MD5_Update(&ctx, password, strlen(password)); MD5_Final(md, &ctx); MD5_Init(&ctx); MD5_Update(&ctx, md, sizeof(md)); MD5_Update(&ctx, ":", 1); MD5_Update(&ctx, ireq.u.digestRequest.serverNonce, strlen(ireq.u.digestRequest.serverNonce)); MD5_Update(&ctx, ":", 1); MD5_Update(&ctx, *ireq.u.digestRequest.nonceCount, strlen(*ireq.u.digestRequest.nonceCount)); if (ireq.u.digestRequest.authid) { MD5_Update(&ctx, ":", 1); MD5_Update(&ctx, *ireq.u.digestRequest.authid, strlen(*ireq.u.digestRequest.authid)); } MD5_Final(md, &ctx); hex_encode(md, sizeof(md), &A1); if (A1 == NULL) { krb5_set_error_string(context, "out of memory"); ret = ENOMEM; goto failed; } MD5_Init(&ctx); MD5_Update(&ctx, "AUTHENTICATE:", sizeof("AUTHENTICATE:") - 1); MD5_Update(&ctx, *ireq.u.digestRequest.uri, strlen(*ireq.u.digestRequest.uri)); /* conf|int */ if (strcmp(ireq.u.digestRequest.digest, "clear") != 0) { static char conf_zeros[] = ":00000000000000000000000000000000"; MD5_Update(&ctx, conf_zeros, sizeof(conf_zeros) - 1); } MD5_Final(md, &ctx); hex_encode(md, sizeof(md), &A2); if (A2 == NULL) { krb5_set_error_string(context, "out of memory"); ret = ENOMEM; free(A1); goto failed; } MD5_Init(&ctx); MD5_Update(&ctx, A1, strlen(A2)); MD5_Update(&ctx, ":", 1); MD5_Update(&ctx, ireq.u.digestRequest.serverNonce, strlen(ireq.u.digestRequest.serverNonce)); MD5_Update(&ctx, ":", 1); MD5_Update(&ctx, *ireq.u.digestRequest.nonceCount, strlen(*ireq.u.digestRequest.nonceCount)); MD5_Update(&ctx, ":", 1); MD5_Update(&ctx, *ireq.u.digestRequest.clientNonce, strlen(*ireq.u.digestRequest.clientNonce)); MD5_Update(&ctx, ":", 1); MD5_Update(&ctx, *ireq.u.digestRequest.qop, strlen(*ireq.u.digestRequest.qop)); MD5_Update(&ctx, ":", 1); MD5_Update(&ctx, A2, strlen(A2)); MD5_Final(md, &ctx); free(A1); free(A2); hex_encode(md, sizeof(md), &mdx); if (mdx == NULL) { krb5_clear_error_string(context); ret = ENOMEM; goto out; } r.element = choice_DigestRepInner_response; ret = strcasecmp(mdx, ireq.u.digestRequest.responseData); free(mdx); if (ret == 0) { r.u.response.success = TRUE; } else { kdc_log(context, config, 0, "DIGEST-MD5 reply mismatch for %s", ireq.u.digestRequest.username); r.u.response.success = FALSE; } } else if (strcasecmp(ireq.u.digestRequest.type, "MS-CHAP-V2") == 0) { unsigned char md[SHA_DIGEST_LENGTH], challange[SHA_DIGEST_LENGTH]; krb5_principal clientprincipal = NULL; char *mdx; const char *username; struct ntlm_buf answer; Key *key = NULL; SHA_CTX ctx; if ((config->digests_allowed & MS_CHAP_V2) == 0) { kdc_log(context, config, 0, "MS-CHAP-V2 not allowed"); goto failed; } if (ireq.u.digestRequest.clientNonce == NULL) { krb5_set_error_string(context, "MS-CHAP-V2 clientNonce missing"); ret = EINVAL; goto failed; } if (serverNonce.length != 16) { krb5_set_error_string(context, "MS-CHAP-V2 serverNonce wrong length"); ret = EINVAL; goto failed; } /* strip of the domain component */ username = strchr(ireq.u.digestRequest.username, '\\'); if (username == NULL) username = ireq.u.digestRequest.username; else username++; /* ChallangeHash */ SHA1_Init(&ctx); { ssize_t ssize; krb5_data clientNonce; clientNonce.length = strlen(*ireq.u.digestRequest.clientNonce); clientNonce.data = malloc(clientNonce.length); if (clientNonce.data == NULL) { ret = ENOMEM; krb5_set_error_string(context, "out of memory"); goto out; } ssize = hex_decode(*ireq.u.digestRequest.clientNonce, clientNonce.data, clientNonce.length); if (ssize != 16) { krb5_set_error_string(context, "Failed to decode clientNonce"); ret = ENOMEM; goto out; } SHA1_Update(&ctx, clientNonce.data, ssize); free(clientNonce.data); } SHA1_Update(&ctx, serverNonce.data, serverNonce.length); SHA1_Update(&ctx, username, strlen(username)); SHA1_Final(challange, &ctx); /* NtPasswordHash */ ret = krb5_parse_name(context, username, &clientprincipal); if (ret) goto failed; ret = _kdc_db_fetch(context, config, clientprincipal, HDB_F_GET_CLIENT, NULL, &user); krb5_free_principal(context, clientprincipal); if (ret) { krb5_set_error_string(context, "MS-CHAP-V2 user %s not in database", username); goto failed; } ret = hdb_enctype2key(context, &user->entry, ETYPE_ARCFOUR_HMAC_MD5, &key); if (ret) { krb5_set_error_string(context, "MS-CHAP-V2 missing arcfour key %s", username); goto failed; } /* ChallengeResponse */ ret = heim_ntlm_calculate_ntlm1(key->key.keyvalue.data, key->key.keyvalue.length, challange, &answer); if (ret) { krb5_set_error_string(context, "NTLM missing arcfour key"); goto failed; } hex_encode(answer.data, answer.length, &mdx); if (mdx == NULL) { free(answer.data); krb5_clear_error_string(context); ret = ENOMEM; goto out; } r.element = choice_DigestRepInner_response; ret = strcasecmp(mdx, ireq.u.digestRequest.responseData); if (ret == 0) { r.u.response.success = TRUE; } else { kdc_log(context, config, 0, "MS-CHAP-V2 hash mismatch for %s", ireq.u.digestRequest.username); r.u.response.success = FALSE; } free(mdx); if (r.u.response.success) { unsigned char hashhash[MD4_DIGEST_LENGTH]; /* hashhash */ { MD4_CTX hctx; MD4_Init(&hctx); MD4_Update(&hctx, key->key.keyvalue.data, key->key.keyvalue.length); MD4_Final(hashhash, &hctx); } /* GenerateAuthenticatorResponse */ SHA1_Init(&ctx); SHA1_Update(&ctx, hashhash, sizeof(hashhash)); SHA1_Update(&ctx, answer.data, answer.length); SHA1_Update(&ctx, ms_chap_v2_magic1,sizeof(ms_chap_v2_magic1)); SHA1_Final(md, &ctx); SHA1_Init(&ctx); SHA1_Update(&ctx, md, sizeof(md)); SHA1_Update(&ctx, challange, 8); SHA1_Update(&ctx, ms_chap_v2_magic2, sizeof(ms_chap_v2_magic2)); SHA1_Final(md, &ctx); r.u.response.rsp = calloc(1, sizeof(*r.u.response.rsp)); if (r.u.response.rsp == NULL) { free(answer.data); krb5_clear_error_string(context); ret = ENOMEM; goto out; } hex_encode(md, sizeof(md), r.u.response.rsp); if (r.u.response.rsp == NULL) { free(answer.data); krb5_clear_error_string(context); ret = ENOMEM; goto out; } /* get_master, rfc 3079 3.4 */ SHA1_Init(&ctx); SHA1_Update(&ctx, hashhash, 16); /* md4(hash) */ SHA1_Update(&ctx, answer.data, answer.length); SHA1_Update(&ctx, ms_rfc3079_magic1, sizeof(ms_rfc3079_magic1)); SHA1_Final(md, &ctx); free(answer.data); r.u.response.session_key = calloc(1, sizeof(*r.u.response.session_key)); if (r.u.response.session_key == NULL) { krb5_clear_error_string(context); ret = ENOMEM; goto out; } ret = krb5_data_copy(r.u.response.session_key, md, 16); if (ret) { krb5_clear_error_string(context); goto out; } } } else { r.element = choice_DigestRepInner_error; asprintf(&r.u.error.reason, "Unsupported digest type %s", ireq.u.digestRequest.type); if (r.u.error.reason == NULL) { krb5_set_error_string(context, "out of memory"); ret = ENOMEM; goto out;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -