📄 digest.c
字号:
/* * Copyright (c) 2006 - 2007 Kungliga Tekniska H鰃skolan * (Royal Institute of Technology, Stockholm, Sweden). * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * 3. Neither the name of the Institute nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */#include "kdc_locl.h"#include <hex.h>RCSID("$Id: digest.c 22374 2007-12-28 18:36:52Z lha $");#define MS_CHAP_V2 0x20#define CHAP_MD5 0x10#define DIGEST_MD5 0x08#define NTLM_V2 0x04#define NTLM_V1_SESSION 0x02#define NTLM_V1 0x01const struct units _kdc_digestunits[] = { {"ms-chap-v2", 1U << 5}, {"chap-md5", 1U << 4}, {"digest-md5", 1U << 3}, {"ntlm-v2", 1U << 2}, {"ntlm-v1-session", 1U << 1}, {"ntlm-v1", 1U << 0}, {NULL, 0}};static krb5_error_codeget_digest_key(krb5_context context, krb5_kdc_configuration *config, hdb_entry_ex *server, krb5_crypto *crypto){ krb5_error_code ret; krb5_enctype enctype; Key *key; ret = _kdc_get_preferred_key(context, config, server, "digest-service", &enctype, &key); if (ret) return ret; return krb5_crypto_init(context, &key->key, 0, crypto);}/* * */static char *get_ntlm_targetname(krb5_context context, hdb_entry_ex *client){ char *targetname, *p; targetname = strdup(krb5_principal_get_realm(context, client->entry.principal)); if (targetname == NULL) return NULL; p = strchr(targetname, '.'); if (p) *p = '\0'; strupr(targetname); return targetname;}static krb5_error_codefill_targetinfo(krb5_context context, char *targetname, hdb_entry_ex *client, krb5_data *data){ struct ntlm_targetinfo ti; krb5_error_code ret; struct ntlm_buf d; krb5_principal p; const char *str; memset(&ti, 0, sizeof(ti)); ti.domainname = targetname; p = client->entry.principal; str = krb5_principal_get_comp_string(context, p, 0); if (str != NULL && (strcmp("host", str) == 0 || strcmp("ftp", str) == 0 || strcmp("imap", str) == 0 || strcmp("pop", str) == 0 || strcmp("smtp", str))) { str = krb5_principal_get_comp_string(context, p, 1); ti.dnsservername = rk_UNCONST(str); } ret = heim_ntlm_encode_targetinfo(&ti, 1, &d); if (ret) return ret; data->data = d.data; data->length = d.length; return 0;}static const unsigned char ms_chap_v2_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};static const unsigned char ms_chap_v2_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};static const unsigned char ms_rfc3079_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};/* * */static krb5_error_codeget_password_entry(krb5_context context, krb5_kdc_configuration *config, const char *username, char **password){ krb5_principal clientprincipal; krb5_error_code ret; hdb_entry_ex *user; HDB *db; /* get username */ ret = krb5_parse_name(context, username, &clientprincipal); if (ret) return ret; ret = _kdc_db_fetch(context, config, clientprincipal, HDB_F_GET_CLIENT, &db, &user); krb5_free_principal(context, clientprincipal); if (ret) return ret; ret = hdb_entry_get_password(context, db, &user->entry, password); if (ret || password == NULL) { if (ret == 0) { ret = EINVAL; krb5_set_error_string(context, "password missing"); } memset(user, 0, sizeof(*user)); } _kdc_free_ent (context, user); return ret;}/* * */krb5_error_code_kdc_do_digest(krb5_context context, krb5_kdc_configuration *config, const DigestREQ *req, krb5_data *reply, const char *from, struct sockaddr *addr){ krb5_error_code ret = 0; krb5_ticket *ticket = NULL; krb5_auth_context ac = NULL; krb5_keytab id = NULL; krb5_crypto crypto = NULL; DigestReqInner ireq; DigestRepInner r; DigestREP rep; krb5_flags ap_req_options; krb5_data buf; size_t size; krb5_storage *sp = NULL; Checksum res; hdb_entry_ex *server = NULL, *user = NULL; hdb_entry_ex *client = NULL; char *client_name = NULL, *password = NULL; krb5_data serverNonce; if(!config->enable_digest) { kdc_log(context, config, 0, "Rejected digest request (disabled) from %s", from); return KRB5KDC_ERR_POLICY; } krb5_data_zero(&buf); krb5_data_zero(reply); krb5_data_zero(&serverNonce); memset(&ireq, 0, sizeof(ireq)); memset(&r, 0, sizeof(r)); memset(&rep, 0, sizeof(rep)); kdc_log(context, config, 0, "Digest request from %s", from); ret = krb5_kt_resolve(context, "HDB:", &id); if (ret) { kdc_log(context, config, 0, "Can't open database for digest"); goto out; } ret = krb5_rd_req(context, &ac, &req->apReq, NULL, id, &ap_req_options, &ticket); if (ret) goto out; /* check the server principal in the ticket matches digest/R@R */ { krb5_principal principal = NULL; const char *p, *r; ret = krb5_ticket_get_server(context, ticket, &principal); if (ret) goto out; ret = EINVAL; krb5_set_error_string(context, "Wrong digest server principal used"); p = krb5_principal_get_comp_string(context, principal, 0); if (p == NULL) { krb5_free_principal(context, principal); goto out; } if (strcmp(p, KRB5_DIGEST_NAME) != 0) { krb5_free_principal(context, principal); goto out; } p = krb5_principal_get_comp_string(context, principal, 1); if (p == NULL) { krb5_free_principal(context, principal); goto out; } r = krb5_principal_get_realm(context, principal); if (r == NULL) { krb5_free_principal(context, principal); goto out; } if (strcmp(p, r) != 0) { krb5_free_principal(context, principal); goto out; } krb5_clear_error_string(context); ret = _kdc_db_fetch(context, config, principal, HDB_F_GET_SERVER, NULL, &server); if (ret) goto out; krb5_free_principal(context, principal); } /* check the client is allowed to do digest auth */ { krb5_principal principal = NULL; ret = krb5_ticket_get_client(context, ticket, &principal); if (ret) goto out; ret = krb5_unparse_name(context, principal, &client_name); if (ret) { krb5_free_principal(context, principal); goto out; } ret = _kdc_db_fetch(context, config, principal, HDB_F_GET_CLIENT, NULL, &client); krb5_free_principal(context, principal); if (ret) goto out; if (client->entry.flags.allow_digest == 0) { kdc_log(context, config, 0, "Client %s tried to use digest " "but is not allowed to", client_name); krb5_set_error_string(context, "Client is not permitted to use digest"); ret = KRB5KDC_ERR_POLICY; goto out; } } /* unpack request */ { krb5_keyblock *key; ret = krb5_auth_con_getremotesubkey(context, ac, &key); if (ret) goto out; if (key == NULL) { krb5_set_error_string(context, "digest: remote subkey not found"); ret = EINVAL; goto out; } ret = krb5_crypto_init(context, key, 0, &crypto); krb5_free_keyblock (context, key); if (ret) goto out; } ret = krb5_decrypt_EncryptedData(context, crypto, KRB5_KU_DIGEST_ENCRYPT, &req->innerReq, &buf); krb5_crypto_destroy(context, crypto); crypto = NULL; if (ret) goto out; ret = decode_DigestReqInner(buf.data, buf.length, &ireq, NULL); krb5_data_free(&buf); if (ret) { krb5_set_error_string(context, "Failed to decode digest inner request"); goto out; } kdc_log(context, config, 0, "Valid digest request from %s (%s)", client_name, from); /* * Process the inner request */ switch (ireq.element) { case choice_DigestReqInner_init: { unsigned char server_nonce[16], identifier; RAND_pseudo_bytes(&identifier, sizeof(identifier)); RAND_pseudo_bytes(server_nonce, sizeof(server_nonce)); server_nonce[0] = kdc_time & 0xff; server_nonce[1] = (kdc_time >> 8) & 0xff; server_nonce[2] = (kdc_time >> 16) & 0xff; server_nonce[3] = (kdc_time >> 24) & 0xff; r.element = choice_DigestRepInner_initReply; hex_encode(server_nonce, sizeof(server_nonce), &r.u.initReply.nonce); if (r.u.initReply.nonce == NULL) { krb5_set_error_string(context, "Failed to decode server nonce"); ret = ENOMEM; goto out; } 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.init.type); if (ret) { krb5_clear_error_string(context); goto out; } if (ireq.u.init.channel) { char *s; asprintf(&s, "%s-%s:%s", r.u.initReply.nonce, ireq.u.init.channel->cb_type, ireq.u.init.channel->cb_binding); if (s == NULL) { krb5_set_error_string(context, "Failed to allocate " "channel binding"); ret = ENOMEM; goto out; } free(r.u.initReply.nonce); r.u.initReply.nonce = s; } ret = krb5_store_stringz(sp, r.u.initReply.nonce); if (ret) { krb5_clear_error_string(context); goto out; } if (strcasecmp(ireq.u.init.type, "CHAP") == 0) { r.u.initReply.identifier = malloc(sizeof(*r.u.initReply.identifier)); if (r.u.initReply.identifier == NULL) { krb5_set_error_string(context, "out of memory"); ret = ENOMEM; goto out; } asprintf(r.u.initReply.identifier, "%02X", identifier & 0xff); if (*r.u.initReply.identifier == NULL) { krb5_set_error_string(context, "out of memory"); ret = ENOMEM; goto out; } } else r.u.initReply.identifier = NULL; if (ireq.u.init.hostname) { ret = krb5_store_stringz(sp, *ireq.u.init.hostname); if (ret) { krb5_clear_error_string(context); goto out; } } ret = krb5_storage_to_data(sp, &buf); if (ret) { krb5_clear_error_string(context); goto out; } ret = get_digest_key(context, config, server, &crypto); if (ret) goto out; ret = krb5_create_checksum(context, crypto, KRB5_KU_DIGEST_OPAQUE, 0, buf.data, buf.length, &res); krb5_crypto_destroy(context, crypto); crypto = NULL; krb5_data_free(&buf); if (ret) goto out; ASN1_MALLOC_ENCODE(Checksum, buf.data, buf.length, &res, &size, ret); free_Checksum(&res); if (ret) { krb5_set_error_string(context, "Failed to encode " "checksum in digest request"); goto out; } if (size != buf.length) krb5_abortx(context, "ASN1 internal error");
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -