Index: projects/openssl111/crypto/heimdal/kdc/digest.c =================================================================== --- projects/openssl111/crypto/heimdal/kdc/digest.c (revision 339197) +++ projects/openssl111/crypto/heimdal/kdc/digest.c (revision 339198) @@ -1,1509 +1,1515 @@ /* * Copyright (c) 2006 - 2007 Kungliga Tekniska Högskolan * (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 #ifdef DIGEST #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 0x01 const 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_code get_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_code fill_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_code get_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, NULL, &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_message(context, ret, "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 struct 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)); memset(&res, 0, sizeof(res)); 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, *rr; ret = krb5_ticket_get_server(context, ticket, &principal); if (ret) goto out; ret = EINVAL; krb5_set_error_message(context, ret, "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; } rr = krb5_principal_get_realm(context, principal); if (rr == NULL) { krb5_free_principal(context, principal); goto out; } if (strcmp(p, rr) != 0) { krb5_free_principal(context, principal); goto out; } krb5_clear_error_message(context); ret = _kdc_db_fetch(context, config, principal, HDB_F_GET_SERVER, NULL, 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, 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); ret = KRB5KDC_ERR_POLICY; krb5_set_error_message(context, ret, "Client is not permitted to use digest"); goto out; } } /* unpack request */ { krb5_keyblock *key; ret = krb5_auth_con_getremotesubkey(context, ac, &key); if (ret) goto out; if (key == NULL) { ret = EINVAL; krb5_set_error_message(context, ret, "digest: remote subkey not found"); 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_message(context, ret, "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)); + RAND_bytes(&identifier, sizeof(identifier)); + RAND_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) { ret = ENOMEM; krb5_set_error_message(context, ret, "Failed to decode server nonce"); goto out; } sp = krb5_storage_emem(); if (sp == NULL) { ret = ENOMEM; krb5_set_error_message(context, ret, "malloc: out of memory"); goto out; } ret = krb5_store_stringz(sp, ireq.u.init.type); if (ret) { krb5_clear_error_message(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) { ret = ENOMEM; krb5_set_error_message(context, ret, "Failed to allocate channel binding"); 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_message(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) { ret = ENOMEM; krb5_set_error_message(context, ret, "malloc: out of memory"); goto out; } asprintf(r.u.initReply.identifier, "%02X", identifier & 0xff); if (*r.u.initReply.identifier == NULL) { ret = ENOMEM; krb5_set_error_message(context, ret, "malloc: out of memory"); 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_message(context); goto out; } } ret = krb5_storage_to_data(sp, &buf); if (ret) { krb5_clear_error_message(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_message(context, ret, "Failed to encode " "checksum in digest request"); goto out; } if (size != buf.length) krb5_abortx(context, "ASN1 internal error"); hex_encode(buf.data, buf.length, &r.u.initReply.opaque); free(buf.data); krb5_data_zero(&buf); if (r.u.initReply.opaque == NULL) { krb5_clear_error_message(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_message(context, ret, "malloc: out of memory"); goto out; } ret = krb5_store_stringz(sp, ireq.u.digestRequest.type); if (ret) { krb5_clear_error_message(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_message(context); goto out; } } buf.length = strlen(ireq.u.digestRequest.opaque); buf.data = malloc(buf.length); if (buf.data == NULL) { ret = ENOMEM; krb5_set_error_message(context, ret, "malloc: out of memory"); goto out; } ret = hex_decode(ireq.u.digestRequest.opaque, buf.data, buf.length); if (ret <= 0) { ret = ENOMEM; krb5_set_error_message(context, ret, "Failed to decode opaque"); goto out; } buf.length = ret; ret = decode_Checksum(buf.data, buf.length, &res, NULL); free(buf.data); krb5_data_zero(&buf); if (ret) { krb5_set_error_message(context, ret, "Failed to decode digest Checksum"); goto out; } ret = krb5_storage_to_data(sp, &buf); if (ret) { krb5_clear_error_message(context); goto out; } serverNonce.length = strlen(ireq.u.digestRequest.serverNonce); serverNonce.data = malloc(serverNonce.length); if (serverNonce.data == NULL) { ret = ENOMEM; krb5_set_error_message(context, ret, "malloc: out of memory"); 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) { ret = ENOMEM; krb5_set_error_message(context, ret, "Failed to decode serverNonce"); 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); free_Checksum(&res); krb5_data_free(&buf); 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) { ret = EINVAL; krb5_set_error_message(context, ret, "server nonce too short"); goto out; } t = p[0] | (p[1] << 8) | (p[2] << 16) | (p[3] << 24); if (abs((kdc_time & 0xffffffff) - t) > context->max_skew) { ret = EINVAL; krb5_set_error_message(context, ret, "time screw in server nonce "); goto out; } } if (strcasecmp(ireq.u.digestRequest.type, "CHAP") == 0) { EVP_MD_CTX *ctx; unsigned char md[MD5_DIGEST_LENGTH]; char *mdx; char idx; 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) { ret = EINVAL; krb5_set_error_message(context, ret, "Identifier missing " "from CHAP request"); goto out; } if (hex_decode(*ireq.u.digestRequest.identifier, &idx, 1) != 1) { ret = EINVAL; krb5_set_error_message(context, ret, "failed to decode identifier"); goto out; } ret = get_password_entry(context, config, ireq.u.digestRequest.username, &password); if (ret) goto out; ctx = EVP_MD_CTX_create(); EVP_DigestInit_ex(ctx, EVP_md5(), NULL); EVP_DigestUpdate(ctx, &idx, 1); EVP_DigestUpdate(ctx, password, strlen(password)); EVP_DigestUpdate(ctx, serverNonce.data, serverNonce.length); EVP_DigestFinal_ex(ctx, md, NULL); EVP_MD_CTX_destroy(ctx); hex_encode(md, sizeof(md), &mdx); if (mdx == NULL) { krb5_clear_error_message(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) { EVP_MD_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; ctx = EVP_MD_CTX_create(); EVP_DigestInit_ex(ctx, EVP_md5(), NULL); EVP_DigestUpdate(ctx, ireq.u.digestRequest.username, strlen(ireq.u.digestRequest.username)); EVP_DigestUpdate(ctx, ":", 1); EVP_DigestUpdate(ctx, *ireq.u.digestRequest.realm, strlen(*ireq.u.digestRequest.realm)); EVP_DigestUpdate(ctx, ":", 1); EVP_DigestUpdate(ctx, password, strlen(password)); EVP_DigestFinal_ex(ctx, md, NULL); EVP_DigestInit_ex(ctx, EVP_md5(), NULL); EVP_DigestUpdate(ctx, md, sizeof(md)); EVP_DigestUpdate(ctx, ":", 1); EVP_DigestUpdate(ctx, ireq.u.digestRequest.serverNonce, strlen(ireq.u.digestRequest.serverNonce)); EVP_DigestUpdate(ctx, ":", 1); EVP_DigestUpdate(ctx, *ireq.u.digestRequest.nonceCount, strlen(*ireq.u.digestRequest.nonceCount)); if (ireq.u.digestRequest.authid) { EVP_DigestUpdate(ctx, ":", 1); EVP_DigestUpdate(ctx, *ireq.u.digestRequest.authid, strlen(*ireq.u.digestRequest.authid)); } EVP_DigestFinal_ex(ctx, md, NULL); hex_encode(md, sizeof(md), &A1); if (A1 == NULL) { ret = ENOMEM; krb5_set_error_message(context, ret, "malloc: out of memory"); EVP_MD_CTX_destroy(ctx); goto failed; } EVP_DigestInit_ex(ctx, EVP_md5(), NULL); EVP_DigestUpdate(ctx, "AUTHENTICATE:", sizeof("AUTHENTICATE:") - 1); EVP_DigestUpdate(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"; EVP_DigestUpdate(ctx, conf_zeros, sizeof(conf_zeros) - 1); } EVP_DigestFinal_ex(ctx, md, NULL); hex_encode(md, sizeof(md), &A2); if (A2 == NULL) { ret = ENOMEM; krb5_set_error_message(context, ret, "malloc: out of memory"); free(A1); goto failed; } EVP_DigestInit_ex(ctx, EVP_md5(), NULL); EVP_DigestUpdate(ctx, A1, strlen(A2)); EVP_DigestUpdate(ctx, ":", 1); EVP_DigestUpdate(ctx, ireq.u.digestRequest.serverNonce, strlen(ireq.u.digestRequest.serverNonce)); EVP_DigestUpdate(ctx, ":", 1); EVP_DigestUpdate(ctx, *ireq.u.digestRequest.nonceCount, strlen(*ireq.u.digestRequest.nonceCount)); EVP_DigestUpdate(ctx, ":", 1); EVP_DigestUpdate(ctx, *ireq.u.digestRequest.clientNonce, strlen(*ireq.u.digestRequest.clientNonce)); EVP_DigestUpdate(ctx, ":", 1); EVP_DigestUpdate(ctx, *ireq.u.digestRequest.qop, strlen(*ireq.u.digestRequest.qop)); EVP_DigestUpdate(ctx, ":", 1); EVP_DigestUpdate(ctx, A2, strlen(A2)); EVP_DigestFinal_ex(ctx, md, NULL); EVP_MD_CTX_destroy(ctx); free(A1); free(A2); hex_encode(md, sizeof(md), &mdx); if (mdx == NULL) { krb5_clear_error_message(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; EVP_MD_CTX *ctp; 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) { ret = EINVAL; krb5_set_error_message(context, ret, "MS-CHAP-V2 clientNonce missing"); goto failed; } if (serverNonce.length != 16) { ret = EINVAL; krb5_set_error_message(context, ret, "MS-CHAP-V2 serverNonce wrong length"); goto failed; } /* strip of the domain component */ username = strchr(ireq.u.digestRequest.username, '\\'); if (username == NULL) username = ireq.u.digestRequest.username; else username++; ctp = EVP_MD_CTX_create(); /* ChallangeHash */ EVP_DigestInit_ex(ctp, EVP_sha1(), NULL); { 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_message(context, ret, "malloc: out of memory"); EVP_MD_CTX_destroy(ctp); goto out; } ssize = hex_decode(*ireq.u.digestRequest.clientNonce, clientNonce.data, clientNonce.length); if (ssize != 16) { ret = ENOMEM; krb5_set_error_message(context, ret, "Failed to decode clientNonce"); EVP_MD_CTX_destroy(ctp); goto out; } EVP_DigestUpdate(ctp, clientNonce.data, ssize); free(clientNonce.data); } EVP_DigestUpdate(ctp, serverNonce.data, serverNonce.length); EVP_DigestUpdate(ctp, username, strlen(username)); EVP_DigestFinal_ex(ctp, challange, NULL); EVP_MD_CTX_destroy(ctp); /* NtPasswordHash */ ret = krb5_parse_name(context, username, &clientprincipal); if (ret) goto failed; ret = _kdc_db_fetch(context, config, clientprincipal, HDB_F_GET_CLIENT, NULL, NULL, &user); krb5_free_principal(context, clientprincipal); if (ret) { krb5_set_error_message(context, ret, "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_message(context, ret, "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_message(context, ret, "NTLM missing arcfour key"); goto failed; } hex_encode(answer.data, answer.length, &mdx); if (mdx == NULL) { free(answer.data); krb5_clear_error_message(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]; EVP_MD_CTX *ctxp; ctxp = EVP_MD_CTX_create(); /* hashhash */ { EVP_DigestInit_ex(ctxp, EVP_md4(), NULL); EVP_DigestUpdate(ctxp, key->key.keyvalue.data, key->key.keyvalue.length); EVP_DigestFinal_ex(ctxp, hashhash, NULL); } /* GenerateAuthenticatorResponse */ EVP_DigestInit_ex(ctxp, EVP_sha1(), NULL); EVP_DigestUpdate(ctxp, hashhash, sizeof(hashhash)); EVP_DigestUpdate(ctxp, answer.data, answer.length); EVP_DigestUpdate(ctxp, ms_chap_v2_magic1, sizeof(ms_chap_v2_magic1)); EVP_DigestFinal_ex(ctxp, md, NULL); EVP_DigestInit_ex(ctxp, EVP_sha1(), NULL); EVP_DigestUpdate(ctxp, md, sizeof(md)); EVP_DigestUpdate(ctxp, challange, 8); EVP_DigestUpdate(ctxp, ms_chap_v2_magic2, sizeof(ms_chap_v2_magic2)); EVP_DigestFinal_ex(ctxp, md, NULL); r.u.response.rsp = calloc(1, sizeof(*r.u.response.rsp)); if (r.u.response.rsp == NULL) { free(answer.data); krb5_clear_error_message(context); EVP_MD_CTX_destroy(ctxp); 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_message(context); EVP_MD_CTX_destroy(ctxp); ret = ENOMEM; goto out; } /* get_master, rfc 3079 3.4 */ EVP_DigestInit_ex(ctxp, EVP_sha1(), NULL); EVP_DigestUpdate(ctxp, hashhash, 16); EVP_DigestUpdate(ctxp, answer.data, answer.length); EVP_DigestUpdate(ctxp, ms_rfc3079_magic1, sizeof(ms_rfc3079_magic1)); EVP_DigestFinal_ex(ctxp, md, NULL); free(answer.data); EVP_MD_CTX_destroy(ctxp); r.u.response.session_key = calloc(1, sizeof(*r.u.response.session_key)); if (r.u.response.session_key == NULL) { krb5_clear_error_message(context); ret = ENOMEM; goto out; } ret = krb5_data_copy(r.u.response.session_key, md, 16); if (ret) { krb5_clear_error_message(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) { ret = ENOMEM; krb5_set_error_message(context, ret, "malloc: out of memory"); goto out; } r.u.error.code = EINVAL; } kdc_log(context, config, 0, "Digest %s request successful %s", ireq.u.digestRequest.type, ireq.u.digestRequest.username); break; } case choice_DigestReqInner_ntlmInit: if ((config->digests_allowed & (NTLM_V1|NTLM_V1_SESSION|NTLM_V2)) == 0) { kdc_log(context, config, 0, "NTLM not allowed"); goto failed; } r.element = choice_DigestRepInner_ntlmInitReply; r.u.ntlmInitReply.flags = NTLM_NEG_UNICODE; if ((ireq.u.ntlmInit.flags & NTLM_NEG_UNICODE) == 0) { kdc_log(context, config, 0, "NTLM client have no unicode"); goto failed; } if (ireq.u.ntlmInit.flags & NTLM_NEG_NTLM) r.u.ntlmInitReply.flags |= NTLM_NEG_NTLM; else { kdc_log(context, config, 0, "NTLM client doesn't support NTLM"); goto failed; } r.u.ntlmInitReply.flags |= NTLM_NEG_TARGET | NTLM_TARGET_DOMAIN | NTLM_ENC_128; #define ALL \ NTLM_NEG_SIGN| \ NTLM_NEG_SEAL| \ NTLM_NEG_ALWAYS_SIGN| \ NTLM_NEG_NTLM2_SESSION| \ NTLM_NEG_KEYEX r.u.ntlmInitReply.flags |= (ireq.u.ntlmInit.flags & (ALL)); #undef ALL r.u.ntlmInitReply.targetname = get_ntlm_targetname(context, client); if (r.u.ntlmInitReply.targetname == NULL) { ret = ENOMEM; krb5_set_error_message(context, ret, "malloc: out of memory"); goto out; } r.u.ntlmInitReply.challange.data = malloc(8); if (r.u.ntlmInitReply.challange.data == NULL) { ret = ENOMEM; krb5_set_error_message(context, ret, "malloc: out of memory"); goto out; } r.u.ntlmInitReply.challange.length = 8; if (RAND_bytes(r.u.ntlmInitReply.challange.data, r.u.ntlmInitReply.challange.length) != 1) { ret = ENOMEM; krb5_set_error_message(context, ret, "out of random error"); goto out; } /* XXX fix targetinfo */ ALLOC(r.u.ntlmInitReply.targetinfo); if (r.u.ntlmInitReply.targetinfo == NULL) { ret = ENOMEM; krb5_set_error_message(context, ret, "malloc: out of memory"); goto out; } ret = fill_targetinfo(context, r.u.ntlmInitReply.targetname, client, r.u.ntlmInitReply.targetinfo); if (ret) { ret = ENOMEM; krb5_set_error_message(context, ret, "malloc: out of memory"); goto out; } /* * Save data encryted in opaque for the second part of the * ntlm authentication */ sp = krb5_storage_emem(); if (sp == NULL) { ret = ENOMEM; krb5_set_error_message(context, ret, "malloc: out of memory"); goto out; } ret = krb5_storage_write(sp, r.u.ntlmInitReply.challange.data, 8); if (ret != 8) { ret = ENOMEM; krb5_set_error_message(context, ret, "storage write challange"); goto out; } ret = krb5_store_uint32(sp, r.u.ntlmInitReply.flags); if (ret) { krb5_clear_error_message(context); goto out; } ret = krb5_storage_to_data(sp, &buf); if (ret) { krb5_clear_error_message(context); goto out; } ret = get_digest_key(context, config, server, &crypto); if (ret) goto out; ret = krb5_encrypt(context, crypto, KRB5_KU_DIGEST_OPAQUE, buf.data, buf.length, &r.u.ntlmInitReply.opaque); krb5_data_free(&buf); krb5_crypto_destroy(context, crypto); crypto = NULL; if (ret) goto out; kdc_log(context, config, 0, "NTLM init from %s", from); break; case choice_DigestReqInner_ntlmRequest: { krb5_principal clientprincipal; unsigned char sessionkey[16]; unsigned char challange[8]; uint32_t flags; Key *key = NULL; int version; r.element = choice_DigestRepInner_ntlmResponse; r.u.ntlmResponse.success = 0; r.u.ntlmResponse.flags = 0; r.u.ntlmResponse.sessionkey = NULL; r.u.ntlmResponse.tickets = NULL; /* get username */ ret = krb5_parse_name(context, ireq.u.ntlmRequest.username, &clientprincipal); if (ret) goto failed; ret = _kdc_db_fetch(context, config, clientprincipal, HDB_F_GET_CLIENT, NULL, NULL, &user); krb5_free_principal(context, clientprincipal); if (ret) { krb5_set_error_message(context, ret, "NTLM user %s not in database", ireq.u.ntlmRequest.username); goto failed; } ret = get_digest_key(context, config, server, &crypto); if (ret) goto failed; ret = krb5_decrypt(context, crypto, KRB5_KU_DIGEST_OPAQUE, ireq.u.ntlmRequest.opaque.data, ireq.u.ntlmRequest.opaque.length, &buf); krb5_crypto_destroy(context, crypto); crypto = NULL; if (ret) { kdc_log(context, config, 0, "Failed to decrypt nonce from %s", from); goto failed; } sp = krb5_storage_from_data(&buf); if (sp == NULL) { ret = ENOMEM; krb5_set_error_message(context, ret, "malloc: out of memory"); goto out; } ret = krb5_storage_read(sp, challange, sizeof(challange)); if (ret != sizeof(challange)) { ret = ENOMEM; krb5_set_error_message(context, ret, "NTLM storage read challange"); goto out; } ret = krb5_ret_uint32(sp, &flags); if (ret) { krb5_set_error_message(context, ret, "NTLM storage read flags"); goto out; } krb5_storage_free(sp); sp = NULL; krb5_data_free(&buf); if ((flags & NTLM_NEG_NTLM) == 0) { ret = EINVAL; krb5_set_error_message(context, ret, "NTLM not negotiated"); goto out; } ret = hdb_enctype2key(context, &user->entry, ETYPE_ARCFOUR_HMAC_MD5, &key); if (ret) { krb5_set_error_message(context, ret, "NTLM missing arcfour key"); goto out; } /* check if this is NTLMv2 */ if (ireq.u.ntlmRequest.ntlm.length != 24) { struct ntlm_buf infotarget, answer; char *targetname; if ((config->digests_allowed & NTLM_V2) == 0) { kdc_log(context, config, 0, "NTLM v2 not allowed"); goto out; } version = 2; targetname = get_ntlm_targetname(context, client); if (targetname == NULL) { ret = ENOMEM; krb5_set_error_message(context, ret, "malloc: out of memory"); goto out; } answer.length = ireq.u.ntlmRequest.ntlm.length; answer.data = ireq.u.ntlmRequest.ntlm.data; ret = heim_ntlm_verify_ntlm2(key->key.keyvalue.data, key->key.keyvalue.length, ireq.u.ntlmRequest.username, targetname, 0, challange, &answer, &infotarget, sessionkey); free(targetname); if (ret) { krb5_set_error_message(context, ret, "NTLM v2 verify failed"); goto failed; } /* XXX verify infotarget matches client (checksum ?) */ free(infotarget.data); /* */ } else { struct ntlm_buf answer; version = 1; if (flags & NTLM_NEG_NTLM2_SESSION) { unsigned char sessionhash[MD5_DIGEST_LENGTH]; EVP_MD_CTX *ctx; if ((config->digests_allowed & NTLM_V1_SESSION) == 0) { kdc_log(context, config, 0, "NTLM v1-session not allowed"); ret = EINVAL; goto failed; } if (ireq.u.ntlmRequest.lm.length != 24) { ret = EINVAL; krb5_set_error_message(context, ret, "LM hash have wrong length " "for NTLM session key"); goto failed; } ctx = EVP_MD_CTX_create(); EVP_DigestInit_ex(ctx, EVP_md5(), NULL); EVP_DigestUpdate(ctx, challange, sizeof(challange)); EVP_DigestUpdate(ctx, ireq.u.ntlmRequest.lm.data, 8); EVP_DigestFinal_ex(ctx, sessionhash, NULL); memcpy(challange, sessionhash, sizeof(challange)); EVP_MD_CTX_destroy(ctx); } else { if ((config->digests_allowed & NTLM_V1) == 0) { kdc_log(context, config, 0, "NTLM v1 not allowed"); goto failed; } } ret = heim_ntlm_calculate_ntlm1(key->key.keyvalue.data, key->key.keyvalue.length, challange, &answer); if (ret) { krb5_set_error_message(context, ret, "NTLM missing arcfour key"); goto failed; } if (ireq.u.ntlmRequest.ntlm.length != answer.length || memcmp(ireq.u.ntlmRequest.ntlm.data, answer.data, answer.length) != 0) { free(answer.data); ret = EINVAL; krb5_set_error_message(context, ret, "NTLM hash mismatch"); goto failed; } free(answer.data); { EVP_MD_CTX *ctx; ctx = EVP_MD_CTX_create(); EVP_DigestInit_ex(ctx, EVP_md4(), NULL); EVP_DigestUpdate(ctx, key->key.keyvalue.data, key->key.keyvalue.length); EVP_DigestFinal_ex(ctx, sessionkey, NULL); EVP_MD_CTX_destroy(ctx); } } if (ireq.u.ntlmRequest.sessionkey) { unsigned char masterkey[MD4_DIGEST_LENGTH]; - EVP_CIPHER_CTX rc4; + EVP_CIPHER_CTX *rc4; size_t len; if ((flags & NTLM_NEG_KEYEX) == 0) { ret = EINVAL; krb5_set_error_message(context, ret, "NTLM client failed to neg key " "exchange but still sent key"); goto failed; } len = ireq.u.ntlmRequest.sessionkey->length; if (len != sizeof(masterkey)){ ret = EINVAL; krb5_set_error_message(context, ret, "NTLM master key wrong length: %lu", (unsigned long)len); goto failed; } - EVP_CIPHER_CTX_init(&rc4); - EVP_CipherInit_ex(&rc4, EVP_rc4(), NULL, sessionkey, NULL, 1); - EVP_Cipher(&rc4, + rc4 = EVP_CIPHER_CTX_new(); + if (rc4 == NULL) { + ret = ENOMEM; + krb5_set_error_message(context, ret, + "NTLM failed to malloc cipher context"); + goto failed; + } + EVP_CipherInit_ex(rc4, EVP_rc4(), NULL, sessionkey, NULL, 1); + EVP_Cipher(rc4, masterkey, ireq.u.ntlmRequest.sessionkey->data, sizeof(masterkey)); - EVP_CIPHER_CTX_cleanup(&rc4); + EVP_CIPHER_CTX_free(rc4); r.u.ntlmResponse.sessionkey = malloc(sizeof(*r.u.ntlmResponse.sessionkey)); if (r.u.ntlmResponse.sessionkey == NULL) { ret = EINVAL; krb5_set_error_message(context, ret, "malloc: out of memory"); goto out; } ret = krb5_data_copy(r.u.ntlmResponse.sessionkey, masterkey, sizeof(masterkey)); if (ret) { krb5_set_error_message(context, ret, "malloc: out of memory"); goto out; } } r.u.ntlmResponse.success = 1; kdc_log(context, config, 0, "NTLM version %d successful for %s", version, ireq.u.ntlmRequest.username); break; } case choice_DigestReqInner_supportedMechs: kdc_log(context, config, 0, "digest supportedMechs from %s", from); r.element = choice_DigestRepInner_supportedMechs; memset(&r.u.supportedMechs, 0, sizeof(r.u.supportedMechs)); if (config->digests_allowed & NTLM_V1) r.u.supportedMechs.ntlm_v1 = 1; if (config->digests_allowed & NTLM_V1_SESSION) r.u.supportedMechs.ntlm_v1_session = 1; if (config->digests_allowed & NTLM_V2) r.u.supportedMechs.ntlm_v2 = 1; if (config->digests_allowed & DIGEST_MD5) r.u.supportedMechs.digest_md5 = 1; if (config->digests_allowed & CHAP_MD5) r.u.supportedMechs.chap_md5 = 1; if (config->digests_allowed & MS_CHAP_V2) r.u.supportedMechs.ms_chap_v2 = 1; break; default: { const char *s; ret = EINVAL; krb5_set_error_message(context, ret, "unknown operation to digest"); failed: s = krb5_get_error_message(context, ret); if (s == NULL) { krb5_clear_error_message(context); goto out; } kdc_log(context, config, 0, "Digest failed with: %s", s); r.element = choice_DigestRepInner_error; r.u.error.reason = strdup("unknown error"); krb5_free_error_message(context, s); if (r.u.error.reason == NULL) { ret = ENOMEM; krb5_set_error_message(context, ret, "malloc: out of memory"); goto out; } r.u.error.code = EINVAL; break; } } ASN1_MALLOC_ENCODE(DigestRepInner, buf.data, buf.length, &r, &size, ret); if (ret) { krb5_set_error_message(context, ret, "Failed to encode inner digest reply"); goto out; } if (size != buf.length) krb5_abortx(context, "ASN1 internal error"); krb5_auth_con_addflags(context, ac, KRB5_AUTH_CONTEXT_USE_SUBKEY, NULL); ret = krb5_mk_rep (context, ac, &rep.apRep); if (ret) goto out; { krb5_keyblock *key; ret = krb5_auth_con_getlocalsubkey(context, ac, &key); if (ret) goto out; ret = krb5_crypto_init(context, key, 0, &crypto); krb5_free_keyblock (context, key); if (ret) goto out; } ret = krb5_encrypt_EncryptedData(context, crypto, KRB5_KU_DIGEST_ENCRYPT, buf.data, buf.length, 0, &rep.innerRep); ASN1_MALLOC_ENCODE(DigestREP, reply->data, reply->length, &rep, &size, ret); if (ret) { krb5_set_error_message(context, ret, "Failed to encode digest reply"); goto out; } if (size != reply->length) krb5_abortx(context, "ASN1 internal error"); out: if (ac) krb5_auth_con_free(context, ac); if (ret) krb5_warn(context, ret, "Digest request from %s failed", from); if (ticket) krb5_free_ticket(context, ticket); if (id) krb5_kt_close(context, id); if (crypto) krb5_crypto_destroy(context, crypto); if (sp) krb5_storage_free(sp); if (user) _kdc_free_ent (context, user); if (server) _kdc_free_ent (context, server); if (client) _kdc_free_ent (context, client); if (password) { memset(password, 0, strlen(password)); free (password); } if (client_name) free (client_name); krb5_data_free(&buf); krb5_data_free(&serverNonce); free_Checksum(&res); free_DigestREP(&rep); free_DigestRepInner(&r); free_DigestReqInner(&ireq); return ret; } #endif /* DIGEST */ Index: projects/openssl111/crypto/heimdal/kdc/kx509.c =================================================================== --- projects/openssl111/crypto/heimdal/kdc/kx509.c (revision 339197) +++ projects/openssl111/crypto/heimdal/kdc/kx509.c (revision 339198) @@ -1,468 +1,478 @@ /* * Copyright (c) 2006 - 2007 Kungliga Tekniska Högskolan * (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 #include #include #ifdef KX509 /* * */ krb5_error_code _kdc_try_kx509_request(void *ptr, size_t len, struct Kx509Request *req, size_t *size) { if (len < 4) return -1; if (memcmp("\x00\x00\x02\x00", ptr, 4) != 0) return -1; return decode_Kx509Request(((unsigned char *)ptr) + 4, len - 4, req, size); } /* * */ static const unsigned char version_2_0[4] = {0 , 0, 2, 0}; static krb5_error_code verify_req_hash(krb5_context context, const Kx509Request *req, krb5_keyblock *key) { unsigned char digest[SHA_DIGEST_LENGTH]; - HMAC_CTX ctx; + HMAC_CTX *ctx; if (req->pk_hash.length != sizeof(digest)) { krb5_set_error_message(context, KRB5KDC_ERR_PREAUTH_FAILED, "pk-hash have wrong length: %lu", (unsigned long)req->pk_hash.length); return KRB5KDC_ERR_PREAUTH_FAILED; } - HMAC_CTX_init(&ctx); - HMAC_Init_ex(&ctx, + ctx = HMAC_CTX_new(); + if (ctx == NULL) { + krb5_set_error_message(context, ENOMEM, + "HMAC context malloc failed"); + return ENOMEM; + } + HMAC_Init_ex(ctx, key->keyvalue.data, key->keyvalue.length, EVP_sha1(), NULL); - if (sizeof(digest) != HMAC_size(&ctx)) + if (sizeof(digest) != HMAC_size(ctx)) krb5_abortx(context, "runtime error, hmac buffer wrong size in kx509"); - HMAC_Update(&ctx, version_2_0, sizeof(version_2_0)); - HMAC_Update(&ctx, req->pk_key.data, req->pk_key.length); - HMAC_Final(&ctx, digest, 0); - HMAC_CTX_cleanup(&ctx); + HMAC_Update(ctx, version_2_0, sizeof(version_2_0)); + HMAC_Update(ctx, req->pk_key.data, req->pk_key.length); + HMAC_Final(ctx, digest, 0); + HMAC_CTX_free(ctx); if (memcmp(req->pk_hash.data, digest, sizeof(digest)) != 0) { krb5_set_error_message(context, KRB5KDC_ERR_PREAUTH_FAILED, "pk-hash is not correct"); return KRB5KDC_ERR_PREAUTH_FAILED; } return 0; } static krb5_error_code calculate_reply_hash(krb5_context context, krb5_keyblock *key, Kx509Response *rep) { krb5_error_code ret; - HMAC_CTX ctx; + HMAC_CTX *ctx; - HMAC_CTX_init(&ctx); + ctx = HMAC_CTX_new(); + if (ctx == NULL) { + krb5_set_error_message(context, ENOMEM, + "HMAC context malloc failed"); + return ENOMEM; + } - HMAC_Init_ex(&ctx, key->keyvalue.data, key->keyvalue.length, + HMAC_Init_ex(ctx, key->keyvalue.data, key->keyvalue.length, EVP_sha1(), NULL); - ret = krb5_data_alloc(rep->hash, HMAC_size(&ctx)); + ret = krb5_data_alloc(rep->hash, HMAC_size(ctx)); if (ret) { - HMAC_CTX_cleanup(&ctx); + HMAC_CTX_free(ctx); krb5_set_error_message(context, ENOMEM, "malloc: out of memory"); return ENOMEM; } - HMAC_Update(&ctx, version_2_0, sizeof(version_2_0)); + HMAC_Update(ctx, version_2_0, sizeof(version_2_0)); if (rep->error_code) { int32_t t = *rep->error_code; do { unsigned char p = (t & 0xff); - HMAC_Update(&ctx, &p, 1); + HMAC_Update(ctx, &p, 1); t >>= 8; } while (t); } if (rep->certificate) - HMAC_Update(&ctx, rep->certificate->data, rep->certificate->length); + HMAC_Update(ctx, rep->certificate->data, rep->certificate->length); if (rep->e_text) - HMAC_Update(&ctx, (unsigned char *)*rep->e_text, strlen(*rep->e_text)); + HMAC_Update(ctx, (unsigned char *)*rep->e_text, strlen(*rep->e_text)); - HMAC_Final(&ctx, rep->hash->data, 0); - HMAC_CTX_cleanup(&ctx); + HMAC_Final(ctx, rep->hash->data, 0); + HMAC_CTX_free(ctx); return 0; } /* * Build a certifate for `principal´ that will expire at `endtime´. */ static krb5_error_code build_certificate(krb5_context context, krb5_kdc_configuration *config, const krb5_data *key, time_t endtime, krb5_principal principal, krb5_data *certificate) { hx509_ca_tbs tbs = NULL; hx509_env env = NULL; hx509_cert cert = NULL; hx509_cert signer = NULL; int ret; if (krb5_principal_get_comp_string(context, principal, 1) != NULL) { kdc_log(context, config, 0, "Principal is not a user"); return EINVAL; } ret = hx509_env_add(context->hx509ctx, &env, "principal-name", krb5_principal_get_comp_string(context, principal, 0)); if (ret) goto out; { hx509_certs certs; hx509_query *q; ret = hx509_certs_init(context->hx509ctx, config->kx509_ca, 0, NULL, &certs); if (ret) { kdc_log(context, config, 0, "Failed to load CA %s", config->kx509_ca); goto out; } ret = hx509_query_alloc(context->hx509ctx, &q); if (ret) { hx509_certs_free(&certs); goto out; } hx509_query_match_option(q, HX509_QUERY_OPTION_PRIVATE_KEY); hx509_query_match_option(q, HX509_QUERY_OPTION_KU_KEYCERTSIGN); ret = hx509_certs_find(context->hx509ctx, certs, q, &signer); hx509_query_free(context->hx509ctx, q); hx509_certs_free(&certs); if (ret) { kdc_log(context, config, 0, "Failed to find a CA in %s", config->kx509_ca); goto out; } } ret = hx509_ca_tbs_init(context->hx509ctx, &tbs); if (ret) goto out; { SubjectPublicKeyInfo spki; heim_any any; memset(&spki, 0, sizeof(spki)); spki.subjectPublicKey.data = key->data; spki.subjectPublicKey.length = key->length * 8; ret = der_copy_oid(&asn1_oid_id_pkcs1_rsaEncryption, &spki.algorithm.algorithm); any.data = "\x05\x00"; any.length = 2; spki.algorithm.parameters = &any; ret = hx509_ca_tbs_set_spki(context->hx509ctx, tbs, &spki); der_free_oid(&spki.algorithm.algorithm); if (ret) goto out; } { hx509_certs certs; hx509_cert template; ret = hx509_certs_init(context->hx509ctx, config->kx509_template, 0, NULL, &certs); if (ret) { kdc_log(context, config, 0, "Failed to load template %s", config->kx509_template); goto out; } ret = hx509_get_one_cert(context->hx509ctx, certs, &template); hx509_certs_free(&certs); if (ret) { kdc_log(context, config, 0, "Failed to find template in %s", config->kx509_template); goto out; } ret = hx509_ca_tbs_set_template(context->hx509ctx, tbs, HX509_CA_TEMPLATE_SUBJECT| HX509_CA_TEMPLATE_KU| HX509_CA_TEMPLATE_EKU, template); hx509_cert_free(template); if (ret) goto out; } hx509_ca_tbs_set_notAfter(context->hx509ctx, tbs, endtime); hx509_ca_tbs_subject_expand(context->hx509ctx, tbs, env); hx509_env_free(&env); ret = hx509_ca_sign(context->hx509ctx, tbs, signer, &cert); hx509_cert_free(signer); if (ret) goto out; hx509_ca_tbs_free(&tbs); ret = hx509_cert_binary(context->hx509ctx, cert, certificate); hx509_cert_free(cert); if (ret) goto out; return 0; out: if (env) hx509_env_free(&env); if (tbs) hx509_ca_tbs_free(&tbs); if (signer) hx509_cert_free(signer); krb5_set_error_message(context, ret, "cert creation failed"); return ret; } /* * */ krb5_error_code _kdc_do_kx509(krb5_context context, krb5_kdc_configuration *config, const struct Kx509Request *req, krb5_data *reply, const char *from, struct sockaddr *addr) { krb5_error_code ret; krb5_ticket *ticket = NULL; krb5_flags ap_req_options; krb5_auth_context ac = NULL; krb5_keytab id = NULL; krb5_principal sprincipal = NULL, cprincipal = NULL; char *cname = NULL; Kx509Response rep; size_t size; krb5_keyblock *key = NULL; krb5_data_zero(reply); memset(&rep, 0, sizeof(rep)); if(!config->enable_kx509) { kdc_log(context, config, 0, "Rejected kx509 request (disabled) from %s", from); return KRB5KDC_ERR_POLICY; } kdc_log(context, config, 0, "Kx509 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->authenticator, NULL, id, &ap_req_options, &ticket); if (ret) goto out; ret = krb5_ticket_get_client(context, ticket, &cprincipal); if (ret) goto out; ret = krb5_unparse_name(context, cprincipal, &cname); if (ret) goto out; /* verify server principal */ ret = krb5_sname_to_principal(context, NULL, "kca_service", KRB5_NT_UNKNOWN, &sprincipal); if (ret) goto out; { krb5_principal principal = NULL; ret = krb5_ticket_get_server(context, ticket, &principal); if (ret) goto out; ret = krb5_principal_compare(context, sprincipal, principal); krb5_free_principal(context, principal); if (ret != TRUE) { char *expected, *used; ret = krb5_unparse_name(context, sprincipal, &expected); if (ret) goto out; ret = krb5_unparse_name(context, principal, &used); if (ret) { krb5_xfree(expected); goto out; } ret = KRB5KDC_ERR_SERVER_NOMATCH; krb5_set_error_message(context, ret, "User %s used wrong Kx509 service " "principal, expected: %s, used %s", cname, expected, used); krb5_xfree(expected); krb5_xfree(used); goto out; } } ret = krb5_auth_con_getkey(context, ac, &key); if (ret == 0 && key == NULL) ret = KRB5KDC_ERR_NULL_KEY; if (ret) { krb5_set_error_message(context, ret, "Kx509 can't get session key"); goto out; } ret = verify_req_hash(context, req, key); if (ret) goto out; /* Verify that the key is encoded RSA key */ { RSAPublicKey key; size_t size; ret = decode_RSAPublicKey(req->pk_key.data, req->pk_key.length, &key, &size); if (ret) goto out; free_RSAPublicKey(&key); if (size != req->pk_key.length) { ret = ASN1_EXTRA_DATA; goto out; } } ALLOC(rep.certificate); if (rep.certificate == NULL) goto out; krb5_data_zero(rep.certificate); ALLOC(rep.hash); if (rep.hash == NULL) goto out; krb5_data_zero(rep.hash); ret = build_certificate(context, config, &req->pk_key, krb5_ticket_get_endtime(context, ticket), cprincipal, rep.certificate); if (ret) goto out; ret = calculate_reply_hash(context, key, &rep); if (ret) goto out; /* * Encode reply, [ version | Kx509Response ] */ { krb5_data data; ASN1_MALLOC_ENCODE(Kx509Response, data.data, data.length, &rep, &size, ret); if (ret) { krb5_set_error_message(context, ret, "Failed to encode kx509 reply"); goto out; } if (size != data.length) krb5_abortx(context, "ASN1 internal error"); ret = krb5_data_alloc(reply, data.length + sizeof(version_2_0)); if (ret) { free(data.data); goto out; } memcpy(reply->data, version_2_0, sizeof(version_2_0)); memcpy(((unsigned char *)reply->data) + sizeof(version_2_0), data.data, data.length); free(data.data); } kdc_log(context, config, 0, "Successful Kx509 request for %s", cname); out: if (ac) krb5_auth_con_free(context, ac); if (ret) krb5_warn(context, ret, "Kx509 request from %s failed", from); if (ticket) krb5_free_ticket(context, ticket); if (id) krb5_kt_close(context, id); if (sprincipal) krb5_free_principal(context, sprincipal); if (cprincipal) krb5_free_principal(context, cprincipal); if (key) krb5_free_keyblock (context, key); if (cname) free(cname); free_Kx509Response(&rep); return 0; } #endif /* KX509 */ Index: projects/openssl111/crypto/heimdal/kdc/pkinit.c =================================================================== --- projects/openssl111/crypto/heimdal/kdc/pkinit.c (revision 339197) +++ projects/openssl111/crypto/heimdal/kdc/pkinit.c (revision 339198) @@ -1,2048 +1,2057 @@ /* * Copyright (c) 2003 - 2008 Kungliga Tekniska Högskolan * (Royal Institute of Technology, Stockholm, Sweden). * All rights reserved. * * Portions Copyright (c) 2009 Apple Inc. 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" #ifdef PKINIT #include #include #include #include #include #include "crypto-headers.h" struct pk_client_params { enum krb5_pk_type type; enum { USE_RSA, USE_DH, USE_ECDH } keyex; union { struct { BIGNUM *public_key; DH *key; } dh; #ifdef HAVE_OPENSSL struct { EC_KEY *public_key; EC_KEY *key; } ecdh; #endif } u; hx509_cert cert; unsigned nonce; EncryptionKey reply_key; char *dh_group_name; hx509_peer_info peer; hx509_certs client_anchors; hx509_verify_ctx verify_ctx; }; struct pk_principal_mapping { unsigned int len; struct pk_allowed_princ { krb5_principal principal; char *subject; } *val; }; static struct krb5_pk_identity *kdc_identity; static struct pk_principal_mapping principal_mappings; static struct krb5_dh_moduli **moduli; static struct { krb5_data data; time_t expire; time_t next_update; } ocsp; /* * */ static krb5_error_code pk_check_pkauthenticator_win2k(krb5_context context, PKAuthenticator_Win2k *a, const KDC_REQ *req) { krb5_timestamp now; krb5_timeofday (context, &now); /* XXX cusec */ if (a->ctime == 0 || abs(a->ctime - now) > context->max_skew) { krb5_clear_error_message(context); return KRB5KRB_AP_ERR_SKEW; } return 0; } static krb5_error_code pk_check_pkauthenticator(krb5_context context, PKAuthenticator *a, const KDC_REQ *req) { u_char *buf = NULL; size_t buf_size; krb5_error_code ret; size_t len = 0; krb5_timestamp now; Checksum checksum; krb5_timeofday (context, &now); /* XXX cusec */ if (a->ctime == 0 || abs(a->ctime - now) > context->max_skew) { krb5_clear_error_message(context); return KRB5KRB_AP_ERR_SKEW; } ASN1_MALLOC_ENCODE(KDC_REQ_BODY, buf, buf_size, &req->req_body, &len, ret); if (ret) { krb5_clear_error_message(context); return ret; } if (buf_size != len) krb5_abortx(context, "Internal error in ASN.1 encoder"); ret = krb5_create_checksum(context, NULL, 0, CKSUMTYPE_SHA1, buf, len, &checksum); free(buf); if (ret) { krb5_clear_error_message(context); return ret; } if (a->paChecksum == NULL) { krb5_clear_error_message(context); ret = KRB5_KDC_ERR_PA_CHECKSUM_MUST_BE_INCLUDED; goto out; } if (der_heim_octet_string_cmp(a->paChecksum, &checksum.checksum) != 0) { krb5_clear_error_message(context); ret = KRB5KRB_ERR_GENERIC; } out: free_Checksum(&checksum); return ret; } void _kdc_pk_free_client_param(krb5_context context, pk_client_params *cp) { if (cp == NULL) return; if (cp->cert) hx509_cert_free(cp->cert); if (cp->verify_ctx) hx509_verify_destroy_ctx(cp->verify_ctx); if (cp->keyex == USE_DH) { if (cp->u.dh.key) DH_free(cp->u.dh.key); if (cp->u.dh.public_key) BN_free(cp->u.dh.public_key); } #ifdef HAVE_OPENSSL if (cp->keyex == USE_ECDH) { if (cp->u.ecdh.key) EC_KEY_free(cp->u.ecdh.key); if (cp->u.ecdh.public_key) EC_KEY_free(cp->u.ecdh.public_key); } #endif krb5_free_keyblock_contents(context, &cp->reply_key); if (cp->dh_group_name) free(cp->dh_group_name); if (cp->peer) hx509_peer_info_free(cp->peer); if (cp->client_anchors) hx509_certs_free(&cp->client_anchors); memset(cp, 0, sizeof(*cp)); free(cp); } static krb5_error_code generate_dh_keyblock(krb5_context context, pk_client_params *client_params, krb5_enctype enctype) { unsigned char *dh_gen_key = NULL; krb5_keyblock key; krb5_error_code ret; size_t dh_gen_keylen, size; memset(&key, 0, sizeof(key)); if (client_params->keyex == USE_DH) { if (client_params->u.dh.public_key == NULL) { ret = KRB5KRB_ERR_GENERIC; krb5_set_error_message(context, ret, "public_key"); goto out; } if (!DH_generate_key(client_params->u.dh.key)) { ret = KRB5KRB_ERR_GENERIC; krb5_set_error_message(context, ret, "Can't generate Diffie-Hellman keys"); goto out; } size = DH_size(client_params->u.dh.key); dh_gen_key = malloc(size); if (dh_gen_key == NULL) { ret = ENOMEM; krb5_set_error_message(context, ret, "malloc: out of memory"); goto out; } dh_gen_keylen = DH_compute_key(dh_gen_key,client_params->u.dh.public_key, client_params->u.dh.key); if (dh_gen_keylen == (size_t)-1) { ret = KRB5KRB_ERR_GENERIC; krb5_set_error_message(context, ret, "Can't compute Diffie-Hellman key"); goto out; } if (dh_gen_keylen < size) { size -= dh_gen_keylen; memmove(dh_gen_key + size, dh_gen_key, dh_gen_keylen); memset(dh_gen_key, 0, size); } ret = 0; #ifdef HAVE_OPENSSL } else if (client_params->keyex == USE_ECDH) { if (client_params->u.ecdh.public_key == NULL) { ret = KRB5KRB_ERR_GENERIC; krb5_set_error_message(context, ret, "public_key"); goto out; } client_params->u.ecdh.key = EC_KEY_new(); if (client_params->u.ecdh.key == NULL) { ret = ENOMEM; goto out; } EC_KEY_set_group(client_params->u.ecdh.key, EC_KEY_get0_group(client_params->u.ecdh.public_key)); if (EC_KEY_generate_key(client_params->u.ecdh.key) != 1) { ret = ENOMEM; goto out; } size = (EC_GROUP_get_degree(EC_KEY_get0_group(client_params->u.ecdh.key)) + 7) / 8; dh_gen_key = malloc(size); if (dh_gen_key == NULL) { ret = ENOMEM; krb5_set_error_message(context, ret, N_("malloc: out of memory", "")); goto out; } dh_gen_keylen = ECDH_compute_key(dh_gen_key, size, EC_KEY_get0_public_key(client_params->u.ecdh.public_key), client_params->u.ecdh.key, NULL); #endif /* HAVE_OPENSSL */ } else { ret = KRB5KRB_ERR_GENERIC; krb5_set_error_message(context, ret, "Diffie-Hellman not selected keys"); goto out; } ret = _krb5_pk_octetstring2key(context, enctype, dh_gen_key, dh_gen_keylen, NULL, NULL, &client_params->reply_key); out: if (dh_gen_key) free(dh_gen_key); if (key.keyvalue.data) krb5_free_keyblock_contents(context, &key); return ret; } static BIGNUM * integer_to_BN(krb5_context context, const char *field, heim_integer *f) { BIGNUM *bn; bn = BN_bin2bn((const unsigned char *)f->data, f->length, NULL); if (bn == NULL) { krb5_set_error_message(context, KRB5_BADMSGTYPE, "PKINIT: parsing BN failed %s", field); return NULL; } BN_set_negative(bn, f->negative); return bn; } static krb5_error_code get_dh_param(krb5_context context, krb5_kdc_configuration *config, SubjectPublicKeyInfo *dh_key_info, pk_client_params *client_params) { DomainParameters dhparam; DH *dh = NULL; + BIGNUM *p, *q, *g; krb5_error_code ret; memset(&dhparam, 0, sizeof(dhparam)); if ((dh_key_info->subjectPublicKey.length % 8) != 0) { ret = KRB5_BADMSGTYPE; krb5_set_error_message(context, ret, "PKINIT: subjectPublicKey not aligned " "to 8 bit boundary"); goto out; } if (dh_key_info->algorithm.parameters == NULL) { krb5_set_error_message(context, KRB5_BADMSGTYPE, "PKINIT missing algorithm parameter " "in clientPublicValue"); return KRB5_BADMSGTYPE; } ret = decode_DomainParameters(dh_key_info->algorithm.parameters->data, dh_key_info->algorithm.parameters->length, &dhparam, NULL); if (ret) { krb5_set_error_message(context, ret, "Can't decode algorithm " "parameters in clientPublicValue"); goto out; } ret = _krb5_dh_group_ok(context, config->pkinit_dh_min_bits, &dhparam.p, &dhparam.g, &dhparam.q, moduli, &client_params->dh_group_name); if (ret) { /* XXX send back proposal of better group */ goto out; } dh = DH_new(); if (dh == NULL) { ret = ENOMEM; krb5_set_error_message(context, ret, "Cannot create DH structure"); goto out; } ret = KRB5_BADMSGTYPE; - dh->p = integer_to_BN(context, "DH prime", &dhparam.p); - if (dh->p == NULL) + p = integer_to_BN(context, "DH prime", &dhparam.p); + g = integer_to_BN(context, "DH base", &dhparam.g); + q = integer_to_BN(context, "DH p-1 factor", &dhparam.q); + if (p == NULL || g == NULL || q == NULL) { + BN_free(p); + BN_free(g); + BN_free(q); goto out; - dh->g = integer_to_BN(context, "DH base", &dhparam.g); - if (dh->g == NULL) + } + if (DH_set0_pqg(dh, p, g, q) != 1) { + BN_free(p); + BN_free(g); + BN_free(q); goto out; - dh->q = integer_to_BN(context, "DH p-1 factor", &dhparam.q); - if (dh->g == NULL) - goto out; + } { heim_integer glue; size_t size; ret = decode_DHPublicKey(dh_key_info->subjectPublicKey.data, dh_key_info->subjectPublicKey.length / 8, &glue, &size); if (ret) { krb5_clear_error_message(context); return ret; } client_params->u.dh.public_key = integer_to_BN(context, "subjectPublicKey", &glue); der_free_heim_integer(&glue); if (client_params->u.dh.public_key == NULL) { ret = KRB5_BADMSGTYPE; goto out; } } client_params->u.dh.key = dh; dh = NULL; ret = 0; out: if (dh) DH_free(dh); free_DomainParameters(&dhparam); return ret; } #ifdef HAVE_OPENSSL static krb5_error_code get_ecdh_param(krb5_context context, krb5_kdc_configuration *config, SubjectPublicKeyInfo *dh_key_info, pk_client_params *client_params) { ECParameters ecp; EC_KEY *public = NULL; krb5_error_code ret; const unsigned char *p; size_t len; int nid; if (dh_key_info->algorithm.parameters == NULL) { krb5_set_error_message(context, KRB5_BADMSGTYPE, "PKINIT missing algorithm parameter " "in clientPublicValue"); return KRB5_BADMSGTYPE; } memset(&ecp, 0, sizeof(ecp)); ret = decode_ECParameters(dh_key_info->algorithm.parameters->data, dh_key_info->algorithm.parameters->length, &ecp, &len); if (ret) goto out; if (ecp.element != choice_ECParameters_namedCurve) { ret = KRB5_BADMSGTYPE; goto out; } if (der_heim_oid_cmp(&ecp.u.namedCurve, &asn1_oid_id_ec_group_secp256r1) == 0) nid = NID_X9_62_prime256v1; else { ret = KRB5_BADMSGTYPE; goto out; } /* XXX verify group is ok */ public = EC_KEY_new_by_curve_name(nid); p = dh_key_info->subjectPublicKey.data; len = dh_key_info->subjectPublicKey.length / 8; if (o2i_ECPublicKey(&public, &p, len) == NULL) { ret = KRB5_BADMSGTYPE; krb5_set_error_message(context, ret, "PKINIT failed to decode ECDH key"); goto out; } client_params->u.ecdh.public_key = public; public = NULL; out: if (public) EC_KEY_free(public); free_ECParameters(&ecp); return ret; } #endif /* HAVE_OPENSSL */ krb5_error_code _kdc_pk_rd_padata(krb5_context context, krb5_kdc_configuration *config, const KDC_REQ *req, const PA_DATA *pa, hdb_entry_ex *client, pk_client_params **ret_params) { pk_client_params *cp; krb5_error_code ret; heim_oid eContentType = { 0, NULL }, contentInfoOid = { 0, NULL }; krb5_data eContent = { 0, NULL }; krb5_data signed_content = { 0, NULL }; const char *type = "unknown type"; hx509_certs trust_anchors; int have_data = 0; const HDB_Ext_PKINIT_cert *pc; *ret_params = NULL; if (!config->enable_pkinit) { kdc_log(context, config, 0, "PK-INIT request but PK-INIT not enabled"); krb5_clear_error_message(context); return 0; } cp = calloc(1, sizeof(*cp)); if (cp == NULL) { krb5_clear_error_message(context); ret = ENOMEM; goto out; } ret = hx509_certs_init(context->hx509ctx, "MEMORY:trust-anchors", 0, NULL, &trust_anchors); if (ret) { krb5_set_error_message(context, ret, "failed to create trust anchors"); goto out; } ret = hx509_certs_merge(context->hx509ctx, trust_anchors, kdc_identity->anchors); if (ret) { hx509_certs_free(&trust_anchors); krb5_set_error_message(context, ret, "failed to create verify context"); goto out; } /* Add any registered certificates for this client as trust anchors */ ret = hdb_entry_get_pkinit_cert(&client->entry, &pc); if (ret == 0 && pc != NULL) { hx509_cert cert; unsigned int i; for (i = 0; i < pc->len; i++) { ret = hx509_cert_init_data(context->hx509ctx, pc->val[i].cert.data, pc->val[i].cert.length, &cert); if (ret) continue; hx509_certs_add(context->hx509ctx, trust_anchors, cert); hx509_cert_free(cert); } } ret = hx509_verify_init_ctx(context->hx509ctx, &cp->verify_ctx); if (ret) { hx509_certs_free(&trust_anchors); krb5_set_error_message(context, ret, "failed to create verify context"); goto out; } hx509_verify_set_time(cp->verify_ctx, kdc_time); hx509_verify_attach_anchors(cp->verify_ctx, trust_anchors); hx509_certs_free(&trust_anchors); if (config->pkinit_allow_proxy_certs) hx509_verify_set_proxy_certificate(cp->verify_ctx, 1); if (pa->padata_type == KRB5_PADATA_PK_AS_REQ_WIN) { PA_PK_AS_REQ_Win2k r; type = "PK-INIT-Win2k"; if (req->req_body.kdc_options.request_anonymous) { ret = KRB5_KDC_ERR_PUBLIC_KEY_ENCRYPTION_NOT_SUPPORTED; krb5_set_error_message(context, ret, "Anon not supported in RSA mode"); goto out; } ret = decode_PA_PK_AS_REQ_Win2k(pa->padata_value.data, pa->padata_value.length, &r, NULL); if (ret) { krb5_set_error_message(context, ret, "Can't decode " "PK-AS-REQ-Win2k: %d", ret); goto out; } ret = hx509_cms_unwrap_ContentInfo(&r.signed_auth_pack, &contentInfoOid, &signed_content, &have_data); free_PA_PK_AS_REQ_Win2k(&r); if (ret) { krb5_set_error_message(context, ret, "Can't unwrap ContentInfo(win): %d", ret); goto out; } } else if (pa->padata_type == KRB5_PADATA_PK_AS_REQ) { PA_PK_AS_REQ r; type = "PK-INIT-IETF"; ret = decode_PA_PK_AS_REQ(pa->padata_value.data, pa->padata_value.length, &r, NULL); if (ret) { krb5_set_error_message(context, ret, "Can't decode PK-AS-REQ: %d", ret); goto out; } /* XXX look at r.kdcPkId */ if (r.trustedCertifiers) { ExternalPrincipalIdentifiers *edi = r.trustedCertifiers; unsigned int i, maxedi; ret = hx509_certs_init(context->hx509ctx, "MEMORY:client-anchors", 0, NULL, &cp->client_anchors); if (ret) { krb5_set_error_message(context, ret, "Can't allocate client anchors: %d", ret); goto out; } /* * If the client sent more then 10 EDI, don't bother * looking more then 10 of performance reasons. */ maxedi = edi->len; if (maxedi > 10) maxedi = 10; for (i = 0; i < maxedi; i++) { IssuerAndSerialNumber iasn; hx509_query *q; hx509_cert cert; size_t size; if (edi->val[i].issuerAndSerialNumber == NULL) continue; ret = hx509_query_alloc(context->hx509ctx, &q); if (ret) { krb5_set_error_message(context, ret, "Failed to allocate hx509_query"); goto out; } ret = decode_IssuerAndSerialNumber(edi->val[i].issuerAndSerialNumber->data, edi->val[i].issuerAndSerialNumber->length, &iasn, &size); if (ret) { hx509_query_free(context->hx509ctx, q); continue; } ret = hx509_query_match_issuer_serial(q, &iasn.issuer, &iasn.serialNumber); free_IssuerAndSerialNumber(&iasn); if (ret) { hx509_query_free(context->hx509ctx, q); continue; } ret = hx509_certs_find(context->hx509ctx, kdc_identity->certs, q, &cert); hx509_query_free(context->hx509ctx, q); if (ret) continue; hx509_certs_add(context->hx509ctx, cp->client_anchors, cert); hx509_cert_free(cert); } } ret = hx509_cms_unwrap_ContentInfo(&r.signedAuthPack, &contentInfoOid, &signed_content, &have_data); free_PA_PK_AS_REQ(&r); if (ret) { krb5_set_error_message(context, ret, "Can't unwrap ContentInfo: %d", ret); goto out; } } else { krb5_clear_error_message(context); ret = KRB5KDC_ERR_PADATA_TYPE_NOSUPP; goto out; } ret = der_heim_oid_cmp(&contentInfoOid, &asn1_oid_id_pkcs7_signedData); if (ret != 0) { ret = KRB5KRB_ERR_GENERIC; krb5_set_error_message(context, ret, "PK-AS-REQ-Win2k invalid content type oid"); goto out; } if (!have_data) { ret = KRB5KRB_ERR_GENERIC; krb5_set_error_message(context, ret, "PK-AS-REQ-Win2k no signed auth pack"); goto out; } { hx509_certs signer_certs; int flags = HX509_CMS_VS_ALLOW_DATA_OID_MISMATCH; /* BTMM */ if (req->req_body.kdc_options.request_anonymous) flags |= HX509_CMS_VS_ALLOW_ZERO_SIGNER; ret = hx509_cms_verify_signed(context->hx509ctx, cp->verify_ctx, flags, signed_content.data, signed_content.length, NULL, kdc_identity->certpool, &eContentType, &eContent, &signer_certs); if (ret) { char *s = hx509_get_error_string(context->hx509ctx, ret); krb5_warnx(context, "PKINIT: failed to verify signature: %s: %d", s, ret); free(s); goto out; } if (signer_certs) { ret = hx509_get_one_cert(context->hx509ctx, signer_certs, &cp->cert); hx509_certs_free(&signer_certs); } if (ret) goto out; } /* Signature is correct, now verify the signed message */ if (der_heim_oid_cmp(&eContentType, &asn1_oid_id_pkcs7_data) != 0 && der_heim_oid_cmp(&eContentType, &asn1_oid_id_pkauthdata) != 0) { ret = KRB5_BADMSGTYPE; krb5_set_error_message(context, ret, "got wrong oid for pkauthdata"); goto out; } if (pa->padata_type == KRB5_PADATA_PK_AS_REQ_WIN) { AuthPack_Win2k ap; ret = decode_AuthPack_Win2k(eContent.data, eContent.length, &ap, NULL); if (ret) { krb5_set_error_message(context, ret, "Can't decode AuthPack: %d", ret); goto out; } ret = pk_check_pkauthenticator_win2k(context, &ap.pkAuthenticator, req); if (ret) { free_AuthPack_Win2k(&ap); goto out; } cp->type = PKINIT_WIN2K; cp->nonce = ap.pkAuthenticator.nonce; if (ap.clientPublicValue) { ret = KRB5KRB_ERR_GENERIC; krb5_set_error_message(context, ret, "DH not supported for windows"); goto out; } free_AuthPack_Win2k(&ap); } else if (pa->padata_type == KRB5_PADATA_PK_AS_REQ) { AuthPack ap; ret = decode_AuthPack(eContent.data, eContent.length, &ap, NULL); if (ret) { krb5_set_error_message(context, ret, "Can't decode AuthPack: %d", ret); free_AuthPack(&ap); goto out; } if (req->req_body.kdc_options.request_anonymous && ap.clientPublicValue == NULL) { free_AuthPack(&ap); ret = KRB5_KDC_ERR_PUBLIC_KEY_ENCRYPTION_NOT_SUPPORTED; krb5_set_error_message(context, ret, "Anon not supported in RSA mode"); goto out; } ret = pk_check_pkauthenticator(context, &ap.pkAuthenticator, req); if (ret) { free_AuthPack(&ap); goto out; } cp->type = PKINIT_27; cp->nonce = ap.pkAuthenticator.nonce; if (ap.clientPublicValue) { if (der_heim_oid_cmp(&ap.clientPublicValue->algorithm.algorithm, &asn1_oid_id_dhpublicnumber) == 0) { cp->keyex = USE_DH; ret = get_dh_param(context, config, ap.clientPublicValue, cp); #ifdef HAVE_OPENSSL } else if (der_heim_oid_cmp(&ap.clientPublicValue->algorithm.algorithm, &asn1_oid_id_ecPublicKey) == 0) { cp->keyex = USE_ECDH; ret = get_ecdh_param(context, config, ap.clientPublicValue, cp); #endif /* HAVE_OPENSSL */ } else { ret = KRB5_BADMSGTYPE; krb5_set_error_message(context, ret, "PKINIT unknown DH mechanism"); } if (ret) { free_AuthPack(&ap); goto out; } } else cp->keyex = USE_RSA; ret = hx509_peer_info_alloc(context->hx509ctx, &cp->peer); if (ret) { free_AuthPack(&ap); goto out; } if (ap.supportedCMSTypes) { ret = hx509_peer_info_set_cms_algs(context->hx509ctx, cp->peer, ap.supportedCMSTypes->val, ap.supportedCMSTypes->len); if (ret) { free_AuthPack(&ap); goto out; } } else { /* assume old client */ hx509_peer_info_add_cms_alg(context->hx509ctx, cp->peer, hx509_crypto_des_rsdi_ede3_cbc()); hx509_peer_info_add_cms_alg(context->hx509ctx, cp->peer, hx509_signature_rsa_with_sha1()); hx509_peer_info_add_cms_alg(context->hx509ctx, cp->peer, hx509_signature_sha1()); } free_AuthPack(&ap); } else krb5_abortx(context, "internal pkinit error"); kdc_log(context, config, 0, "PK-INIT request of type %s", type); out: if (ret) krb5_warn(context, ret, "PKINIT"); if (signed_content.data) free(signed_content.data); krb5_data_free(&eContent); der_free_oid(&eContentType); der_free_oid(&contentInfoOid); if (ret) { _kdc_pk_free_client_param(context, cp); } else *ret_params = cp; return ret; } /* * */ static krb5_error_code -BN_to_integer(krb5_context context, BIGNUM *bn, heim_integer *integer) +BN_to_integer(krb5_context context, const BIGNUM *bn, heim_integer *integer) { integer->length = BN_num_bytes(bn); integer->data = malloc(integer->length); if (integer->data == NULL) { krb5_clear_error_message(context); return ENOMEM; } BN_bn2bin(bn, integer->data); integer->negative = BN_is_negative(bn); return 0; } static krb5_error_code pk_mk_pa_reply_enckey(krb5_context context, krb5_kdc_configuration *config, pk_client_params *cp, const KDC_REQ *req, const krb5_data *req_buffer, krb5_keyblock *reply_key, ContentInfo *content_info, hx509_cert *kdc_cert) { const heim_oid *envelopedAlg = NULL, *sdAlg = NULL, *evAlg = NULL; krb5_error_code ret; krb5_data buf, signed_data; size_t size = 0; int do_win2k = 0; krb5_data_zero(&buf); krb5_data_zero(&signed_data); *kdc_cert = NULL; /* * If the message client is a win2k-type but it send pa data * 09-binding it expects a IETF (checksum) reply so there can be * no replay attacks. */ switch (cp->type) { case PKINIT_WIN2K: { int i = 0; if (_kdc_find_padata(req, &i, KRB5_PADATA_PK_AS_09_BINDING) == NULL && config->pkinit_require_binding == 0) { do_win2k = 1; } sdAlg = &asn1_oid_id_pkcs7_data; evAlg = &asn1_oid_id_pkcs7_data; envelopedAlg = &asn1_oid_id_rsadsi_des_ede3_cbc; break; } case PKINIT_27: sdAlg = &asn1_oid_id_pkrkeydata; evAlg = &asn1_oid_id_pkcs7_signedData; break; default: krb5_abortx(context, "internal pkinit error"); } if (do_win2k) { ReplyKeyPack_Win2k kp; memset(&kp, 0, sizeof(kp)); ret = copy_EncryptionKey(reply_key, &kp.replyKey); if (ret) { krb5_clear_error_message(context); goto out; } kp.nonce = cp->nonce; ASN1_MALLOC_ENCODE(ReplyKeyPack_Win2k, buf.data, buf.length, &kp, &size,ret); free_ReplyKeyPack_Win2k(&kp); } else { krb5_crypto ascrypto; ReplyKeyPack kp; memset(&kp, 0, sizeof(kp)); ret = copy_EncryptionKey(reply_key, &kp.replyKey); if (ret) { krb5_clear_error_message(context); goto out; } ret = krb5_crypto_init(context, reply_key, 0, &ascrypto); if (ret) { krb5_clear_error_message(context); goto out; } ret = krb5_create_checksum(context, ascrypto, 6, 0, req_buffer->data, req_buffer->length, &kp.asChecksum); if (ret) { krb5_clear_error_message(context); goto out; } ret = krb5_crypto_destroy(context, ascrypto); if (ret) { krb5_clear_error_message(context); goto out; } ASN1_MALLOC_ENCODE(ReplyKeyPack, buf.data, buf.length, &kp, &size,ret); free_ReplyKeyPack(&kp); } if (ret) { krb5_set_error_message(context, ret, "ASN.1 encoding of ReplyKeyPack " "failed (%d)", ret); goto out; } if (buf.length != size) krb5_abortx(context, "Internal ASN.1 encoder error"); { hx509_query *q; hx509_cert cert; ret = hx509_query_alloc(context->hx509ctx, &q); if (ret) goto out; hx509_query_match_option(q, HX509_QUERY_OPTION_PRIVATE_KEY); if (config->pkinit_kdc_friendly_name) hx509_query_match_friendly_name(q, config->pkinit_kdc_friendly_name); ret = hx509_certs_find(context->hx509ctx, kdc_identity->certs, q, &cert); hx509_query_free(context->hx509ctx, q); if (ret) goto out; ret = hx509_cms_create_signed_1(context->hx509ctx, 0, sdAlg, buf.data, buf.length, NULL, cert, cp->peer, cp->client_anchors, kdc_identity->certpool, &signed_data); *kdc_cert = cert; } krb5_data_free(&buf); if (ret) goto out; if (cp->type == PKINIT_WIN2K) { ret = hx509_cms_wrap_ContentInfo(&asn1_oid_id_pkcs7_signedData, &signed_data, &buf); if (ret) goto out; krb5_data_free(&signed_data); signed_data = buf; } ret = hx509_cms_envelope_1(context->hx509ctx, HX509_CMS_EV_NO_KU_CHECK, cp->cert, signed_data.data, signed_data.length, envelopedAlg, evAlg, &buf); if (ret) goto out; ret = _krb5_pk_mk_ContentInfo(context, &buf, &asn1_oid_id_pkcs7_envelopedData, content_info); out: if (ret && *kdc_cert) { hx509_cert_free(*kdc_cert); *kdc_cert = NULL; } krb5_data_free(&buf); krb5_data_free(&signed_data); return ret; } /* * */ static krb5_error_code pk_mk_pa_reply_dh(krb5_context context, krb5_kdc_configuration *config, pk_client_params *cp, ContentInfo *content_info, hx509_cert *kdc_cert) { KDCDHKeyInfo dh_info; krb5_data signed_data, buf; ContentInfo contentinfo; krb5_error_code ret; hx509_cert cert; hx509_query *q; size_t size = 0; memset(&contentinfo, 0, sizeof(contentinfo)); memset(&dh_info, 0, sizeof(dh_info)); krb5_data_zero(&signed_data); krb5_data_zero(&buf); *kdc_cert = NULL; if (cp->keyex == USE_DH) { DH *kdc_dh = cp->u.dh.key; + const BIGNUM *pub_key; heim_integer i; - ret = BN_to_integer(context, kdc_dh->pub_key, &i); + DH_get0_key(kdc_dh, &pub_key, NULL); + ret = BN_to_integer(context, pub_key, &i); if (ret) return ret; ASN1_MALLOC_ENCODE(DHPublicKey, buf.data, buf.length, &i, &size, ret); der_free_heim_integer(&i); if (ret) { krb5_set_error_message(context, ret, "ASN.1 encoding of " "DHPublicKey failed (%d)", ret); return ret; } if (buf.length != size) krb5_abortx(context, "Internal ASN.1 encoder error"); dh_info.subjectPublicKey.length = buf.length * 8; dh_info.subjectPublicKey.data = buf.data; krb5_data_zero(&buf); #ifdef HAVE_OPENSSL } else if (cp->keyex == USE_ECDH) { unsigned char *p; int len; len = i2o_ECPublicKey(cp->u.ecdh.key, NULL); if (len <= 0) abort(); p = malloc(len); if (p == NULL) abort(); dh_info.subjectPublicKey.length = len * 8; dh_info.subjectPublicKey.data = p; len = i2o_ECPublicKey(cp->u.ecdh.key, &p); if (len <= 0) abort(); #endif } else krb5_abortx(context, "no keyex selected ?"); dh_info.nonce = cp->nonce; ASN1_MALLOC_ENCODE(KDCDHKeyInfo, buf.data, buf.length, &dh_info, &size, ret); if (ret) { krb5_set_error_message(context, ret, "ASN.1 encoding of " "KdcDHKeyInfo failed (%d)", ret); goto out; } if (buf.length != size) krb5_abortx(context, "Internal ASN.1 encoder error"); /* * Create the SignedData structure and sign the KdcDHKeyInfo * filled in above */ ret = hx509_query_alloc(context->hx509ctx, &q); if (ret) goto out; hx509_query_match_option(q, HX509_QUERY_OPTION_PRIVATE_KEY); if (config->pkinit_kdc_friendly_name) hx509_query_match_friendly_name(q, config->pkinit_kdc_friendly_name); ret = hx509_certs_find(context->hx509ctx, kdc_identity->certs, q, &cert); hx509_query_free(context->hx509ctx, q); if (ret) goto out; ret = hx509_cms_create_signed_1(context->hx509ctx, 0, &asn1_oid_id_pkdhkeydata, buf.data, buf.length, NULL, cert, cp->peer, cp->client_anchors, kdc_identity->certpool, &signed_data); if (ret) { kdc_log(context, config, 0, "Failed signing the DH* reply: %d", ret); goto out; } *kdc_cert = cert; ret = _krb5_pk_mk_ContentInfo(context, &signed_data, &asn1_oid_id_pkcs7_signedData, content_info); if (ret) goto out; out: if (ret && *kdc_cert) { hx509_cert_free(*kdc_cert); *kdc_cert = NULL; } krb5_data_free(&buf); krb5_data_free(&signed_data); free_KDCDHKeyInfo(&dh_info); return ret; } /* * */ krb5_error_code _kdc_pk_mk_pa_reply(krb5_context context, krb5_kdc_configuration *config, pk_client_params *cp, const hdb_entry_ex *client, krb5_enctype sessionetype, const KDC_REQ *req, const krb5_data *req_buffer, krb5_keyblock **reply_key, krb5_keyblock *sessionkey, METHOD_DATA *md) { krb5_error_code ret; void *buf = NULL; size_t len = 0, size = 0; krb5_enctype enctype; int pa_type; hx509_cert kdc_cert = NULL; size_t i; if (!config->enable_pkinit) { krb5_clear_error_message(context); return 0; } if (req->req_body.etype.len > 0) { for (i = 0; i < req->req_body.etype.len; i++) if (krb5_enctype_valid(context, req->req_body.etype.val[i]) == 0) break; if (req->req_body.etype.len <= i) { ret = KRB5KRB_ERR_GENERIC; krb5_set_error_message(context, ret, "No valid enctype available from client"); goto out; } enctype = req->req_body.etype.val[i]; } else enctype = ETYPE_DES3_CBC_SHA1; if (cp->type == PKINIT_27) { PA_PK_AS_REP rep; const char *type, *other = ""; memset(&rep, 0, sizeof(rep)); pa_type = KRB5_PADATA_PK_AS_REP; if (cp->keyex == USE_RSA) { ContentInfo info; type = "enckey"; rep.element = choice_PA_PK_AS_REP_encKeyPack; ret = krb5_generate_random_keyblock(context, enctype, &cp->reply_key); if (ret) { free_PA_PK_AS_REP(&rep); goto out; } ret = pk_mk_pa_reply_enckey(context, config, cp, req, req_buffer, &cp->reply_key, &info, &kdc_cert); if (ret) { free_PA_PK_AS_REP(&rep); goto out; } ASN1_MALLOC_ENCODE(ContentInfo, rep.u.encKeyPack.data, rep.u.encKeyPack.length, &info, &size, ret); free_ContentInfo(&info); if (ret) { krb5_set_error_message(context, ret, "encoding of Key ContentInfo " "failed %d", ret); free_PA_PK_AS_REP(&rep); goto out; } if (rep.u.encKeyPack.length != size) krb5_abortx(context, "Internal ASN.1 encoder error"); ret = krb5_generate_random_keyblock(context, sessionetype, sessionkey); if (ret) { free_PA_PK_AS_REP(&rep); goto out; } } else { ContentInfo info; switch (cp->keyex) { case USE_DH: type = "dh"; break; #ifdef HAVE_OPENSSL case USE_ECDH: type = "ecdh"; break; #endif default: krb5_abortx(context, "unknown keyex"); break; } if (cp->dh_group_name) other = cp->dh_group_name; rep.element = choice_PA_PK_AS_REP_dhInfo; ret = generate_dh_keyblock(context, cp, enctype); if (ret) return ret; ret = pk_mk_pa_reply_dh(context, config, cp, &info, &kdc_cert); if (ret) { free_PA_PK_AS_REP(&rep); krb5_set_error_message(context, ret, "create pa-reply-dh " "failed %d", ret); goto out; } ASN1_MALLOC_ENCODE(ContentInfo, rep.u.dhInfo.dhSignedData.data, rep.u.dhInfo.dhSignedData.length, &info, &size, ret); free_ContentInfo(&info); if (ret) { krb5_set_error_message(context, ret, "encoding of Key ContentInfo " "failed %d", ret); free_PA_PK_AS_REP(&rep); goto out; } if (rep.u.encKeyPack.length != size) krb5_abortx(context, "Internal ASN.1 encoder error"); /* XXX KRB-FX-CF2 */ ret = krb5_generate_random_keyblock(context, sessionetype, sessionkey); if (ret) { free_PA_PK_AS_REP(&rep); goto out; } /* XXX Add PA-PKINIT-KX */ } #define use_btmm_with_enckey 0 if (use_btmm_with_enckey && rep.element == choice_PA_PK_AS_REP_encKeyPack) { PA_PK_AS_REP_BTMM btmm; heim_any any; any.data = rep.u.encKeyPack.data; any.length = rep.u.encKeyPack.length; btmm.dhSignedData = NULL; btmm.encKeyPack = &any; ASN1_MALLOC_ENCODE(PA_PK_AS_REP_BTMM, buf, len, &btmm, &size, ret); } else { ASN1_MALLOC_ENCODE(PA_PK_AS_REP, buf, len, &rep, &size, ret); } free_PA_PK_AS_REP(&rep); if (ret) { krb5_set_error_message(context, ret, "encode PA-PK-AS-REP failed %d", ret); goto out; } if (len != size) krb5_abortx(context, "Internal ASN.1 encoder error"); kdc_log(context, config, 0, "PK-INIT using %s %s", type, other); } else if (cp->type == PKINIT_WIN2K) { PA_PK_AS_REP_Win2k rep; ContentInfo info; if (cp->keyex != USE_RSA) { ret = KRB5KRB_ERR_GENERIC; krb5_set_error_message(context, ret, "Windows PK-INIT doesn't support DH"); goto out; } memset(&rep, 0, sizeof(rep)); pa_type = KRB5_PADATA_PK_AS_REP_19; rep.element = choice_PA_PK_AS_REP_Win2k_encKeyPack; ret = krb5_generate_random_keyblock(context, enctype, &cp->reply_key); if (ret) { free_PA_PK_AS_REP_Win2k(&rep); goto out; } ret = pk_mk_pa_reply_enckey(context, config, cp, req, req_buffer, &cp->reply_key, &info, &kdc_cert); if (ret) { free_PA_PK_AS_REP_Win2k(&rep); goto out; } ASN1_MALLOC_ENCODE(ContentInfo, rep.u.encKeyPack.data, rep.u.encKeyPack.length, &info, &size, ret); free_ContentInfo(&info); if (ret) { krb5_set_error_message(context, ret, "encoding of Key ContentInfo " "failed %d", ret); free_PA_PK_AS_REP_Win2k(&rep); goto out; } if (rep.u.encKeyPack.length != size) krb5_abortx(context, "Internal ASN.1 encoder error"); ASN1_MALLOC_ENCODE(PA_PK_AS_REP_Win2k, buf, len, &rep, &size, ret); free_PA_PK_AS_REP_Win2k(&rep); if (ret) { krb5_set_error_message(context, ret, "encode PA-PK-AS-REP-Win2k failed %d", ret); goto out; } if (len != size) krb5_abortx(context, "Internal ASN.1 encoder error"); ret = krb5_generate_random_keyblock(context, sessionetype, sessionkey); if (ret) { free(buf); goto out; } } else krb5_abortx(context, "PK-INIT internal error"); ret = krb5_padata_add(context, md, pa_type, buf, len); if (ret) { krb5_set_error_message(context, ret, "Failed adding PA-PK-AS-REP %d", ret); free(buf); goto out; } if (config->pkinit_kdc_ocsp_file) { if (ocsp.expire == 0 && ocsp.next_update > kdc_time) { struct stat sb; int fd; krb5_data_free(&ocsp.data); ocsp.expire = 0; ocsp.next_update = kdc_time + 60 * 5; fd = open(config->pkinit_kdc_ocsp_file, O_RDONLY); if (fd < 0) { kdc_log(context, config, 0, "PK-INIT failed to open ocsp data file %d", errno); goto out_ocsp; } ret = fstat(fd, &sb); if (ret) { ret = errno; close(fd); kdc_log(context, config, 0, "PK-INIT failed to stat ocsp data %d", ret); goto out_ocsp; } ret = krb5_data_alloc(&ocsp.data, sb.st_size); if (ret) { close(fd); kdc_log(context, config, 0, "PK-INIT failed to stat ocsp data %d", ret); goto out_ocsp; } ocsp.data.length = sb.st_size; ret = read(fd, ocsp.data.data, sb.st_size); close(fd); if (ret != sb.st_size) { kdc_log(context, config, 0, "PK-INIT failed to read ocsp data %d", errno); goto out_ocsp; } ret = hx509_ocsp_verify(context->hx509ctx, kdc_time, kdc_cert, 0, ocsp.data.data, ocsp.data.length, &ocsp.expire); if (ret) { kdc_log(context, config, 0, "PK-INIT failed to verify ocsp data %d", ret); krb5_data_free(&ocsp.data); ocsp.expire = 0; } else if (ocsp.expire > 180) { ocsp.expire -= 180; /* refetch the ocsp before it expire */ ocsp.next_update = ocsp.expire; } else { ocsp.next_update = kdc_time; } out_ocsp: ret = 0; } if (ocsp.expire != 0 && ocsp.expire > kdc_time) { ret = krb5_padata_add(context, md, KRB5_PADATA_PA_PK_OCSP_RESPONSE, ocsp.data.data, ocsp.data.length); if (ret) { krb5_set_error_message(context, ret, "Failed adding OCSP response %d", ret); goto out; } } } out: if (kdc_cert) hx509_cert_free(kdc_cert); if (ret == 0) *reply_key = &cp->reply_key; return ret; } static int match_rfc_san(krb5_context context, krb5_kdc_configuration *config, hx509_context hx509ctx, hx509_cert client_cert, krb5_const_principal match) { hx509_octet_string_list list; int ret, found = 0; size_t i; memset(&list, 0 , sizeof(list)); ret = hx509_cert_find_subjectAltName_otherName(hx509ctx, client_cert, &asn1_oid_id_pkinit_san, &list); if (ret) goto out; for (i = 0; !found && i < list.len; i++) { krb5_principal_data principal; KRB5PrincipalName kn; size_t size; ret = decode_KRB5PrincipalName(list.val[i].data, list.val[i].length, &kn, &size); if (ret) { const char *msg = krb5_get_error_message(context, ret); kdc_log(context, config, 0, "Decoding kerberos name in certificate failed: %s", msg); krb5_free_error_message(context, msg); break; } if (size != list.val[i].length) { kdc_log(context, config, 0, "Decoding kerberos name have extra bits on the end"); return KRB5_KDC_ERR_CLIENT_NAME_MISMATCH; } principal.name = kn.principalName; principal.realm = kn.realm; if (krb5_principal_compare(context, &principal, match) == TRUE) found = 1; free_KRB5PrincipalName(&kn); } out: hx509_free_octet_string_list(&list); if (ret) return ret; if (!found) return KRB5_KDC_ERR_CLIENT_NAME_MISMATCH; return 0; } static int match_ms_upn_san(krb5_context context, krb5_kdc_configuration *config, hx509_context hx509ctx, hx509_cert client_cert, HDB *clientdb, hdb_entry_ex *client) { hx509_octet_string_list list; krb5_principal principal = NULL; int ret; MS_UPN_SAN upn; size_t size; memset(&list, 0 , sizeof(list)); ret = hx509_cert_find_subjectAltName_otherName(hx509ctx, client_cert, &asn1_oid_id_pkinit_ms_san, &list); if (ret) goto out; if (list.len != 1) { kdc_log(context, config, 0, "More then one PK-INIT MS UPN SAN"); goto out; } ret = decode_MS_UPN_SAN(list.val[0].data, list.val[0].length, &upn, &size); if (ret) { kdc_log(context, config, 0, "Decode of MS-UPN-SAN failed"); goto out; } if (size != list.val[0].length) { free_MS_UPN_SAN(&upn); kdc_log(context, config, 0, "Trailing data in "); ret = KRB5_KDC_ERR_CLIENT_NAME_MISMATCH; goto out; } kdc_log(context, config, 0, "found MS UPN SAN: %s", upn); ret = krb5_parse_name(context, upn, &principal); free_MS_UPN_SAN(&upn); if (ret) { kdc_log(context, config, 0, "Failed to parse principal in MS UPN SAN"); goto out; } if (clientdb->hdb_check_pkinit_ms_upn_match) { ret = clientdb->hdb_check_pkinit_ms_upn_match(context, clientdb, client, principal); } else { /* * This is very wrong, but will do for a fallback */ strupr(principal->realm); if (krb5_principal_compare(context, principal, client->entry.principal) == FALSE) ret = KRB5_KDC_ERR_CLIENT_NAME_MISMATCH; } out: if (principal) krb5_free_principal(context, principal); hx509_free_octet_string_list(&list); return ret; } krb5_error_code _kdc_pk_check_client(krb5_context context, krb5_kdc_configuration *config, HDB *clientdb, hdb_entry_ex *client, pk_client_params *cp, char **subject_name) { const HDB_Ext_PKINIT_acl *acl; const HDB_Ext_PKINIT_cert *pc; krb5_error_code ret; hx509_name name; size_t i; if (cp->cert == NULL) { *subject_name = strdup("anonymous client client"); if (*subject_name == NULL) return ENOMEM; return 0; } ret = hx509_cert_get_base_subject(context->hx509ctx, cp->cert, &name); if (ret) return ret; ret = hx509_name_to_string(name, subject_name); hx509_name_free(&name); if (ret) return ret; kdc_log(context, config, 0, "Trying to authorize PK-INIT subject DN %s", *subject_name); ret = hdb_entry_get_pkinit_cert(&client->entry, &pc); if (ret == 0 && pc) { hx509_cert cert; size_t j; for (j = 0; j < pc->len; j++) { ret = hx509_cert_init_data(context->hx509ctx, pc->val[j].cert.data, pc->val[j].cert.length, &cert); if (ret) continue; ret = hx509_cert_cmp(cert, cp->cert); hx509_cert_free(cert); if (ret == 0) { kdc_log(context, config, 5, "Found matching PK-INIT cert in hdb"); return 0; } } } if (config->pkinit_princ_in_cert) { ret = match_rfc_san(context, config, context->hx509ctx, cp->cert, client->entry.principal); if (ret == 0) { kdc_log(context, config, 5, "Found matching PK-INIT SAN in certificate"); return 0; } ret = match_ms_upn_san(context, config, context->hx509ctx, cp->cert, clientdb, client); if (ret == 0) { kdc_log(context, config, 5, "Found matching MS UPN SAN in certificate"); return 0; } } ret = hdb_entry_get_pkinit_acl(&client->entry, &acl); if (ret == 0 && acl != NULL) { /* * Cheat here and compare the generated name with the string * and not the reverse. */ for (i = 0; i < acl->len; i++) { if (strcmp(*subject_name, acl->val[0].subject) != 0) continue; /* Don't support isser and anchor checking right now */ if (acl->val[0].issuer) continue; if (acl->val[0].anchor) continue; kdc_log(context, config, 5, "Found matching PK-INIT database ACL"); return 0; } } for (i = 0; i < principal_mappings.len; i++) { krb5_boolean b; b = krb5_principal_compare(context, client->entry.principal, principal_mappings.val[i].principal); if (b == FALSE) continue; if (strcmp(principal_mappings.val[i].subject, *subject_name) != 0) continue; kdc_log(context, config, 5, "Found matching PK-INIT FILE ACL"); return 0; } ret = KRB5_KDC_ERR_CLIENT_NAME_MISMATCH; krb5_set_error_message(context, ret, "PKINIT no matching principals for %s", *subject_name); kdc_log(context, config, 5, "PKINIT no matching principals for %s", *subject_name); free(*subject_name); *subject_name = NULL; return ret; } static krb5_error_code add_principal_mapping(krb5_context context, const char *principal_name, const char * subject) { struct pk_allowed_princ *tmp; krb5_principal principal; krb5_error_code ret; tmp = realloc(principal_mappings.val, (principal_mappings.len + 1) * sizeof(*tmp)); if (tmp == NULL) return ENOMEM; principal_mappings.val = tmp; ret = krb5_parse_name(context, principal_name, &principal); if (ret) return ret; principal_mappings.val[principal_mappings.len].principal = principal; principal_mappings.val[principal_mappings.len].subject = strdup(subject); if (principal_mappings.val[principal_mappings.len].subject == NULL) { krb5_free_principal(context, principal); return ENOMEM; } principal_mappings.len++; return 0; } krb5_error_code _kdc_add_inital_verified_cas(krb5_context context, krb5_kdc_configuration *config, pk_client_params *cp, EncTicketPart *tkt) { AD_INITIAL_VERIFIED_CAS cas; krb5_error_code ret; krb5_data data; size_t size = 0; memset(&cas, 0, sizeof(cas)); /* XXX add CAs to cas here */ ASN1_MALLOC_ENCODE(AD_INITIAL_VERIFIED_CAS, data.data, data.length, &cas, &size, ret); if (ret) return ret; if (data.length != size) krb5_abortx(context, "internal asn.1 encoder error"); ret = _kdc_tkt_add_if_relevant_ad(context, tkt, KRB5_AUTHDATA_INITIAL_VERIFIED_CAS, &data); krb5_data_free(&data); return ret; } /* * */ static void load_mappings(krb5_context context, const char *fn) { krb5_error_code ret; char buf[1024]; unsigned long lineno = 0; FILE *f; f = fopen(fn, "r"); if (f == NULL) return; while (fgets(buf, sizeof(buf), f) != NULL) { char *subject_name, *p; buf[strcspn(buf, "\n")] = '\0'; lineno++; p = buf + strspn(buf, " \t"); if (*p == '#' || *p == '\0') continue; subject_name = strchr(p, ':'); if (subject_name == NULL) { krb5_warnx(context, "pkinit mapping file line %lu " "missing \":\" :%s", lineno, buf); continue; } *subject_name++ = '\0'; ret = add_principal_mapping(context, p, subject_name); if (ret) { krb5_warn(context, ret, "failed to add line %lu \":\" :%s\n", lineno, buf); continue; } } fclose(f); } /* * */ krb5_error_code krb5_kdc_pk_initialize(krb5_context context, krb5_kdc_configuration *config, const char *user_id, const char *anchors, char **pool, char **revoke_list) { const char *file; char *fn = NULL; krb5_error_code ret; file = krb5_config_get_string(context, NULL, "libdefaults", "moduli", NULL); ret = _krb5_parse_moduli(context, file, &moduli); if (ret) krb5_err(context, 1, ret, "PKINIT: failed to load modidi file"); principal_mappings.len = 0; principal_mappings.val = NULL; ret = _krb5_pk_load_id(context, &kdc_identity, user_id, anchors, pool, revoke_list, NULL, NULL, NULL); if (ret) { krb5_warn(context, ret, "PKINIT: "); config->enable_pkinit = 0; return ret; } { hx509_query *q; hx509_cert cert; ret = hx509_query_alloc(context->hx509ctx, &q); if (ret) { krb5_warnx(context, "PKINIT: out of memory"); return ENOMEM; } hx509_query_match_option(q, HX509_QUERY_OPTION_PRIVATE_KEY); if (config->pkinit_kdc_friendly_name) hx509_query_match_friendly_name(q, config->pkinit_kdc_friendly_name); ret = hx509_certs_find(context->hx509ctx, kdc_identity->certs, q, &cert); hx509_query_free(context->hx509ctx, q); if (ret == 0) { if (hx509_cert_check_eku(context->hx509ctx, cert, &asn1_oid_id_pkkdcekuoid, 0)) { hx509_name name; char *str; ret = hx509_cert_get_subject(cert, &name); if (ret == 0) { hx509_name_to_string(name, &str); krb5_warnx(context, "WARNING Found KDC certificate (%s)" "is missing the PK-INIT KDC EKU, this is bad for " "interoperability.", str); hx509_name_free(&name); free(str); } } hx509_cert_free(cert); } else krb5_warnx(context, "PKINIT: failed to find a signing " "certifiate with a public key"); } if (krb5_config_get_bool_default(context, NULL, FALSE, "kdc", "pkinit_allow_proxy_certificate", NULL)) config->pkinit_allow_proxy_certs = 1; file = krb5_config_get_string(context, NULL, "kdc", "pkinit_mappings_file", NULL); if (file == NULL) { asprintf(&fn, "%s/pki-mapping", hdb_db_dir(context)); file = fn; } load_mappings(context, file); if (fn) free(fn); return 0; } #endif /* PKINIT */ Index: projects/openssl111/crypto/heimdal/lib/gssapi/krb5/arcfour.c =================================================================== --- projects/openssl111/crypto/heimdal/lib/gssapi/krb5/arcfour.c (revision 339197) +++ projects/openssl111/crypto/heimdal/lib/gssapi/krb5/arcfour.c (revision 339198) @@ -1,762 +1,791 @@ /* * Copyright (c) 2003 - 2006 Kungliga Tekniska Högskolan * (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 "gsskrb5_locl.h" /* * Implements draft-brezak-win2k-krb-rc4-hmac-04.txt * * The arcfour message have the following formats: * * MIC token * TOK_ID[2] = 01 01 * SGN_ALG[2] = 11 00 * Filler[4] * SND_SEQ[8] * SGN_CKSUM[8] * * WRAP token * TOK_ID[2] = 02 01 * SGN_ALG[2]; * SEAL_ALG[2] * Filler[2] * SND_SEQ[2] * SGN_CKSUM[8] * Confounder[8] */ /* * WRAP in DCE-style have a fixed size header, the oid and length over * the WRAP header is a total of * GSS_ARCFOUR_WRAP_TOKEN_DCE_DER_HEADER_SIZE + * GSS_ARCFOUR_WRAP_TOKEN_SIZE byte (ie total of 45 bytes overhead, * remember the 2 bytes from APPL [0] SEQ). */ #define GSS_ARCFOUR_WRAP_TOKEN_SIZE 32 #define GSS_ARCFOUR_WRAP_TOKEN_DCE_DER_HEADER_SIZE 13 static krb5_error_code arcfour_mic_key(krb5_context context, krb5_keyblock *key, void *cksum_data, size_t cksum_size, void *key6_data, size_t key6_size) { krb5_error_code ret; Checksum cksum_k5; krb5_keyblock key5; char k5_data[16]; Checksum cksum_k6; char T[4]; memset(T, 0, 4); cksum_k5.checksum.data = k5_data; cksum_k5.checksum.length = sizeof(k5_data); if (key->keytype == ENCTYPE_ARCFOUR_HMAC_MD5_56) { char L40[14] = "fortybits"; memcpy(L40 + 10, T, sizeof(T)); ret = krb5_hmac(context, CKSUMTYPE_RSA_MD5, L40, 14, 0, key, &cksum_k5); memset(&k5_data[7], 0xAB, 9); } else { ret = krb5_hmac(context, CKSUMTYPE_RSA_MD5, T, 4, 0, key, &cksum_k5); } if (ret) return ret; key5.keytype = ENCTYPE_ARCFOUR_HMAC_MD5; key5.keyvalue = cksum_k5.checksum; cksum_k6.checksum.data = key6_data; cksum_k6.checksum.length = key6_size; return krb5_hmac(context, CKSUMTYPE_RSA_MD5, cksum_data, cksum_size, 0, &key5, &cksum_k6); } static krb5_error_code arcfour_mic_cksum(krb5_context context, krb5_keyblock *key, unsigned usage, u_char *sgn_cksum, size_t sgn_cksum_sz, const u_char *v1, size_t l1, const void *v2, size_t l2, const void *v3, size_t l3) { Checksum CKSUM; u_char *ptr; size_t len; krb5_crypto crypto; krb5_error_code ret; assert(sgn_cksum_sz == 8); len = l1 + l2 + l3; ptr = malloc(len); if (ptr == NULL) return ENOMEM; memcpy(ptr, v1, l1); memcpy(ptr + l1, v2, l2); memcpy(ptr + l1 + l2, v3, l3); ret = krb5_crypto_init(context, key, 0, &crypto); if (ret) { free(ptr); return ret; } ret = krb5_create_checksum(context, crypto, usage, 0, ptr, len, &CKSUM); free(ptr); if (ret == 0) { memcpy(sgn_cksum, CKSUM.checksum.data, sgn_cksum_sz); free_Checksum(&CKSUM); } krb5_crypto_destroy(context, crypto); return ret; } OM_uint32 _gssapi_get_mic_arcfour(OM_uint32 * minor_status, const gsskrb5_ctx context_handle, krb5_context context, gss_qop_t qop_req, const gss_buffer_t message_buffer, gss_buffer_t message_token, krb5_keyblock *key) { krb5_error_code ret; int32_t seq_number; size_t len, total_len; u_char k6_data[16], *p0, *p; - EVP_CIPHER_CTX rc4_key; + EVP_CIPHER_CTX *rc4_key; _gsskrb5_encap_length (22, &len, &total_len, GSS_KRB5_MECHANISM); message_token->length = total_len; message_token->value = malloc (total_len); if (message_token->value == NULL) { *minor_status = ENOMEM; return GSS_S_FAILURE; } p0 = _gssapi_make_mech_header(message_token->value, len, GSS_KRB5_MECHANISM); p = p0; *p++ = 0x01; /* TOK_ID */ *p++ = 0x01; *p++ = 0x11; /* SGN_ALG */ *p++ = 0x00; *p++ = 0xff; /* Filler */ *p++ = 0xff; *p++ = 0xff; *p++ = 0xff; p = NULL; ret = arcfour_mic_cksum(context, key, KRB5_KU_USAGE_SIGN, p0 + 16, 8, /* SGN_CKSUM */ p0, 8, /* TOK_ID, SGN_ALG, Filer */ message_buffer->value, message_buffer->length, NULL, 0); if (ret) { _gsskrb5_release_buffer(minor_status, message_token); *minor_status = ret; return GSS_S_FAILURE; } ret = arcfour_mic_key(context, key, p0 + 16, 8, /* SGN_CKSUM */ k6_data, sizeof(k6_data)); if (ret) { _gsskrb5_release_buffer(minor_status, message_token); *minor_status = ret; return GSS_S_FAILURE; } HEIMDAL_MUTEX_lock(&context_handle->ctx_id_mutex); krb5_auth_con_getlocalseqnumber (context, context_handle->auth_context, &seq_number); p = p0 + 8; /* SND_SEQ */ _gsskrb5_encode_be_om_uint32(seq_number, p); krb5_auth_con_setlocalseqnumber (context, context_handle->auth_context, ++seq_number); HEIMDAL_MUTEX_unlock(&context_handle->ctx_id_mutex); memset (p + 4, (context_handle->more_flags & LOCAL) ? 0 : 0xff, 4); - EVP_CIPHER_CTX_init(&rc4_key); - EVP_CipherInit_ex(&rc4_key, EVP_rc4(), NULL, k6_data, NULL, 1); - EVP_Cipher(&rc4_key, p, p, 8); - EVP_CIPHER_CTX_cleanup(&rc4_key); + rc4_key = EVP_CIPHER_CTX_new(); + if (rc4_key == NULL) { + _gsskrb5_release_buffer(minor_status, message_token); + *minor_status = ENOMEM; + return GSS_S_FAILURE; + } + EVP_CipherInit_ex(rc4_key, EVP_rc4(), NULL, k6_data, NULL, 1); + EVP_Cipher(rc4_key, p, p, 8); + EVP_CIPHER_CTX_free(rc4_key); + memset(k6_data, 0, sizeof(k6_data)); *minor_status = 0; return GSS_S_COMPLETE; } OM_uint32 _gssapi_verify_mic_arcfour(OM_uint32 * minor_status, const gsskrb5_ctx context_handle, krb5_context context, const gss_buffer_t message_buffer, const gss_buffer_t token_buffer, gss_qop_t * qop_state, krb5_keyblock *key, const char *type) { krb5_error_code ret; uint32_t seq_number; OM_uint32 omret; u_char SND_SEQ[8], cksum_data[8], *p; char k6_data[16]; int cmp; if (qop_state) *qop_state = 0; p = token_buffer->value; omret = _gsskrb5_verify_header (&p, token_buffer->length, type, GSS_KRB5_MECHANISM); if (omret) return omret; if (memcmp(p, "\x11\x00", 2) != 0) /* SGN_ALG = HMAC MD5 ARCFOUR */ return GSS_S_BAD_SIG; p += 2; if (memcmp (p, "\xff\xff\xff\xff", 4) != 0) return GSS_S_BAD_MIC; p += 4; ret = arcfour_mic_cksum(context, key, KRB5_KU_USAGE_SIGN, cksum_data, sizeof(cksum_data), p - 8, 8, message_buffer->value, message_buffer->length, NULL, 0); if (ret) { *minor_status = ret; return GSS_S_FAILURE; } ret = arcfour_mic_key(context, key, cksum_data, sizeof(cksum_data), k6_data, sizeof(k6_data)); if (ret) { *minor_status = ret; return GSS_S_FAILURE; } cmp = ct_memcmp(cksum_data, p + 8, 8); if (cmp) { *minor_status = 0; return GSS_S_BAD_MIC; } { - EVP_CIPHER_CTX rc4_key; + EVP_CIPHER_CTX *rc4_key; - EVP_CIPHER_CTX_init(&rc4_key); - EVP_CipherInit_ex(&rc4_key, EVP_rc4(), NULL, (void *)k6_data, NULL, 0); - EVP_Cipher(&rc4_key, SND_SEQ, p, 8); - EVP_CIPHER_CTX_cleanup(&rc4_key); + rc4_key = EVP_CIPHER_CTX_new(); + if (rc4_key == NULL) { + *minor_status = ENOMEM; + return GSS_S_FAILURE; + } + EVP_CipherInit_ex(rc4_key, EVP_rc4(), NULL, (void *)k6_data, NULL, 0); + EVP_Cipher(rc4_key, SND_SEQ, p, 8); + EVP_CIPHER_CTX_free(rc4_key); memset(k6_data, 0, sizeof(k6_data)); } _gsskrb5_decode_be_om_uint32(SND_SEQ, &seq_number); if (context_handle->more_flags & LOCAL) cmp = memcmp(&SND_SEQ[4], "\xff\xff\xff\xff", 4); else cmp = memcmp(&SND_SEQ[4], "\x00\x00\x00\x00", 4); memset(SND_SEQ, 0, sizeof(SND_SEQ)); if (cmp != 0) { *minor_status = 0; return GSS_S_BAD_MIC; } HEIMDAL_MUTEX_lock(&context_handle->ctx_id_mutex); omret = _gssapi_msg_order_check(context_handle->order, seq_number); HEIMDAL_MUTEX_unlock(&context_handle->ctx_id_mutex); if (omret) return omret; *minor_status = 0; return GSS_S_COMPLETE; } OM_uint32 _gssapi_wrap_arcfour(OM_uint32 * minor_status, const gsskrb5_ctx context_handle, krb5_context context, int conf_req_flag, gss_qop_t qop_req, const gss_buffer_t input_message_buffer, int * conf_state, gss_buffer_t output_message_buffer, krb5_keyblock *key) { u_char Klocaldata[16], k6_data[16], *p, *p0; size_t len, total_len, datalen; krb5_keyblock Klocal; krb5_error_code ret; int32_t seq_number; if (conf_state) *conf_state = 0; datalen = input_message_buffer->length; if (IS_DCE_STYLE(context_handle)) { len = GSS_ARCFOUR_WRAP_TOKEN_SIZE; _gssapi_encap_length(len, &len, &total_len, GSS_KRB5_MECHANISM); total_len += datalen; } else { datalen += 1; /* padding */ len = datalen + GSS_ARCFOUR_WRAP_TOKEN_SIZE; _gssapi_encap_length(len, &len, &total_len, GSS_KRB5_MECHANISM); } output_message_buffer->length = total_len; output_message_buffer->value = malloc (total_len); if (output_message_buffer->value == NULL) { *minor_status = ENOMEM; return GSS_S_FAILURE; } p0 = _gssapi_make_mech_header(output_message_buffer->value, len, GSS_KRB5_MECHANISM); p = p0; *p++ = 0x02; /* TOK_ID */ *p++ = 0x01; *p++ = 0x11; /* SGN_ALG */ *p++ = 0x00; if (conf_req_flag) { *p++ = 0x10; /* SEAL_ALG */ *p++ = 0x00; } else { *p++ = 0xff; /* SEAL_ALG */ *p++ = 0xff; } *p++ = 0xff; /* Filler */ *p++ = 0xff; p = NULL; HEIMDAL_MUTEX_lock(&context_handle->ctx_id_mutex); krb5_auth_con_getlocalseqnumber (context, context_handle->auth_context, &seq_number); _gsskrb5_encode_be_om_uint32(seq_number, p0 + 8); krb5_auth_con_setlocalseqnumber (context, context_handle->auth_context, ++seq_number); HEIMDAL_MUTEX_unlock(&context_handle->ctx_id_mutex); memset (p0 + 8 + 4, (context_handle->more_flags & LOCAL) ? 0 : 0xff, 4); krb5_generate_random_block(p0 + 24, 8); /* fill in Confounder */ /* p points to data */ p = p0 + GSS_ARCFOUR_WRAP_TOKEN_SIZE; memcpy(p, input_message_buffer->value, input_message_buffer->length); if (!IS_DCE_STYLE(context_handle)) p[input_message_buffer->length] = 1; /* padding */ ret = arcfour_mic_cksum(context, key, KRB5_KU_USAGE_SEAL, p0 + 16, 8, /* SGN_CKSUM */ p0, 8, /* TOK_ID, SGN_ALG, SEAL_ALG, Filler */ p0 + 24, 8, /* Confounder */ p0 + GSS_ARCFOUR_WRAP_TOKEN_SIZE, datalen); if (ret) { *minor_status = ret; _gsskrb5_release_buffer(minor_status, output_message_buffer); return GSS_S_FAILURE; } { int i; Klocal.keytype = key->keytype; Klocal.keyvalue.data = Klocaldata; Klocal.keyvalue.length = sizeof(Klocaldata); for (i = 0; i < 16; i++) Klocaldata[i] = ((u_char *)key->keyvalue.data)[i] ^ 0xF0; } ret = arcfour_mic_key(context, &Klocal, p0 + 8, 4, /* SND_SEQ */ k6_data, sizeof(k6_data)); memset(Klocaldata, 0, sizeof(Klocaldata)); if (ret) { _gsskrb5_release_buffer(minor_status, output_message_buffer); *minor_status = ret; return GSS_S_FAILURE; } if(conf_req_flag) { - EVP_CIPHER_CTX rc4_key; + EVP_CIPHER_CTX *rc4_key; - EVP_CIPHER_CTX_init(&rc4_key); - EVP_CipherInit_ex(&rc4_key, EVP_rc4(), NULL, k6_data, NULL, 1); - EVP_Cipher(&rc4_key, p0 + 24, p0 + 24, 8 + datalen); - EVP_CIPHER_CTX_cleanup(&rc4_key); + rc4_key = EVP_CIPHER_CTX_new(); + if (rc4_key == NULL) { + _gsskrb5_release_buffer(minor_status, output_message_buffer); + *minor_status = ENOMEM; + return GSS_S_FAILURE; + } + EVP_CipherInit_ex(rc4_key, EVP_rc4(), NULL, k6_data, NULL, 1); + EVP_Cipher(rc4_key, p0 + 24, p0 + 24, 8 + datalen); + EVP_CIPHER_CTX_free(rc4_key); } memset(k6_data, 0, sizeof(k6_data)); ret = arcfour_mic_key(context, key, p0 + 16, 8, /* SGN_CKSUM */ k6_data, sizeof(k6_data)); if (ret) { _gsskrb5_release_buffer(minor_status, output_message_buffer); *minor_status = ret; return GSS_S_FAILURE; } { - EVP_CIPHER_CTX rc4_key; + EVP_CIPHER_CTX *rc4_key; - EVP_CIPHER_CTX_init(&rc4_key); - EVP_CipherInit_ex(&rc4_key, EVP_rc4(), NULL, k6_data, NULL, 1); - EVP_Cipher(&rc4_key, p0 + 8, p0 + 8 /* SND_SEQ */, 8); - EVP_CIPHER_CTX_cleanup(&rc4_key); + rc4_key = EVP_CIPHER_CTX_new(); + if (rc4_key == NULL) { + _gsskrb5_release_buffer(minor_status, output_message_buffer); + *minor_status = ENOMEM; + return GSS_S_FAILURE; + } + EVP_CipherInit_ex(rc4_key, EVP_rc4(), NULL, k6_data, NULL, 1); + EVP_Cipher(rc4_key, p0 + 8, p0 + 8 /* SND_SEQ */, 8); + EVP_CIPHER_CTX_free(rc4_key); memset(k6_data, 0, sizeof(k6_data)); } if (conf_state) *conf_state = conf_req_flag; *minor_status = 0; return GSS_S_COMPLETE; } OM_uint32 _gssapi_unwrap_arcfour(OM_uint32 *minor_status, const gsskrb5_ctx context_handle, krb5_context context, const gss_buffer_t input_message_buffer, gss_buffer_t output_message_buffer, int *conf_state, gss_qop_t *qop_state, krb5_keyblock *key) { u_char Klocaldata[16]; krb5_keyblock Klocal; krb5_error_code ret; uint32_t seq_number; size_t datalen; OM_uint32 omret; u_char k6_data[16], SND_SEQ[8], Confounder[8]; u_char cksum_data[8]; u_char *p, *p0; int cmp; int conf_flag; size_t padlen = 0, len; if (conf_state) *conf_state = 0; if (qop_state) *qop_state = 0; p0 = input_message_buffer->value; if (IS_DCE_STYLE(context_handle)) { len = GSS_ARCFOUR_WRAP_TOKEN_SIZE + GSS_ARCFOUR_WRAP_TOKEN_DCE_DER_HEADER_SIZE; if (input_message_buffer->length < len) return GSS_S_BAD_MECH; } else { len = input_message_buffer->length; } omret = _gssapi_verify_mech_header(&p0, len, GSS_KRB5_MECHANISM); if (omret) return omret; /* length of mech header */ len = (p0 - (u_char *)input_message_buffer->value) + GSS_ARCFOUR_WRAP_TOKEN_SIZE; if (len > input_message_buffer->length) return GSS_S_BAD_MECH; /* length of data */ datalen = input_message_buffer->length - len; p = p0; if (memcmp(p, "\x02\x01", 2) != 0) return GSS_S_BAD_SIG; p += 2; if (memcmp(p, "\x11\x00", 2) != 0) /* SGN_ALG = HMAC MD5 ARCFOUR */ return GSS_S_BAD_SIG; p += 2; if (memcmp (p, "\x10\x00", 2) == 0) conf_flag = 1; else if (memcmp (p, "\xff\xff", 2) == 0) conf_flag = 0; else return GSS_S_BAD_SIG; p += 2; if (memcmp (p, "\xff\xff", 2) != 0) return GSS_S_BAD_MIC; p = NULL; ret = arcfour_mic_key(context, key, p0 + 16, 8, /* SGN_CKSUM */ k6_data, sizeof(k6_data)); if (ret) { *minor_status = ret; return GSS_S_FAILURE; } { - EVP_CIPHER_CTX rc4_key; + EVP_CIPHER_CTX *rc4_key; - EVP_CIPHER_CTX_init(&rc4_key); - EVP_CipherInit_ex(&rc4_key, EVP_rc4(), NULL, k6_data, NULL, 1); - EVP_Cipher(&rc4_key, SND_SEQ, p0 + 8, 8); - EVP_CIPHER_CTX_cleanup(&rc4_key); + rc4_key = EVP_CIPHER_CTX_new(); + if (rc4_key == NULL) { + *minor_status = ENOMEM; + return GSS_S_FAILURE; + } + EVP_CipherInit_ex(rc4_key, EVP_rc4(), NULL, k6_data, NULL, 1); + EVP_Cipher(rc4_key, SND_SEQ, p0 + 8, 8); + EVP_CIPHER_CTX_free(rc4_key); memset(k6_data, 0, sizeof(k6_data)); } _gsskrb5_decode_be_om_uint32(SND_SEQ, &seq_number); if (context_handle->more_flags & LOCAL) cmp = memcmp(&SND_SEQ[4], "\xff\xff\xff\xff", 4); else cmp = memcmp(&SND_SEQ[4], "\x00\x00\x00\x00", 4); if (cmp != 0) { *minor_status = 0; return GSS_S_BAD_MIC; } { int i; Klocal.keytype = key->keytype; Klocal.keyvalue.data = Klocaldata; Klocal.keyvalue.length = sizeof(Klocaldata); for (i = 0; i < 16; i++) Klocaldata[i] = ((u_char *)key->keyvalue.data)[i] ^ 0xF0; } ret = arcfour_mic_key(context, &Klocal, SND_SEQ, 4, k6_data, sizeof(k6_data)); memset(Klocaldata, 0, sizeof(Klocaldata)); if (ret) { *minor_status = ret; return GSS_S_FAILURE; } output_message_buffer->value = malloc(datalen); if (output_message_buffer->value == NULL) { *minor_status = ENOMEM; return GSS_S_FAILURE; } output_message_buffer->length = datalen; if(conf_flag) { - EVP_CIPHER_CTX rc4_key; + EVP_CIPHER_CTX *rc4_key; - EVP_CIPHER_CTX_init(&rc4_key); - EVP_CipherInit_ex(&rc4_key, EVP_rc4(), NULL, k6_data, NULL, 1); - EVP_Cipher(&rc4_key, Confounder, p0 + 24, 8); - EVP_Cipher(&rc4_key, output_message_buffer->value, p0 + GSS_ARCFOUR_WRAP_TOKEN_SIZE, datalen); - EVP_CIPHER_CTX_cleanup(&rc4_key); + rc4_key = EVP_CIPHER_CTX_new(); + if (rc4_key == NULL) { + _gsskrb5_release_buffer(minor_status, output_message_buffer); + *minor_status = ENOMEM; + return GSS_S_FAILURE; + } + EVP_CipherInit_ex(rc4_key, EVP_rc4(), NULL, k6_data, NULL, 1); + EVP_Cipher(rc4_key, Confounder, p0 + 24, 8); + EVP_Cipher(rc4_key, output_message_buffer->value, p0 + GSS_ARCFOUR_WRAP_TOKEN_SIZE, datalen); + EVP_CIPHER_CTX_free(rc4_key); } else { memcpy(Confounder, p0 + 24, 8); /* Confounder */ memcpy(output_message_buffer->value, p0 + GSS_ARCFOUR_WRAP_TOKEN_SIZE, datalen); } memset(k6_data, 0, sizeof(k6_data)); if (!IS_DCE_STYLE(context_handle)) { ret = _gssapi_verify_pad(output_message_buffer, datalen, &padlen); if (ret) { _gsskrb5_release_buffer(minor_status, output_message_buffer); *minor_status = 0; return ret; } output_message_buffer->length -= padlen; } ret = arcfour_mic_cksum(context, key, KRB5_KU_USAGE_SEAL, cksum_data, sizeof(cksum_data), p0, 8, Confounder, sizeof(Confounder), output_message_buffer->value, output_message_buffer->length + padlen); if (ret) { _gsskrb5_release_buffer(minor_status, output_message_buffer); *minor_status = ret; return GSS_S_FAILURE; } cmp = ct_memcmp(cksum_data, p0 + 16, 8); /* SGN_CKSUM */ if (cmp) { _gsskrb5_release_buffer(minor_status, output_message_buffer); *minor_status = 0; return GSS_S_BAD_MIC; } HEIMDAL_MUTEX_lock(&context_handle->ctx_id_mutex); omret = _gssapi_msg_order_check(context_handle->order, seq_number); HEIMDAL_MUTEX_unlock(&context_handle->ctx_id_mutex); if (omret) return omret; if (conf_state) *conf_state = conf_flag; *minor_status = 0; return GSS_S_COMPLETE; } static OM_uint32 max_wrap_length_arcfour(const gsskrb5_ctx ctx, krb5_crypto crypto, size_t input_length, OM_uint32 *max_input_size) { /* * if GSS_C_DCE_STYLE is in use: * - we only need to encapsulate the WRAP token * However, since this is a fixed since, we just */ if (IS_DCE_STYLE(ctx)) { size_t len, total_len; len = GSS_ARCFOUR_WRAP_TOKEN_SIZE; _gssapi_encap_length(len, &len, &total_len, GSS_KRB5_MECHANISM); if (input_length < len) *max_input_size = 0; else *max_input_size = input_length - len; } else { size_t extrasize = GSS_ARCFOUR_WRAP_TOKEN_SIZE; size_t blocksize = 8; size_t len, total_len; len = 8 + input_length + blocksize + extrasize; _gsskrb5_encap_length(len, &len, &total_len, GSS_KRB5_MECHANISM); total_len -= input_length; /* token length */ if (total_len < input_length) { *max_input_size = (input_length - total_len); (*max_input_size) &= (~(OM_uint32)(blocksize - 1)); } else { *max_input_size = 0; } } return GSS_S_COMPLETE; } OM_uint32 _gssapi_wrap_size_arcfour(OM_uint32 *minor_status, const gsskrb5_ctx ctx, krb5_context context, int conf_req_flag, gss_qop_t qop_req, OM_uint32 req_output_size, OM_uint32 *max_input_size, krb5_keyblock *key) { krb5_error_code ret; krb5_crypto crypto; ret = krb5_crypto_init(context, key, 0, &crypto); if (ret != 0) { *minor_status = ret; return GSS_S_FAILURE; } ret = max_wrap_length_arcfour(ctx, crypto, req_output_size, max_input_size); if (ret != 0) { *minor_status = ret; krb5_crypto_destroy(context, crypto); return GSS_S_FAILURE; } krb5_crypto_destroy(context, crypto); return GSS_S_COMPLETE; } Index: projects/openssl111/crypto/heimdal/lib/gssapi/krb5/get_mic.c =================================================================== --- projects/openssl111/crypto/heimdal/lib/gssapi/krb5/get_mic.c (revision 339197) +++ projects/openssl111/crypto/heimdal/lib/gssapi/krb5/get_mic.c (revision 339198) @@ -1,329 +1,339 @@ /* * Copyright (c) 1997 - 2003 Kungliga Tekniska Högskolan * (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 "gsskrb5_locl.h" #ifdef HEIM_WEAK_CRYPTO static OM_uint32 mic_des (OM_uint32 * minor_status, const gsskrb5_ctx ctx, krb5_context context, gss_qop_t qop_req, const gss_buffer_t message_buffer, gss_buffer_t message_token, krb5_keyblock *key ) { u_char *p; EVP_MD_CTX *md5; u_char hash[16]; DES_key_schedule schedule; - EVP_CIPHER_CTX des_ctx; + EVP_CIPHER_CTX *des_ctx; DES_cblock deskey; DES_cblock zero; int32_t seq_number; size_t len, total_len; _gsskrb5_encap_length (22, &len, &total_len, GSS_KRB5_MECHANISM); message_token->length = total_len; message_token->value = malloc (total_len); if (message_token->value == NULL) { message_token->length = 0; *minor_status = ENOMEM; return GSS_S_FAILURE; } p = _gsskrb5_make_header(message_token->value, len, "\x01\x01", /* TOK_ID */ GSS_KRB5_MECHANISM); memcpy (p, "\x00\x00", 2); /* SGN_ALG = DES MAC MD5 */ p += 2; memcpy (p, "\xff\xff\xff\xff", 4); /* Filler */ p += 4; /* Fill in later (SND-SEQ) */ memset (p, 0, 16); p += 16; /* checksum */ md5 = EVP_MD_CTX_create(); EVP_DigestInit_ex(md5, EVP_md5(), NULL); EVP_DigestUpdate(md5, p - 24, 8); EVP_DigestUpdate(md5, message_buffer->value, message_buffer->length); EVP_DigestFinal_ex(md5, hash, NULL); EVP_MD_CTX_destroy(md5); memset (&zero, 0, sizeof(zero)); memcpy (&deskey, key->keyvalue.data, sizeof(deskey)); DES_set_key_unchecked (&deskey, &schedule); DES_cbc_cksum ((void *)hash, (void *)hash, sizeof(hash), &schedule, &zero); memcpy (p - 8, hash, 8); /* SGN_CKSUM */ + des_ctx = EVP_CIPHER_CTX_new(); + if (des_ctx == NULL) { + memset (deskey, 0, sizeof(deskey)); + memset (&schedule, 0, sizeof(schedule)); + free (message_token->value); + message_token->value = NULL; + message_token->length = 0; + *minor_status = ENOMEM; + return GSS_S_FAILURE; + } + HEIMDAL_MUTEX_lock(&ctx->ctx_id_mutex); /* sequence number */ krb5_auth_con_getlocalseqnumber (context, ctx->auth_context, &seq_number); p -= 16; /* SND_SEQ */ p[0] = (seq_number >> 0) & 0xFF; p[1] = (seq_number >> 8) & 0xFF; p[2] = (seq_number >> 16) & 0xFF; p[3] = (seq_number >> 24) & 0xFF; memset (p + 4, (ctx->more_flags & LOCAL) ? 0 : 0xFF, 4); - EVP_CIPHER_CTX_init(&des_ctx); - EVP_CipherInit_ex(&des_ctx, EVP_des_cbc(), NULL, key->keyvalue.data, p + 8, 1); - EVP_Cipher(&des_ctx, p, p, 8); - EVP_CIPHER_CTX_cleanup(&des_ctx); + EVP_CipherInit_ex(des_ctx, EVP_des_cbc(), NULL, key->keyvalue.data, p + 8, 1); + EVP_Cipher(des_ctx, p, p, 8); + EVP_CIPHER_CTX_free(des_ctx); krb5_auth_con_setlocalseqnumber (context, ctx->auth_context, ++seq_number); HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex); memset (deskey, 0, sizeof(deskey)); memset (&schedule, 0, sizeof(schedule)); *minor_status = 0; return GSS_S_COMPLETE; } #endif static OM_uint32 mic_des3 (OM_uint32 * minor_status, const gsskrb5_ctx ctx, krb5_context context, gss_qop_t qop_req, const gss_buffer_t message_buffer, gss_buffer_t message_token, krb5_keyblock *key ) { u_char *p; Checksum cksum; u_char seq[8]; int32_t seq_number; size_t len, total_len; krb5_crypto crypto; krb5_error_code kret; krb5_data encdata; char *tmp; char ivec[8]; _gsskrb5_encap_length (36, &len, &total_len, GSS_KRB5_MECHANISM); message_token->length = total_len; message_token->value = malloc (total_len); if (message_token->value == NULL) { message_token->length = 0; *minor_status = ENOMEM; return GSS_S_FAILURE; } p = _gsskrb5_make_header(message_token->value, len, "\x01\x01", /* TOK-ID */ GSS_KRB5_MECHANISM); memcpy (p, "\x04\x00", 2); /* SGN_ALG = HMAC SHA1 DES3-KD */ p += 2; memcpy (p, "\xff\xff\xff\xff", 4); /* filler */ p += 4; /* this should be done in parts */ tmp = malloc (message_buffer->length + 8); if (tmp == NULL) { free (message_token->value); message_token->value = NULL; message_token->length = 0; *minor_status = ENOMEM; return GSS_S_FAILURE; } memcpy (tmp, p - 8, 8); memcpy (tmp + 8, message_buffer->value, message_buffer->length); kret = krb5_crypto_init(context, key, 0, &crypto); if (kret) { free (message_token->value); message_token->value = NULL; message_token->length = 0; free (tmp); *minor_status = kret; return GSS_S_FAILURE; } kret = krb5_create_checksum (context, crypto, KRB5_KU_USAGE_SIGN, 0, tmp, message_buffer->length + 8, &cksum); free (tmp); krb5_crypto_destroy (context, crypto); if (kret) { free (message_token->value); message_token->value = NULL; message_token->length = 0; *minor_status = kret; return GSS_S_FAILURE; } memcpy (p + 8, cksum.checksum.data, cksum.checksum.length); HEIMDAL_MUTEX_lock(&ctx->ctx_id_mutex); /* sequence number */ krb5_auth_con_getlocalseqnumber (context, ctx->auth_context, &seq_number); seq[0] = (seq_number >> 0) & 0xFF; seq[1] = (seq_number >> 8) & 0xFF; seq[2] = (seq_number >> 16) & 0xFF; seq[3] = (seq_number >> 24) & 0xFF; memset (seq + 4, (ctx->more_flags & LOCAL) ? 0 : 0xFF, 4); kret = krb5_crypto_init(context, key, ETYPE_DES3_CBC_NONE, &crypto); if (kret) { free (message_token->value); message_token->value = NULL; message_token->length = 0; *minor_status = kret; return GSS_S_FAILURE; } if (ctx->more_flags & COMPAT_OLD_DES3) memset(ivec, 0, 8); else memcpy(ivec, p + 8, 8); kret = krb5_encrypt_ivec (context, crypto, KRB5_KU_USAGE_SEQ, seq, 8, &encdata, ivec); krb5_crypto_destroy (context, crypto); if (kret) { free (message_token->value); message_token->value = NULL; message_token->length = 0; *minor_status = kret; return GSS_S_FAILURE; } assert (encdata.length == 8); memcpy (p, encdata.data, encdata.length); krb5_data_free (&encdata); krb5_auth_con_setlocalseqnumber (context, ctx->auth_context, ++seq_number); HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex); free_Checksum (&cksum); *minor_status = 0; return GSS_S_COMPLETE; } OM_uint32 GSSAPI_CALLCONV _gsskrb5_get_mic (OM_uint32 * minor_status, const gss_ctx_id_t context_handle, gss_qop_t qop_req, const gss_buffer_t message_buffer, gss_buffer_t message_token ) { krb5_context context; const gsskrb5_ctx ctx = (const gsskrb5_ctx) context_handle; krb5_keyblock *key; OM_uint32 ret; krb5_keytype keytype; GSSAPI_KRB5_INIT (&context); if (ctx->more_flags & IS_CFX) return _gssapi_mic_cfx (minor_status, ctx, context, qop_req, message_buffer, message_token); HEIMDAL_MUTEX_lock(&ctx->ctx_id_mutex); ret = _gsskrb5i_get_token_key(ctx, context, &key); HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex); if (ret) { *minor_status = ret; return GSS_S_FAILURE; } krb5_enctype_to_keytype (context, key->keytype, &keytype); switch (keytype) { case KEYTYPE_DES : #ifdef HEIM_WEAK_CRYPTO ret = mic_des (minor_status, ctx, context, qop_req, message_buffer, message_token, key); #else ret = GSS_S_FAILURE; #endif break; case KEYTYPE_DES3 : ret = mic_des3 (minor_status, ctx, context, qop_req, message_buffer, message_token, key); break; case KEYTYPE_ARCFOUR: case KEYTYPE_ARCFOUR_56: ret = _gssapi_get_mic_arcfour (minor_status, ctx, context, qop_req, message_buffer, message_token, key); break; default : abort(); break; } krb5_free_keyblock (context, key); return ret; } Index: projects/openssl111/crypto/heimdal/lib/gssapi/krb5/unwrap.c =================================================================== --- projects/openssl111/crypto/heimdal/lib/gssapi/krb5/unwrap.c (revision 339197) +++ projects/openssl111/crypto/heimdal/lib/gssapi/krb5/unwrap.c (revision 339198) @@ -1,448 +1,463 @@ /* * Copyright (c) 1997 - 2004 Kungliga Tekniska Högskolan * (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 "gsskrb5_locl.h" #ifdef HEIM_WEAK_CRYPTO static OM_uint32 unwrap_des (OM_uint32 * minor_status, const gsskrb5_ctx context_handle, const gss_buffer_t input_message_buffer, gss_buffer_t output_message_buffer, int * conf_state, gss_qop_t * qop_state, krb5_keyblock *key ) { u_char *p, *seq; size_t len; EVP_MD_CTX *md5; u_char hash[16]; - EVP_CIPHER_CTX des_ctx; + EVP_CIPHER_CTX *des_ctx; DES_key_schedule schedule; DES_cblock deskey; DES_cblock zero; size_t i; uint32_t seq_number; size_t padlength; OM_uint32 ret; int cstate; int cmp; int token_len; if (IS_DCE_STYLE(context_handle)) { token_len = 22 + 8 + 15; /* 45 */ } else { token_len = input_message_buffer->length; } p = input_message_buffer->value; ret = _gsskrb5_verify_header (&p, token_len, "\x02\x01", GSS_KRB5_MECHANISM); if (ret) return ret; if (memcmp (p, "\x00\x00", 2) != 0) return GSS_S_BAD_SIG; p += 2; if (memcmp (p, "\x00\x00", 2) == 0) { cstate = 1; } else if (memcmp (p, "\xFF\xFF", 2) == 0) { cstate = 0; } else return GSS_S_BAD_MIC; p += 2; if(conf_state != NULL) *conf_state = cstate; if (memcmp (p, "\xff\xff", 2) != 0) return GSS_S_DEFECTIVE_TOKEN; p += 2; p += 16; len = p - (u_char *)input_message_buffer->value; if(cstate) { /* decrypt data */ memcpy (&deskey, key->keyvalue.data, sizeof(deskey)); memset (&zero, 0, sizeof(zero)); for (i = 0; i < sizeof(deskey); ++i) deskey[i] ^= 0xf0; - EVP_CIPHER_CTX_init(&des_ctx); - EVP_CipherInit_ex(&des_ctx, EVP_des_cbc(), NULL, deskey, zero, 0); - EVP_Cipher(&des_ctx, p, p, input_message_buffer->length - len); - EVP_CIPHER_CTX_cleanup(&des_ctx); + des_ctx = EVP_CIPHER_CTX_new(); + if (des_ctx == NULL) { + memset (deskey, 0, sizeof(deskey)); + *minor_status = ENOMEM; + return GSS_S_FAILURE; + } + EVP_CipherInit_ex(des_ctx, EVP_des_cbc(), NULL, deskey, zero, 0); + EVP_Cipher(des_ctx, p, p, input_message_buffer->length - len); + EVP_CIPHER_CTX_free(des_ctx); - memset (&schedule, 0, sizeof(schedule)); + memset (deskey, 0, sizeof(deskey)); } if (IS_DCE_STYLE(context_handle)) { padlength = 0; } else { /* check pad */ ret = _gssapi_verify_pad(input_message_buffer, input_message_buffer->length - len, &padlength); if (ret) return ret; } md5 = EVP_MD_CTX_create(); EVP_DigestInit_ex(md5, EVP_md5(), NULL); EVP_DigestUpdate(md5, p - 24, 8); EVP_DigestUpdate(md5, p, input_message_buffer->length - len); EVP_DigestFinal_ex(md5, hash, NULL); EVP_MD_CTX_destroy(md5); memset (&zero, 0, sizeof(zero)); memcpy (&deskey, key->keyvalue.data, sizeof(deskey)); DES_set_key_unchecked (&deskey, &schedule); DES_cbc_cksum ((void *)hash, (void *)hash, sizeof(hash), &schedule, &zero); - if (ct_memcmp (p - 8, hash, 8) != 0) + if (ct_memcmp (p - 8, hash, 8) != 0) { + memset (deskey, 0, sizeof(deskey)); + memset (&schedule, 0, sizeof(schedule)); return GSS_S_BAD_MIC; + } /* verify sequence number */ + des_ctx = EVP_CIPHER_CTX_new(); + if (des_ctx == NULL) { + memset (deskey, 0, sizeof(deskey)); + memset (&schedule, 0, sizeof(schedule)); + *minor_status = ENOMEM; + return GSS_S_FAILURE; + } + HEIMDAL_MUTEX_lock(&context_handle->ctx_id_mutex); p -= 16; - EVP_CIPHER_CTX_init(&des_ctx); - EVP_CipherInit_ex(&des_ctx, EVP_des_cbc(), NULL, key->keyvalue.data, hash, 0); - EVP_Cipher(&des_ctx, p, p, 8); - EVP_CIPHER_CTX_cleanup(&des_ctx); + EVP_CipherInit_ex(des_ctx, EVP_des_cbc(), NULL, key->keyvalue.data, hash, 0); + EVP_Cipher(des_ctx, p, p, 8); + EVP_CIPHER_CTX_free(des_ctx); memset (deskey, 0, sizeof(deskey)); memset (&schedule, 0, sizeof(schedule)); seq = p; _gsskrb5_decode_om_uint32(seq, &seq_number); if (context_handle->more_flags & LOCAL) cmp = ct_memcmp(&seq[4], "\xff\xff\xff\xff", 4); else cmp = ct_memcmp(&seq[4], "\x00\x00\x00\x00", 4); if (cmp != 0) { HEIMDAL_MUTEX_unlock(&context_handle->ctx_id_mutex); return GSS_S_BAD_MIC; } ret = _gssapi_msg_order_check(context_handle->order, seq_number); if (ret) { HEIMDAL_MUTEX_unlock(&context_handle->ctx_id_mutex); return ret; } HEIMDAL_MUTEX_unlock(&context_handle->ctx_id_mutex); /* copy out data */ output_message_buffer->length = input_message_buffer->length - len - padlength - 8; output_message_buffer->value = malloc(output_message_buffer->length); if(output_message_buffer->length != 0 && output_message_buffer->value == NULL) return GSS_S_FAILURE; memcpy (output_message_buffer->value, p + 24, output_message_buffer->length); return GSS_S_COMPLETE; } #endif static OM_uint32 unwrap_des3 (OM_uint32 * minor_status, const gsskrb5_ctx context_handle, krb5_context context, const gss_buffer_t input_message_buffer, gss_buffer_t output_message_buffer, int * conf_state, gss_qop_t * qop_state, krb5_keyblock *key ) { u_char *p; size_t len; u_char *seq; krb5_data seq_data; u_char cksum[20]; uint32_t seq_number; size_t padlength; OM_uint32 ret; int cstate; krb5_crypto crypto; Checksum csum; int cmp; int token_len; if (IS_DCE_STYLE(context_handle)) { token_len = 34 + 8 + 15; /* 57 */ } else { token_len = input_message_buffer->length; } p = input_message_buffer->value; ret = _gsskrb5_verify_header (&p, token_len, "\x02\x01", GSS_KRB5_MECHANISM); if (ret) return ret; if (memcmp (p, "\x04\x00", 2) != 0) /* HMAC SHA1 DES3_KD */ return GSS_S_BAD_SIG; p += 2; if (ct_memcmp (p, "\x02\x00", 2) == 0) { cstate = 1; } else if (ct_memcmp (p, "\xff\xff", 2) == 0) { cstate = 0; } else return GSS_S_BAD_MIC; p += 2; if(conf_state != NULL) *conf_state = cstate; if (ct_memcmp (p, "\xff\xff", 2) != 0) return GSS_S_DEFECTIVE_TOKEN; p += 2; p += 28; len = p - (u_char *)input_message_buffer->value; if(cstate) { /* decrypt data */ krb5_data tmp; ret = krb5_crypto_init(context, key, ETYPE_DES3_CBC_NONE, &crypto); if (ret) { *minor_status = ret; return GSS_S_FAILURE; } ret = krb5_decrypt(context, crypto, KRB5_KU_USAGE_SEAL, p, input_message_buffer->length - len, &tmp); krb5_crypto_destroy(context, crypto); if (ret) { *minor_status = ret; return GSS_S_FAILURE; } assert (tmp.length == input_message_buffer->length - len); memcpy (p, tmp.data, tmp.length); krb5_data_free(&tmp); } if (IS_DCE_STYLE(context_handle)) { padlength = 0; } else { /* check pad */ ret = _gssapi_verify_pad(input_message_buffer, input_message_buffer->length - len, &padlength); if (ret) return ret; } /* verify sequence number */ HEIMDAL_MUTEX_lock(&context_handle->ctx_id_mutex); p -= 28; ret = krb5_crypto_init(context, key, ETYPE_DES3_CBC_NONE, &crypto); if (ret) { *minor_status = ret; HEIMDAL_MUTEX_unlock(&context_handle->ctx_id_mutex); return GSS_S_FAILURE; } { DES_cblock ivec; memcpy(&ivec, p + 8, 8); ret = krb5_decrypt_ivec (context, crypto, KRB5_KU_USAGE_SEQ, p, 8, &seq_data, &ivec); } krb5_crypto_destroy (context, crypto); if (ret) { *minor_status = ret; HEIMDAL_MUTEX_unlock(&context_handle->ctx_id_mutex); return GSS_S_FAILURE; } if (seq_data.length != 8) { krb5_data_free (&seq_data); *minor_status = 0; HEIMDAL_MUTEX_unlock(&context_handle->ctx_id_mutex); return GSS_S_BAD_MIC; } seq = seq_data.data; _gsskrb5_decode_om_uint32(seq, &seq_number); if (context_handle->more_flags & LOCAL) cmp = ct_memcmp(&seq[4], "\xff\xff\xff\xff", 4); else cmp = ct_memcmp(&seq[4], "\x00\x00\x00\x00", 4); krb5_data_free (&seq_data); if (cmp != 0) { *minor_status = 0; HEIMDAL_MUTEX_unlock(&context_handle->ctx_id_mutex); return GSS_S_BAD_MIC; } ret = _gssapi_msg_order_check(context_handle->order, seq_number); if (ret) { *minor_status = 0; HEIMDAL_MUTEX_unlock(&context_handle->ctx_id_mutex); return ret; } HEIMDAL_MUTEX_unlock(&context_handle->ctx_id_mutex); /* verify checksum */ memcpy (cksum, p + 8, 20); memcpy (p + 20, p - 8, 8); csum.cksumtype = CKSUMTYPE_HMAC_SHA1_DES3; csum.checksum.length = 20; csum.checksum.data = cksum; ret = krb5_crypto_init(context, key, 0, &crypto); if (ret) { *minor_status = ret; return GSS_S_FAILURE; } ret = krb5_verify_checksum (context, crypto, KRB5_KU_USAGE_SIGN, p + 20, input_message_buffer->length - len + 8, &csum); krb5_crypto_destroy (context, crypto); if (ret) { *minor_status = ret; return GSS_S_FAILURE; } /* copy out data */ output_message_buffer->length = input_message_buffer->length - len - padlength - 8; output_message_buffer->value = malloc(output_message_buffer->length); if(output_message_buffer->length != 0 && output_message_buffer->value == NULL) return GSS_S_FAILURE; memcpy (output_message_buffer->value, p + 36, output_message_buffer->length); return GSS_S_COMPLETE; } OM_uint32 GSSAPI_CALLCONV _gsskrb5_unwrap (OM_uint32 * minor_status, const gss_ctx_id_t context_handle, const gss_buffer_t input_message_buffer, gss_buffer_t output_message_buffer, int * conf_state, gss_qop_t * qop_state ) { krb5_keyblock *key; krb5_context context; OM_uint32 ret; krb5_keytype keytype; gsskrb5_ctx ctx = (gsskrb5_ctx) context_handle; output_message_buffer->value = NULL; output_message_buffer->length = 0; if (qop_state != NULL) *qop_state = GSS_C_QOP_DEFAULT; GSSAPI_KRB5_INIT (&context); if (ctx->more_flags & IS_CFX) return _gssapi_unwrap_cfx (minor_status, ctx, context, input_message_buffer, output_message_buffer, conf_state, qop_state); HEIMDAL_MUTEX_lock(&ctx->ctx_id_mutex); ret = _gsskrb5i_get_token_key(ctx, context, &key); HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex); if (ret) { *minor_status = ret; return GSS_S_FAILURE; } krb5_enctype_to_keytype (context, key->keytype, &keytype); *minor_status = 0; switch (keytype) { case KEYTYPE_DES : #ifdef HEIM_WEAK_CRYPTO ret = unwrap_des (minor_status, ctx, input_message_buffer, output_message_buffer, conf_state, qop_state, key); #else ret = GSS_S_FAILURE; #endif break; case KEYTYPE_DES3 : ret = unwrap_des3 (minor_status, ctx, context, input_message_buffer, output_message_buffer, conf_state, qop_state, key); break; case KEYTYPE_ARCFOUR: case KEYTYPE_ARCFOUR_56: ret = _gssapi_unwrap_arcfour (minor_status, ctx, context, input_message_buffer, output_message_buffer, conf_state, qop_state, key); break; default : abort(); break; } krb5_free_keyblock (context, key); return ret; } Index: projects/openssl111/crypto/heimdal/lib/gssapi/krb5/verify_mic.c =================================================================== --- projects/openssl111/crypto/heimdal/lib/gssapi/krb5/verify_mic.c (revision 339197) +++ projects/openssl111/crypto/heimdal/lib/gssapi/krb5/verify_mic.c (revision 339198) @@ -1,362 +1,369 @@ /* * Copyright (c) 1997 - 2003 Kungliga Tekniska Högskolan * (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 "gsskrb5_locl.h" #ifdef HEIM_WEAK_CRYPTO static OM_uint32 verify_mic_des (OM_uint32 * minor_status, const gsskrb5_ctx context_handle, krb5_context context, const gss_buffer_t message_buffer, const gss_buffer_t token_buffer, gss_qop_t * qop_state, krb5_keyblock *key, const char *type ) { u_char *p; EVP_MD_CTX *md5; u_char hash[16], *seq; DES_key_schedule schedule; - EVP_CIPHER_CTX des_ctx; + EVP_CIPHER_CTX *des_ctx; DES_cblock zero; DES_cblock deskey; uint32_t seq_number; OM_uint32 ret; int cmp; p = token_buffer->value; ret = _gsskrb5_verify_header (&p, token_buffer->length, type, GSS_KRB5_MECHANISM); if (ret) return ret; if (memcmp(p, "\x00\x00", 2) != 0) return GSS_S_BAD_SIG; p += 2; if (memcmp (p, "\xff\xff\xff\xff", 4) != 0) return GSS_S_BAD_MIC; p += 4; p += 16; /* verify checksum */ md5 = EVP_MD_CTX_create(); EVP_DigestInit_ex(md5, EVP_md5(), NULL); EVP_DigestUpdate(md5, p - 24, 8); EVP_DigestUpdate(md5, message_buffer->value, message_buffer->length); EVP_DigestFinal_ex(md5, hash, NULL); EVP_MD_CTX_destroy(md5); memset (&zero, 0, sizeof(zero)); memcpy (&deskey, key->keyvalue.data, sizeof(deskey)); DES_set_key_unchecked (&deskey, &schedule); DES_cbc_cksum ((void *)hash, (void *)hash, sizeof(hash), &schedule, &zero); if (ct_memcmp (p - 8, hash, 8) != 0) { memset (deskey, 0, sizeof(deskey)); memset (&schedule, 0, sizeof(schedule)); return GSS_S_BAD_MIC; } /* verify sequence number */ + des_ctx = EVP_CIPHER_CTX_new(); + if (des_ctx == NULL) { + memset (deskey, 0, sizeof(deskey)); + memset (&schedule, 0, sizeof(schedule)); + *minor_status = ENOMEM; + return GSS_S_FAILURE; + } + HEIMDAL_MUTEX_lock(&context_handle->ctx_id_mutex); p -= 16; - EVP_CIPHER_CTX_init(&des_ctx); - EVP_CipherInit_ex(&des_ctx, EVP_des_cbc(), NULL, key->keyvalue.data, hash, 0); - EVP_Cipher(&des_ctx, p, p, 8); - EVP_CIPHER_CTX_cleanup(&des_ctx); + EVP_CipherInit_ex(des_ctx, EVP_des_cbc(), NULL, key->keyvalue.data, hash, 0); + EVP_Cipher(des_ctx, p, p, 8); + EVP_CIPHER_CTX_free(des_ctx); memset (deskey, 0, sizeof(deskey)); memset (&schedule, 0, sizeof(schedule)); seq = p; _gsskrb5_decode_om_uint32(seq, &seq_number); if (context_handle->more_flags & LOCAL) cmp = ct_memcmp(&seq[4], "\xff\xff\xff\xff", 4); else cmp = ct_memcmp(&seq[4], "\x00\x00\x00\x00", 4); if (cmp != 0) { HEIMDAL_MUTEX_unlock(&context_handle->ctx_id_mutex); return GSS_S_BAD_MIC; } ret = _gssapi_msg_order_check(context_handle->order, seq_number); if (ret) { HEIMDAL_MUTEX_unlock(&context_handle->ctx_id_mutex); return ret; } HEIMDAL_MUTEX_unlock(&context_handle->ctx_id_mutex); return GSS_S_COMPLETE; } #endif static OM_uint32 verify_mic_des3 (OM_uint32 * minor_status, const gsskrb5_ctx context_handle, krb5_context context, const gss_buffer_t message_buffer, const gss_buffer_t token_buffer, gss_qop_t * qop_state, krb5_keyblock *key, const char *type ) { u_char *p; u_char *seq; uint32_t seq_number; OM_uint32 ret; krb5_crypto crypto; krb5_data seq_data; int cmp, docompat; Checksum csum; char *tmp; char ivec[8]; p = token_buffer->value; ret = _gsskrb5_verify_header (&p, token_buffer->length, type, GSS_KRB5_MECHANISM); if (ret) return ret; if (memcmp(p, "\x04\x00", 2) != 0) /* SGN_ALG = HMAC SHA1 DES3-KD */ return GSS_S_BAD_SIG; p += 2; if (memcmp (p, "\xff\xff\xff\xff", 4) != 0) return GSS_S_BAD_MIC; p += 4; ret = krb5_crypto_init(context, key, ETYPE_DES3_CBC_NONE, &crypto); if (ret){ *minor_status = ret; return GSS_S_FAILURE; } /* verify sequence number */ docompat = 0; retry: if (docompat) memset(ivec, 0, 8); else memcpy(ivec, p + 8, 8); ret = krb5_decrypt_ivec (context, crypto, KRB5_KU_USAGE_SEQ, p, 8, &seq_data, ivec); if (ret) { if (docompat++) { krb5_crypto_destroy (context, crypto); *minor_status = ret; return GSS_S_FAILURE; } else goto retry; } if (seq_data.length != 8) { krb5_data_free (&seq_data); if (docompat++) { krb5_crypto_destroy (context, crypto); return GSS_S_BAD_MIC; } else goto retry; } HEIMDAL_MUTEX_lock(&context_handle->ctx_id_mutex); seq = seq_data.data; _gsskrb5_decode_om_uint32(seq, &seq_number); if (context_handle->more_flags & LOCAL) cmp = ct_memcmp(&seq[4], "\xff\xff\xff\xff", 4); else cmp = ct_memcmp(&seq[4], "\x00\x00\x00\x00", 4); krb5_data_free (&seq_data); if (cmp != 0) { krb5_crypto_destroy (context, crypto); *minor_status = 0; HEIMDAL_MUTEX_unlock(&context_handle->ctx_id_mutex); return GSS_S_BAD_MIC; } ret = _gssapi_msg_order_check(context_handle->order, seq_number); if (ret) { krb5_crypto_destroy (context, crypto); *minor_status = 0; HEIMDAL_MUTEX_unlock(&context_handle->ctx_id_mutex); return ret; } /* verify checksum */ tmp = malloc (message_buffer->length + 8); if (tmp == NULL) { krb5_crypto_destroy (context, crypto); HEIMDAL_MUTEX_unlock(&context_handle->ctx_id_mutex); *minor_status = ENOMEM; return GSS_S_FAILURE; } memcpy (tmp, p - 8, 8); memcpy (tmp + 8, message_buffer->value, message_buffer->length); csum.cksumtype = CKSUMTYPE_HMAC_SHA1_DES3; csum.checksum.length = 20; csum.checksum.data = p + 8; krb5_crypto_destroy (context, crypto); ret = krb5_crypto_init(context, key, ETYPE_DES3_CBC_SHA1, &crypto); if (ret){ *minor_status = ret; return GSS_S_FAILURE; } ret = krb5_verify_checksum (context, crypto, KRB5_KU_USAGE_SIGN, tmp, message_buffer->length + 8, &csum); free (tmp); if (ret) { krb5_crypto_destroy (context, crypto); *minor_status = ret; HEIMDAL_MUTEX_unlock(&context_handle->ctx_id_mutex); return GSS_S_BAD_MIC; } HEIMDAL_MUTEX_unlock(&context_handle->ctx_id_mutex); krb5_crypto_destroy (context, crypto); return GSS_S_COMPLETE; } OM_uint32 _gsskrb5_verify_mic_internal (OM_uint32 * minor_status, const gsskrb5_ctx ctx, krb5_context context, const gss_buffer_t message_buffer, const gss_buffer_t token_buffer, gss_qop_t * qop_state, const char * type ) { krb5_keyblock *key; OM_uint32 ret; krb5_keytype keytype; if (ctx->more_flags & IS_CFX) return _gssapi_verify_mic_cfx (minor_status, ctx, context, message_buffer, token_buffer, qop_state); HEIMDAL_MUTEX_lock(&ctx->ctx_id_mutex); ret = _gsskrb5i_get_token_key(ctx, context, &key); HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex); if (ret) { *minor_status = ret; return GSS_S_FAILURE; } *minor_status = 0; krb5_enctype_to_keytype (context, key->keytype, &keytype); switch (keytype) { case KEYTYPE_DES : #ifdef HEIM_WEAK_CRYPTO ret = verify_mic_des (minor_status, ctx, context, message_buffer, token_buffer, qop_state, key, type); #else ret = GSS_S_FAILURE; #endif break; case KEYTYPE_DES3 : ret = verify_mic_des3 (minor_status, ctx, context, message_buffer, token_buffer, qop_state, key, type); break; case KEYTYPE_ARCFOUR : case KEYTYPE_ARCFOUR_56 : ret = _gssapi_verify_mic_arcfour (minor_status, ctx, context, message_buffer, token_buffer, qop_state, key, type); break; default : abort(); } krb5_free_keyblock (context, key); return ret; } OM_uint32 GSSAPI_CALLCONV _gsskrb5_verify_mic (OM_uint32 * minor_status, const gss_ctx_id_t context_handle, const gss_buffer_t message_buffer, const gss_buffer_t token_buffer, gss_qop_t * qop_state ) { krb5_context context; OM_uint32 ret; GSSAPI_KRB5_INIT (&context); if (qop_state != NULL) *qop_state = GSS_C_QOP_DEFAULT; ret = _gsskrb5_verify_mic_internal(minor_status, (gsskrb5_ctx)context_handle, context, message_buffer, token_buffer, qop_state, (void *)(intptr_t)"\x01\x01"); return ret; } Index: projects/openssl111/crypto/heimdal/lib/gssapi/krb5/wrap.c =================================================================== --- projects/openssl111/crypto/heimdal/lib/gssapi/krb5/wrap.c (revision 339197) +++ projects/openssl111/crypto/heimdal/lib/gssapi/krb5/wrap.c (revision 339198) @@ -1,590 +1,599 @@ /* * Copyright (c) 1997 - 2003 Kungliga Tekniska Högskolan * (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 "gsskrb5_locl.h" /* * Return initiator subkey, or if that doesn't exists, the subkey. */ krb5_error_code _gsskrb5i_get_initiator_subkey(const gsskrb5_ctx ctx, krb5_context context, krb5_keyblock **key) { krb5_error_code ret; *key = NULL; if (ctx->more_flags & LOCAL) { ret = krb5_auth_con_getlocalsubkey(context, ctx->auth_context, key); } else { ret = krb5_auth_con_getremotesubkey(context, ctx->auth_context, key); } if (ret == 0 && *key == NULL) ret = krb5_auth_con_getkey(context, ctx->auth_context, key); if (ret == 0 && *key == NULL) { krb5_set_error_message(context, 0, "No initiator subkey available"); return GSS_KRB5_S_KG_NO_SUBKEY; } return ret; } krb5_error_code _gsskrb5i_get_acceptor_subkey(const gsskrb5_ctx ctx, krb5_context context, krb5_keyblock **key) { krb5_error_code ret; *key = NULL; if (ctx->more_flags & LOCAL) { ret = krb5_auth_con_getremotesubkey(context, ctx->auth_context, key); } else { ret = krb5_auth_con_getlocalsubkey(context, ctx->auth_context, key); } if (ret == 0 && *key == NULL) { krb5_set_error_message(context, 0, "No acceptor subkey available"); return GSS_KRB5_S_KG_NO_SUBKEY; } return ret; } OM_uint32 _gsskrb5i_get_token_key(const gsskrb5_ctx ctx, krb5_context context, krb5_keyblock **key) { _gsskrb5i_get_acceptor_subkey(ctx, context, key); if(*key == NULL) { /* * Only use the initiator subkey or ticket session key if an * acceptor subkey was not required. */ if ((ctx->more_flags & ACCEPTOR_SUBKEY) == 0) _gsskrb5i_get_initiator_subkey(ctx, context, key); } if (*key == NULL) { krb5_set_error_message(context, 0, "No token key available"); return GSS_KRB5_S_KG_NO_SUBKEY; } return 0; } static OM_uint32 sub_wrap_size ( OM_uint32 req_output_size, OM_uint32 * max_input_size, int blocksize, int extrasize ) { size_t len, total_len; len = 8 + req_output_size + blocksize + extrasize; _gsskrb5_encap_length(len, &len, &total_len, GSS_KRB5_MECHANISM); total_len -= req_output_size; /* token length */ if (total_len < req_output_size) { *max_input_size = (req_output_size - total_len); (*max_input_size) &= (~(OM_uint32)(blocksize - 1)); } else { *max_input_size = 0; } return GSS_S_COMPLETE; } OM_uint32 GSSAPI_CALLCONV _gsskrb5_wrap_size_limit ( OM_uint32 * minor_status, const gss_ctx_id_t context_handle, int conf_req_flag, gss_qop_t qop_req, OM_uint32 req_output_size, OM_uint32 * max_input_size ) { krb5_context context; krb5_keyblock *key; OM_uint32 ret; krb5_keytype keytype; const gsskrb5_ctx ctx = (const gsskrb5_ctx) context_handle; GSSAPI_KRB5_INIT (&context); if (ctx->more_flags & IS_CFX) return _gssapi_wrap_size_cfx(minor_status, ctx, context, conf_req_flag, qop_req, req_output_size, max_input_size); HEIMDAL_MUTEX_lock(&ctx->ctx_id_mutex); ret = _gsskrb5i_get_token_key(ctx, context, &key); HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex); if (ret) { *minor_status = ret; return GSS_S_FAILURE; } krb5_enctype_to_keytype (context, key->keytype, &keytype); switch (keytype) { case KEYTYPE_DES : #ifdef HEIM_WEAK_CRYPTO ret = sub_wrap_size(req_output_size, max_input_size, 8, 22); #else ret = GSS_S_FAILURE; #endif break; case ENCTYPE_ARCFOUR_HMAC_MD5: case ENCTYPE_ARCFOUR_HMAC_MD5_56: ret = _gssapi_wrap_size_arcfour(minor_status, ctx, context, conf_req_flag, qop_req, req_output_size, max_input_size, key); break; case KEYTYPE_DES3 : ret = sub_wrap_size(req_output_size, max_input_size, 8, 34); break; default : abort(); break; } krb5_free_keyblock (context, key); *minor_status = 0; return ret; } #ifdef HEIM_WEAK_CRYPTO static OM_uint32 wrap_des (OM_uint32 * minor_status, const gsskrb5_ctx ctx, krb5_context context, int conf_req_flag, gss_qop_t qop_req, const gss_buffer_t input_message_buffer, int * conf_state, gss_buffer_t output_message_buffer, krb5_keyblock *key ) { u_char *p; EVP_MD_CTX *md5; u_char hash[16]; DES_key_schedule schedule; - EVP_CIPHER_CTX des_ctx; + EVP_CIPHER_CTX *des_ctx; DES_cblock deskey; DES_cblock zero; size_t i; int32_t seq_number; size_t len, total_len, padlength, datalen; if (IS_DCE_STYLE(ctx)) { padlength = 0; datalen = input_message_buffer->length; len = 22 + 8; _gsskrb5_encap_length (len, &len, &total_len, GSS_KRB5_MECHANISM); total_len += datalen; datalen += 8; } else { padlength = 8 - (input_message_buffer->length % 8); datalen = input_message_buffer->length + padlength + 8; len = datalen + 22; _gsskrb5_encap_length (len, &len, &total_len, GSS_KRB5_MECHANISM); } output_message_buffer->length = total_len; output_message_buffer->value = malloc (total_len); if (output_message_buffer->value == NULL) { output_message_buffer->length = 0; *minor_status = ENOMEM; return GSS_S_FAILURE; } p = _gsskrb5_make_header(output_message_buffer->value, len, "\x02\x01", /* TOK_ID */ GSS_KRB5_MECHANISM); /* SGN_ALG */ memcpy (p, "\x00\x00", 2); p += 2; /* SEAL_ALG */ if(conf_req_flag) memcpy (p, "\x00\x00", 2); else memcpy (p, "\xff\xff", 2); p += 2; /* Filler */ memcpy (p, "\xff\xff", 2); p += 2; /* fill in later */ memset (p, 0, 16); p += 16; /* confounder + data + pad */ krb5_generate_random_block(p, 8); memcpy (p + 8, input_message_buffer->value, input_message_buffer->length); memset (p + 8 + input_message_buffer->length, padlength, padlength); /* checksum */ md5 = EVP_MD_CTX_create(); EVP_DigestInit_ex(md5, EVP_md5(), NULL); EVP_DigestUpdate(md5, p - 24, 8); EVP_DigestUpdate(md5, p, datalen); EVP_DigestFinal_ex(md5, hash, NULL); EVP_MD_CTX_destroy(md5); memset (&zero, 0, sizeof(zero)); memcpy (&deskey, key->keyvalue.data, sizeof(deskey)); DES_set_key_unchecked (&deskey, &schedule); DES_cbc_cksum ((void *)hash, (void *)hash, sizeof(hash), &schedule, &zero); memcpy (p - 8, hash, 8); + des_ctx = EVP_CIPHER_CTX_new(); + if (des_ctx == NULL) { + memset (deskey, 0, sizeof(deskey)); + memset (&schedule, 0, sizeof(schedule)); + free(output_message_buffer->value); + output_message_buffer->value = NULL; + output_message_buffer->length = 0; + *minor_status = ENOMEM; + return GSS_S_FAILURE; + } + /* sequence number */ HEIMDAL_MUTEX_lock(&ctx->ctx_id_mutex); krb5_auth_con_getlocalseqnumber (context, ctx->auth_context, &seq_number); p -= 16; p[0] = (seq_number >> 0) & 0xFF; p[1] = (seq_number >> 8) & 0xFF; p[2] = (seq_number >> 16) & 0xFF; p[3] = (seq_number >> 24) & 0xFF; memset (p + 4, (ctx->more_flags & LOCAL) ? 0 : 0xFF, 4); - EVP_CIPHER_CTX_init(&des_ctx); - EVP_CipherInit_ex(&des_ctx, EVP_des_cbc(), NULL, key->keyvalue.data, p + 8, 1); - EVP_Cipher(&des_ctx, p, p, 8); - EVP_CIPHER_CTX_cleanup(&des_ctx); + EVP_CipherInit_ex(des_ctx, EVP_des_cbc(), NULL, key->keyvalue.data, p + 8, 1); + EVP_Cipher(des_ctx, p, p, 8); krb5_auth_con_setlocalseqnumber (context, ctx->auth_context, ++seq_number); HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex); /* encrypt the data */ p += 16; if(conf_req_flag) { memcpy (&deskey, key->keyvalue.data, sizeof(deskey)); for (i = 0; i < sizeof(deskey); ++i) deskey[i] ^= 0xf0; - EVP_CIPHER_CTX_init(&des_ctx); - EVP_CipherInit_ex(&des_ctx, EVP_des_cbc(), NULL, deskey, zero, 1); - EVP_Cipher(&des_ctx, p, p, datalen); - EVP_CIPHER_CTX_cleanup(&des_ctx); + EVP_CIPHER_CTX_reset(des_ctx); + EVP_CipherInit_ex(des_ctx, EVP_des_cbc(), NULL, deskey, zero, 1); + EVP_Cipher(des_ctx, p, p, datalen); } + EVP_CIPHER_CTX_free(des_ctx); memset (deskey, 0, sizeof(deskey)); memset (&schedule, 0, sizeof(schedule)); if(conf_state != NULL) *conf_state = conf_req_flag; *minor_status = 0; return GSS_S_COMPLETE; } #endif static OM_uint32 wrap_des3 (OM_uint32 * minor_status, const gsskrb5_ctx ctx, krb5_context context, int conf_req_flag, gss_qop_t qop_req, const gss_buffer_t input_message_buffer, int * conf_state, gss_buffer_t output_message_buffer, krb5_keyblock *key ) { u_char *p; u_char seq[8]; int32_t seq_number; size_t len, total_len, padlength, datalen; uint32_t ret; krb5_crypto crypto; Checksum cksum; krb5_data encdata; if (IS_DCE_STYLE(ctx)) { padlength = 0; datalen = input_message_buffer->length; len = 34 + 8; _gsskrb5_encap_length (len, &len, &total_len, GSS_KRB5_MECHANISM); total_len += datalen; datalen += 8; } else { padlength = 8 - (input_message_buffer->length % 8); datalen = input_message_buffer->length + padlength + 8; len = datalen + 34; _gsskrb5_encap_length (len, &len, &total_len, GSS_KRB5_MECHANISM); } output_message_buffer->length = total_len; output_message_buffer->value = malloc (total_len); if (output_message_buffer->value == NULL) { output_message_buffer->length = 0; *minor_status = ENOMEM; return GSS_S_FAILURE; } p = _gsskrb5_make_header(output_message_buffer->value, len, "\x02\x01", /* TOK_ID */ GSS_KRB5_MECHANISM); /* SGN_ALG */ memcpy (p, "\x04\x00", 2); /* HMAC SHA1 DES3-KD */ p += 2; /* SEAL_ALG */ if(conf_req_flag) memcpy (p, "\x02\x00", 2); /* DES3-KD */ else memcpy (p, "\xff\xff", 2); p += 2; /* Filler */ memcpy (p, "\xff\xff", 2); p += 2; /* calculate checksum (the above + confounder + data + pad) */ memcpy (p + 20, p - 8, 8); krb5_generate_random_block(p + 28, 8); memcpy (p + 28 + 8, input_message_buffer->value, input_message_buffer->length); memset (p + 28 + 8 + input_message_buffer->length, padlength, padlength); ret = krb5_crypto_init(context, key, 0, &crypto); if (ret) { free (output_message_buffer->value); output_message_buffer->length = 0; output_message_buffer->value = NULL; *minor_status = ret; return GSS_S_FAILURE; } ret = krb5_create_checksum (context, crypto, KRB5_KU_USAGE_SIGN, 0, p + 20, datalen + 8, &cksum); krb5_crypto_destroy (context, crypto); if (ret) { free (output_message_buffer->value); output_message_buffer->length = 0; output_message_buffer->value = NULL; *minor_status = ret; return GSS_S_FAILURE; } /* zero out SND_SEQ + SGN_CKSUM in case */ memset (p, 0, 28); memcpy (p + 8, cksum.checksum.data, cksum.checksum.length); free_Checksum (&cksum); HEIMDAL_MUTEX_lock(&ctx->ctx_id_mutex); /* sequence number */ krb5_auth_con_getlocalseqnumber (context, ctx->auth_context, &seq_number); seq[0] = (seq_number >> 0) & 0xFF; seq[1] = (seq_number >> 8) & 0xFF; seq[2] = (seq_number >> 16) & 0xFF; seq[3] = (seq_number >> 24) & 0xFF; memset (seq + 4, (ctx->more_flags & LOCAL) ? 0 : 0xFF, 4); ret = krb5_crypto_init(context, key, ETYPE_DES3_CBC_NONE, &crypto); if (ret) { free (output_message_buffer->value); output_message_buffer->length = 0; output_message_buffer->value = NULL; *minor_status = ret; return GSS_S_FAILURE; } { DES_cblock ivec; memcpy (&ivec, p + 8, 8); ret = krb5_encrypt_ivec (context, crypto, KRB5_KU_USAGE_SEQ, seq, 8, &encdata, &ivec); } krb5_crypto_destroy (context, crypto); if (ret) { free (output_message_buffer->value); output_message_buffer->length = 0; output_message_buffer->value = NULL; *minor_status = ret; return GSS_S_FAILURE; } assert (encdata.length == 8); memcpy (p, encdata.data, encdata.length); krb5_data_free (&encdata); krb5_auth_con_setlocalseqnumber (context, ctx->auth_context, ++seq_number); HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex); /* encrypt the data */ p += 28; if(conf_req_flag) { krb5_data tmp; ret = krb5_crypto_init(context, key, ETYPE_DES3_CBC_NONE, &crypto); if (ret) { free (output_message_buffer->value); output_message_buffer->length = 0; output_message_buffer->value = NULL; *minor_status = ret; return GSS_S_FAILURE; } ret = krb5_encrypt(context, crypto, KRB5_KU_USAGE_SEAL, p, datalen, &tmp); krb5_crypto_destroy(context, crypto); if (ret) { free (output_message_buffer->value); output_message_buffer->length = 0; output_message_buffer->value = NULL; *minor_status = ret; return GSS_S_FAILURE; } assert (tmp.length == datalen); memcpy (p, tmp.data, datalen); krb5_data_free(&tmp); } if(conf_state != NULL) *conf_state = conf_req_flag; *minor_status = 0; return GSS_S_COMPLETE; } OM_uint32 GSSAPI_CALLCONV _gsskrb5_wrap (OM_uint32 * minor_status, const gss_ctx_id_t context_handle, int conf_req_flag, gss_qop_t qop_req, const gss_buffer_t input_message_buffer, int * conf_state, gss_buffer_t output_message_buffer ) { krb5_context context; krb5_keyblock *key; OM_uint32 ret; krb5_keytype keytype; const gsskrb5_ctx ctx = (const gsskrb5_ctx) context_handle; output_message_buffer->value = NULL; output_message_buffer->length = 0; GSSAPI_KRB5_INIT (&context); if (ctx->more_flags & IS_CFX) return _gssapi_wrap_cfx (minor_status, ctx, context, conf_req_flag, input_message_buffer, conf_state, output_message_buffer); HEIMDAL_MUTEX_lock(&ctx->ctx_id_mutex); ret = _gsskrb5i_get_token_key(ctx, context, &key); HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex); if (ret) { *minor_status = ret; return GSS_S_FAILURE; } krb5_enctype_to_keytype (context, key->keytype, &keytype); switch (keytype) { case KEYTYPE_DES : #ifdef HEIM_WEAK_CRYPTO ret = wrap_des (minor_status, ctx, context, conf_req_flag, qop_req, input_message_buffer, conf_state, output_message_buffer, key); #else ret = GSS_S_FAILURE; #endif break; case KEYTYPE_DES3 : ret = wrap_des3 (minor_status, ctx, context, conf_req_flag, qop_req, input_message_buffer, conf_state, output_message_buffer, key); break; case KEYTYPE_ARCFOUR: case KEYTYPE_ARCFOUR_56: ret = _gssapi_wrap_arcfour (minor_status, ctx, context, conf_req_flag, qop_req, input_message_buffer, conf_state, output_message_buffer, key); break; default : abort(); break; } krb5_free_keyblock (context, key); return ret; } Index: projects/openssl111/crypto/heimdal/lib/gssapi/ntlm/crypto.c =================================================================== --- projects/openssl111/crypto/heimdal/lib/gssapi/ntlm/crypto.c (revision 339197) +++ projects/openssl111/crypto/heimdal/lib/gssapi/ntlm/crypto.c (revision 339198) @@ -1,590 +1,592 @@ /* * Copyright (c) 2006 Kungliga Tekniska Högskolan * (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 "ntlm.h" uint32_t _krb5_crc_update (const char *p, size_t len, uint32_t res); void _krb5_crc_init_table(void); /* * */ static void encode_le_uint32(uint32_t n, unsigned char *p) { p[0] = (n >> 0) & 0xFF; p[1] = (n >> 8) & 0xFF; p[2] = (n >> 16) & 0xFF; p[3] = (n >> 24) & 0xFF; } static void decode_le_uint32(const void *ptr, uint32_t *n) { const unsigned char *p = ptr; *n = (p[0] << 0) | (p[1] << 8) | (p[2] << 16) | (p[3] << 24); } /* * */ const char a2i_signmagic[] = "session key to server-to-client signing key magic constant"; const char a2i_sealmagic[] = "session key to server-to-client sealing key magic constant"; const char i2a_signmagic[] = "session key to client-to-server signing key magic constant"; const char i2a_sealmagic[] = "session key to client-to-server sealing key magic constant"; void _gss_ntlm_set_key(struct ntlmv2_key *key, int acceptor, int sealsign, unsigned char *data, size_t len) { unsigned char out[16]; EVP_MD_CTX *ctx; const char *signmagic; const char *sealmagic; if (acceptor) { signmagic = a2i_signmagic; sealmagic = a2i_sealmagic; } else { signmagic = i2a_signmagic; sealmagic = i2a_sealmagic; } key->seq = 0; ctx = EVP_MD_CTX_create(); EVP_DigestInit_ex(ctx, EVP_md5(), NULL); EVP_DigestUpdate(ctx, data, len); EVP_DigestUpdate(ctx, signmagic, strlen(signmagic) + 1); EVP_DigestFinal_ex(ctx, key->signkey, NULL); EVP_DigestInit_ex(ctx, EVP_md5(), NULL); EVP_DigestUpdate(ctx, data, len); EVP_DigestUpdate(ctx, sealmagic, strlen(sealmagic) + 1); EVP_DigestFinal_ex(ctx, out, NULL); EVP_MD_CTX_destroy(ctx); RC4_set_key(&key->sealkey, 16, out); if (sealsign) key->signsealkey = &key->sealkey; } /* * */ static OM_uint32 v1_sign_message(gss_buffer_t in, RC4_KEY *signkey, uint32_t seq, unsigned char out[16]) { unsigned char sigature[12]; uint32_t crc; _krb5_crc_init_table(); crc = _krb5_crc_update(in->value, in->length, 0); encode_le_uint32(0, &sigature[0]); encode_le_uint32(crc, &sigature[4]); encode_le_uint32(seq, &sigature[8]); encode_le_uint32(1, out); /* version */ RC4(signkey, sizeof(sigature), sigature, out + 4); if (RAND_bytes(out + 4, 4) != 1) return GSS_S_UNAVAILABLE; return 0; } static OM_uint32 v2_sign_message(gss_buffer_t in, unsigned char signkey[16], RC4_KEY *sealkey, uint32_t seq, unsigned char out[16]) { unsigned char hmac[16]; unsigned int hmaclen; - HMAC_CTX c; + HMAC_CTX *c; - HMAC_CTX_init(&c); - HMAC_Init_ex(&c, signkey, 16, EVP_md5(), NULL); + c = HMAC_CTX_new(); + if (c == NULL) + return GSS_S_FAILURE; + HMAC_Init_ex(c, signkey, 16, EVP_md5(), NULL); encode_le_uint32(seq, hmac); - HMAC_Update(&c, hmac, 4); - HMAC_Update(&c, in->value, in->length); - HMAC_Final(&c, hmac, &hmaclen); - HMAC_CTX_cleanup(&c); + HMAC_Update(c, hmac, 4); + HMAC_Update(c, in->value, in->length); + HMAC_Final(c, hmac, &hmaclen); + HMAC_CTX_free(c); encode_le_uint32(1, &out[0]); if (sealkey) RC4(sealkey, 8, hmac, &out[4]); else memcpy(&out[4], hmac, 8); memset(&out[12], 0, 4); return GSS_S_COMPLETE; } static OM_uint32 v2_verify_message(gss_buffer_t in, unsigned char signkey[16], RC4_KEY *sealkey, uint32_t seq, const unsigned char checksum[16]) { OM_uint32 ret; unsigned char out[16]; ret = v2_sign_message(in, signkey, sealkey, seq, out); if (ret) return ret; if (memcmp(checksum, out, 16) != 0) return GSS_S_BAD_MIC; return GSS_S_COMPLETE; } static OM_uint32 v2_seal_message(const gss_buffer_t in, unsigned char signkey[16], uint32_t seq, RC4_KEY *sealkey, gss_buffer_t out) { unsigned char *p; OM_uint32 ret; if (in->length + 16 < in->length) return EINVAL; p = malloc(in->length + 16); if (p == NULL) return ENOMEM; RC4(sealkey, in->length, in->value, p); ret = v2_sign_message(in, signkey, sealkey, seq, &p[in->length]); if (ret) { free(p); return ret; } out->value = p; out->length = in->length + 16; return 0; } static OM_uint32 v2_unseal_message(gss_buffer_t in, unsigned char signkey[16], uint32_t seq, RC4_KEY *sealkey, gss_buffer_t out) { OM_uint32 ret; if (in->length < 16) return GSS_S_BAD_MIC; out->length = in->length - 16; out->value = malloc(out->length); if (out->value == NULL) return GSS_S_BAD_MIC; RC4(sealkey, out->length, in->value, out->value); ret = v2_verify_message(out, signkey, sealkey, seq, ((const unsigned char *)in->value) + out->length); if (ret) { OM_uint32 junk; gss_release_buffer(&junk, out); } return ret; } /* * */ #define CTX_FLAGS_ISSET(_ctx,_flags) \ (((_ctx)->flags & (_flags)) == (_flags)) /* * */ OM_uint32 GSSAPI_CALLCONV _gss_ntlm_get_mic (OM_uint32 * minor_status, const gss_ctx_id_t context_handle, gss_qop_t qop_req, const gss_buffer_t message_buffer, gss_buffer_t message_token ) { ntlm_ctx ctx = (ntlm_ctx)context_handle; OM_uint32 junk; *minor_status = 0; message_token->value = malloc(16); message_token->length = 16; if (message_token->value == NULL) { *minor_status = ENOMEM; return GSS_S_FAILURE; } if (CTX_FLAGS_ISSET(ctx, NTLM_NEG_SIGN|NTLM_NEG_NTLM2_SESSION)) { OM_uint32 ret; if ((ctx->status & STATUS_SESSIONKEY) == 0) { gss_release_buffer(&junk, message_token); return GSS_S_UNAVAILABLE; } ret = v2_sign_message(message_buffer, ctx->u.v2.send.signkey, ctx->u.v2.send.signsealkey, ctx->u.v2.send.seq++, message_token->value); if (ret) gss_release_buffer(&junk, message_token); return ret; } else if (CTX_FLAGS_ISSET(ctx, NTLM_NEG_SIGN)) { OM_uint32 ret; if ((ctx->status & STATUS_SESSIONKEY) == 0) { gss_release_buffer(&junk, message_token); return GSS_S_UNAVAILABLE; } ret = v1_sign_message(message_buffer, &ctx->u.v1.crypto_send.key, ctx->u.v1.crypto_send.seq++, message_token->value); if (ret) gss_release_buffer(&junk, message_token); return ret; } else if (CTX_FLAGS_ISSET(ctx, NTLM_NEG_ALWAYS_SIGN)) { unsigned char *sigature; sigature = message_token->value; encode_le_uint32(1, &sigature[0]); /* version */ encode_le_uint32(0, &sigature[4]); encode_le_uint32(0, &sigature[8]); encode_le_uint32(0, &sigature[12]); return GSS_S_COMPLETE; } gss_release_buffer(&junk, message_token); return GSS_S_UNAVAILABLE; } /* * */ OM_uint32 GSSAPI_CALLCONV _gss_ntlm_verify_mic (OM_uint32 * minor_status, const gss_ctx_id_t context_handle, const gss_buffer_t message_buffer, const gss_buffer_t token_buffer, gss_qop_t * qop_state ) { ntlm_ctx ctx = (ntlm_ctx)context_handle; if (qop_state != NULL) *qop_state = GSS_C_QOP_DEFAULT; *minor_status = 0; if (token_buffer->length != 16) return GSS_S_BAD_MIC; if (CTX_FLAGS_ISSET(ctx, NTLM_NEG_SIGN|NTLM_NEG_NTLM2_SESSION)) { OM_uint32 ret; if ((ctx->status & STATUS_SESSIONKEY) == 0) return GSS_S_UNAVAILABLE; ret = v2_verify_message(message_buffer, ctx->u.v2.recv.signkey, ctx->u.v2.recv.signsealkey, ctx->u.v2.recv.seq++, token_buffer->value); if (ret) return ret; return GSS_S_COMPLETE; } else if (CTX_FLAGS_ISSET(ctx, NTLM_NEG_SIGN)) { unsigned char sigature[12]; uint32_t crc, num; if ((ctx->status & STATUS_SESSIONKEY) == 0) return GSS_S_UNAVAILABLE; decode_le_uint32(token_buffer->value, &num); if (num != 1) return GSS_S_BAD_MIC; RC4(&ctx->u.v1.crypto_recv.key, sizeof(sigature), ((unsigned char *)token_buffer->value) + 4, sigature); _krb5_crc_init_table(); crc = _krb5_crc_update(message_buffer->value, message_buffer->length, 0); /* skip first 4 bytes in the encrypted checksum */ decode_le_uint32(&sigature[4], &num); if (num != crc) return GSS_S_BAD_MIC; decode_le_uint32(&sigature[8], &num); if (ctx->u.v1.crypto_recv.seq != num) return GSS_S_BAD_MIC; ctx->u.v1.crypto_recv.seq++; return GSS_S_COMPLETE; } else if (ctx->flags & NTLM_NEG_ALWAYS_SIGN) { uint32_t num; unsigned char *p; p = (unsigned char*)(token_buffer->value); decode_le_uint32(&p[0], &num); /* version */ if (num != 1) return GSS_S_BAD_MIC; decode_le_uint32(&p[4], &num); if (num != 0) return GSS_S_BAD_MIC; decode_le_uint32(&p[8], &num); if (num != 0) return GSS_S_BAD_MIC; decode_le_uint32(&p[12], &num); if (num != 0) return GSS_S_BAD_MIC; return GSS_S_COMPLETE; } return GSS_S_UNAVAILABLE; } /* * */ OM_uint32 GSSAPI_CALLCONV _gss_ntlm_wrap_size_limit ( OM_uint32 * minor_status, const gss_ctx_id_t context_handle, int conf_req_flag, gss_qop_t qop_req, OM_uint32 req_output_size, OM_uint32 * max_input_size ) { ntlm_ctx ctx = (ntlm_ctx)context_handle; *minor_status = 0; if(ctx->flags & NTLM_NEG_SEAL) { if (req_output_size < 16) *max_input_size = 0; else *max_input_size = req_output_size - 16; return GSS_S_COMPLETE; } return GSS_S_UNAVAILABLE; } /* * */ OM_uint32 GSSAPI_CALLCONV _gss_ntlm_wrap (OM_uint32 * minor_status, const gss_ctx_id_t context_handle, int conf_req_flag, gss_qop_t qop_req, const gss_buffer_t input_message_buffer, int * conf_state, gss_buffer_t output_message_buffer ) { ntlm_ctx ctx = (ntlm_ctx)context_handle; OM_uint32 ret; *minor_status = 0; if (conf_state) *conf_state = 0; if (output_message_buffer == GSS_C_NO_BUFFER) return GSS_S_FAILURE; if (CTX_FLAGS_ISSET(ctx, NTLM_NEG_SEAL|NTLM_NEG_NTLM2_SESSION)) { return v2_seal_message(input_message_buffer, ctx->u.v2.send.signkey, ctx->u.v2.send.seq++, &ctx->u.v2.send.sealkey, output_message_buffer); } else if (CTX_FLAGS_ISSET(ctx, NTLM_NEG_SEAL)) { gss_buffer_desc trailer; OM_uint32 junk; output_message_buffer->length = input_message_buffer->length + 16; output_message_buffer->value = malloc(output_message_buffer->length); if (output_message_buffer->value == NULL) { output_message_buffer->length = 0; return GSS_S_FAILURE; } RC4(&ctx->u.v1.crypto_send.key, input_message_buffer->length, input_message_buffer->value, output_message_buffer->value); ret = _gss_ntlm_get_mic(minor_status, context_handle, 0, input_message_buffer, &trailer); if (ret) { gss_release_buffer(&junk, output_message_buffer); return ret; } if (trailer.length != 16) { gss_release_buffer(&junk, output_message_buffer); gss_release_buffer(&junk, &trailer); return GSS_S_FAILURE; } memcpy(((unsigned char *)output_message_buffer->value) + input_message_buffer->length, trailer.value, trailer.length); gss_release_buffer(&junk, &trailer); return GSS_S_COMPLETE; } return GSS_S_UNAVAILABLE; } /* * */ OM_uint32 GSSAPI_CALLCONV _gss_ntlm_unwrap (OM_uint32 * minor_status, const gss_ctx_id_t context_handle, const gss_buffer_t input_message_buffer, gss_buffer_t output_message_buffer, int * conf_state, gss_qop_t * qop_state ) { ntlm_ctx ctx = (ntlm_ctx)context_handle; OM_uint32 ret; *minor_status = 0; output_message_buffer->value = NULL; output_message_buffer->length = 0; if (conf_state) *conf_state = 0; if (qop_state) *qop_state = 0; if (CTX_FLAGS_ISSET(ctx, NTLM_NEG_SEAL|NTLM_NEG_NTLM2_SESSION)) { return v2_unseal_message(input_message_buffer, ctx->u.v2.recv.signkey, ctx->u.v2.recv.seq++, &ctx->u.v2.recv.sealkey, output_message_buffer); } else if (CTX_FLAGS_ISSET(ctx, NTLM_NEG_SEAL)) { gss_buffer_desc trailer; OM_uint32 junk; if (input_message_buffer->length < 16) return GSS_S_BAD_MIC; output_message_buffer->length = input_message_buffer->length - 16; output_message_buffer->value = malloc(output_message_buffer->length); if (output_message_buffer->value == NULL) { output_message_buffer->length = 0; return GSS_S_FAILURE; } RC4(&ctx->u.v1.crypto_recv.key, output_message_buffer->length, input_message_buffer->value, output_message_buffer->value); trailer.value = ((unsigned char *)input_message_buffer->value) + output_message_buffer->length; trailer.length = 16; ret = _gss_ntlm_verify_mic(minor_status, context_handle, output_message_buffer, &trailer, NULL); if (ret) { gss_release_buffer(&junk, output_message_buffer); return ret; } return GSS_S_COMPLETE; } return GSS_S_UNAVAILABLE; } Index: projects/openssl111/crypto/heimdal/lib/hx509/crypto.c =================================================================== --- projects/openssl111/crypto/heimdal/lib/hx509/crypto.c (revision 339197) +++ projects/openssl111/crypto/heimdal/lib/hx509/crypto.c (revision 339198) @@ -1,3207 +1,3289 @@ /* * Copyright (c) 2004 - 2007 Kungliga Tekniska Högskolan * (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 "hx_locl.h" struct hx509_crypto; struct signature_alg; struct hx509_generate_private_context { const heim_oid *key_oid; int isCA; unsigned long num_bits; }; struct hx509_private_key_ops { const char *pemtype; const heim_oid *key_oid; int (*available)(const hx509_private_key, const AlgorithmIdentifier *); int (*get_spki)(hx509_context, const hx509_private_key, SubjectPublicKeyInfo *); int (*export)(hx509_context context, const hx509_private_key, hx509_key_format_t, heim_octet_string *); int (*import)(hx509_context, const AlgorithmIdentifier *, const void *, size_t, hx509_key_format_t, hx509_private_key); int (*generate_private_key)(hx509_context, struct hx509_generate_private_context *, hx509_private_key); BIGNUM *(*get_internal)(hx509_context, hx509_private_key, const char *); }; struct hx509_private_key { unsigned int ref; const struct signature_alg *md; const heim_oid *signature_alg; union { RSA *rsa; void *keydata; #ifdef HAVE_OPENSSL EC_KEY *ecdsa; #endif } private_key; hx509_private_key_ops *ops; }; /* * */ struct signature_alg { const char *name; const heim_oid *sig_oid; const AlgorithmIdentifier *sig_alg; const heim_oid *key_oid; const AlgorithmIdentifier *digest_alg; int flags; #define PROVIDE_CONF 0x1 #define REQUIRE_SIGNER 0x2 #define SELF_SIGNED_OK 0x4 #define SIG_DIGEST 0x100 #define SIG_PUBLIC_SIG 0x200 #define SIG_SECRET 0x400 #define RA_RSA_USES_DIGEST_INFO 0x1000000 time_t best_before; /* refuse signature made after best before date */ const EVP_MD *(*evp_md)(void); int (*verify_signature)(hx509_context context, const struct signature_alg *, const Certificate *, const AlgorithmIdentifier *, const heim_octet_string *, const heim_octet_string *); int (*create_signature)(hx509_context, const struct signature_alg *, const hx509_private_key, const AlgorithmIdentifier *, const heim_octet_string *, AlgorithmIdentifier *, heim_octet_string *); int digest_size; }; static const struct signature_alg * find_sig_alg(const heim_oid *oid); /* * */ static const heim_octet_string null_entry_oid = { 2, rk_UNCONST("\x05\x00") }; static const unsigned sha512_oid_tree[] = { 2, 16, 840, 1, 101, 3, 4, 2, 3 }; const AlgorithmIdentifier _hx509_signature_sha512_data = { { 9, rk_UNCONST(sha512_oid_tree) }, rk_UNCONST(&null_entry_oid) }; static const unsigned sha384_oid_tree[] = { 2, 16, 840, 1, 101, 3, 4, 2, 2 }; const AlgorithmIdentifier _hx509_signature_sha384_data = { { 9, rk_UNCONST(sha384_oid_tree) }, rk_UNCONST(&null_entry_oid) }; static const unsigned sha256_oid_tree[] = { 2, 16, 840, 1, 101, 3, 4, 2, 1 }; const AlgorithmIdentifier _hx509_signature_sha256_data = { { 9, rk_UNCONST(sha256_oid_tree) }, rk_UNCONST(&null_entry_oid) }; static const unsigned sha1_oid_tree[] = { 1, 3, 14, 3, 2, 26 }; const AlgorithmIdentifier _hx509_signature_sha1_data = { { 6, rk_UNCONST(sha1_oid_tree) }, rk_UNCONST(&null_entry_oid) }; static const unsigned md5_oid_tree[] = { 1, 2, 840, 113549, 2, 5 }; const AlgorithmIdentifier _hx509_signature_md5_data = { { 6, rk_UNCONST(md5_oid_tree) }, rk_UNCONST(&null_entry_oid) }; static const unsigned ecPublicKey[] ={ 1, 2, 840, 10045, 2, 1 }; const AlgorithmIdentifier _hx509_signature_ecPublicKey = { { 6, rk_UNCONST(ecPublicKey) }, NULL }; static const unsigned ecdsa_with_sha256_oid[] ={ 1, 2, 840, 10045, 4, 3, 2 }; const AlgorithmIdentifier _hx509_signature_ecdsa_with_sha256_data = { { 7, rk_UNCONST(ecdsa_with_sha256_oid) }, NULL }; static const unsigned ecdsa_with_sha1_oid[] ={ 1, 2, 840, 10045, 4, 1 }; const AlgorithmIdentifier _hx509_signature_ecdsa_with_sha1_data = { { 6, rk_UNCONST(ecdsa_with_sha1_oid) }, NULL }; static const unsigned rsa_with_sha512_oid[] ={ 1, 2, 840, 113549, 1, 1, 13 }; const AlgorithmIdentifier _hx509_signature_rsa_with_sha512_data = { { 7, rk_UNCONST(rsa_with_sha512_oid) }, NULL }; static const unsigned rsa_with_sha384_oid[] ={ 1, 2, 840, 113549, 1, 1, 12 }; const AlgorithmIdentifier _hx509_signature_rsa_with_sha384_data = { { 7, rk_UNCONST(rsa_with_sha384_oid) }, NULL }; static const unsigned rsa_with_sha256_oid[] ={ 1, 2, 840, 113549, 1, 1, 11 }; const AlgorithmIdentifier _hx509_signature_rsa_with_sha256_data = { { 7, rk_UNCONST(rsa_with_sha256_oid) }, NULL }; static const unsigned rsa_with_sha1_oid[] ={ 1, 2, 840, 113549, 1, 1, 5 }; const AlgorithmIdentifier _hx509_signature_rsa_with_sha1_data = { { 7, rk_UNCONST(rsa_with_sha1_oid) }, NULL }; static const unsigned rsa_with_md5_oid[] ={ 1, 2, 840, 113549, 1, 1, 4 }; const AlgorithmIdentifier _hx509_signature_rsa_with_md5_data = { { 7, rk_UNCONST(rsa_with_md5_oid) }, NULL }; static const unsigned rsa_oid[] ={ 1, 2, 840, 113549, 1, 1, 1 }; const AlgorithmIdentifier _hx509_signature_rsa_data = { { 7, rk_UNCONST(rsa_oid) }, NULL }; static const unsigned rsa_pkcs1_x509_oid[] ={ 1, 2, 752, 43, 16, 1 }; const AlgorithmIdentifier _hx509_signature_rsa_pkcs1_x509_data = { { 6, rk_UNCONST(rsa_pkcs1_x509_oid) }, NULL }; static const unsigned des_rsdi_ede3_cbc_oid[] ={ 1, 2, 840, 113549, 3, 7 }; const AlgorithmIdentifier _hx509_des_rsdi_ede3_cbc_oid = { { 6, rk_UNCONST(des_rsdi_ede3_cbc_oid) }, NULL }; static const unsigned aes128_cbc_oid[] ={ 2, 16, 840, 1, 101, 3, 4, 1, 2 }; const AlgorithmIdentifier _hx509_crypto_aes128_cbc_data = { { 9, rk_UNCONST(aes128_cbc_oid) }, NULL }; static const unsigned aes256_cbc_oid[] ={ 2, 16, 840, 1, 101, 3, 4, 1, 42 }; const AlgorithmIdentifier _hx509_crypto_aes256_cbc_data = { { 9, rk_UNCONST(aes256_cbc_oid) }, NULL }; /* * */ static BIGNUM * heim_int2BN(const heim_integer *i) { BIGNUM *bn; bn = BN_bin2bn(i->data, i->length, NULL); - BN_set_negative(bn, i->negative); + if (bn != NULL) + BN_set_negative(bn, i->negative); return bn; } /* * */ static int set_digest_alg(DigestAlgorithmIdentifier *id, const heim_oid *oid, const void *param, size_t length) { int ret; if (param) { id->parameters = malloc(sizeof(*id->parameters)); if (id->parameters == NULL) return ENOMEM; id->parameters->data = malloc(length); if (id->parameters->data == NULL) { free(id->parameters); id->parameters = NULL; return ENOMEM; } memcpy(id->parameters->data, param, length); id->parameters->length = length; } else id->parameters = NULL; ret = der_copy_oid(oid, &id->algorithm); if (ret) { if (id->parameters) { free(id->parameters->data); free(id->parameters); id->parameters = NULL; } return ret; } return 0; } #ifdef HAVE_OPENSSL static int heim_oid2ecnid(heim_oid *oid) { /* * Now map to openssl OID fun */ if (der_heim_oid_cmp(oid, ASN1_OID_ID_EC_GROUP_SECP256R1) == 0) return NID_X9_62_prime256v1; else if (der_heim_oid_cmp(oid, ASN1_OID_ID_EC_GROUP_SECP160R1) == 0) return NID_secp160r1; else if (der_heim_oid_cmp(oid, ASN1_OID_ID_EC_GROUP_SECP160R2) == 0) return NID_secp160r2; return -1; } static int parse_ECParameters(hx509_context context, heim_octet_string *parameters, int *nid) { ECParameters ecparam; size_t size; int ret; if (parameters == NULL) { ret = HX509_PARSING_KEY_FAILED; hx509_set_error_string(context, 0, ret, "EC parameters missing"); return ret; } ret = decode_ECParameters(parameters->data, parameters->length, &ecparam, &size); if (ret) { hx509_set_error_string(context, 0, ret, "Failed to decode EC parameters"); return ret; } if (ecparam.element != choice_ECParameters_namedCurve) { free_ECParameters(&ecparam); hx509_set_error_string(context, 0, ret, "EC parameters is not a named curve"); return HX509_CRYPTO_SIG_INVALID_FORMAT; } *nid = heim_oid2ecnid(&ecparam.u.namedCurve); free_ECParameters(&ecparam); if (*nid == -1) { hx509_set_error_string(context, 0, ret, "Failed to find matcing NID for EC curve"); return HX509_CRYPTO_SIG_INVALID_FORMAT; } return 0; } /* * */ static int ecdsa_verify_signature(hx509_context context, const struct signature_alg *sig_alg, const Certificate *signer, const AlgorithmIdentifier *alg, const heim_octet_string *data, const heim_octet_string *sig) { const AlgorithmIdentifier *digest_alg; const SubjectPublicKeyInfo *spi; heim_octet_string digest; int ret; EC_KEY *key = NULL; int groupnid; EC_GROUP *group; const unsigned char *p; long len; digest_alg = sig_alg->digest_alg; ret = _hx509_create_signature(context, NULL, digest_alg, data, NULL, &digest); if (ret) return ret; /* set up EC KEY */ spi = &signer->tbsCertificate.subjectPublicKeyInfo; if (der_heim_oid_cmp(&spi->algorithm.algorithm, ASN1_OID_ID_ECPUBLICKEY) != 0) return HX509_CRYPTO_SIG_INVALID_FORMAT; #ifdef HAVE_OPENSSL /* * Find the group id */ ret = parse_ECParameters(context, spi->algorithm.parameters, &groupnid); if (ret) { der_free_octet_string(&digest); return ret; } /* * Create group, key, parse key */ key = EC_KEY_new(); group = EC_GROUP_new_by_curve_name(groupnid); EC_KEY_set_group(key, group); EC_GROUP_free(group); p = spi->subjectPublicKey.data; len = spi->subjectPublicKey.length / 8; if (o2i_ECPublicKey(&key, &p, len) == NULL) { EC_KEY_free(key); return HX509_CRYPTO_SIG_INVALID_FORMAT; } #else key = SubjectPublicKeyInfo2EC_KEY(spi); #endif ret = ECDSA_verify(-1, digest.data, digest.length, sig->data, sig->length, key); der_free_octet_string(&digest); EC_KEY_free(key); if (ret != 1) { ret = HX509_CRYPTO_SIG_INVALID_FORMAT; return ret; } return 0; } static int ecdsa_create_signature(hx509_context context, const struct signature_alg *sig_alg, const hx509_private_key signer, const AlgorithmIdentifier *alg, const heim_octet_string *data, AlgorithmIdentifier *signatureAlgorithm, heim_octet_string *sig) { const AlgorithmIdentifier *digest_alg; heim_octet_string indata; const heim_oid *sig_oid; unsigned int siglen; int ret; if (signer->ops && der_heim_oid_cmp(signer->ops->key_oid, ASN1_OID_ID_ECPUBLICKEY) != 0) _hx509_abort("internal error passing private key to wrong ops"); sig_oid = sig_alg->sig_oid; digest_alg = sig_alg->digest_alg; if (signatureAlgorithm) { ret = set_digest_alg(signatureAlgorithm, sig_oid, "\x05\x00", 2); if (ret) { hx509_clear_error_string(context); goto error; } } ret = _hx509_create_signature(context, NULL, digest_alg, data, NULL, &indata); if (ret) { if (signatureAlgorithm) free_AlgorithmIdentifier(signatureAlgorithm); goto error; } sig->length = ECDSA_size(signer->private_key.ecdsa); sig->data = malloc(sig->length); if (sig->data == NULL) { der_free_octet_string(&indata); ret = ENOMEM; hx509_set_error_string(context, 0, ret, "out of memory"); goto error; } siglen = sig->length; ret = ECDSA_sign(-1, indata.data, indata.length, sig->data, &siglen, signer->private_key.ecdsa); der_free_octet_string(&indata); if (ret != 1) { ret = HX509_CMS_FAILED_CREATE_SIGATURE; hx509_set_error_string(context, 0, ret, "ECDSA sign failed: %d", ret); goto error; } if (siglen > sig->length) _hx509_abort("ECDSA signature prelen longer the output len"); sig->length = siglen; return 0; error: if (signatureAlgorithm) free_AlgorithmIdentifier(signatureAlgorithm); return ret; } static int ecdsa_available(const hx509_private_key signer, const AlgorithmIdentifier *sig_alg) { const struct signature_alg *sig; const EC_GROUP *group; BN_CTX *bnctx = NULL; BIGNUM *order = NULL; int ret = 0; if (der_heim_oid_cmp(signer->ops->key_oid, &asn1_oid_id_ecPublicKey) != 0) _hx509_abort("internal error passing private key to wrong ops"); sig = find_sig_alg(&sig_alg->algorithm); if (sig == NULL || sig->digest_size == 0) return 0; group = EC_KEY_get0_group(signer->private_key.ecdsa); if (group == NULL) return 0; bnctx = BN_CTX_new(); order = BN_new(); if (order == NULL) goto err; if (EC_GROUP_get_order(group, order, bnctx) != 1) goto err; if (BN_num_bytes(order) > sig->digest_size) ret = 1; err: if (bnctx) BN_CTX_free(bnctx); if (order) BN_clear_free(order); return ret; } #endif /* HAVE_OPENSSL */ /* * */ static int rsa_verify_signature(hx509_context context, const struct signature_alg *sig_alg, const Certificate *signer, const AlgorithmIdentifier *alg, const heim_octet_string *data, const heim_octet_string *sig) { const SubjectPublicKeyInfo *spi; DigestInfo di; unsigned char *to; int tosize, retsize; int ret; RSA *rsa; size_t size; const unsigned char *p; memset(&di, 0, sizeof(di)); spi = &signer->tbsCertificate.subjectPublicKeyInfo; p = spi->subjectPublicKey.data; size = spi->subjectPublicKey.length / 8; rsa = d2i_RSAPublicKey(NULL, &p, size); if (rsa == NULL) { ret = ENOMEM; hx509_set_error_string(context, 0, ret, "out of memory"); goto out; } tosize = RSA_size(rsa); to = malloc(tosize); if (to == NULL) { ret = ENOMEM; hx509_set_error_string(context, 0, ret, "out of memory"); goto out; } retsize = RSA_public_decrypt(sig->length, (unsigned char *)sig->data, to, rsa, RSA_PKCS1_PADDING); if (retsize <= 0) { ret = HX509_CRYPTO_SIG_INVALID_FORMAT; hx509_set_error_string(context, 0, ret, "RSA public decrypt failed: %d", retsize); free(to); goto out; } if (retsize > tosize) _hx509_abort("internal rsa decryption failure: ret > tosize"); if (sig_alg->flags & RA_RSA_USES_DIGEST_INFO) { ret = decode_DigestInfo(to, retsize, &di, &size); free(to); if (ret) { goto out; } /* Check for extra data inside the sigature */ if (size != (size_t)retsize) { ret = HX509_CRYPTO_SIG_INVALID_FORMAT; hx509_set_error_string(context, 0, ret, "size from decryption mismatch"); goto out; } if (sig_alg->digest_alg && der_heim_oid_cmp(&di.digestAlgorithm.algorithm, &sig_alg->digest_alg->algorithm) != 0) { ret = HX509_CRYPTO_OID_MISMATCH; hx509_set_error_string(context, 0, ret, "object identifier in RSA sig mismatch"); goto out; } /* verify that the parameters are NULL or the NULL-type */ if (di.digestAlgorithm.parameters != NULL && (di.digestAlgorithm.parameters->length != 2 || memcmp(di.digestAlgorithm.parameters->data, "\x05\x00", 2) != 0)) { ret = HX509_CRYPTO_SIG_INVALID_FORMAT; hx509_set_error_string(context, 0, ret, "Extra parameters inside RSA signature"); goto out; } ret = _hx509_verify_signature(context, NULL, &di.digestAlgorithm, data, &di.digest); } else { if ((size_t)retsize != data->length || ct_memcmp(to, data->data, retsize) != 0) { ret = HX509_CRYPTO_SIG_INVALID_FORMAT; hx509_set_error_string(context, 0, ret, "RSA Signature incorrect"); goto out; } free(to); } ret = 0; out: free_DigestInfo(&di); if (rsa) RSA_free(rsa); return ret; } static int rsa_create_signature(hx509_context context, const struct signature_alg *sig_alg, const hx509_private_key signer, const AlgorithmIdentifier *alg, const heim_octet_string *data, AlgorithmIdentifier *signatureAlgorithm, heim_octet_string *sig) { const AlgorithmIdentifier *digest_alg; heim_octet_string indata; const heim_oid *sig_oid; size_t size; int ret; if (signer->ops && der_heim_oid_cmp(signer->ops->key_oid, ASN1_OID_ID_PKCS1_RSAENCRYPTION) != 0) return HX509_ALG_NOT_SUPP; if (alg) sig_oid = &alg->algorithm; else sig_oid = signer->signature_alg; if (der_heim_oid_cmp(sig_oid, ASN1_OID_ID_PKCS1_SHA512WITHRSAENCRYPTION) == 0) { digest_alg = hx509_signature_sha512(); } else if (der_heim_oid_cmp(sig_oid, ASN1_OID_ID_PKCS1_SHA384WITHRSAENCRYPTION) == 0) { digest_alg = hx509_signature_sha384(); } else if (der_heim_oid_cmp(sig_oid, ASN1_OID_ID_PKCS1_SHA256WITHRSAENCRYPTION) == 0) { digest_alg = hx509_signature_sha256(); } else if (der_heim_oid_cmp(sig_oid, ASN1_OID_ID_PKCS1_SHA1WITHRSAENCRYPTION) == 0) { digest_alg = hx509_signature_sha1(); } else if (der_heim_oid_cmp(sig_oid, ASN1_OID_ID_PKCS1_MD5WITHRSAENCRYPTION) == 0) { digest_alg = hx509_signature_md5(); } else if (der_heim_oid_cmp(sig_oid, ASN1_OID_ID_PKCS1_MD5WITHRSAENCRYPTION) == 0) { digest_alg = hx509_signature_md5(); } else if (der_heim_oid_cmp(sig_oid, ASN1_OID_ID_DSA_WITH_SHA1) == 0) { digest_alg = hx509_signature_sha1(); } else if (der_heim_oid_cmp(sig_oid, ASN1_OID_ID_PKCS1_RSAENCRYPTION) == 0) { digest_alg = hx509_signature_sha1(); } else if (der_heim_oid_cmp(sig_oid, ASN1_OID_ID_HEIM_RSA_PKCS1_X509) == 0) { digest_alg = NULL; } else return HX509_ALG_NOT_SUPP; if (signatureAlgorithm) { ret = set_digest_alg(signatureAlgorithm, sig_oid, "\x05\x00", 2); if (ret) { hx509_clear_error_string(context); return ret; } } if (digest_alg) { DigestInfo di; memset(&di, 0, sizeof(di)); ret = _hx509_create_signature(context, NULL, digest_alg, data, &di.digestAlgorithm, &di.digest); if (ret) return ret; ASN1_MALLOC_ENCODE(DigestInfo, indata.data, indata.length, &di, &size, ret); free_DigestInfo(&di); if (ret) { hx509_set_error_string(context, 0, ret, "out of memory"); return ret; } if (indata.length != size) _hx509_abort("internal ASN.1 encoder error"); } else { indata = *data; } sig->length = RSA_size(signer->private_key.rsa); sig->data = malloc(sig->length); if (sig->data == NULL) { der_free_octet_string(&indata); hx509_set_error_string(context, 0, ENOMEM, "out of memory"); return ENOMEM; } ret = RSA_private_encrypt(indata.length, indata.data, sig->data, signer->private_key.rsa, RSA_PKCS1_PADDING); if (indata.data != data->data) der_free_octet_string(&indata); if (ret <= 0) { ret = HX509_CMS_FAILED_CREATE_SIGATURE; hx509_set_error_string(context, 0, ret, "RSA private encrypt failed: %d", ret); return ret; } if ((size_t)ret > sig->length) _hx509_abort("RSA signature prelen longer the output len"); sig->length = ret; return 0; } static int rsa_private_key_import(hx509_context context, const AlgorithmIdentifier *keyai, const void *data, size_t len, hx509_key_format_t format, hx509_private_key private_key) { switch (format) { case HX509_KEY_FORMAT_DER: { const unsigned char *p = data; private_key->private_key.rsa = d2i_RSAPrivateKey(NULL, &p, len); if (private_key->private_key.rsa == NULL) { hx509_set_error_string(context, 0, HX509_PARSING_KEY_FAILED, "Failed to parse RSA key"); return HX509_PARSING_KEY_FAILED; } private_key->signature_alg = ASN1_OID_ID_PKCS1_SHA1WITHRSAENCRYPTION; break; } default: return HX509_CRYPTO_KEY_FORMAT_UNSUPPORTED; } return 0; } static int rsa_private_key2SPKI(hx509_context context, hx509_private_key private_key, SubjectPublicKeyInfo *spki) { int len, ret; memset(spki, 0, sizeof(*spki)); len = i2d_RSAPublicKey(private_key->private_key.rsa, NULL); spki->subjectPublicKey.data = malloc(len); if (spki->subjectPublicKey.data == NULL) { hx509_set_error_string(context, 0, ENOMEM, "malloc - out of memory"); return ENOMEM; } spki->subjectPublicKey.length = len * 8; ret = set_digest_alg(&spki->algorithm, ASN1_OID_ID_PKCS1_RSAENCRYPTION, "\x05\x00", 2); if (ret) { hx509_set_error_string(context, 0, ret, "malloc - out of memory"); free(spki->subjectPublicKey.data); spki->subjectPublicKey.data = NULL; spki->subjectPublicKey.length = 0; return ret; } { unsigned char *pp = spki->subjectPublicKey.data; i2d_RSAPublicKey(private_key->private_key.rsa, &pp); } return 0; } static int rsa_generate_private_key(hx509_context context, struct hx509_generate_private_context *ctx, hx509_private_key private_key) { BIGNUM *e; int ret; unsigned long bits; static const int default_rsa_e = 65537; static const int default_rsa_bits = 2048; private_key->private_key.rsa = RSA_new(); if (private_key->private_key.rsa == NULL) { hx509_set_error_string(context, 0, HX509_PARSING_KEY_FAILED, "Failed to generate RSA key"); return HX509_PARSING_KEY_FAILED; } e = BN_new(); BN_set_word(e, default_rsa_e); bits = default_rsa_bits; if (ctx->num_bits) bits = ctx->num_bits; ret = RSA_generate_key_ex(private_key->private_key.rsa, bits, e, NULL); BN_free(e); if (ret != 1) { hx509_set_error_string(context, 0, HX509_PARSING_KEY_FAILED, "Failed to generate RSA key"); return HX509_PARSING_KEY_FAILED; } private_key->signature_alg = ASN1_OID_ID_PKCS1_SHA1WITHRSAENCRYPTION; return 0; } static int rsa_private_key_export(hx509_context context, const hx509_private_key key, hx509_key_format_t format, heim_octet_string *data) { int ret; data->data = NULL; data->length = 0; switch (format) { case HX509_KEY_FORMAT_DER: ret = i2d_RSAPrivateKey(key->private_key.rsa, NULL); if (ret <= 0) { ret = EINVAL; hx509_set_error_string(context, 0, ret, "Private key is not exportable"); return ret; } data->data = malloc(ret); if (data->data == NULL) { ret = ENOMEM; hx509_set_error_string(context, 0, ret, "malloc out of memory"); return ret; } data->length = ret; { unsigned char *p = data->data; i2d_RSAPrivateKey(key->private_key.rsa, &p); } break; default: return HX509_CRYPTO_KEY_FORMAT_UNSUPPORTED; } return 0; } static BIGNUM * rsa_get_internal(hx509_context context, hx509_private_key key, const char *type) { + const BIGNUM *n; + if (strcasecmp(type, "rsa-modulus") == 0) { - return BN_dup(key->private_key.rsa->n); + RSA_get0_key(key->private_key.rsa, &n, NULL, NULL); } else if (strcasecmp(type, "rsa-exponent") == 0) { - return BN_dup(key->private_key.rsa->e); + RSA_get0_key(key->private_key.rsa, NULL, &n, NULL); } else return NULL; + return BN_dup(n); } static hx509_private_key_ops rsa_private_key_ops = { "RSA PRIVATE KEY", ASN1_OID_ID_PKCS1_RSAENCRYPTION, NULL, rsa_private_key2SPKI, rsa_private_key_export, rsa_private_key_import, rsa_generate_private_key, rsa_get_internal }; #ifdef HAVE_OPENSSL static int ecdsa_private_key2SPKI(hx509_context context, hx509_private_key private_key, SubjectPublicKeyInfo *spki) { memset(spki, 0, sizeof(*spki)); return ENOMEM; } static int ecdsa_private_key_export(hx509_context context, const hx509_private_key key, hx509_key_format_t format, heim_octet_string *data) { return HX509_CRYPTO_KEY_FORMAT_UNSUPPORTED; } static int ecdsa_private_key_import(hx509_context context, const AlgorithmIdentifier *keyai, const void *data, size_t len, hx509_key_format_t format, hx509_private_key private_key) { const unsigned char *p = data; EC_KEY **pkey = NULL; if (keyai->parameters) { EC_GROUP *group; int groupnid; EC_KEY *key; int ret; ret = parse_ECParameters(context, keyai->parameters, &groupnid); if (ret) return ret; key = EC_KEY_new(); if (key == NULL) return ENOMEM; group = EC_GROUP_new_by_curve_name(groupnid); if (group == NULL) { EC_KEY_free(key); return ENOMEM; } EC_GROUP_set_asn1_flag(group, OPENSSL_EC_NAMED_CURVE); if (EC_KEY_set_group(key, group) == 0) { EC_KEY_free(key); EC_GROUP_free(group); return ENOMEM; } EC_GROUP_free(group); pkey = &key; } switch (format) { case HX509_KEY_FORMAT_DER: private_key->private_key.ecdsa = d2i_ECPrivateKey(pkey, &p, len); if (private_key->private_key.ecdsa == NULL) { hx509_set_error_string(context, 0, HX509_PARSING_KEY_FAILED, "Failed to parse EC private key"); return HX509_PARSING_KEY_FAILED; } private_key->signature_alg = ASN1_OID_ID_ECDSA_WITH_SHA256; break; default: return HX509_CRYPTO_KEY_FORMAT_UNSUPPORTED; } return 0; } static int ecdsa_generate_private_key(hx509_context context, struct hx509_generate_private_context *ctx, hx509_private_key private_key) { return ENOMEM; } static BIGNUM * ecdsa_get_internal(hx509_context context, hx509_private_key key, const char *type) { return NULL; } static hx509_private_key_ops ecdsa_private_key_ops = { "EC PRIVATE KEY", ASN1_OID_ID_ECPUBLICKEY, ecdsa_available, ecdsa_private_key2SPKI, ecdsa_private_key_export, ecdsa_private_key_import, ecdsa_generate_private_key, ecdsa_get_internal }; #endif /* HAVE_OPENSSL */ /* * */ static int dsa_verify_signature(hx509_context context, const struct signature_alg *sig_alg, const Certificate *signer, const AlgorithmIdentifier *alg, const heim_octet_string *data, const heim_octet_string *sig) { const SubjectPublicKeyInfo *spi; DSAPublicKey pk; DSAParams param; size_t size; + BIGNUM *key, *p, *q, *g; DSA *dsa; int ret; spi = &signer->tbsCertificate.subjectPublicKeyInfo; dsa = DSA_new(); if (dsa == NULL) { hx509_set_error_string(context, 0, ENOMEM, "out of memory"); return ENOMEM; } ret = decode_DSAPublicKey(spi->subjectPublicKey.data, spi->subjectPublicKey.length / 8, &pk, &size); if (ret) goto out; - dsa->pub_key = heim_int2BN(&pk); + key = heim_int2BN(&pk); free_DSAPublicKey(&pk); - if (dsa->pub_key == NULL) { + if (key == NULL) { ret = ENOMEM; hx509_set_error_string(context, 0, ret, "out of memory"); goto out; } + ret = DSA_set0_key(dsa, key, NULL); + + if (ret != 1) { + BN_free(key); + ret = EINVAL; + hx509_set_error_string(context, 0, ret, "failed to set DSA key"); + goto out; + } + if (spi->algorithm.parameters == NULL) { ret = HX509_CRYPTO_SIG_INVALID_FORMAT; hx509_set_error_string(context, 0, ret, "DSA parameters missing"); goto out; } ret = decode_DSAParams(spi->algorithm.parameters->data, spi->algorithm.parameters->length, ¶m, &size); if (ret) { hx509_set_error_string(context, 0, ret, "DSA parameters failed to decode"); goto out; } - dsa->p = heim_int2BN(¶m.p); - dsa->q = heim_int2BN(¶m.q); - dsa->g = heim_int2BN(¶m.g); + p = heim_int2BN(¶m.p); + q = heim_int2BN(¶m.q); + g = heim_int2BN(¶m.g); free_DSAParams(¶m); - if (dsa->p == NULL || dsa->q == NULL || dsa->g == NULL) { + if (p == NULL || q == NULL || g == NULL) { + BN_free(p); + BN_free(q); + BN_free(g); ret = ENOMEM; hx509_set_error_string(context, 0, ret, "out of memory"); goto out; } + ret = DSA_set0_pqg(dsa, p, q, g); + + if (ret != 1) { + BN_free(p); + BN_free(q); + BN_free(g); + ret = EINVAL; + hx509_set_error_string(context, 0, ret, "failed to set DSA parameters"); + goto out; + } + ret = DSA_verify(-1, data->data, data->length, (unsigned char*)sig->data, sig->length, dsa); if (ret == 1) ret = 0; else if (ret == 0 || ret == -1) { ret = HX509_CRYPTO_BAD_SIGNATURE; hx509_set_error_string(context, 0, ret, "BAD DSA sigature"); } else { ret = HX509_CRYPTO_SIG_INVALID_FORMAT; hx509_set_error_string(context, 0, ret, "Invalid format of DSA sigature"); } out: DSA_free(dsa); return ret; } #if 0 static int dsa_parse_private_key(hx509_context context, const void *data, size_t len, hx509_private_key private_key) { const unsigned char *p = data; private_key->private_key.dsa = d2i_DSAPrivateKey(NULL, &p, len); if (private_key->private_key.dsa == NULL) return EINVAL; private_key->signature_alg = ASN1_OID_ID_DSA_WITH_SHA1; return 0; /* else */ hx509_set_error_string(context, 0, HX509_PARSING_KEY_FAILED, "No support to parse DSA keys"); return HX509_PARSING_KEY_FAILED; } #endif static int evp_md_create_signature(hx509_context context, const struct signature_alg *sig_alg, const hx509_private_key signer, const AlgorithmIdentifier *alg, const heim_octet_string *data, AlgorithmIdentifier *signatureAlgorithm, heim_octet_string *sig) { size_t sigsize = EVP_MD_size(sig_alg->evp_md()); EVP_MD_CTX *ctx; memset(sig, 0, sizeof(*sig)); if (signatureAlgorithm) { int ret; ret = set_digest_alg(signatureAlgorithm, sig_alg->sig_oid, "\x05\x00", 2); if (ret) return ret; } sig->data = malloc(sigsize); if (sig->data == NULL) { sig->length = 0; return ENOMEM; } sig->length = sigsize; ctx = EVP_MD_CTX_create(); EVP_DigestInit_ex(ctx, sig_alg->evp_md(), NULL); EVP_DigestUpdate(ctx, data->data, data->length); EVP_DigestFinal_ex(ctx, sig->data, NULL); EVP_MD_CTX_destroy(ctx); return 0; } static int evp_md_verify_signature(hx509_context context, const struct signature_alg *sig_alg, const Certificate *signer, const AlgorithmIdentifier *alg, const heim_octet_string *data, const heim_octet_string *sig) { unsigned char digest[EVP_MAX_MD_SIZE]; EVP_MD_CTX *ctx; size_t sigsize = EVP_MD_size(sig_alg->evp_md()); if (sig->length != sigsize || sigsize > sizeof(digest)) { hx509_set_error_string(context, 0, HX509_CRYPTO_SIG_INVALID_FORMAT, "SHA256 sigature have wrong length"); return HX509_CRYPTO_SIG_INVALID_FORMAT; } ctx = EVP_MD_CTX_create(); EVP_DigestInit_ex(ctx, sig_alg->evp_md(), NULL); EVP_DigestUpdate(ctx, data->data, data->length); EVP_DigestFinal_ex(ctx, digest, NULL); EVP_MD_CTX_destroy(ctx); if (ct_memcmp(digest, sig->data, sigsize) != 0) { hx509_set_error_string(context, 0, HX509_CRYPTO_BAD_SIGNATURE, "Bad %s sigature", sig_alg->name); return HX509_CRYPTO_BAD_SIGNATURE; } return 0; } #ifdef HAVE_OPENSSL static const struct signature_alg ecdsa_with_sha256_alg = { "ecdsa-with-sha256", ASN1_OID_ID_ECDSA_WITH_SHA256, &_hx509_signature_ecdsa_with_sha256_data, ASN1_OID_ID_ECPUBLICKEY, &_hx509_signature_sha256_data, PROVIDE_CONF|REQUIRE_SIGNER|RA_RSA_USES_DIGEST_INFO|SIG_PUBLIC_SIG|SELF_SIGNED_OK, 0, NULL, ecdsa_verify_signature, ecdsa_create_signature, 32 }; static const struct signature_alg ecdsa_with_sha1_alg = { "ecdsa-with-sha1", ASN1_OID_ID_ECDSA_WITH_SHA1, &_hx509_signature_ecdsa_with_sha1_data, ASN1_OID_ID_ECPUBLICKEY, &_hx509_signature_sha1_data, PROVIDE_CONF|REQUIRE_SIGNER|RA_RSA_USES_DIGEST_INFO|SIG_PUBLIC_SIG|SELF_SIGNED_OK, 0, NULL, ecdsa_verify_signature, ecdsa_create_signature, 20 }; #endif static const struct signature_alg heim_rsa_pkcs1_x509 = { "rsa-pkcs1-x509", ASN1_OID_ID_HEIM_RSA_PKCS1_X509, &_hx509_signature_rsa_pkcs1_x509_data, ASN1_OID_ID_PKCS1_RSAENCRYPTION, NULL, PROVIDE_CONF|REQUIRE_SIGNER|SIG_PUBLIC_SIG, 0, NULL, rsa_verify_signature, rsa_create_signature, 0 }; static const struct signature_alg pkcs1_rsa_sha1_alg = { "rsa", ASN1_OID_ID_PKCS1_RSAENCRYPTION, &_hx509_signature_rsa_with_sha1_data, ASN1_OID_ID_PKCS1_RSAENCRYPTION, NULL, PROVIDE_CONF|REQUIRE_SIGNER|RA_RSA_USES_DIGEST_INFO|SIG_PUBLIC_SIG|SELF_SIGNED_OK, 0, NULL, rsa_verify_signature, rsa_create_signature, 0 }; static const struct signature_alg rsa_with_sha512_alg = { "rsa-with-sha512", ASN1_OID_ID_PKCS1_SHA512WITHRSAENCRYPTION, &_hx509_signature_rsa_with_sha512_data, ASN1_OID_ID_PKCS1_RSAENCRYPTION, &_hx509_signature_sha512_data, PROVIDE_CONF|REQUIRE_SIGNER|RA_RSA_USES_DIGEST_INFO|SIG_PUBLIC_SIG|SELF_SIGNED_OK, 0, NULL, rsa_verify_signature, rsa_create_signature, 0 }; static const struct signature_alg rsa_with_sha384_alg = { "rsa-with-sha384", ASN1_OID_ID_PKCS1_SHA384WITHRSAENCRYPTION, &_hx509_signature_rsa_with_sha384_data, ASN1_OID_ID_PKCS1_RSAENCRYPTION, &_hx509_signature_sha384_data, PROVIDE_CONF|REQUIRE_SIGNER|RA_RSA_USES_DIGEST_INFO|SIG_PUBLIC_SIG|SELF_SIGNED_OK, 0, NULL, rsa_verify_signature, rsa_create_signature, 0 }; static const struct signature_alg rsa_with_sha256_alg = { "rsa-with-sha256", ASN1_OID_ID_PKCS1_SHA256WITHRSAENCRYPTION, &_hx509_signature_rsa_with_sha256_data, ASN1_OID_ID_PKCS1_RSAENCRYPTION, &_hx509_signature_sha256_data, PROVIDE_CONF|REQUIRE_SIGNER|RA_RSA_USES_DIGEST_INFO|SIG_PUBLIC_SIG|SELF_SIGNED_OK, 0, NULL, rsa_verify_signature, rsa_create_signature, 0 }; static const struct signature_alg rsa_with_sha1_alg = { "rsa-with-sha1", ASN1_OID_ID_PKCS1_SHA1WITHRSAENCRYPTION, &_hx509_signature_rsa_with_sha1_data, ASN1_OID_ID_PKCS1_RSAENCRYPTION, &_hx509_signature_sha1_data, PROVIDE_CONF|REQUIRE_SIGNER|RA_RSA_USES_DIGEST_INFO|SIG_PUBLIC_SIG|SELF_SIGNED_OK, 0, NULL, rsa_verify_signature, rsa_create_signature, 0 }; static const struct signature_alg rsa_with_sha1_alg_secsig = { "rsa-with-sha1", ASN1_OID_ID_SECSIG_SHA_1WITHRSAENCRYPTION, &_hx509_signature_rsa_with_sha1_data, ASN1_OID_ID_PKCS1_RSAENCRYPTION, &_hx509_signature_sha1_data, PROVIDE_CONF|REQUIRE_SIGNER|RA_RSA_USES_DIGEST_INFO|SIG_PUBLIC_SIG|SELF_SIGNED_OK, 0, NULL, rsa_verify_signature, rsa_create_signature, 0 }; static const struct signature_alg rsa_with_md5_alg = { "rsa-with-md5", ASN1_OID_ID_PKCS1_MD5WITHRSAENCRYPTION, &_hx509_signature_rsa_with_md5_data, ASN1_OID_ID_PKCS1_RSAENCRYPTION, &_hx509_signature_md5_data, PROVIDE_CONF|REQUIRE_SIGNER|RA_RSA_USES_DIGEST_INFO|SIG_PUBLIC_SIG, 1230739889, NULL, rsa_verify_signature, rsa_create_signature, 0 }; static const struct signature_alg dsa_sha1_alg = { "dsa-with-sha1", ASN1_OID_ID_DSA_WITH_SHA1, NULL, ASN1_OID_ID_DSA, &_hx509_signature_sha1_data, PROVIDE_CONF|REQUIRE_SIGNER|SIG_PUBLIC_SIG, 0, NULL, dsa_verify_signature, /* create_signature */ NULL, 0 }; static const struct signature_alg sha512_alg = { "sha-512", ASN1_OID_ID_SHA512, &_hx509_signature_sha512_data, NULL, NULL, SIG_DIGEST, 0, EVP_sha512, evp_md_verify_signature, evp_md_create_signature, 0 }; static const struct signature_alg sha384_alg = { "sha-384", ASN1_OID_ID_SHA512, &_hx509_signature_sha384_data, NULL, NULL, SIG_DIGEST, 0, EVP_sha384, evp_md_verify_signature, evp_md_create_signature, 0 }; static const struct signature_alg sha256_alg = { "sha-256", ASN1_OID_ID_SHA256, &_hx509_signature_sha256_data, NULL, NULL, SIG_DIGEST, 0, EVP_sha256, evp_md_verify_signature, evp_md_create_signature, 0 }; static const struct signature_alg sha1_alg = { "sha1", ASN1_OID_ID_SECSIG_SHA_1, &_hx509_signature_sha1_data, NULL, NULL, SIG_DIGEST, 0, EVP_sha1, evp_md_verify_signature, evp_md_create_signature, 0 }; static const struct signature_alg md5_alg = { "rsa-md5", ASN1_OID_ID_RSA_DIGEST_MD5, &_hx509_signature_md5_data, NULL, NULL, SIG_DIGEST, 0, EVP_md5, evp_md_verify_signature, NULL, 0 }; /* * Order matter in this structure, "best" first for each "key * compatible" type (type is ECDSA, RSA, DSA, none, etc) */ static const struct signature_alg *sig_algs[] = { #ifdef HAVE_OPENSSL &ecdsa_with_sha256_alg, &ecdsa_with_sha1_alg, #endif &rsa_with_sha512_alg, &rsa_with_sha384_alg, &rsa_with_sha256_alg, &rsa_with_sha1_alg, &rsa_with_sha1_alg_secsig, &pkcs1_rsa_sha1_alg, &rsa_with_md5_alg, &heim_rsa_pkcs1_x509, &dsa_sha1_alg, &sha512_alg, &sha384_alg, &sha256_alg, &sha1_alg, &md5_alg, NULL }; static const struct signature_alg * find_sig_alg(const heim_oid *oid) { unsigned int i; for (i = 0; sig_algs[i]; i++) if (der_heim_oid_cmp(sig_algs[i]->sig_oid, oid) == 0) return sig_algs[i]; return NULL; } static const AlgorithmIdentifier * alg_for_privatekey(const hx509_private_key pk, int type) { const heim_oid *keytype; unsigned int i; if (pk->ops == NULL) return NULL; keytype = pk->ops->key_oid; for (i = 0; sig_algs[i]; i++) { if (sig_algs[i]->key_oid == NULL) continue; if (der_heim_oid_cmp(sig_algs[i]->key_oid, keytype) != 0) continue; if (pk->ops->available && pk->ops->available(pk, sig_algs[i]->sig_alg) == 0) continue; if (type == HX509_SELECT_PUBLIC_SIG) return sig_algs[i]->sig_alg; if (type == HX509_SELECT_DIGEST) return sig_algs[i]->digest_alg; return NULL; } return NULL; } /* * */ static struct hx509_private_key_ops *private_algs[] = { &rsa_private_key_ops, #ifdef HAVE_OPENSSL &ecdsa_private_key_ops, #endif NULL }; hx509_private_key_ops * hx509_find_private_alg(const heim_oid *oid) { int i; for (i = 0; private_algs[i]; i++) { if (private_algs[i]->key_oid == NULL) continue; if (der_heim_oid_cmp(private_algs[i]->key_oid, oid) == 0) return private_algs[i]; } return NULL; } /* * Check if the algorithm `alg' have a best before date, and if it * des, make sure the its before the time `t'. */ int _hx509_signature_best_before(hx509_context context, const AlgorithmIdentifier *alg, time_t t) { const struct signature_alg *md; md = find_sig_alg(&alg->algorithm); if (md == NULL) { hx509_clear_error_string(context); return HX509_SIG_ALG_NO_SUPPORTED; } if (md->best_before && md->best_before < t) { hx509_set_error_string(context, 0, HX509_CRYPTO_ALGORITHM_BEST_BEFORE, "Algorithm %s has passed it best before date", md->name); return HX509_CRYPTO_ALGORITHM_BEST_BEFORE; } return 0; } int _hx509_self_signed_valid(hx509_context context, const AlgorithmIdentifier *alg) { const struct signature_alg *md; md = find_sig_alg(&alg->algorithm); if (md == NULL) { hx509_clear_error_string(context); return HX509_SIG_ALG_NO_SUPPORTED; } if ((md->flags & SELF_SIGNED_OK) == 0) { hx509_set_error_string(context, 0, HX509_CRYPTO_ALGORITHM_BEST_BEFORE, "Algorithm %s not trusted for self signatures", md->name); return HX509_CRYPTO_ALGORITHM_BEST_BEFORE; } return 0; } int _hx509_verify_signature(hx509_context context, const hx509_cert cert, const AlgorithmIdentifier *alg, const heim_octet_string *data, const heim_octet_string *sig) { const struct signature_alg *md; const Certificate *signer = NULL; if (cert) signer = _hx509_get_cert(cert); md = find_sig_alg(&alg->algorithm); if (md == NULL) { hx509_clear_error_string(context); return HX509_SIG_ALG_NO_SUPPORTED; } if (signer && (md->flags & PROVIDE_CONF) == 0) { hx509_clear_error_string(context); return HX509_CRYPTO_SIG_NO_CONF; } if (signer == NULL && (md->flags & REQUIRE_SIGNER)) { hx509_clear_error_string(context); return HX509_CRYPTO_SIGNATURE_WITHOUT_SIGNER; } if (md->key_oid && signer) { const SubjectPublicKeyInfo *spi; spi = &signer->tbsCertificate.subjectPublicKeyInfo; if (der_heim_oid_cmp(&spi->algorithm.algorithm, md->key_oid) != 0) { hx509_clear_error_string(context); return HX509_SIG_ALG_DONT_MATCH_KEY_ALG; } } return (*md->verify_signature)(context, md, signer, alg, data, sig); } int _hx509_create_signature(hx509_context context, const hx509_private_key signer, const AlgorithmIdentifier *alg, const heim_octet_string *data, AlgorithmIdentifier *signatureAlgorithm, heim_octet_string *sig) { const struct signature_alg *md; md = find_sig_alg(&alg->algorithm); if (md == NULL) { hx509_set_error_string(context, 0, HX509_SIG_ALG_NO_SUPPORTED, "algorithm no supported"); return HX509_SIG_ALG_NO_SUPPORTED; } if (signer && (md->flags & PROVIDE_CONF) == 0) { hx509_set_error_string(context, 0, HX509_SIG_ALG_NO_SUPPORTED, "algorithm provides no conf"); return HX509_CRYPTO_SIG_NO_CONF; } return (*md->create_signature)(context, md, signer, alg, data, signatureAlgorithm, sig); } int _hx509_create_signature_bitstring(hx509_context context, const hx509_private_key signer, const AlgorithmIdentifier *alg, const heim_octet_string *data, AlgorithmIdentifier *signatureAlgorithm, heim_bit_string *sig) { heim_octet_string os; int ret; ret = _hx509_create_signature(context, signer, alg, data, signatureAlgorithm, &os); if (ret) return ret; sig->data = os.data; sig->length = os.length * 8; return 0; } int _hx509_public_encrypt(hx509_context context, const heim_octet_string *cleartext, const Certificate *cert, heim_oid *encryption_oid, heim_octet_string *ciphertext) { const SubjectPublicKeyInfo *spi; unsigned char *to; int tosize; int ret; RSA *rsa; size_t size; const unsigned char *p; ciphertext->data = NULL; ciphertext->length = 0; spi = &cert->tbsCertificate.subjectPublicKeyInfo; p = spi->subjectPublicKey.data; size = spi->subjectPublicKey.length / 8; rsa = d2i_RSAPublicKey(NULL, &p, size); if (rsa == NULL) { hx509_set_error_string(context, 0, ENOMEM, "out of memory"); return ENOMEM; } tosize = RSA_size(rsa); to = malloc(tosize); if (to == NULL) { RSA_free(rsa); hx509_set_error_string(context, 0, ENOMEM, "out of memory"); return ENOMEM; } ret = RSA_public_encrypt(cleartext->length, (unsigned char *)cleartext->data, to, rsa, RSA_PKCS1_PADDING); RSA_free(rsa); if (ret <= 0) { free(to); hx509_set_error_string(context, 0, HX509_CRYPTO_RSA_PUBLIC_ENCRYPT, "RSA public encrypt failed with %d", ret); return HX509_CRYPTO_RSA_PUBLIC_ENCRYPT; } if (ret > tosize) _hx509_abort("internal rsa decryption failure: ret > tosize"); ciphertext->length = ret; ciphertext->data = to; ret = der_copy_oid(ASN1_OID_ID_PKCS1_RSAENCRYPTION, encryption_oid); if (ret) { der_free_octet_string(ciphertext); hx509_set_error_string(context, 0, ENOMEM, "out of memory"); return ENOMEM; } return 0; } int hx509_private_key_private_decrypt(hx509_context context, const heim_octet_string *ciphertext, const heim_oid *encryption_oid, hx509_private_key p, heim_octet_string *cleartext) { int ret; cleartext->data = NULL; cleartext->length = 0; if (p->private_key.rsa == NULL) { hx509_set_error_string(context, 0, HX509_PRIVATE_KEY_MISSING, "Private RSA key missing"); return HX509_PRIVATE_KEY_MISSING; } cleartext->length = RSA_size(p->private_key.rsa); cleartext->data = malloc(cleartext->length); if (cleartext->data == NULL) { hx509_set_error_string(context, 0, ENOMEM, "out of memory"); return ENOMEM; } ret = RSA_private_decrypt(ciphertext->length, ciphertext->data, cleartext->data, p->private_key.rsa, RSA_PKCS1_PADDING); if (ret <= 0) { der_free_octet_string(cleartext); hx509_set_error_string(context, 0, HX509_CRYPTO_RSA_PRIVATE_DECRYPT, "Failed to decrypt using private key: %d", ret); return HX509_CRYPTO_RSA_PRIVATE_DECRYPT; } if (cleartext->length < (size_t)ret) _hx509_abort("internal rsa decryption failure: ret > tosize"); cleartext->length = ret; return 0; } int hx509_parse_private_key(hx509_context context, const AlgorithmIdentifier *keyai, const void *data, size_t len, hx509_key_format_t format, hx509_private_key *private_key) { struct hx509_private_key_ops *ops; int ret; *private_key = NULL; ops = hx509_find_private_alg(&keyai->algorithm); if (ops == NULL) { hx509_clear_error_string(context); return HX509_SIG_ALG_NO_SUPPORTED; } ret = hx509_private_key_init(private_key, ops, NULL); if (ret) { hx509_set_error_string(context, 0, ret, "out of memory"); return ret; } ret = (*ops->import)(context, keyai, data, len, format, *private_key); if (ret) hx509_private_key_free(private_key); return ret; } /* * */ int hx509_private_key2SPKI(hx509_context context, hx509_private_key private_key, SubjectPublicKeyInfo *spki) { const struct hx509_private_key_ops *ops = private_key->ops; if (ops == NULL || ops->get_spki == NULL) { hx509_set_error_string(context, 0, HX509_UNIMPLEMENTED_OPERATION, "Private key have no key2SPKI function"); return HX509_UNIMPLEMENTED_OPERATION; } return (*ops->get_spki)(context, private_key, spki); } int _hx509_generate_private_key_init(hx509_context context, const heim_oid *oid, struct hx509_generate_private_context **ctx) { *ctx = NULL; if (der_heim_oid_cmp(oid, ASN1_OID_ID_PKCS1_RSAENCRYPTION) != 0) { hx509_set_error_string(context, 0, EINVAL, "private key not an RSA key"); return EINVAL; } *ctx = calloc(1, sizeof(**ctx)); if (*ctx == NULL) { hx509_set_error_string(context, 0, ENOMEM, "out of memory"); return ENOMEM; } (*ctx)->key_oid = oid; return 0; } int _hx509_generate_private_key_is_ca(hx509_context context, struct hx509_generate_private_context *ctx) { ctx->isCA = 1; return 0; } int _hx509_generate_private_key_bits(hx509_context context, struct hx509_generate_private_context *ctx, unsigned long bits) { ctx->num_bits = bits; return 0; } void _hx509_generate_private_key_free(struct hx509_generate_private_context **ctx) { free(*ctx); *ctx = NULL; } int _hx509_generate_private_key(hx509_context context, struct hx509_generate_private_context *ctx, hx509_private_key *private_key) { struct hx509_private_key_ops *ops; int ret; *private_key = NULL; ops = hx509_find_private_alg(ctx->key_oid); if (ops == NULL) { hx509_clear_error_string(context); return HX509_SIG_ALG_NO_SUPPORTED; } ret = hx509_private_key_init(private_key, ops, NULL); if (ret) { hx509_set_error_string(context, 0, ret, "out of memory"); return ret; } ret = (*ops->generate_private_key)(context, ctx, *private_key); if (ret) hx509_private_key_free(private_key); return ret; } /* * */ const AlgorithmIdentifier * hx509_signature_sha512(void) { return &_hx509_signature_sha512_data; } const AlgorithmIdentifier * hx509_signature_sha384(void) { return &_hx509_signature_sha384_data; } const AlgorithmIdentifier * hx509_signature_sha256(void) { return &_hx509_signature_sha256_data; } const AlgorithmIdentifier * hx509_signature_sha1(void) { return &_hx509_signature_sha1_data; } const AlgorithmIdentifier * hx509_signature_md5(void) { return &_hx509_signature_md5_data; } const AlgorithmIdentifier * hx509_signature_ecPublicKey(void) { return &_hx509_signature_ecPublicKey; } const AlgorithmIdentifier * hx509_signature_ecdsa_with_sha256(void) { return &_hx509_signature_ecdsa_with_sha256_data; } const AlgorithmIdentifier * hx509_signature_ecdsa_with_sha1(void) { return &_hx509_signature_ecdsa_with_sha1_data; } const AlgorithmIdentifier * hx509_signature_rsa_with_sha512(void) { return &_hx509_signature_rsa_with_sha512_data; } const AlgorithmIdentifier * hx509_signature_rsa_with_sha384(void) { return &_hx509_signature_rsa_with_sha384_data; } const AlgorithmIdentifier * hx509_signature_rsa_with_sha256(void) { return &_hx509_signature_rsa_with_sha256_data; } const AlgorithmIdentifier * hx509_signature_rsa_with_sha1(void) { return &_hx509_signature_rsa_with_sha1_data; } const AlgorithmIdentifier * hx509_signature_rsa_with_md5(void) { return &_hx509_signature_rsa_with_md5_data; } const AlgorithmIdentifier * hx509_signature_rsa(void) { return &_hx509_signature_rsa_data; } const AlgorithmIdentifier * hx509_signature_rsa_pkcs1_x509(void) { return &_hx509_signature_rsa_pkcs1_x509_data; } const AlgorithmIdentifier * hx509_crypto_des_rsdi_ede3_cbc(void) { return &_hx509_des_rsdi_ede3_cbc_oid; } const AlgorithmIdentifier * hx509_crypto_aes128_cbc(void) { return &_hx509_crypto_aes128_cbc_data; } const AlgorithmIdentifier * hx509_crypto_aes256_cbc(void) { return &_hx509_crypto_aes256_cbc_data; } /* * */ const AlgorithmIdentifier * _hx509_crypto_default_sig_alg = &_hx509_signature_rsa_with_sha256_data; const AlgorithmIdentifier * _hx509_crypto_default_digest_alg = &_hx509_signature_sha256_data; const AlgorithmIdentifier * _hx509_crypto_default_secret_alg = &_hx509_crypto_aes128_cbc_data; /* * */ int hx509_private_key_init(hx509_private_key *key, hx509_private_key_ops *ops, void *keydata) { *key = calloc(1, sizeof(**key)); if (*key == NULL) return ENOMEM; (*key)->ref = 1; (*key)->ops = ops; (*key)->private_key.keydata = keydata; return 0; } hx509_private_key _hx509_private_key_ref(hx509_private_key key) { if (key->ref == 0) _hx509_abort("key refcount <= 0 on ref"); key->ref++; if (key->ref == UINT_MAX) _hx509_abort("key refcount == UINT_MAX on ref"); return key; } const char * _hx509_private_pem_name(hx509_private_key key) { return key->ops->pemtype; } int hx509_private_key_free(hx509_private_key *key) { if (key == NULL || *key == NULL) return 0; if ((*key)->ref == 0) _hx509_abort("key refcount == 0 on free"); if (--(*key)->ref > 0) return 0; if ((*key)->ops && der_heim_oid_cmp((*key)->ops->key_oid, ASN1_OID_ID_PKCS1_RSAENCRYPTION) == 0) { if ((*key)->private_key.rsa) RSA_free((*key)->private_key.rsa); #ifdef HAVE_OPENSSL } else if ((*key)->ops && der_heim_oid_cmp((*key)->ops->key_oid, ASN1_OID_ID_ECPUBLICKEY) == 0) { if ((*key)->private_key.ecdsa) EC_KEY_free((*key)->private_key.ecdsa); #endif } (*key)->private_key.rsa = NULL; free(*key); *key = NULL; return 0; } void hx509_private_key_assign_rsa(hx509_private_key key, void *ptr) { if (key->private_key.rsa) RSA_free(key->private_key.rsa); key->private_key.rsa = ptr; key->signature_alg = ASN1_OID_ID_PKCS1_SHA1WITHRSAENCRYPTION; key->md = &pkcs1_rsa_sha1_alg; } int _hx509_private_key_oid(hx509_context context, const hx509_private_key key, heim_oid *data) { int ret; ret = der_copy_oid(key->ops->key_oid, data); if (ret) hx509_set_error_string(context, 0, ret, "malloc out of memory"); return ret; } int _hx509_private_key_exportable(hx509_private_key key) { if (key->ops->export == NULL) return 0; return 1; } BIGNUM * _hx509_private_key_get_internal(hx509_context context, hx509_private_key key, const char *type) { if (key->ops->get_internal == NULL) return NULL; return (*key->ops->get_internal)(context, key, type); } int _hx509_private_key_export(hx509_context context, const hx509_private_key key, hx509_key_format_t format, heim_octet_string *data) { if (key->ops->export == NULL) { hx509_clear_error_string(context); return HX509_UNIMPLEMENTED_OPERATION; } return (*key->ops->export)(context, key, format, data); } /* * */ struct hx509cipher { const char *name; int flags; #define CIPHER_WEAK 1 const heim_oid *oid; const AlgorithmIdentifier *(*ai_func)(void); const EVP_CIPHER *(*evp_func)(void); int (*get_params)(hx509_context, const hx509_crypto, const heim_octet_string *, heim_octet_string *); int (*set_params)(hx509_context, const heim_octet_string *, hx509_crypto, heim_octet_string *); }; struct hx509_crypto_data { char *name; int flags; #define ALLOW_WEAK 1 #define PADDING_NONE 2 #define PADDING_PKCS7 4 #define PADDING_FLAGS (2|4) const struct hx509cipher *cipher; const EVP_CIPHER *c; heim_octet_string key; heim_oid oid; void *param; }; /* * */ static unsigned private_rc2_40_oid_data[] = { 127, 1 }; static heim_oid asn1_oid_private_rc2_40 = { 2, private_rc2_40_oid_data }; /* * */ static int CMSCBCParam_get(hx509_context context, const hx509_crypto crypto, const heim_octet_string *ivec, heim_octet_string *param) { size_t size; int ret; assert(crypto->param == NULL); if (ivec == NULL) return 0; ASN1_MALLOC_ENCODE(CMSCBCParameter, param->data, param->length, ivec, &size, ret); if (ret == 0 && size != param->length) _hx509_abort("Internal asn1 encoder failure"); if (ret) hx509_clear_error_string(context); return ret; } static int CMSCBCParam_set(hx509_context context, const heim_octet_string *param, hx509_crypto crypto, heim_octet_string *ivec) { int ret; if (ivec == NULL) return 0; ret = decode_CMSCBCParameter(param->data, param->length, ivec, NULL); if (ret) hx509_clear_error_string(context); return ret; } struct _RC2_params { int maximum_effective_key; }; static int CMSRC2CBCParam_get(hx509_context context, const hx509_crypto crypto, const heim_octet_string *ivec, heim_octet_string *param) { CMSRC2CBCParameter rc2params; const struct _RC2_params *p = crypto->param; int maximum_effective_key = 128; size_t size; int ret; memset(&rc2params, 0, sizeof(rc2params)); if (p) maximum_effective_key = p->maximum_effective_key; switch(maximum_effective_key) { case 40: rc2params.rc2ParameterVersion = 160; break; case 64: rc2params.rc2ParameterVersion = 120; break; case 128: rc2params.rc2ParameterVersion = 58; break; } rc2params.iv = *ivec; ASN1_MALLOC_ENCODE(CMSRC2CBCParameter, param->data, param->length, &rc2params, &size, ret); if (ret == 0 && size != param->length) _hx509_abort("Internal asn1 encoder failure"); return ret; } static int CMSRC2CBCParam_set(hx509_context context, const heim_octet_string *param, hx509_crypto crypto, heim_octet_string *ivec) { CMSRC2CBCParameter rc2param; struct _RC2_params *p; size_t size; int ret; ret = decode_CMSRC2CBCParameter(param->data, param->length, &rc2param, &size); if (ret) { hx509_clear_error_string(context); return ret; } p = calloc(1, sizeof(*p)); if (p == NULL) { free_CMSRC2CBCParameter(&rc2param); hx509_clear_error_string(context); return ENOMEM; } switch(rc2param.rc2ParameterVersion) { case 160: crypto->c = EVP_rc2_40_cbc(); p->maximum_effective_key = 40; break; case 120: crypto->c = EVP_rc2_64_cbc(); p->maximum_effective_key = 64; break; case 58: crypto->c = EVP_rc2_cbc(); p->maximum_effective_key = 128; break; default: free(p); free_CMSRC2CBCParameter(&rc2param); return HX509_CRYPTO_SIG_INVALID_FORMAT; } if (ivec) ret = der_copy_octet_string(&rc2param.iv, ivec); free_CMSRC2CBCParameter(&rc2param); if (ret) { free(p); hx509_clear_error_string(context); } else crypto->param = p; return ret; } /* * */ static const struct hx509cipher ciphers[] = { { "rc2-cbc", CIPHER_WEAK, ASN1_OID_ID_PKCS3_RC2_CBC, NULL, EVP_rc2_cbc, CMSRC2CBCParam_get, CMSRC2CBCParam_set }, { "rc2-cbc", CIPHER_WEAK, ASN1_OID_ID_RSADSI_RC2_CBC, NULL, EVP_rc2_cbc, CMSRC2CBCParam_get, CMSRC2CBCParam_set }, { "rc2-40-cbc", CIPHER_WEAK, &asn1_oid_private_rc2_40, NULL, EVP_rc2_40_cbc, CMSRC2CBCParam_get, CMSRC2CBCParam_set }, { "des-ede3-cbc", 0, ASN1_OID_ID_PKCS3_DES_EDE3_CBC, NULL, EVP_des_ede3_cbc, CMSCBCParam_get, CMSCBCParam_set }, { "des-ede3-cbc", 0, ASN1_OID_ID_RSADSI_DES_EDE3_CBC, hx509_crypto_des_rsdi_ede3_cbc, EVP_des_ede3_cbc, CMSCBCParam_get, CMSCBCParam_set }, { "aes-128-cbc", 0, ASN1_OID_ID_AES_128_CBC, hx509_crypto_aes128_cbc, EVP_aes_128_cbc, CMSCBCParam_get, CMSCBCParam_set }, { "aes-192-cbc", 0, ASN1_OID_ID_AES_192_CBC, NULL, EVP_aes_192_cbc, CMSCBCParam_get, CMSCBCParam_set }, { "aes-256-cbc", 0, ASN1_OID_ID_AES_256_CBC, hx509_crypto_aes256_cbc, EVP_aes_256_cbc, CMSCBCParam_get, CMSCBCParam_set } }; static const struct hx509cipher * find_cipher_by_oid(const heim_oid *oid) { size_t i; for (i = 0; i < sizeof(ciphers)/sizeof(ciphers[0]); i++) if (der_heim_oid_cmp(oid, ciphers[i].oid) == 0) return &ciphers[i]; return NULL; } static const struct hx509cipher * find_cipher_by_name(const char *name) { size_t i; for (i = 0; i < sizeof(ciphers)/sizeof(ciphers[0]); i++) if (strcasecmp(name, ciphers[i].name) == 0) return &ciphers[i]; return NULL; } const heim_oid * hx509_crypto_enctype_by_name(const char *name) { const struct hx509cipher *cipher; cipher = find_cipher_by_name(name); if (cipher == NULL) return NULL; return cipher->oid; } int hx509_crypto_init(hx509_context context, const char *provider, const heim_oid *enctype, hx509_crypto *crypto) { const struct hx509cipher *cipher; *crypto = NULL; cipher = find_cipher_by_oid(enctype); if (cipher == NULL) { hx509_set_error_string(context, 0, HX509_ALG_NOT_SUPP, "Algorithm not supported"); return HX509_ALG_NOT_SUPP; } *crypto = calloc(1, sizeof(**crypto)); if (*crypto == NULL) { hx509_clear_error_string(context); return ENOMEM; } (*crypto)->flags = PADDING_PKCS7; (*crypto)->cipher = cipher; (*crypto)->c = (*cipher->evp_func)(); if (der_copy_oid(enctype, &(*crypto)->oid)) { hx509_crypto_destroy(*crypto); *crypto = NULL; hx509_clear_error_string(context); return ENOMEM; } return 0; } const char * hx509_crypto_provider(hx509_crypto crypto) { return "unknown"; } void hx509_crypto_destroy(hx509_crypto crypto) { if (crypto->name) free(crypto->name); if (crypto->key.data) free(crypto->key.data); if (crypto->param) free(crypto->param); der_free_oid(&crypto->oid); memset(crypto, 0, sizeof(*crypto)); free(crypto); } int hx509_crypto_set_key_name(hx509_crypto crypto, const char *name) { return 0; } void hx509_crypto_allow_weak(hx509_crypto crypto) { crypto->flags |= ALLOW_WEAK; } void hx509_crypto_set_padding(hx509_crypto crypto, int padding_type) { switch (padding_type) { case HX509_CRYPTO_PADDING_PKCS7: crypto->flags &= ~PADDING_FLAGS; crypto->flags |= PADDING_PKCS7; break; case HX509_CRYPTO_PADDING_NONE: crypto->flags &= ~PADDING_FLAGS; crypto->flags |= PADDING_NONE; break; default: _hx509_abort("Invalid padding"); } } int hx509_crypto_set_key_data(hx509_crypto crypto, const void *data, size_t length) { if (EVP_CIPHER_key_length(crypto->c) > (int)length) return HX509_CRYPTO_INTERNAL_ERROR; if (crypto->key.data) { free(crypto->key.data); crypto->key.data = NULL; crypto->key.length = 0; } crypto->key.data = malloc(length); if (crypto->key.data == NULL) return ENOMEM; memcpy(crypto->key.data, data, length); crypto->key.length = length; return 0; } int hx509_crypto_set_random_key(hx509_crypto crypto, heim_octet_string *key) { if (crypto->key.data) { free(crypto->key.data); crypto->key.length = 0; } crypto->key.length = EVP_CIPHER_key_length(crypto->c); crypto->key.data = malloc(crypto->key.length); if (crypto->key.data == NULL) { crypto->key.length = 0; return ENOMEM; } if (RAND_bytes(crypto->key.data, crypto->key.length) <= 0) { free(crypto->key.data); crypto->key.data = NULL; crypto->key.length = 0; return HX509_CRYPTO_INTERNAL_ERROR; } if (key) return der_copy_octet_string(&crypto->key, key); else return 0; } int hx509_crypto_set_params(hx509_context context, hx509_crypto crypto, const heim_octet_string *param, heim_octet_string *ivec) { return (*crypto->cipher->set_params)(context, param, crypto, ivec); } int hx509_crypto_get_params(hx509_context context, hx509_crypto crypto, const heim_octet_string *ivec, heim_octet_string *param) { return (*crypto->cipher->get_params)(context, crypto, ivec, param); } int hx509_crypto_random_iv(hx509_crypto crypto, heim_octet_string *ivec) { ivec->length = EVP_CIPHER_iv_length(crypto->c); ivec->data = malloc(ivec->length); if (ivec->data == NULL) { ivec->length = 0; return ENOMEM; } if (RAND_bytes(ivec->data, ivec->length) <= 0) { free(ivec->data); ivec->data = NULL; ivec->length = 0; return HX509_CRYPTO_INTERNAL_ERROR; } return 0; } int hx509_crypto_encrypt(hx509_crypto crypto, const void *data, const size_t length, const heim_octet_string *ivec, heim_octet_string **ciphertext) { - EVP_CIPHER_CTX evp; + EVP_CIPHER_CTX *evp; size_t padsize, bsize; int ret; *ciphertext = NULL; if ((crypto->cipher->flags & CIPHER_WEAK) && (crypto->flags & ALLOW_WEAK) == 0) return HX509_CRYPTO_ALGORITHM_BEST_BEFORE; assert(EVP_CIPHER_iv_length(crypto->c) == (int)ivec->length); - EVP_CIPHER_CTX_init(&evp); + evp = EVP_CIPHER_CTX_new(); + if (evp == NULL) + return ENOMEM; - ret = EVP_CipherInit_ex(&evp, crypto->c, NULL, + ret = EVP_CipherInit_ex(evp, crypto->c, NULL, crypto->key.data, ivec->data, 1); if (ret != 1) { - EVP_CIPHER_CTX_cleanup(&evp); ret = HX509_CRYPTO_INTERNAL_ERROR; goto out; } *ciphertext = calloc(1, sizeof(**ciphertext)); if (*ciphertext == NULL) { ret = ENOMEM; goto out; } assert(crypto->flags & PADDING_FLAGS); bsize = EVP_CIPHER_block_size(crypto->c); padsize = 0; if (crypto->flags & PADDING_NONE) { if (bsize != 1 && (length % bsize) != 0) return HX509_CMS_PADDING_ERROR; } else if (crypto->flags & PADDING_PKCS7) { if (bsize != 1) padsize = bsize - (length % bsize); } (*ciphertext)->length = length + padsize; (*ciphertext)->data = malloc(length + padsize); if ((*ciphertext)->data == NULL) { ret = ENOMEM; goto out; } memcpy((*ciphertext)->data, data, length); if (padsize) { size_t i; unsigned char *p = (*ciphertext)->data; p += length; for (i = 0; i < padsize; i++) *p++ = padsize; } - ret = EVP_Cipher(&evp, (*ciphertext)->data, + ret = EVP_Cipher(evp, (*ciphertext)->data, (*ciphertext)->data, length + padsize); if (ret != 1) { ret = HX509_CRYPTO_INTERNAL_ERROR; goto out; } ret = 0; out: if (ret) { if (*ciphertext) { if ((*ciphertext)->data) { free((*ciphertext)->data); } free(*ciphertext); *ciphertext = NULL; } } - EVP_CIPHER_CTX_cleanup(&evp); + EVP_CIPHER_CTX_free(evp); return ret; } int hx509_crypto_decrypt(hx509_crypto crypto, const void *data, const size_t length, heim_octet_string *ivec, heim_octet_string *clear) { - EVP_CIPHER_CTX evp; + EVP_CIPHER_CTX *evp; void *idata = NULL; int ret; clear->data = NULL; clear->length = 0; if ((crypto->cipher->flags & CIPHER_WEAK) && (crypto->flags & ALLOW_WEAK) == 0) return HX509_CRYPTO_ALGORITHM_BEST_BEFORE; if (ivec && EVP_CIPHER_iv_length(crypto->c) < (int)ivec->length) return HX509_CRYPTO_INTERNAL_ERROR; if (crypto->key.data == NULL) return HX509_CRYPTO_INTERNAL_ERROR; if (ivec) idata = ivec->data; - EVP_CIPHER_CTX_init(&evp); + evp = EVP_CIPHER_CTX_new(); + if (evp == NULL) + return ENOMEM; - ret = EVP_CipherInit_ex(&evp, crypto->c, NULL, + ret = EVP_CipherInit_ex(evp, crypto->c, NULL, crypto->key.data, idata, 0); if (ret != 1) { - EVP_CIPHER_CTX_cleanup(&evp); + EVP_CIPHER_CTX_free(evp); return HX509_CRYPTO_INTERNAL_ERROR; } clear->length = length; clear->data = malloc(length); if (clear->data == NULL) { - EVP_CIPHER_CTX_cleanup(&evp); + EVP_CIPHER_CTX_free(evp); clear->length = 0; return ENOMEM; } - if (EVP_Cipher(&evp, clear->data, data, length) != 1) { + if (EVP_Cipher(evp, clear->data, data, length) != 1) { + EVP_CIPHER_CTX_free(evp); return HX509_CRYPTO_INTERNAL_ERROR; } - EVP_CIPHER_CTX_cleanup(&evp); + EVP_CIPHER_CTX_free(evp); if ((crypto->flags & PADDING_PKCS7) && EVP_CIPHER_block_size(crypto->c) > 1) { int padsize; unsigned char *p; int j, bsize = EVP_CIPHER_block_size(crypto->c); if ((int)clear->length < bsize) { ret = HX509_CMS_PADDING_ERROR; goto out; } p = clear->data; p += clear->length - 1; padsize = *p; if (padsize > bsize) { ret = HX509_CMS_PADDING_ERROR; goto out; } clear->length -= padsize; for (j = 0; j < padsize; j++) { if (*p-- != padsize) { ret = HX509_CMS_PADDING_ERROR; goto out; } } } return 0; out: if (clear->data) free(clear->data); clear->data = NULL; clear->length = 0; return ret; } typedef int (*PBE_string2key_func)(hx509_context, const char *, const heim_octet_string *, hx509_crypto *, heim_octet_string *, heim_octet_string *, const heim_oid *, const EVP_MD *); static int PBE_string2key(hx509_context context, const char *password, const heim_octet_string *parameters, hx509_crypto *crypto, heim_octet_string *key, heim_octet_string *iv, const heim_oid *enc_oid, const EVP_MD *md) { PKCS12_PBEParams p12params; int passwordlen; hx509_crypto c; int iter, saltlen, ret; unsigned char *salt; passwordlen = password ? strlen(password) : 0; if (parameters == NULL) return HX509_ALG_NOT_SUPP; ret = decode_PKCS12_PBEParams(parameters->data, parameters->length, &p12params, NULL); if (ret) goto out; if (p12params.iterations) iter = *p12params.iterations; else iter = 1; salt = p12params.salt.data; saltlen = p12params.salt.length; if (!PKCS12_key_gen (password, passwordlen, salt, saltlen, PKCS12_KEY_ID, iter, key->length, key->data, md)) { ret = HX509_CRYPTO_INTERNAL_ERROR; goto out; } if (!PKCS12_key_gen (password, passwordlen, salt, saltlen, PKCS12_IV_ID, iter, iv->length, iv->data, md)) { ret = HX509_CRYPTO_INTERNAL_ERROR; goto out; } ret = hx509_crypto_init(context, NULL, enc_oid, &c); if (ret) goto out; hx509_crypto_allow_weak(c); ret = hx509_crypto_set_key_data(c, key->data, key->length); if (ret) { hx509_crypto_destroy(c); goto out; } *crypto = c; out: free_PKCS12_PBEParams(&p12params); return ret; } static const heim_oid * find_string2key(const heim_oid *oid, const EVP_CIPHER **c, const EVP_MD **md, PBE_string2key_func *s2k) { if (der_heim_oid_cmp(oid, ASN1_OID_ID_PBEWITHSHAAND40BITRC2_CBC) == 0) { *c = EVP_rc2_40_cbc(); *md = EVP_sha1(); *s2k = PBE_string2key; return &asn1_oid_private_rc2_40; } else if (der_heim_oid_cmp(oid, ASN1_OID_ID_PBEWITHSHAAND128BITRC2_CBC) == 0) { *c = EVP_rc2_cbc(); *md = EVP_sha1(); *s2k = PBE_string2key; return ASN1_OID_ID_PKCS3_RC2_CBC; #if 0 } else if (der_heim_oid_cmp(oid, ASN1_OID_ID_PBEWITHSHAAND40BITRC4) == 0) { *c = EVP_rc4_40(); *md = EVP_sha1(); *s2k = PBE_string2key; return NULL; } else if (der_heim_oid_cmp(oid, ASN1_OID_ID_PBEWITHSHAAND128BITRC4) == 0) { *c = EVP_rc4(); *md = EVP_sha1(); *s2k = PBE_string2key; return ASN1_OID_ID_PKCS3_RC4; #endif } else if (der_heim_oid_cmp(oid, ASN1_OID_ID_PBEWITHSHAAND3_KEYTRIPLEDES_CBC) == 0) { *c = EVP_des_ede3_cbc(); *md = EVP_sha1(); *s2k = PBE_string2key; return ASN1_OID_ID_PKCS3_DES_EDE3_CBC; } return NULL; } /* * */ int _hx509_pbe_encrypt(hx509_context context, hx509_lock lock, const AlgorithmIdentifier *ai, const heim_octet_string *content, heim_octet_string *econtent) { hx509_clear_error_string(context); return EINVAL; } /* * */ int _hx509_pbe_decrypt(hx509_context context, hx509_lock lock, const AlgorithmIdentifier *ai, const heim_octet_string *econtent, heim_octet_string *content) { const struct _hx509_password *pw; heim_octet_string key, iv; const heim_oid *enc_oid; const EVP_CIPHER *c; const EVP_MD *md; PBE_string2key_func s2k; int ret = 0; size_t i; memset(&key, 0, sizeof(key)); memset(&iv, 0, sizeof(iv)); memset(content, 0, sizeof(*content)); enc_oid = find_string2key(&ai->algorithm, &c, &md, &s2k); if (enc_oid == NULL) { hx509_set_error_string(context, 0, HX509_ALG_NOT_SUPP, "String to key algorithm not supported"); ret = HX509_ALG_NOT_SUPP; goto out; } key.length = EVP_CIPHER_key_length(c); key.data = malloc(key.length); if (key.data == NULL) { ret = ENOMEM; hx509_clear_error_string(context); goto out; } iv.length = EVP_CIPHER_iv_length(c); iv.data = malloc(iv.length); if (iv.data == NULL) { ret = ENOMEM; hx509_clear_error_string(context); goto out; } pw = _hx509_lock_get_passwords(lock); ret = HX509_CRYPTO_INTERNAL_ERROR; for (i = 0; i < pw->len + 1; i++) { hx509_crypto crypto; const char *password; if (i < pw->len) password = pw->val[i]; else if (i < pw->len + 1) password = ""; else password = NULL; ret = (*s2k)(context, password, ai->parameters, &crypto, &key, &iv, enc_oid, md); if (ret) goto out; ret = hx509_crypto_decrypt(crypto, econtent->data, econtent->length, &iv, content); hx509_crypto_destroy(crypto); if (ret == 0) goto out; } out: if (key.data) der_free_octet_string(&key); if (iv.data) der_free_octet_string(&iv); return ret; } /* * */ static int match_keys_rsa(hx509_cert c, hx509_private_key private_key) { const Certificate *cert; const SubjectPublicKeyInfo *spi; RSAPublicKey pk; RSA *rsa; + const BIGNUM *d, *p, *q, *dmp1, *dmq1, *iqmp; + BIGNUM *new_d, *new_p, *new_q, *new_dmp1, *new_dmq1, *new_iqmp, *n, *e; size_t size; int ret; if (private_key->private_key.rsa == NULL) return 0; rsa = private_key->private_key.rsa; - if (rsa->d == NULL || rsa->p == NULL || rsa->q == NULL) + RSA_get0_key(rsa, NULL, NULL, &d); + RSA_get0_factors(rsa, &p, &q); + RSA_get0_crt_params(rsa, &dmp1, &dmq1, &iqmp); + if (d == NULL || p == NULL || q == NULL) return 0; cert = _hx509_get_cert(c); spi = &cert->tbsCertificate.subjectPublicKeyInfo; rsa = RSA_new(); if (rsa == NULL) return 0; ret = decode_RSAPublicKey(spi->subjectPublicKey.data, spi->subjectPublicKey.length / 8, &pk, &size); if (ret) { RSA_free(rsa); return 0; } - rsa->n = heim_int2BN(&pk.modulus); - rsa->e = heim_int2BN(&pk.publicExponent); + n = heim_int2BN(&pk.modulus); + e = heim_int2BN(&pk.publicExponent); free_RSAPublicKey(&pk); - rsa->d = BN_dup(private_key->private_key.rsa->d); - rsa->p = BN_dup(private_key->private_key.rsa->p); - rsa->q = BN_dup(private_key->private_key.rsa->q); - rsa->dmp1 = BN_dup(private_key->private_key.rsa->dmp1); - rsa->dmq1 = BN_dup(private_key->private_key.rsa->dmq1); - rsa->iqmp = BN_dup(private_key->private_key.rsa->iqmp); + new_d = BN_dup(d); + new_p = BN_dup(p); + new_q = BN_dup(q); + new_dmp1 = BN_dup(dmp1); + new_dmq1 = BN_dup(dmq1); + new_iqmp = BN_dup(iqmp); - if (rsa->n == NULL || rsa->e == NULL || - rsa->d == NULL || rsa->p == NULL|| rsa->q == NULL || - rsa->dmp1 == NULL || rsa->dmq1 == NULL) { + if (n == NULL || e == NULL || + new_d == NULL || new_p == NULL|| new_q == NULL || + new_dmp1 == NULL || new_dmq1 == NULL || new_iqmp == NULL) { + BN_free(n); + BN_free(e); + BN_free(new_d); + BN_free(new_p); + BN_free(new_q); + BN_free(new_dmp1); + BN_free(new_dmq1); + BN_free(new_iqmp); + RSA_free(rsa); + return 0; + } + + ret = RSA_set0_key(rsa, new_d, n, e); + + if (ret != 1) { + BN_free(n); + BN_free(e); + BN_free(new_d); + BN_free(new_p); + BN_free(new_q); + BN_free(new_dmp1); + BN_free(new_dmq1); + BN_free(new_iqmp); + RSA_free(rsa); + return 0; + } + + ret = RSA_set0_factors(rsa, new_p, new_q); + + if (ret != 1) { + BN_free(new_p); + BN_free(new_q); + BN_free(new_dmp1); + BN_free(new_dmq1); + BN_free(new_iqmp); + RSA_free(rsa); + return 0; + } + + ret = RSA_set0_crt_params(rsa, new_dmp1, new_dmq1, new_iqmp); + + if (ret != 1) { + BN_free(new_dmp1); + BN_free(new_dmq1); + BN_free(new_iqmp); RSA_free(rsa); return 0; } ret = RSA_check_key(rsa); RSA_free(rsa); return ret == 1; } static int match_keys_ec(hx509_cert c, hx509_private_key private_key) { return 1; /* XXX use EC_KEY_check_key */ } int _hx509_match_keys(hx509_cert c, hx509_private_key key) { if (der_heim_oid_cmp(key->ops->key_oid, ASN1_OID_ID_PKCS1_RSAENCRYPTION) == 0) return match_keys_rsa(c, key); if (der_heim_oid_cmp(key->ops->key_oid, ASN1_OID_ID_ECPUBLICKEY) == 0) return match_keys_ec(c, key); return 0; } static const heim_oid * find_keytype(const hx509_private_key key) { const struct signature_alg *md; if (key == NULL) return NULL; md = find_sig_alg(key->signature_alg); if (md == NULL) return NULL; return md->key_oid; } int hx509_crypto_select(const hx509_context context, int type, const hx509_private_key source, hx509_peer_info peer, AlgorithmIdentifier *selected) { const AlgorithmIdentifier *def = NULL; size_t i, j; int ret, bits; memset(selected, 0, sizeof(*selected)); if (type == HX509_SELECT_DIGEST) { bits = SIG_DIGEST; if (source) def = alg_for_privatekey(source, type); if (def == NULL) def = _hx509_crypto_default_digest_alg; } else if (type == HX509_SELECT_PUBLIC_SIG) { bits = SIG_PUBLIC_SIG; /* XXX depend on `source´ and `peer´ */ if (source) def = alg_for_privatekey(source, type); if (def == NULL) def = _hx509_crypto_default_sig_alg; } else if (type == HX509_SELECT_SECRET_ENC) { bits = SIG_SECRET; def = _hx509_crypto_default_secret_alg; } else { hx509_set_error_string(context, 0, EINVAL, "Unknown type %d of selection", type); return EINVAL; } if (peer) { const heim_oid *keytype = NULL; keytype = find_keytype(source); for (i = 0; i < peer->len; i++) { for (j = 0; sig_algs[j]; j++) { if ((sig_algs[j]->flags & bits) != bits) continue; if (der_heim_oid_cmp(sig_algs[j]->sig_oid, &peer->val[i].algorithm) != 0) continue; if (keytype && sig_algs[j]->key_oid && der_heim_oid_cmp(keytype, sig_algs[j]->key_oid)) continue; /* found one, use that */ ret = copy_AlgorithmIdentifier(&peer->val[i], selected); if (ret) hx509_clear_error_string(context); return ret; } if (bits & SIG_SECRET) { const struct hx509cipher *cipher; cipher = find_cipher_by_oid(&peer->val[i].algorithm); if (cipher == NULL) continue; if (cipher->ai_func == NULL) continue; ret = copy_AlgorithmIdentifier(cipher->ai_func(), selected); if (ret) hx509_clear_error_string(context); return ret; } } } /* use default */ ret = copy_AlgorithmIdentifier(def, selected); if (ret) hx509_clear_error_string(context); return ret; } int hx509_crypto_available(hx509_context context, int type, hx509_cert source, AlgorithmIdentifier **val, unsigned int *plen) { const heim_oid *keytype = NULL; unsigned int len, i; void *ptr; int bits, ret; *val = NULL; if (type == HX509_SELECT_ALL) { bits = SIG_DIGEST | SIG_PUBLIC_SIG | SIG_SECRET; } else if (type == HX509_SELECT_DIGEST) { bits = SIG_DIGEST; } else if (type == HX509_SELECT_PUBLIC_SIG) { bits = SIG_PUBLIC_SIG; } else { hx509_set_error_string(context, 0, EINVAL, "Unknown type %d of available", type); return EINVAL; } if (source) keytype = find_keytype(_hx509_cert_private_key(source)); len = 0; for (i = 0; sig_algs[i]; i++) { if ((sig_algs[i]->flags & bits) == 0) continue; if (sig_algs[i]->sig_alg == NULL) continue; if (keytype && sig_algs[i]->key_oid && der_heim_oid_cmp(sig_algs[i]->key_oid, keytype)) continue; /* found one, add that to the list */ ptr = realloc(*val, sizeof(**val) * (len + 1)); if (ptr == NULL) goto out; *val = ptr; ret = copy_AlgorithmIdentifier(sig_algs[i]->sig_alg, &(*val)[len]); if (ret) goto out; len++; } /* Add AES */ if (bits & SIG_SECRET) { for (i = 0; i < sizeof(ciphers)/sizeof(ciphers[0]); i++) { if (ciphers[i].flags & CIPHER_WEAK) continue; if (ciphers[i].ai_func == NULL) continue; ptr = realloc(*val, sizeof(**val) * (len + 1)); if (ptr == NULL) goto out; *val = ptr; ret = copy_AlgorithmIdentifier((ciphers[i].ai_func)(), &(*val)[len]); if (ret) goto out; len++; } } *plen = len; return 0; out: for (i = 0; i < len; i++) free_AlgorithmIdentifier(&(*val)[i]); free(*val); *val = NULL; hx509_set_error_string(context, 0, ENOMEM, "out of memory"); return ENOMEM; } void hx509_crypto_free_algs(AlgorithmIdentifier *val, unsigned int len) { unsigned int i; for (i = 0; i < len; i++) free_AlgorithmIdentifier(&val[i]); free(val); } Index: projects/openssl111/crypto/heimdal/lib/hx509/hxtool.c =================================================================== --- projects/openssl111/crypto/heimdal/lib/hx509/hxtool.c (revision 339197) +++ projects/openssl111/crypto/heimdal/lib/hx509/hxtool.c (revision 339198) @@ -1,2241 +1,2241 @@ /* * Copyright (c) 2004 - 2007 Kungliga Tekniska Högskolan * (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 "hx_locl.h" #include #include #include #include static hx509_context context; static char *stat_file_string; static int version_flag; static int help_flag; struct getargs args[] = { { "statistic-file", 0, arg_string, &stat_file_string, NULL, NULL }, { "version", 0, arg_flag, &version_flag, NULL, NULL }, { "help", 0, arg_flag, &help_flag, NULL, NULL } }; int num_args = sizeof(args) / sizeof(args[0]); static void usage(int code) { arg_printusage(args, num_args, NULL, "command"); printf("Use \"%s help\" to get more help\n", getprogname()); exit(code); } /* * */ static void lock_strings(hx509_lock lock, getarg_strings *pass) { int i; for (i = 0; i < pass->num_strings; i++) { int ret = hx509_lock_command_string(lock, pass->strings[i]); if (ret) errx(1, "hx509_lock_command_string: %s: %d", pass->strings[i], ret); } } /* * */ static void certs_strings(hx509_context contextp, const char *type, hx509_certs certs, hx509_lock lock, const getarg_strings *s) { int i, ret; for (i = 0; i < s->num_strings; i++) { ret = hx509_certs_append(contextp, certs, lock, s->strings[i]); if (ret) hx509_err(contextp, 1, ret, "hx509_certs_append: %s %s", type, s->strings[i]); } } /* * */ static void parse_oid(const char *str, const heim_oid *def, heim_oid *oid) { int ret; if (str) ret = der_parse_heim_oid (str, " .", oid); else ret = der_copy_oid(def, oid); if (ret) errx(1, "parse_oid failed for: %s", str ? str : "default oid"); } /* * */ static void peer_strings(hx509_context contextp, hx509_peer_info *peer, const getarg_strings *s) { AlgorithmIdentifier *val; int ret, i; ret = hx509_peer_info_alloc(contextp, peer); if (ret) hx509_err(contextp, 1, ret, "hx509_peer_info_alloc"); val = calloc(s->num_strings, sizeof(*val)); if (val == NULL) err(1, "malloc"); for (i = 0; i < s->num_strings; i++) parse_oid(s->strings[i], NULL, &val[i].algorithm); ret = hx509_peer_info_set_cms_algs(contextp, *peer, val, s->num_strings); if (ret) hx509_err(contextp, 1, ret, "hx509_peer_info_set_cms_algs"); for (i = 0; i < s->num_strings; i++) free_AlgorithmIdentifier(&val[i]); free(val); } /* * */ struct pem_data { heim_octet_string *os; int detached_data; }; static int pem_reader(hx509_context contextp, const char *type, const hx509_pem_header *headers, const void *data , size_t length, void *ctx) { struct pem_data *p = (struct pem_data *)ctx; const char *h; p->os->data = malloc(length); if (p->os->data == NULL) return ENOMEM; memcpy(p->os->data, data, length); p->os->length = length; h = hx509_pem_find_header(headers, "Content-disposition"); if (h && strcasecmp(h, "detached") == 0) p->detached_data = 1; return 0; } /* * */ int cms_verify_sd(struct cms_verify_sd_options *opt, int argc, char **argv) { hx509_verify_ctx ctx = NULL; heim_oid type; heim_octet_string c, co, signeddata, *sd = NULL; hx509_certs store = NULL; hx509_certs signers = NULL; hx509_certs anchors = NULL; hx509_lock lock; int ret, flags = 0; size_t sz; void *p = NULL; if (opt->missing_revoke_flag) hx509_context_set_missing_revoke(context, 1); hx509_lock_init(context, &lock); lock_strings(lock, &opt->pass_strings); ret = hx509_verify_init_ctx(context, &ctx); if (ret) hx509_err(context, 1, ret, "hx509_verify_init_ctx"); ret = hx509_certs_init(context, "MEMORY:cms-anchors", 0, NULL, &anchors); if (ret) hx509_err(context, 1, ret, "hx509_certs_init: MEMORY"); ret = hx509_certs_init(context, "MEMORY:cert-store", 0, NULL, &store); if (ret) hx509_err(context, 1, ret, "hx509_certs_init: MEMORY"); certs_strings(context, "anchors", anchors, lock, &opt->anchors_strings); certs_strings(context, "store", store, lock, &opt->certificate_strings); if (opt->pem_flag) { struct pem_data pd; FILE *f; pd.os = &co; pd.detached_data = 0; f = fopen(argv[0], "r"); if (f == NULL) err(1, "Failed to open file %s", argv[0]); ret = hx509_pem_read(context, f, pem_reader, &pd); fclose(f); if (ret) errx(1, "PEM reader failed: %d", ret); if (pd.detached_data && opt->signed_content_string == NULL) { char *r = strrchr(argv[0], '.'); if (r && strcasecmp(r, ".pem") == 0) { char *s = strdup(argv[0]); if (s == NULL) errx(1, "malloc: out of memory"); s[r - argv[0]] = '\0'; ret = _hx509_map_file_os(s, &signeddata); if (ret) errx(1, "map_file: %s: %d", s, ret); free(s); sd = &signeddata; } } } else { ret = rk_undumpdata(argv[0], &p, &sz); if (ret) err(1, "map_file: %s: %d", argv[0], ret); co.data = p; co.length = sz; } if (opt->signed_content_string) { ret = _hx509_map_file_os(opt->signed_content_string, &signeddata); if (ret) errx(1, "map_file: %s: %d", opt->signed_content_string, ret); sd = &signeddata; } if (opt->content_info_flag) { heim_octet_string uwco; heim_oid oid; ret = hx509_cms_unwrap_ContentInfo(&co, &oid, &uwco, NULL); if (ret) errx(1, "hx509_cms_unwrap_ContentInfo: %d", ret); if (der_heim_oid_cmp(&oid, &asn1_oid_id_pkcs7_signedData) != 0) errx(1, "Content is not SignedData"); der_free_oid(&oid); if (p == NULL) der_free_octet_string(&co); else { rk_xfree(p); p = NULL; } co = uwco; } hx509_verify_attach_anchors(ctx, anchors); if (!opt->signer_allowed_flag) flags |= HX509_CMS_VS_ALLOW_ZERO_SIGNER; if (opt->allow_wrong_oid_flag) flags |= HX509_CMS_VS_ALLOW_DATA_OID_MISMATCH; ret = hx509_cms_verify_signed(context, ctx, flags, co.data, co.length, sd, store, &type, &c, &signers); if (p != co.data) der_free_octet_string(&co); else rk_xfree(p); if (ret) hx509_err(context, 1, ret, "hx509_cms_verify_signed"); { char *str; der_print_heim_oid(&type, '.', &str); printf("type: %s\n", str); free(str); der_free_oid(&type); } if (signers == NULL) { printf("unsigned\n"); } else { printf("signers:\n"); hx509_certs_iter_f(context, signers, hx509_ci_print_names, stdout); } hx509_verify_destroy_ctx(ctx); hx509_certs_free(&store); hx509_certs_free(&signers); hx509_certs_free(&anchors); hx509_lock_free(lock); if (argc > 1) { ret = _hx509_write_file(argv[1], c.data, c.length); if (ret) errx(1, "hx509_write_file: %d", ret); } der_free_octet_string(&c); if (sd) _hx509_unmap_file_os(sd); return 0; } static int print_signer(hx509_context contextp, void *ctx, hx509_cert cert) { hx509_pem_header **header = ctx; char *signer_name = NULL; hx509_name name; int ret; ret = hx509_cert_get_subject(cert, &name); if (ret) errx(1, "hx509_cert_get_subject"); ret = hx509_name_to_string(name, &signer_name); hx509_name_free(&name); if (ret) errx(1, "hx509_name_to_string"); hx509_pem_add_header(header, "Signer", signer_name); free(signer_name); return 0; } int cms_create_sd(struct cms_create_sd_options *opt, int argc, char **argv) { heim_oid contentType; hx509_peer_info peer = NULL; heim_octet_string o; hx509_query *q; hx509_lock lock; hx509_certs store, pool, anchors, signer = NULL; size_t sz; void *p; int ret, flags = 0; char *infile, *outfile = NULL; memset(&contentType, 0, sizeof(contentType)); infile = argv[0]; if (argc < 2) { asprintf(&outfile, "%s.%s", infile, opt->pem_flag ? "pem" : "cms-signeddata"); if (outfile == NULL) errx(1, "out of memory"); } else outfile = argv[1]; hx509_lock_init(context, &lock); lock_strings(lock, &opt->pass_strings); ret = hx509_certs_init(context, "MEMORY:cert-store", 0, NULL, &store); if (ret) hx509_err(context, 1, ret, "hx509_certs_init: MEMORY"); ret = hx509_certs_init(context, "MEMORY:cert-pool", 0, NULL, &pool); if (ret) hx509_err(context, 1, ret, "hx509_certs_init: MEMORY"); certs_strings(context, "store", store, lock, &opt->certificate_strings); certs_strings(context, "pool", pool, lock, &opt->pool_strings); if (opt->anchors_strings.num_strings) { ret = hx509_certs_init(context, "MEMORY:cert-anchors", 0, NULL, &anchors); if (ret) hx509_err(context, 1, ret, "hx509_certs_init: MEMORY"); certs_strings(context, "anchors", anchors, lock, &opt->anchors_strings); } else anchors = NULL; if (opt->detached_signature_flag) flags |= HX509_CMS_SIGNATURE_DETACHED; if (opt->id_by_name_flag) flags |= HX509_CMS_SIGNATURE_ID_NAME; if (!opt->signer_flag) { flags |= HX509_CMS_SIGNATURE_NO_SIGNER; } if (opt->signer_flag) { ret = hx509_query_alloc(context, &q); if (ret) errx(1, "hx509_query_alloc: %d", ret); hx509_query_match_option(q, HX509_QUERY_OPTION_PRIVATE_KEY); hx509_query_match_option(q, HX509_QUERY_OPTION_KU_DIGITALSIGNATURE); if (opt->signer_string) hx509_query_match_friendly_name(q, opt->signer_string); ret = hx509_certs_filter(context, store, q, &signer); hx509_query_free(context, q); if (ret) hx509_err(context, 1, ret, "hx509_certs_find"); } if (!opt->embedded_certs_flag) flags |= HX509_CMS_SIGNATURE_NO_CERTS; if (opt->embed_leaf_only_flag) flags |= HX509_CMS_SIGNATURE_LEAF_ONLY; ret = rk_undumpdata(infile, &p, &sz); if (ret) err(1, "map_file: %s: %d", infile, ret); if (opt->peer_alg_strings.num_strings) peer_strings(context, &peer, &opt->peer_alg_strings); parse_oid(opt->content_type_string, &asn1_oid_id_pkcs7_data, &contentType); ret = hx509_cms_create_signed(context, flags, &contentType, p, sz, NULL, signer, peer, anchors, pool, &o); if (ret) hx509_err(context, 1, ret, "hx509_cms_create_signed: %d", ret); hx509_certs_free(&anchors); hx509_certs_free(&pool); hx509_certs_free(&store); rk_xfree(p); hx509_lock_free(lock); hx509_peer_info_free(peer); der_free_oid(&contentType); if (opt->content_info_flag) { heim_octet_string wo; ret = hx509_cms_wrap_ContentInfo(&asn1_oid_id_pkcs7_signedData, &o, &wo); if (ret) errx(1, "hx509_cms_wrap_ContentInfo: %d", ret); der_free_octet_string(&o); o = wo; } if (opt->pem_flag) { hx509_pem_header *header = NULL; FILE *f; hx509_pem_add_header(&header, "Content-disposition", opt->detached_signature_flag ? "detached" : "inline"); if (signer) { ret = hx509_certs_iter_f(context, signer, print_signer, header); if (ret) hx509_err(context, 1, ret, "print signer"); } f = fopen(outfile, "w"); if (f == NULL) err(1, "open %s", outfile); ret = hx509_pem_write(context, "CMS SIGNEDDATA", header, f, o.data, o.length); fclose(f); hx509_pem_free_header(header); if (ret) errx(1, "hx509_pem_write: %d", ret); } else { ret = _hx509_write_file(outfile, o.data, o.length); if (ret) errx(1, "hx509_write_file: %d", ret); } hx509_certs_free(&signer); free(o.data); return 0; } int cms_unenvelope(struct cms_unenvelope_options *opt, int argc, char **argv) { heim_oid contentType = { 0, NULL }; heim_octet_string o, co; hx509_certs certs; size_t sz; void *p; int ret; hx509_lock lock; int flags = 0; hx509_lock_init(context, &lock); lock_strings(lock, &opt->pass_strings); ret = rk_undumpdata(argv[0], &p, &sz); if (ret) err(1, "map_file: %s: %d", argv[0], ret); co.data = p; co.length = sz; if (opt->content_info_flag) { heim_octet_string uwco; heim_oid oid; ret = hx509_cms_unwrap_ContentInfo(&co, &oid, &uwco, NULL); if (ret) errx(1, "hx509_cms_unwrap_ContentInfo: %d", ret); if (der_heim_oid_cmp(&oid, &asn1_oid_id_pkcs7_envelopedData) != 0) errx(1, "Content is not SignedData"); der_free_oid(&oid); co = uwco; } ret = hx509_certs_init(context, "MEMORY:cert-store", 0, NULL, &certs); if (ret) errx(1, "hx509_certs_init: MEMORY: %d", ret); certs_strings(context, "store", certs, lock, &opt->certificate_strings); if (opt->allow_weak_crypto_flag) flags |= HX509_CMS_UE_ALLOW_WEAK; ret = hx509_cms_unenvelope(context, certs, flags, co.data, co.length, NULL, 0, &contentType, &o); if (co.data != p) der_free_octet_string(&co); if (ret) hx509_err(context, 1, ret, "hx509_cms_unenvelope"); rk_xfree(p); hx509_lock_free(lock); hx509_certs_free(&certs); der_free_oid(&contentType); ret = _hx509_write_file(argv[1], o.data, o.length); if (ret) errx(1, "hx509_write_file: %d", ret); der_free_octet_string(&o); return 0; } int cms_create_enveloped(struct cms_envelope_options *opt, int argc, char **argv) { heim_oid contentType; heim_octet_string o; const heim_oid *enctype = NULL; hx509_query *q; hx509_certs certs; hx509_cert cert; int ret; size_t sz; void *p; hx509_lock lock; int flags = 0; memset(&contentType, 0, sizeof(contentType)); hx509_lock_init(context, &lock); lock_strings(lock, &opt->pass_strings); ret = rk_undumpdata(argv[0], &p, &sz); if (ret) err(1, "map_file: %s: %d", argv[0], ret); ret = hx509_certs_init(context, "MEMORY:cert-store", 0, NULL, &certs); if (ret) hx509_err(context, 1, ret, "hx509_certs_init: MEMORY"); certs_strings(context, "store", certs, lock, &opt->certificate_strings); if (opt->allow_weak_crypto_flag) flags |= HX509_CMS_EV_ALLOW_WEAK; if (opt->encryption_type_string) { enctype = hx509_crypto_enctype_by_name(opt->encryption_type_string); if (enctype == NULL) errx(1, "encryption type: %s no found", opt->encryption_type_string); } ret = hx509_query_alloc(context, &q); if (ret) errx(1, "hx509_query_alloc: %d", ret); hx509_query_match_option(q, HX509_QUERY_OPTION_KU_ENCIPHERMENT); ret = hx509_certs_find(context, certs, q, &cert); hx509_query_free(context, q); if (ret) errx(1, "hx509_certs_find: %d", ret); parse_oid(opt->content_type_string, &asn1_oid_id_pkcs7_data, &contentType); ret = hx509_cms_envelope_1(context, flags, cert, p, sz, enctype, &contentType, &o); if (ret) errx(1, "hx509_cms_envelope_1: %d", ret); hx509_cert_free(cert); hx509_certs_free(&certs); rk_xfree(p); der_free_oid(&contentType); if (opt->content_info_flag) { heim_octet_string wo; ret = hx509_cms_wrap_ContentInfo(&asn1_oid_id_pkcs7_envelopedData, &o, &wo); if (ret) errx(1, "hx509_cms_wrap_ContentInfo: %d", ret); der_free_octet_string(&o); o = wo; } hx509_lock_free(lock); ret = _hx509_write_file(argv[1], o.data, o.length); if (ret) errx(1, "hx509_write_file: %d", ret); der_free_octet_string(&o); return 0; } static void print_certificate(hx509_context hxcontext, hx509_cert cert, int verbose) { const char *fn; int ret; fn = hx509_cert_get_friendly_name(cert); if (fn) printf(" friendly name: %s\n", fn); printf(" private key: %s\n", _hx509_cert_private_key(cert) ? "yes" : "no"); ret = hx509_print_cert(hxcontext, cert, NULL); if (ret) errx(1, "failed to print cert"); if (verbose) { hx509_validate_ctx vctx; hx509_validate_ctx_init(hxcontext, &vctx); hx509_validate_ctx_set_print(vctx, hx509_print_stdout, stdout); hx509_validate_ctx_add_flags(vctx, HX509_VALIDATE_F_VALIDATE); hx509_validate_ctx_add_flags(vctx, HX509_VALIDATE_F_VERBOSE); hx509_validate_cert(hxcontext, vctx, cert); hx509_validate_ctx_free(vctx); } } struct print_s { int counter; int verbose; }; static int print_f(hx509_context hxcontext, void *ctx, hx509_cert cert) { struct print_s *s = ctx; printf("cert: %d\n", s->counter++); print_certificate(context, cert, s->verbose); return 0; } int pcert_print(struct print_options *opt, int argc, char **argv) { hx509_certs certs; hx509_lock lock; struct print_s s; s.counter = 0; s.verbose = opt->content_flag; hx509_lock_init(context, &lock); lock_strings(lock, &opt->pass_strings); while(argc--) { int ret; ret = hx509_certs_init(context, argv[0], 0, lock, &certs); if (ret) { if (opt->never_fail_flag) { printf("ignoreing failure: %d\n", ret); continue; } hx509_err(context, 1, ret, "hx509_certs_init"); } if (opt->info_flag) hx509_certs_info(context, certs, NULL, NULL); hx509_certs_iter_f(context, certs, print_f, &s); hx509_certs_free(&certs); argv++; } hx509_lock_free(lock); return 0; } static int validate_f(hx509_context hxcontext, void *ctx, hx509_cert c) { hx509_validate_cert(hxcontext, ctx, c); return 0; } int pcert_validate(struct validate_options *opt, int argc, char **argv) { hx509_validate_ctx ctx; hx509_certs certs; hx509_lock lock; hx509_lock_init(context, &lock); lock_strings(lock, &opt->pass_strings); hx509_validate_ctx_init(context, &ctx); hx509_validate_ctx_set_print(ctx, hx509_print_stdout, stdout); hx509_validate_ctx_add_flags(ctx, HX509_VALIDATE_F_VALIDATE); while(argc--) { int ret; ret = hx509_certs_init(context, argv[0], 0, lock, &certs); if (ret) errx(1, "hx509_certs_init: %d", ret); hx509_certs_iter_f(context, certs, validate_f, ctx); hx509_certs_free(&certs); argv++; } hx509_validate_ctx_free(ctx); hx509_lock_free(lock); return 0; } int certificate_copy(struct certificate_copy_options *opt, int argc, char **argv) { hx509_certs certs; hx509_lock inlock, outlock = NULL; int ret; hx509_lock_init(context, &inlock); lock_strings(inlock, &opt->in_pass_strings); if (opt->out_pass_string) { hx509_lock_init(context, &outlock); ret = hx509_lock_command_string(outlock, opt->out_pass_string); if (ret) errx(1, "hx509_lock_command_string: %s: %d", opt->out_pass_string, ret); } ret = hx509_certs_init(context, argv[argc - 1], HX509_CERTS_CREATE, inlock, &certs); if (ret) hx509_err(context, 1, ret, "hx509_certs_init"); while(argc-- > 1) { int retx; retx = hx509_certs_append(context, certs, inlock, argv[0]); if (retx) hx509_err(context, 1, retx, "hx509_certs_append"); argv++; } ret = hx509_certs_store(context, certs, 0, outlock); if (ret) hx509_err(context, 1, ret, "hx509_certs_store"); hx509_certs_free(&certs); hx509_lock_free(inlock); hx509_lock_free(outlock); return 0; } struct verify { hx509_verify_ctx ctx; hx509_certs chain; const char *hostname; int errors; int count; }; static int verify_f(hx509_context hxcontext, void *ctx, hx509_cert c) { struct verify *v = ctx; int ret; ret = hx509_verify_path(hxcontext, v->ctx, c, v->chain); if (ret) { char *s = hx509_get_error_string(hxcontext, ret); printf("verify_path: %s: %d\n", s, ret); hx509_free_error_string(s); v->errors++; } else { v->count++; printf("path ok\n"); } if (v->hostname) { ret = hx509_verify_hostname(hxcontext, c, 0, HX509_HN_HOSTNAME, v->hostname, NULL, 0); if (ret) { printf("verify_hostname: %d\n", ret); v->errors++; } } return 0; } int pcert_verify(struct verify_options *opt, int argc, char **argv) { hx509_certs anchors, chain, certs; hx509_revoke_ctx revoke_ctx; hx509_verify_ctx ctx; struct verify v; int ret; memset(&v, 0, sizeof(v)); if (opt->missing_revoke_flag) hx509_context_set_missing_revoke(context, 1); ret = hx509_verify_init_ctx(context, &ctx); if (ret) hx509_err(context, 1, ret, "hx509_verify_init_ctx"); ret = hx509_certs_init(context, "MEMORY:anchors", 0, NULL, &anchors); if (ret) hx509_err(context, 1, ret, "hx509_certs_init: MEMORY"); ret = hx509_certs_init(context, "MEMORY:chain", 0, NULL, &chain); if (ret) hx509_err(context, 1, ret, "hx509_certs_init: MEMORY"); ret = hx509_certs_init(context, "MEMORY:certs", 0, NULL, &certs); if (ret) hx509_err(context, 1, ret, "hx509_certs_init: MEMORY"); if (opt->allow_proxy_certificate_flag) hx509_verify_set_proxy_certificate(ctx, 1); if (opt->time_string) { const char *p; struct tm tm; time_t t; memset(&tm, 0, sizeof(tm)); p = strptime (opt->time_string, "%Y-%m-%d", &tm); if (p == NULL) errx(1, "Failed to parse time %s, need to be on format %%Y-%%m-%%d", opt->time_string); t = tm2time (tm, 0); hx509_verify_set_time(ctx, t); } if (opt->hostname_string) v.hostname = opt->hostname_string; if (opt->max_depth_integer) hx509_verify_set_max_depth(ctx, opt->max_depth_integer); ret = hx509_revoke_init(context, &revoke_ctx); if (ret) errx(1, "hx509_revoke_init: %d", ret); while(argc--) { char *s = *argv++; if (strncmp(s, "chain:", 6) == 0) { s += 6; ret = hx509_certs_append(context, chain, NULL, s); if (ret) hx509_err(context, 1, ret, "hx509_certs_append: chain: %s: %d", s, ret); } else if (strncmp(s, "anchor:", 7) == 0) { s += 7; ret = hx509_certs_append(context, anchors, NULL, s); if (ret) hx509_err(context, 1, ret, "hx509_certs_append: anchor: %s: %d", s, ret); } else if (strncmp(s, "cert:", 5) == 0) { s += 5; ret = hx509_certs_append(context, certs, NULL, s); if (ret) hx509_err(context, 1, ret, "hx509_certs_append: certs: %s: %d", s, ret); } else if (strncmp(s, "crl:", 4) == 0) { s += 4; ret = hx509_revoke_add_crl(context, revoke_ctx, s); if (ret) errx(1, "hx509_revoke_add_crl: %s: %d", s, ret); } else if (strncmp(s, "ocsp:", 4) == 0) { s += 5; ret = hx509_revoke_add_ocsp(context, revoke_ctx, s); if (ret) errx(1, "hx509_revoke_add_ocsp: %s: %d", s, ret); } else { errx(1, "unknown option to verify: `%s'\n", s); } } hx509_verify_attach_anchors(ctx, anchors); hx509_verify_attach_revoke(ctx, revoke_ctx); v.ctx = ctx; v.chain = chain; hx509_certs_iter_f(context, certs, verify_f, &v); hx509_verify_destroy_ctx(ctx); hx509_certs_free(&certs); hx509_certs_free(&chain); hx509_certs_free(&anchors); hx509_revoke_free(&revoke_ctx); if (v.count == 0) { printf("no certs verify at all\n"); return 1; } if (v.errors) { printf("failed verifing %d checks\n", v.errors); return 1; } return 0; } int query(struct query_options *opt, int argc, char **argv) { hx509_lock lock; hx509_query *q; hx509_certs certs; hx509_cert c; int ret; ret = hx509_query_alloc(context, &q); if (ret) errx(1, "hx509_query_alloc: %d", ret); hx509_lock_init(context, &lock); lock_strings(lock, &opt->pass_strings); ret = hx509_certs_init(context, "MEMORY:cert-store", 0, NULL, &certs); if (ret) hx509_err(context, 1, ret, "hx509_certs_init: MEMORY"); while (argc > 0) { ret = hx509_certs_append(context, certs, lock, argv[0]); if (ret) errx(1, "hx509_certs_append: %s: %d", argv[0], ret); argc--; argv++; } if (opt->friendlyname_string) hx509_query_match_friendly_name(q, opt->friendlyname_string); if (opt->eku_string) { heim_oid oid; parse_oid(opt->eku_string, NULL, &oid); ret = hx509_query_match_eku(q, &oid); if (ret) errx(1, "hx509_query_match_eku: %d", ret); der_free_oid(&oid); } if (opt->private_key_flag) hx509_query_match_option(q, HX509_QUERY_OPTION_PRIVATE_KEY); if (opt->keyEncipherment_flag) hx509_query_match_option(q, HX509_QUERY_OPTION_KU_ENCIPHERMENT); if (opt->digitalSignature_flag) hx509_query_match_option(q, HX509_QUERY_OPTION_KU_DIGITALSIGNATURE); if (opt->expr_string) hx509_query_match_expr(context, q, opt->expr_string); ret = hx509_certs_find(context, certs, q, &c); hx509_query_free(context, q); if (ret) printf("no match found (%d)\n", ret); else { printf("match found\n"); if (opt->print_flag) print_certificate(context, c, 0); } hx509_cert_free(c); hx509_certs_free(&certs); hx509_lock_free(lock); return ret; } int ocsp_fetch(struct ocsp_fetch_options *opt, int argc, char **argv) { hx509_certs reqcerts, pool; heim_octet_string req, nonce_data, *nonce = &nonce_data; hx509_lock lock; int i, ret; char *file; const char *url = "/"; memset(&nonce, 0, sizeof(nonce)); hx509_lock_init(context, &lock); lock_strings(lock, &opt->pass_strings); /* no nonce */ if (!opt->nonce_flag) nonce = NULL; if (opt->url_path_string) url = opt->url_path_string; ret = hx509_certs_init(context, "MEMORY:ocsp-pool", 0, NULL, &pool); if (ret) hx509_err(context, 1, ret, "hx509_certs_init: MEMORY"); certs_strings(context, "ocsp-pool", pool, lock, &opt->pool_strings); file = argv[0]; ret = hx509_certs_init(context, "MEMORY:ocsp-req", 0, NULL, &reqcerts); if (ret) hx509_err(context, 1, ret, "hx509_certs_init: MEMORY"); for (i = 1; i < argc; i++) { ret = hx509_certs_append(context, reqcerts, lock, argv[i]); if (ret) errx(1, "hx509_certs_append: req: %s: %d", argv[i], ret); } ret = hx509_ocsp_request(context, reqcerts, pool, NULL, NULL, &req, nonce); if (ret) errx(1, "hx509_ocsp_request: req: %d", ret); { FILE *f; f = fopen(file, "w"); if (f == NULL) abort(); fprintf(f, "POST %s HTTP/1.0\r\n" "Content-Type: application/ocsp-request\r\n" "Content-Length: %ld\r\n" "\r\n", url, (unsigned long)req.length); fwrite(req.data, req.length, 1, f); fclose(f); } if (nonce) der_free_octet_string(nonce); hx509_certs_free(&reqcerts); hx509_certs_free(&pool); return 0; } int ocsp_print(struct ocsp_print_options *opt, int argc, char **argv) { hx509_revoke_ocsp_print(context, argv[0], stdout); return 0; } /* * */ static int verify_o(hx509_context hxcontext, void *ctx, hx509_cert c) { heim_octet_string *os = ctx; time_t expiration; int ret; ret = hx509_ocsp_verify(context, 0, c, 0, os->data, os->length, &expiration); if (ret) { char *s = hx509_get_error_string(hxcontext, ret); printf("ocsp_verify: %s: %d\n", s, ret); hx509_free_error_string(s); } else printf("expire: %d\n", (int)expiration); return ret; } int ocsp_verify(struct ocsp_verify_options *opt, int argc, char **argv) { hx509_lock lock; hx509_certs certs; int ret, i; heim_octet_string os; hx509_lock_init(context, &lock); if (opt->ocsp_file_string == NULL) errx(1, "no ocsp file given"); ret = _hx509_map_file_os(opt->ocsp_file_string, &os); if (ret) err(1, "map_file: %s: %d", argv[0], ret); ret = hx509_certs_init(context, "MEMORY:test-certs", 0, NULL, &certs); if (ret) hx509_err(context, 1, ret, "hx509_certs_init: MEMORY"); for (i = 0; i < argc; i++) { ret = hx509_certs_append(context, certs, lock, argv[i]); if (ret) hx509_err(context, 1, ret, "hx509_certs_append: %s", argv[i]); } ret = hx509_certs_iter_f(context, certs, verify_o, &os); hx509_certs_free(&certs); _hx509_unmap_file_os(&os); hx509_lock_free(lock); return ret; } static int read_private_key(const char *fn, hx509_private_key *key) { hx509_private_key *keys; hx509_certs certs; int ret; *key = NULL; ret = hx509_certs_init(context, fn, 0, NULL, &certs); if (ret) hx509_err(context, 1, ret, "hx509_certs_init: %s", fn); ret = _hx509_certs_keys_get(context, certs, &keys); hx509_certs_free(&certs); if (ret) hx509_err(context, 1, ret, "hx509_certs_keys_get"); if (keys[0] == NULL) errx(1, "no keys in key store: %s", fn); *key = _hx509_private_key_ref(keys[0]); _hx509_certs_keys_free(context, keys); return 0; } static void get_key(const char *fn, const char *type, int optbits, hx509_private_key *signer) { int ret; if (type) { BIGNUM *e; RSA *rsa; unsigned char *p0, *p; size_t len; int bits = 1024; if (fn == NULL) errx(1, "no key argument, don't know here to store key"); if (strcasecmp(type, "rsa") != 0) errx(1, "can only handle rsa keys for now"); e = BN_new(); BN_set_word(e, 0x10001); if (optbits) bits = optbits; rsa = RSA_new(); if(rsa == NULL) errx(1, "RSA_new failed"); ret = RSA_generate_key_ex(rsa, bits, e, NULL); if(ret != 1) errx(1, "RSA_new failed"); BN_free(e); len = i2d_RSAPrivateKey(rsa, NULL); p0 = p = malloc(len); if (p == NULL) errx(1, "out of memory"); i2d_RSAPrivateKey(rsa, &p); rk_dumpdata(fn, p0, len); memset(p0, 0, len); free(p0); RSA_free(rsa); } else if (fn == NULL) err(1, "no private key"); ret = read_private_key(fn, signer); if (ret) err(1, "read_private_key"); } int request_create(struct request_create_options *opt, int argc, char **argv) { heim_octet_string request; hx509_request req; int ret, i; hx509_private_key signer; SubjectPublicKeyInfo key; const char *outfile = argv[0]; memset(&key, 0, sizeof(key)); get_key(opt->key_string, opt->generate_key_string, opt->key_bits_integer, &signer); hx509_request_init(context, &req); if (opt->subject_string) { hx509_name name = NULL; ret = hx509_parse_name(context, opt->subject_string, &name); if (ret) errx(1, "hx509_parse_name: %d\n", ret); hx509_request_set_name(context, req, name); if (opt->verbose_flag) { char *s; hx509_name_to_string(name, &s); printf("%s\n", s); } hx509_name_free(&name); } for (i = 0; i < opt->email_strings.num_strings; i++) { ret = _hx509_request_add_email(context, req, opt->email_strings.strings[i]); if (ret) hx509_err(context, 1, ret, "hx509_request_add_email"); } for (i = 0; i < opt->dnsname_strings.num_strings; i++) { ret = _hx509_request_add_dns_name(context, req, opt->dnsname_strings.strings[i]); if (ret) hx509_err(context, 1, ret, "hx509_request_add_dns_name"); } ret = hx509_private_key2SPKI(context, signer, &key); if (ret) errx(1, "hx509_private_key2SPKI: %d\n", ret); ret = hx509_request_set_SubjectPublicKeyInfo(context, req, &key); free_SubjectPublicKeyInfo(&key); if (ret) hx509_err(context, 1, ret, "hx509_request_set_SubjectPublicKeyInfo"); ret = _hx509_request_to_pkcs10(context, req, signer, &request); if (ret) hx509_err(context, 1, ret, "_hx509_request_to_pkcs10"); hx509_private_key_free(&signer); hx509_request_free(&req); if (ret == 0) rk_dumpdata(outfile, request.data, request.length); der_free_octet_string(&request); return 0; } int request_print(struct request_print_options *opt, int argc, char **argv) { int ret, i; printf("request print\n"); for (i = 0; i < argc; i++) { hx509_request req; ret = _hx509_request_parse(context, argv[i], &req); if (ret) hx509_err(context, 1, ret, "parse_request: %s", argv[i]); ret = _hx509_request_print(context, req, stdout); hx509_request_free(&req); if (ret) hx509_err(context, 1, ret, "Failed to print file %s", argv[i]); } return 0; } int info(void *opt, int argc, char **argv) { ENGINE_add_conf_module(); { const RSA_METHOD *m = RSA_get_default_method(); if (m != NULL) - printf("rsa: %s\n", m->name); + printf("rsa: %s\n", RSA_meth_get0_name(m)); } { const DH_METHOD *m = DH_get_default_method(); if (m != NULL) - printf("dh: %s\n", m->name); + printf("dh: %s\n", DH_meth_get0_name(m)); } #ifdef HAVE_OPENSSL { printf("ecdsa: ECDSA_METHOD-not-export\n"); } #else { printf("ecdsa: hcrypto null\n"); } #endif { int ret = RAND_status(); printf("rand: %s\n", ret == 1 ? "ok" : "not available"); } return 0; } int random_data(void *opt, int argc, char **argv) { void *ptr; int len, ret; len = parse_bytes(argv[0], "byte"); if (len <= 0) { fprintf(stderr, "bad argument to random-data\n"); return 1; } ptr = malloc(len); if (ptr == NULL) { fprintf(stderr, "out of memory\n"); return 1; } ret = RAND_bytes(ptr, len); if (ret != 1) { free(ptr); fprintf(stderr, "did not get cryptographic strong random\n"); return 1; } fwrite(ptr, len, 1, stdout); fflush(stdout); free(ptr); return 0; } int crypto_available(struct crypto_available_options *opt, int argc, char **argv) { AlgorithmIdentifier *val; unsigned int len, i; int ret, type = HX509_SELECT_ALL; if (opt->type_string) { if (strcmp(opt->type_string, "all") == 0) type = HX509_SELECT_ALL; else if (strcmp(opt->type_string, "digest") == 0) type = HX509_SELECT_DIGEST; else if (strcmp(opt->type_string, "public-sig") == 0) type = HX509_SELECT_PUBLIC_SIG; else if (strcmp(opt->type_string, "secret") == 0) type = HX509_SELECT_SECRET_ENC; else errx(1, "unknown type: %s", opt->type_string); } ret = hx509_crypto_available(context, type, NULL, &val, &len); if (ret) errx(1, "hx509_crypto_available"); for (i = 0; i < len; i++) { char *s; der_print_heim_oid (&val[i].algorithm, '.', &s); printf("%s\n", s); free(s); } hx509_crypto_free_algs(val, len); return 0; } int crypto_select(struct crypto_select_options *opt, int argc, char **argv) { hx509_peer_info peer = NULL; AlgorithmIdentifier selected; int ret, type = HX509_SELECT_DIGEST; char *s; if (opt->type_string) { if (strcmp(opt->type_string, "digest") == 0) type = HX509_SELECT_DIGEST; else if (strcmp(opt->type_string, "public-sig") == 0) type = HX509_SELECT_PUBLIC_SIG; else if (strcmp(opt->type_string, "secret") == 0) type = HX509_SELECT_SECRET_ENC; else errx(1, "unknown type: %s", opt->type_string); } if (opt->peer_cmstype_strings.num_strings) peer_strings(context, &peer, &opt->peer_cmstype_strings); ret = hx509_crypto_select(context, type, NULL, peer, &selected); if (ret) errx(1, "hx509_crypto_available"); der_print_heim_oid (&selected.algorithm, '.', &s); printf("%s\n", s); free(s); free_AlgorithmIdentifier(&selected); hx509_peer_info_free(peer); return 0; } int hxtool_hex(struct hex_options *opt, int argc, char **argv) { if (opt->decode_flag) { char buf[1024], buf2[1024], *p; ssize_t len; while(fgets(buf, sizeof(buf), stdin) != NULL) { buf[strcspn(buf, "\r\n")] = '\0'; p = buf; while(isspace(*(unsigned char *)p)) p++; len = hex_decode(p, buf2, strlen(p)); if (len < 0) errx(1, "hex_decode failed"); if (fwrite(buf2, 1, len, stdout) != (size_t)len) errx(1, "fwrite failed"); } } else { char buf[28], *p; ssize_t len; while((len = fread(buf, 1, sizeof(buf), stdin)) != 0) { len = hex_encode(buf, len, &p); if (len < 0) continue; fprintf(stdout, "%s\n", p); free(p); } } return 0; } struct cert_type_opt { int pkinit; }; static int https_server(hx509_context contextp, hx509_ca_tbs tbs, struct cert_type_opt *opt) { return hx509_ca_tbs_add_eku(contextp, tbs, &asn1_oid_id_pkix_kp_serverAuth); } static int https_client(hx509_context contextp, hx509_ca_tbs tbs, struct cert_type_opt *opt) { return hx509_ca_tbs_add_eku(contextp, tbs, &asn1_oid_id_pkix_kp_clientAuth); } static int peap_server(hx509_context contextp, hx509_ca_tbs tbs, struct cert_type_opt *opt) { return hx509_ca_tbs_add_eku(contextp, tbs, &asn1_oid_id_pkix_kp_serverAuth); } static int pkinit_kdc(hx509_context contextp, hx509_ca_tbs tbs, struct cert_type_opt *opt) { opt->pkinit++; return hx509_ca_tbs_add_eku(contextp, tbs, &asn1_oid_id_pkkdcekuoid); } static int pkinit_client(hx509_context contextp, hx509_ca_tbs tbs, struct cert_type_opt *opt) { int ret; opt->pkinit++; ret = hx509_ca_tbs_add_eku(contextp, tbs, &asn1_oid_id_pkekuoid); if (ret) return ret; ret = hx509_ca_tbs_add_eku(context, tbs, &asn1_oid_id_ms_client_authentication); if (ret) return ret; return hx509_ca_tbs_add_eku(context, tbs, &asn1_oid_id_pkinit_ms_eku); } static int email_client(hx509_context contextp, hx509_ca_tbs tbs, struct cert_type_opt *opt) { return hx509_ca_tbs_add_eku(contextp, tbs, &asn1_oid_id_pkix_kp_emailProtection); } struct { const char *type; const char *desc; int (*eval)(hx509_context, hx509_ca_tbs, struct cert_type_opt *); } certtypes[] = { { "https-server", "Used for HTTPS server and many other TLS server certificate types", https_server }, { "https-client", "Used for HTTPS client certificates", https_client }, { "email-client", "Certificate will be use for email", email_client }, { "pkinit-client", "Certificate used for Kerberos PK-INIT client certificates", pkinit_client }, { "pkinit-kdc", "Certificates used for Kerberos PK-INIT KDC certificates", pkinit_kdc }, { "peap-server", "Certificate used for Radius PEAP (Protected EAP)", peap_server } }; static void print_eval_types(FILE *out) { rtbl_t table; unsigned i; table = rtbl_create(); rtbl_add_column_by_id (table, 0, "Name", 0); rtbl_add_column_by_id (table, 1, "Description", 0); for (i = 0; i < sizeof(certtypes)/sizeof(certtypes[0]); i++) { rtbl_add_column_entry_by_id(table, 0, certtypes[i].type); rtbl_add_column_entry_by_id(table, 1, certtypes[i].desc); } rtbl_format (table, out); rtbl_destroy (table); } static int eval_types(hx509_context contextp, hx509_ca_tbs tbs, const struct certificate_sign_options *opt) { struct cert_type_opt ctopt; int i; size_t j; int ret; memset(&ctopt, 0, sizeof(ctopt)); for (i = 0; i < opt->type_strings.num_strings; i++) { const char *type = opt->type_strings.strings[i]; for (j = 0; j < sizeof(certtypes)/sizeof(certtypes[0]); j++) { if (strcasecmp(type, certtypes[j].type) == 0) { ret = (*certtypes[j].eval)(contextp, tbs, &ctopt); if (ret) hx509_err(contextp, 1, ret, "Failed to evaluate cert type %s", type); break; } } if (j >= sizeof(certtypes)/sizeof(certtypes[0])) { fprintf(stderr, "Unknown certificate type %s\n\n", type); fprintf(stderr, "Available types:\n"); print_eval_types(stderr); exit(1); } } if (opt->pk_init_principal_string) { if (!ctopt.pkinit) errx(1, "pk-init principal given but no pk-init oid"); ret = hx509_ca_tbs_add_san_pkinit(contextp, tbs, opt->pk_init_principal_string); if (ret) hx509_err(contextp, 1, ret, "hx509_ca_tbs_add_san_pkinit"); } if (opt->ms_upn_string) { if (!ctopt.pkinit) errx(1, "MS upn given but no pk-init oid"); ret = hx509_ca_tbs_add_san_ms_upn(contextp, tbs, opt->ms_upn_string); if (ret) hx509_err(contextp, 1, ret, "hx509_ca_tbs_add_san_ms_upn"); } for (i = 0; i < opt->hostname_strings.num_strings; i++) { const char *hostname = opt->hostname_strings.strings[i]; ret = hx509_ca_tbs_add_san_hostname(contextp, tbs, hostname); if (ret) hx509_err(contextp, 1, ret, "hx509_ca_tbs_add_san_hostname"); } for (i = 0; i < opt->email_strings.num_strings; i++) { const char *email = opt->email_strings.strings[i]; ret = hx509_ca_tbs_add_san_rfc822name(contextp, tbs, email); if (ret) hx509_err(contextp, 1, ret, "hx509_ca_tbs_add_san_hostname"); ret = hx509_ca_tbs_add_eku(contextp, tbs, &asn1_oid_id_pkix_kp_emailProtection); if (ret) hx509_err(contextp, 1, ret, "hx509_ca_tbs_add_eku"); } if (opt->jid_string) { ret = hx509_ca_tbs_add_san_jid(contextp, tbs, opt->jid_string); if (ret) hx509_err(contextp, 1, ret, "hx509_ca_tbs_add_san_jid"); } return 0; } int hxtool_ca(struct certificate_sign_options *opt, int argc, char **argv) { int ret; hx509_ca_tbs tbs; hx509_cert signer = NULL, cert = NULL; hx509_private_key private_key = NULL; hx509_private_key cert_key = NULL; hx509_name subject = NULL; SubjectPublicKeyInfo spki; int delta = 0; memset(&spki, 0, sizeof(spki)); if (opt->ca_certificate_string == NULL && !opt->self_signed_flag) errx(1, "--ca-certificate argument missing (not using --self-signed)"); if (opt->ca_private_key_string == NULL && opt->generate_key_string == NULL && opt->self_signed_flag) errx(1, "--ca-private-key argument missing (using --self-signed)"); if (opt->certificate_string == NULL) errx(1, "--certificate argument missing"); if (opt->template_certificate_string) { if (opt->template_fields_string == NULL) errx(1, "--template-certificate not no --template-fields"); } if (opt->lifetime_string) { delta = parse_time(opt->lifetime_string, "day"); if (delta < 0) errx(1, "Invalid lifetime: %s", opt->lifetime_string); } if (opt->ca_certificate_string) { hx509_certs cacerts = NULL; hx509_query *q; ret = hx509_certs_init(context, opt->ca_certificate_string, 0, NULL, &cacerts); if (ret) hx509_err(context, 1, ret, "hx509_certs_init: %s", opt->ca_certificate_string); ret = hx509_query_alloc(context, &q); if (ret) errx(1, "hx509_query_alloc: %d", ret); hx509_query_match_option(q, HX509_QUERY_OPTION_PRIVATE_KEY); if (!opt->issue_proxy_flag) hx509_query_match_option(q, HX509_QUERY_OPTION_KU_KEYCERTSIGN); ret = hx509_certs_find(context, cacerts, q, &signer); hx509_query_free(context, q); hx509_certs_free(&cacerts); if (ret) hx509_err(context, 1, ret, "no CA certificate found"); } else if (opt->self_signed_flag) { if (opt->generate_key_string == NULL && opt->ca_private_key_string == NULL) errx(1, "no signing private key"); if (opt->req_string) errx(1, "can't be self-signing and have a request at the same time"); } else errx(1, "missing ca key"); if (opt->ca_private_key_string) { ret = read_private_key(opt->ca_private_key_string, &private_key); if (ret) err(1, "read_private_key"); ret = hx509_private_key2SPKI(context, private_key, &spki); if (ret) errx(1, "hx509_private_key2SPKI: %d\n", ret); if (opt->self_signed_flag) cert_key = private_key; } if (opt->req_string) { hx509_request req; ret = _hx509_request_parse(context, opt->req_string, &req); if (ret) hx509_err(context, 1, ret, "parse_request: %s", opt->req_string); ret = hx509_request_get_name(context, req, &subject); if (ret) hx509_err(context, 1, ret, "get name"); ret = hx509_request_get_SubjectPublicKeyInfo(context, req, &spki); if (ret) hx509_err(context, 1, ret, "get spki"); hx509_request_free(&req); } if (opt->generate_key_string) { struct hx509_generate_private_context *keyctx; ret = _hx509_generate_private_key_init(context, &asn1_oid_id_pkcs1_rsaEncryption, &keyctx); if (ret) hx509_err(context, 1, ret, "generate private key"); if (opt->issue_ca_flag) _hx509_generate_private_key_is_ca(context, keyctx); if (opt->key_bits_integer) _hx509_generate_private_key_bits(context, keyctx, opt->key_bits_integer); ret = _hx509_generate_private_key(context, keyctx, &cert_key); _hx509_generate_private_key_free(&keyctx); if (ret) hx509_err(context, 1, ret, "generate private key"); ret = hx509_private_key2SPKI(context, cert_key, &spki); if (ret) errx(1, "hx509_private_key2SPKI: %d\n", ret); if (opt->self_signed_flag) private_key = cert_key; } if (opt->certificate_private_key_string) { ret = read_private_key(opt->certificate_private_key_string, &cert_key); if (ret) err(1, "read_private_key for certificate"); } if (opt->subject_string) { if (subject) hx509_name_free(&subject); ret = hx509_parse_name(context, opt->subject_string, &subject); if (ret) hx509_err(context, 1, ret, "hx509_parse_name"); } /* * */ ret = hx509_ca_tbs_init(context, &tbs); if (ret) hx509_err(context, 1, ret, "hx509_ca_tbs_init"); if (opt->template_certificate_string) { hx509_cert template; hx509_certs tcerts; int flags; ret = hx509_certs_init(context, opt->template_certificate_string, 0, NULL, &tcerts); if (ret) hx509_err(context, 1, ret, "hx509_certs_init: %s", opt->template_certificate_string); ret = hx509_get_one_cert(context, tcerts, &template); hx509_certs_free(&tcerts); if (ret) hx509_err(context, 1, ret, "no template certificate found"); flags = parse_units(opt->template_fields_string, hx509_ca_tbs_template_units(), ""); ret = hx509_ca_tbs_set_template(context, tbs, flags, template); if (ret) hx509_err(context, 1, ret, "hx509_ca_tbs_set_template"); hx509_cert_free(template); } if (opt->serial_number_string) { heim_integer serialNumber; ret = der_parse_hex_heim_integer(opt->serial_number_string, &serialNumber); if (ret) err(1, "der_parse_hex_heim_integer"); ret = hx509_ca_tbs_set_serialnumber(context, tbs, &serialNumber); if (ret) hx509_err(context, 1, ret, "hx509_ca_tbs_init"); der_free_heim_integer(&serialNumber); } if (spki.subjectPublicKey.length) { ret = hx509_ca_tbs_set_spki(context, tbs, &spki); if (ret) hx509_err(context, 1, ret, "hx509_ca_tbs_set_spki"); } if (subject) { ret = hx509_ca_tbs_set_subject(context, tbs, subject); if (ret) hx509_err(context, 1, ret, "hx509_ca_tbs_set_subject"); } if (opt->crl_uri_string) { ret = hx509_ca_tbs_add_crl_dp_uri(context, tbs, opt->crl_uri_string, NULL); if (ret) hx509_err(context, 1, ret, "hx509_ca_tbs_add_crl_dp_uri"); } eval_types(context, tbs, opt); if (opt->issue_ca_flag) { ret = hx509_ca_tbs_set_ca(context, tbs, opt->path_length_integer); if (ret) hx509_err(context, 1, ret, "hx509_ca_tbs_set_ca"); } if (opt->issue_proxy_flag) { ret = hx509_ca_tbs_set_proxy(context, tbs, opt->path_length_integer); if (ret) hx509_err(context, 1, ret, "hx509_ca_tbs_set_proxy"); } if (opt->domain_controller_flag) { hx509_ca_tbs_set_domaincontroller(context, tbs); if (ret) hx509_err(context, 1, ret, "hx509_ca_tbs_set_domaincontroller"); } if (delta) { ret = hx509_ca_tbs_set_notAfter_lifetime(context, tbs, delta); if (ret) hx509_err(context, 1, ret, "hx509_ca_tbs_set_notAfter_lifetime"); } if (opt->self_signed_flag) { ret = hx509_ca_sign_self(context, tbs, private_key, &cert); if (ret) hx509_err(context, 1, ret, "hx509_ca_sign_self"); } else { ret = hx509_ca_sign(context, tbs, signer, &cert); if (ret) hx509_err(context, 1, ret, "hx509_ca_sign"); } if (cert_key) { ret = _hx509_cert_assign_key(cert, cert_key); if (ret) hx509_err(context, 1, ret, "_hx509_cert_assign_key"); } { hx509_certs certs; ret = hx509_certs_init(context, opt->certificate_string, HX509_CERTS_CREATE, NULL, &certs); if (ret) hx509_err(context, 1, ret, "hx509_certs_init"); ret = hx509_certs_add(context, certs, cert); if (ret) hx509_err(context, 1, ret, "hx509_certs_add"); ret = hx509_certs_store(context, certs, 0, NULL); if (ret) hx509_err(context, 1, ret, "hx509_certs_store"); hx509_certs_free(&certs); } if (subject) hx509_name_free(&subject); if (signer) hx509_cert_free(signer); hx509_cert_free(cert); free_SubjectPublicKeyInfo(&spki); if (private_key != cert_key) hx509_private_key_free(&private_key); hx509_private_key_free(&cert_key); hx509_ca_tbs_free(&tbs); return 0; } static int test_one_cert(hx509_context hxcontext, void *ctx, hx509_cert cert) { heim_octet_string sd, c; hx509_verify_ctx vctx = ctx; hx509_certs signer = NULL; heim_oid type; int ret; if (_hx509_cert_private_key(cert) == NULL) return 0; ret = hx509_cms_create_signed_1(context, 0, NULL, NULL, 0, NULL, cert, NULL, NULL, NULL, &sd); if (ret) errx(1, "hx509_cms_create_signed_1"); ret = hx509_cms_verify_signed(context, vctx, 0, sd.data, sd.length, NULL, NULL, &type, &c, &signer); free(sd.data); if (ret) hx509_err(context, 1, ret, "hx509_cms_verify_signed"); printf("create-signature verify-sigature done\n"); free(c.data); return 0; } int test_crypto(struct test_crypto_options *opt, int argc, char ** argv) { hx509_verify_ctx vctx; hx509_certs certs; hx509_lock lock; int i, ret; hx509_lock_init(context, &lock); lock_strings(lock, &opt->pass_strings); ret = hx509_certs_init(context, "MEMORY:test-crypto", 0, NULL, &certs); if (ret) hx509_err(context, 1, ret, "hx509_certs_init: MEMORY"); for (i = 0; i < argc; i++) { ret = hx509_certs_append(context, certs, lock, argv[i]); if (ret) hx509_err(context, 1, ret, "hx509_certs_append"); } ret = hx509_verify_init_ctx(context, &vctx); if (ret) hx509_err(context, 1, ret, "hx509_verify_init_ctx"); hx509_verify_attach_anchors(vctx, certs); ret = hx509_certs_iter_f(context, certs, test_one_cert, vctx); if (ret) hx509_err(context, 1, ret, "hx509_cert_iter"); hx509_certs_free(&certs); return 0; } int statistic_print(struct statistic_print_options*opt, int argc, char **argv) { int type = 0; if (stat_file_string == NULL) errx(1, "no stat file"); if (opt->type_integer) type = opt->type_integer; hx509_query_unparse_stats(context, type, stdout); return 0; } /* * */ int crl_sign(struct crl_sign_options *opt, int argc, char **argv) { hx509_crl crl; heim_octet_string os; hx509_cert signer = NULL; hx509_lock lock; int ret; hx509_lock_init(context, &lock); lock_strings(lock, &opt->pass_strings); ret = hx509_crl_alloc(context, &crl); if (ret) errx(1, "crl alloc"); if (opt->signer_string == NULL) errx(1, "signer missing"); { hx509_certs certs = NULL; hx509_query *q; ret = hx509_certs_init(context, opt->signer_string, 0, NULL, &certs); if (ret) hx509_err(context, 1, ret, "hx509_certs_init: %s", opt->signer_string); ret = hx509_query_alloc(context, &q); if (ret) hx509_err(context, 1, ret, "hx509_query_alloc: %d", ret); hx509_query_match_option(q, HX509_QUERY_OPTION_PRIVATE_KEY); ret = hx509_certs_find(context, certs, q, &signer); hx509_query_free(context, q); hx509_certs_free(&certs); if (ret) hx509_err(context, 1, ret, "no signer certificate found"); } if (opt->lifetime_string) { int delta; delta = parse_time(opt->lifetime_string, "day"); if (delta < 0) errx(1, "Invalid lifetime: %s", opt->lifetime_string); hx509_crl_lifetime(context, crl, delta); } { hx509_certs revoked = NULL; int i; ret = hx509_certs_init(context, "MEMORY:revoked-certs", 0, NULL, &revoked); if (ret) hx509_err(context, 1, ret, "hx509_certs_init: MEMORY cert"); for (i = 0; i < argc; i++) { ret = hx509_certs_append(context, revoked, lock, argv[i]); if (ret) hx509_err(context, 1, ret, "hx509_certs_append: %s", argv[i]); } hx509_crl_add_revoked_certs(context, crl, revoked); hx509_certs_free(&revoked); } hx509_crl_sign(context, signer, crl, &os); if (opt->crl_file_string) rk_dumpdata(opt->crl_file_string, os.data, os.length); free(os.data); hx509_crl_free(context, &crl); hx509_cert_free(signer); hx509_lock_free(lock); return 0; } /* * */ int help(void *opt, int argc, char **argv) { sl_slc_help(commands, argc, argv); return 0; } int main(int argc, char **argv) { int ret, optidx = 0; setprogname (argv[0]); if(getarg(args, num_args, argc, argv, &optidx)) usage(1); if(help_flag) usage(0); if(version_flag) { print_version(NULL); exit(0); } argv += optidx; argc -= optidx; if (argc == 0) usage(1); ret = hx509_context_init(&context); if (ret) errx(1, "hx509_context_init failed with %d", ret); if (stat_file_string) hx509_query_statistic_file(context, stat_file_string); ret = sl_command(commands, argc, argv); if(ret == -1) warnx ("unrecognized command: %s", argv[0]); hx509_context_free(&context); return ret; } Index: projects/openssl111/crypto/heimdal/lib/hx509/ks_file.c =================================================================== --- projects/openssl111/crypto/heimdal/lib/hx509/ks_file.c (revision 339197) +++ projects/openssl111/crypto/heimdal/lib/hx509/ks_file.c (revision 339198) @@ -1,690 +1,697 @@ /* * Copyright (c) 2005 - 2007 Kungliga Tekniska Högskolan * (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 "hx_locl.h" typedef enum { USE_PEM, USE_DER } outformat; struct ks_file { hx509_certs certs; char *fn; outformat format; }; /* * */ static int parse_certificate(hx509_context context, const char *fn, struct hx509_collector *c, const hx509_pem_header *headers, const void *data, size_t len, const AlgorithmIdentifier *ai) { hx509_cert cert; int ret; ret = hx509_cert_init_data(context, data, len, &cert); if (ret) return ret; ret = _hx509_collector_certs_add(context, c, cert); hx509_cert_free(cert); return ret; } static int try_decrypt(hx509_context context, struct hx509_collector *collector, const AlgorithmIdentifier *alg, const EVP_CIPHER *c, const void *ivdata, const void *password, size_t passwordlen, const void *cipher, size_t len) { heim_octet_string clear; size_t keylen; void *key; int ret; keylen = EVP_CIPHER_key_length(c); key = malloc(keylen); if (key == NULL) { hx509_clear_error_string(context); return ENOMEM; } ret = EVP_BytesToKey(c, EVP_md5(), ivdata, password, passwordlen, 1, key, NULL); if (ret <= 0) { hx509_set_error_string(context, 0, HX509_CRYPTO_INTERNAL_ERROR, "Failed to do string2key for private key"); return HX509_CRYPTO_INTERNAL_ERROR; } clear.data = malloc(len); if (clear.data == NULL) { hx509_set_error_string(context, 0, ENOMEM, "Out of memory to decrypt for private key"); ret = ENOMEM; goto out; } clear.length = len; { - EVP_CIPHER_CTX ctx; - EVP_CIPHER_CTX_init(&ctx); - EVP_CipherInit_ex(&ctx, c, NULL, key, ivdata, 0); - EVP_Cipher(&ctx, clear.data, cipher, len); - EVP_CIPHER_CTX_cleanup(&ctx); + EVP_CIPHER_CTX *ctx; + + ctx = EVP_CIPHER_CTX_new(); + if (ctx == NULL) { + hx509_set_error_string(context, 0, ENOMEM, + "Out of memory to decrypt for private key"); + ret = ENOMEM; + goto out; + } + EVP_CipherInit_ex(ctx, c, NULL, key, ivdata, 0); + EVP_Cipher(ctx, clear.data, cipher, len); + EVP_CIPHER_CTX_free(ctx); } ret = _hx509_collector_private_key_add(context, collector, alg, NULL, &clear, NULL); memset(clear.data, 0, clear.length); - free(clear.data); out: + free(clear.data); memset(key, 0, keylen); free(key); return ret; } static int parse_pkcs8_private_key(hx509_context context, const char *fn, struct hx509_collector *c, const hx509_pem_header *headers, const void *data, size_t length, const AlgorithmIdentifier *ai) { PKCS8PrivateKeyInfo ki; heim_octet_string keydata; int ret; ret = decode_PKCS8PrivateKeyInfo(data, length, &ki, NULL); if (ret) return ret; keydata.data = rk_UNCONST(data); keydata.length = length; ret = _hx509_collector_private_key_add(context, c, &ki.privateKeyAlgorithm, NULL, &ki.privateKey, &keydata); free_PKCS8PrivateKeyInfo(&ki); return ret; } static int parse_pem_private_key(hx509_context context, const char *fn, struct hx509_collector *c, const hx509_pem_header *headers, const void *data, size_t len, const AlgorithmIdentifier *ai) { int ret = 0; const char *enc; enc = hx509_pem_find_header(headers, "Proc-Type"); if (enc) { const char *dek; char *type, *iv; ssize_t ssize, size; void *ivdata; const EVP_CIPHER *cipher; const struct _hx509_password *pw; hx509_lock lock; int decrypted = 0; size_t i; lock = _hx509_collector_get_lock(c); if (lock == NULL) { hx509_set_error_string(context, 0, HX509_ALG_NOT_SUPP, "Failed to get password for " "password protected file %s", fn); return HX509_ALG_NOT_SUPP; } if (strcmp(enc, "4,ENCRYPTED") != 0) { hx509_set_error_string(context, 0, HX509_PARSING_KEY_FAILED, "Private key encrypted in unknown method %s " "in file", enc, fn); hx509_clear_error_string(context); return HX509_PARSING_KEY_FAILED; } dek = hx509_pem_find_header(headers, "DEK-Info"); if (dek == NULL) { hx509_set_error_string(context, 0, HX509_PARSING_KEY_FAILED, "Encrypted private key missing DEK-Info"); return HX509_PARSING_KEY_FAILED; } type = strdup(dek); if (type == NULL) { hx509_clear_error_string(context); return ENOMEM; } iv = strchr(type, ','); if (iv == NULL) { free(type); hx509_set_error_string(context, 0, HX509_PARSING_KEY_FAILED, "IV missing"); return HX509_PARSING_KEY_FAILED; } *iv++ = '\0'; size = strlen(iv); ivdata = malloc(size); if (ivdata == NULL) { hx509_clear_error_string(context); free(type); return ENOMEM; } cipher = EVP_get_cipherbyname(type); if (cipher == NULL) { free(ivdata); hx509_set_error_string(context, 0, HX509_ALG_NOT_SUPP, "Private key encrypted with " "unsupported cipher: %s", type); free(type); return HX509_ALG_NOT_SUPP; } #define PKCS5_SALT_LEN 8 ssize = hex_decode(iv, ivdata, size); free(type); type = NULL; iv = NULL; if (ssize < 0 || ssize < PKCS5_SALT_LEN || ssize < EVP_CIPHER_iv_length(cipher)) { free(ivdata); hx509_set_error_string(context, 0, HX509_PARSING_KEY_FAILED, "Salt have wrong length in " "private key file"); return HX509_PARSING_KEY_FAILED; } pw = _hx509_lock_get_passwords(lock); if (pw != NULL) { const void *password; size_t passwordlen; for (i = 0; i < pw->len; i++) { password = pw->val[i]; passwordlen = strlen(password); ret = try_decrypt(context, c, ai, cipher, ivdata, password, passwordlen, data, len); if (ret == 0) { decrypted = 1; break; } } } if (!decrypted) { hx509_prompt prompt; char password[128]; memset(&prompt, 0, sizeof(prompt)); prompt.prompt = "Password for keyfile: "; prompt.type = HX509_PROMPT_TYPE_PASSWORD; prompt.reply.data = password; prompt.reply.length = sizeof(password); ret = hx509_lock_prompt(lock, &prompt); if (ret == 0) ret = try_decrypt(context, c, ai, cipher, ivdata, password, strlen(password), data, len); /* XXX add password to lock password collection ? */ memset(password, 0, sizeof(password)); } free(ivdata); } else { heim_octet_string keydata; keydata.data = rk_UNCONST(data); keydata.length = len; ret = _hx509_collector_private_key_add(context, c, ai, NULL, &keydata, NULL); } return ret; } struct pem_formats { const char *name; int (*func)(hx509_context, const char *, struct hx509_collector *, const hx509_pem_header *, const void *, size_t, const AlgorithmIdentifier *); const AlgorithmIdentifier *(*ai)(void); } formats[] = { { "CERTIFICATE", parse_certificate, NULL }, { "PRIVATE KEY", parse_pkcs8_private_key, NULL }, { "RSA PRIVATE KEY", parse_pem_private_key, hx509_signature_rsa }, { "EC PRIVATE KEY", parse_pem_private_key, hx509_signature_ecPublicKey } }; struct pem_ctx { int flags; struct hx509_collector *c; }; static int pem_func(hx509_context context, const char *type, const hx509_pem_header *header, const void *data, size_t len, void *ctx) { struct pem_ctx *pem_ctx = (struct pem_ctx*)ctx; int ret = 0; size_t j; for (j = 0; j < sizeof(formats)/sizeof(formats[0]); j++) { const char *q = formats[j].name; if (strcasecmp(type, q) == 0) { const AlgorithmIdentifier *ai = NULL; if (formats[j].ai != NULL) ai = (*formats[j].ai)(); ret = (*formats[j].func)(context, NULL, pem_ctx->c, header, data, len, ai); if (ret && (pem_ctx->flags & HX509_CERTS_UNPROTECT_ALL)) { hx509_set_error_string(context, HX509_ERROR_APPEND, ret, "Failed parseing PEM format %s", type); return ret; } break; } } if (j == sizeof(formats)/sizeof(formats[0])) { ret = HX509_UNSUPPORTED_OPERATION; hx509_set_error_string(context, 0, ret, "Found no matching PEM format for %s", type); return ret; } return 0; } /* * */ static int file_init_common(hx509_context context, hx509_certs certs, void **data, int flags, const char *residue, hx509_lock lock, outformat format) { char *p, *pnext; struct ks_file *ksf = NULL; hx509_private_key *keys = NULL; int ret; struct pem_ctx pem_ctx; pem_ctx.flags = flags; pem_ctx.c = NULL; *data = NULL; if (lock == NULL) lock = _hx509_empty_lock; ksf = calloc(1, sizeof(*ksf)); if (ksf == NULL) { hx509_clear_error_string(context); return ENOMEM; } ksf->format = format; ksf->fn = strdup(residue); if (ksf->fn == NULL) { hx509_clear_error_string(context); ret = ENOMEM; goto out; } /* * XXX this is broken, the function should parse the file before * overwriting it */ if (flags & HX509_CERTS_CREATE) { ret = hx509_certs_init(context, "MEMORY:ks-file-create", 0, lock, &ksf->certs); if (ret) goto out; *data = ksf; return 0; } ret = _hx509_collector_alloc(context, lock, &pem_ctx.c); if (ret) goto out; for (p = ksf->fn; p != NULL; p = pnext) { FILE *f; pnext = strchr(p, ','); if (pnext) *pnext++ = '\0'; if ((f = fopen(p, "r")) == NULL) { ret = ENOENT; hx509_set_error_string(context, 0, ret, "Failed to open PEM file \"%s\": %s", p, strerror(errno)); goto out; } rk_cloexec_file(f); ret = hx509_pem_read(context, f, pem_func, &pem_ctx); fclose(f); if (ret != 0 && ret != HX509_PARSING_KEY_FAILED) goto out; else if (ret == HX509_PARSING_KEY_FAILED) { size_t length; void *ptr; size_t i; ret = rk_undumpdata(p, &ptr, &length); if (ret) { hx509_clear_error_string(context); goto out; } for (i = 0; i < sizeof(formats)/sizeof(formats[0]); i++) { const AlgorithmIdentifier *ai = NULL; if (formats[i].ai != NULL) ai = (*formats[i].ai)(); ret = (*formats[i].func)(context, p, pem_ctx.c, NULL, ptr, length, ai); if (ret == 0) break; } rk_xfree(ptr); if (ret) { hx509_clear_error_string(context); goto out; } } } ret = _hx509_collector_collect_certs(context, pem_ctx.c, &ksf->certs); if (ret) goto out; ret = _hx509_collector_collect_private_keys(context, pem_ctx.c, &keys); if (ret == 0) { int i; for (i = 0; keys[i]; i++) _hx509_certs_keys_add(context, ksf->certs, keys[i]); _hx509_certs_keys_free(context, keys); } out: if (ret == 0) *data = ksf; else { if (ksf->fn) free(ksf->fn); free(ksf); } if (pem_ctx.c) _hx509_collector_free(pem_ctx.c); return ret; } static int file_init_pem(hx509_context context, hx509_certs certs, void **data, int flags, const char *residue, hx509_lock lock) { return file_init_common(context, certs, data, flags, residue, lock, USE_PEM); } static int file_init_der(hx509_context context, hx509_certs certs, void **data, int flags, const char *residue, hx509_lock lock) { return file_init_common(context, certs, data, flags, residue, lock, USE_DER); } static int file_free(hx509_certs certs, void *data) { struct ks_file *ksf = data; hx509_certs_free(&ksf->certs); free(ksf->fn); free(ksf); return 0; } struct store_ctx { FILE *f; outformat format; }; static int store_func(hx509_context context, void *ctx, hx509_cert c) { struct store_ctx *sc = ctx; heim_octet_string data; int ret; ret = hx509_cert_binary(context, c, &data); if (ret) return ret; switch (sc->format) { case USE_DER: fwrite(data.data, data.length, 1, sc->f); free(data.data); break; case USE_PEM: hx509_pem_write(context, "CERTIFICATE", NULL, sc->f, data.data, data.length); free(data.data); if (_hx509_cert_private_key_exportable(c)) { hx509_private_key key = _hx509_cert_private_key(c); ret = _hx509_private_key_export(context, key, HX509_KEY_FORMAT_DER, &data); if (ret) break; hx509_pem_write(context, _hx509_private_pem_name(key), NULL, sc->f, data.data, data.length); free(data.data); } break; } return 0; } static int file_store(hx509_context context, hx509_certs certs, void *data, int flags, hx509_lock lock) { struct ks_file *ksf = data; struct store_ctx sc; int ret; sc.f = fopen(ksf->fn, "w"); if (sc.f == NULL) { hx509_set_error_string(context, 0, ENOENT, "Failed to open file %s for writing"); return ENOENT; } rk_cloexec_file(sc.f); sc.format = ksf->format; ret = hx509_certs_iter_f(context, ksf->certs, store_func, &sc); fclose(sc.f); return ret; } static int file_add(hx509_context context, hx509_certs certs, void *data, hx509_cert c) { struct ks_file *ksf = data; return hx509_certs_add(context, ksf->certs, c); } static int file_iter_start(hx509_context context, hx509_certs certs, void *data, void **cursor) { struct ks_file *ksf = data; return hx509_certs_start_seq(context, ksf->certs, cursor); } static int file_iter(hx509_context context, hx509_certs certs, void *data, void *iter, hx509_cert *cert) { struct ks_file *ksf = data; return hx509_certs_next_cert(context, ksf->certs, iter, cert); } static int file_iter_end(hx509_context context, hx509_certs certs, void *data, void *cursor) { struct ks_file *ksf = data; return hx509_certs_end_seq(context, ksf->certs, cursor); } static int file_getkeys(hx509_context context, hx509_certs certs, void *data, hx509_private_key **keys) { struct ks_file *ksf = data; return _hx509_certs_keys_get(context, ksf->certs, keys); } static int file_addkey(hx509_context context, hx509_certs certs, void *data, hx509_private_key key) { struct ks_file *ksf = data; return _hx509_certs_keys_add(context, ksf->certs, key); } static struct hx509_keyset_ops keyset_file = { "FILE", 0, file_init_pem, file_store, file_free, file_add, NULL, file_iter_start, file_iter, file_iter_end, NULL, file_getkeys, file_addkey }; static struct hx509_keyset_ops keyset_pemfile = { "PEM-FILE", 0, file_init_pem, file_store, file_free, file_add, NULL, file_iter_start, file_iter, file_iter_end, NULL, file_getkeys, file_addkey }; static struct hx509_keyset_ops keyset_derfile = { "DER-FILE", 0, file_init_der, file_store, file_free, file_add, NULL, file_iter_start, file_iter, file_iter_end, NULL, file_getkeys, file_addkey }; void _hx509_ks_file_register(hx509_context context) { _hx509_ks_register(context, &keyset_file); _hx509_ks_register(context, &keyset_pemfile); _hx509_ks_register(context, &keyset_derfile); } Index: projects/openssl111/crypto/heimdal/lib/hx509/ks_p11.c =================================================================== --- projects/openssl111/crypto/heimdal/lib/hx509/ks_p11.c (revision 339197) +++ projects/openssl111/crypto/heimdal/lib/hx509/ks_p11.c (revision 339198) @@ -1,1190 +1,1228 @@ /* * Copyright (c) 2004 - 2008 Kungliga Tekniska Högskolan * (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 "hx_locl.h" #ifdef HAVE_DLFCN_H #include #endif #ifdef HAVE_DLOPEN #include "pkcs11.h" struct p11_slot { int flags; #define P11_SESSION 1 #define P11_SESSION_IN_USE 2 #define P11_LOGIN_REQ 4 #define P11_LOGIN_DONE 8 #define P11_TOKEN_PRESENT 16 CK_SESSION_HANDLE session; CK_SLOT_ID id; CK_BBOOL token; char *name; hx509_certs certs; char *pin; struct { CK_MECHANISM_TYPE_PTR list; CK_ULONG num; CK_MECHANISM_INFO_PTR *infos; } mechs; }; struct p11_module { void *dl_handle; CK_FUNCTION_LIST_PTR funcs; CK_ULONG num_slots; unsigned int ref; struct p11_slot *slot; }; #define P11FUNC(module,f,args) (*(module)->funcs->C_##f)args static int p11_get_session(hx509_context, struct p11_module *, struct p11_slot *, hx509_lock, CK_SESSION_HANDLE *); static int p11_put_session(struct p11_module *, struct p11_slot *, CK_SESSION_HANDLE); static void p11_release_module(struct p11_module *); static int p11_list_keys(hx509_context, struct p11_module *, struct p11_slot *, CK_SESSION_HANDLE, hx509_lock, hx509_certs *); /* * */ struct p11_rsa { struct p11_module *p; struct p11_slot *slot; CK_OBJECT_HANDLE private_key; CK_OBJECT_HANDLE public_key; }; static int p11_rsa_public_encrypt(int flen, const unsigned char *from, unsigned char *to, RSA *rsa, int padding) { return -1; } static int p11_rsa_public_decrypt(int flen, const unsigned char *from, unsigned char *to, RSA *rsa, int padding) { return -1; } static int p11_rsa_private_encrypt(int flen, const unsigned char *from, unsigned char *to, RSA *rsa, int padding) { struct p11_rsa *p11rsa = RSA_get_app_data(rsa); CK_OBJECT_HANDLE key = p11rsa->private_key; CK_SESSION_HANDLE session; CK_MECHANISM mechanism; CK_ULONG ck_sigsize; int ret; if (padding != RSA_PKCS1_PADDING) return -1; memset(&mechanism, 0, sizeof(mechanism)); mechanism.mechanism = CKM_RSA_PKCS; ck_sigsize = RSA_size(rsa); ret = p11_get_session(NULL, p11rsa->p, p11rsa->slot, NULL, &session); if (ret) return -1; ret = P11FUNC(p11rsa->p, SignInit, (session, &mechanism, key)); if (ret != CKR_OK) { p11_put_session(p11rsa->p, p11rsa->slot, session); return -1; } ret = P11FUNC(p11rsa->p, Sign, (session, (CK_BYTE *)(intptr_t)from, flen, to, &ck_sigsize)); p11_put_session(p11rsa->p, p11rsa->slot, session); if (ret != CKR_OK) return -1; return ck_sigsize; } static int p11_rsa_private_decrypt(int flen, const unsigned char *from, unsigned char *to, RSA * rsa, int padding) { struct p11_rsa *p11rsa = RSA_get_app_data(rsa); CK_OBJECT_HANDLE key = p11rsa->private_key; CK_SESSION_HANDLE session; CK_MECHANISM mechanism; CK_ULONG ck_sigsize; int ret; if (padding != RSA_PKCS1_PADDING) return -1; memset(&mechanism, 0, sizeof(mechanism)); mechanism.mechanism = CKM_RSA_PKCS; ck_sigsize = RSA_size(rsa); ret = p11_get_session(NULL, p11rsa->p, p11rsa->slot, NULL, &session); if (ret) return -1; ret = P11FUNC(p11rsa->p, DecryptInit, (session, &mechanism, key)); if (ret != CKR_OK) { p11_put_session(p11rsa->p, p11rsa->slot, session); return -1; } ret = P11FUNC(p11rsa->p, Decrypt, (session, (CK_BYTE *)(intptr_t)from, flen, to, &ck_sigsize)); p11_put_session(p11rsa->p, p11rsa->slot, session); if (ret != CKR_OK) return -1; return ck_sigsize; } static int p11_rsa_init(RSA *rsa) { return 1; } static int p11_rsa_finish(RSA *rsa) { struct p11_rsa *p11rsa = RSA_get_app_data(rsa); p11_release_module(p11rsa->p); free(p11rsa); return 1; } -static const RSA_METHOD p11_rsa_pkcs1_method = { - "hx509 PKCS11 PKCS#1 RSA", - p11_rsa_public_encrypt, - p11_rsa_public_decrypt, - p11_rsa_private_encrypt, - p11_rsa_private_decrypt, - NULL, - NULL, - p11_rsa_init, - p11_rsa_finish, - 0, - NULL, - NULL, - NULL -}; +static const RSA_METHOD * +get_p11_rsa_pkcs1_method(void) +{ + static const RSA_METHOD *p11_rsa_pkcs1_method; + RSA_METHOD *new_method; + if (p11_rsa_pkcs1_method != NULL) + return p11_rsa_pkcs1_method; + + new_method = RSA_meth_new("hx509 PKCS11 PKCS#1 RSA", 0); + if (new_method == NULL) + return NULL; + + if (RSA_meth_set_pub_enc(new_method, p11_rsa_public_encrypt) != 1) + goto out; + + if (RSA_meth_set_pub_dec(new_method, p11_rsa_public_decrypt) != 1) + goto out; + + if (RSA_meth_set_priv_enc(new_method, p11_rsa_private_encrypt) != 1) + goto out; + + if (RSA_meth_set_priv_dec(new_method, p11_rsa_private_decrypt) != 1) + goto out; + + if (RSA_meth_set_init(new_method, p11_rsa_init) != 1) + goto out; + + if (RSA_meth_set_finish(new_method, p11_rsa_finish) != 1) + goto out; + + /* + * This might overwrite a previously-created method if multiple + * threads invoke this concurrently which will leak memory. + */ + p11_rsa_pkcs1_method = new_method; + return p11_rsa_pkcs1_method; +out: + RSA_meth_free(new_method); + return NULL; +} + /* * */ static int p11_mech_info(hx509_context context, struct p11_module *p, struct p11_slot *slot, int num) { CK_ULONG i; int ret; ret = P11FUNC(p, GetMechanismList, (slot->id, NULL_PTR, &i)); if (ret) { hx509_set_error_string(context, 0, HX509_PKCS11_NO_MECH, "Failed to get mech list count for slot %d", num); return HX509_PKCS11_NO_MECH; } if (i == 0) { hx509_set_error_string(context, 0, HX509_PKCS11_NO_MECH, "no mech supported for slot %d", num); return HX509_PKCS11_NO_MECH; } slot->mechs.list = calloc(i, sizeof(slot->mechs.list[0])); if (slot->mechs.list == NULL) { hx509_set_error_string(context, 0, ENOMEM, "out of memory"); return ENOMEM; } slot->mechs.num = i; ret = P11FUNC(p, GetMechanismList, (slot->id, slot->mechs.list, &i)); if (ret) { hx509_set_error_string(context, 0, HX509_PKCS11_NO_MECH, "Failed to get mech list for slot %d", num); return HX509_PKCS11_NO_MECH; } assert(i == slot->mechs.num); slot->mechs.infos = calloc(i, sizeof(*slot->mechs.infos)); if (slot->mechs.list == NULL) { hx509_set_error_string(context, 0, ENOMEM, "out of memory"); return ENOMEM; } for (i = 0; i < slot->mechs.num; i++) { slot->mechs.infos[i] = calloc(1, sizeof(*(slot->mechs.infos[0]))); if (slot->mechs.infos[i] == NULL) { hx509_set_error_string(context, 0, ENOMEM, "out of memory"); return ENOMEM; } ret = P11FUNC(p, GetMechanismInfo, (slot->id, slot->mechs.list[i], slot->mechs.infos[i])); if (ret) { hx509_set_error_string(context, 0, HX509_PKCS11_NO_MECH, "Failed to get mech info for slot %d", num); return HX509_PKCS11_NO_MECH; } } return 0; } static int p11_init_slot(hx509_context context, struct p11_module *p, hx509_lock lock, CK_SLOT_ID id, int num, struct p11_slot *slot) { CK_SESSION_HANDLE session; CK_SLOT_INFO slot_info; CK_TOKEN_INFO token_info; size_t i; int ret; slot->certs = NULL; slot->id = id; ret = P11FUNC(p, GetSlotInfo, (slot->id, &slot_info)); if (ret) { hx509_set_error_string(context, 0, HX509_PKCS11_TOKEN_CONFUSED, "Failed to init PKCS11 slot %d", num); return HX509_PKCS11_TOKEN_CONFUSED; } for (i = sizeof(slot_info.slotDescription) - 1; i > 0; i--) { char c = slot_info.slotDescription[i]; if (c == ' ' || c == '\t' || c == '\n' || c == '\r' || c == '\0') continue; i++; break; } asprintf(&slot->name, "%.*s", (int)i, slot_info.slotDescription); if ((slot_info.flags & CKF_TOKEN_PRESENT) == 0) return 0; ret = P11FUNC(p, GetTokenInfo, (slot->id, &token_info)); if (ret) { hx509_set_error_string(context, 0, HX509_PKCS11_NO_TOKEN, "Failed to init PKCS11 slot %d " "with error 0x08x", num, ret); return HX509_PKCS11_NO_TOKEN; } slot->flags |= P11_TOKEN_PRESENT; if (token_info.flags & CKF_LOGIN_REQUIRED) slot->flags |= P11_LOGIN_REQ; ret = p11_get_session(context, p, slot, lock, &session); if (ret) return ret; ret = p11_mech_info(context, p, slot, num); if (ret) goto out; ret = p11_list_keys(context, p, slot, session, lock, &slot->certs); out: p11_put_session(p, slot, session); return ret; } static int p11_get_session(hx509_context context, struct p11_module *p, struct p11_slot *slot, hx509_lock lock, CK_SESSION_HANDLE *psession) { CK_RV ret; if (slot->flags & P11_SESSION_IN_USE) _hx509_abort("slot already in session"); if (slot->flags & P11_SESSION) { slot->flags |= P11_SESSION_IN_USE; *psession = slot->session; return 0; } ret = P11FUNC(p, OpenSession, (slot->id, CKF_SERIAL_SESSION, NULL, NULL, &slot->session)); if (ret != CKR_OK) { if (context) hx509_set_error_string(context, 0, HX509_PKCS11_OPEN_SESSION, "Failed to OpenSession for slot id %d " "with error: 0x%08x", (int)slot->id, ret); return HX509_PKCS11_OPEN_SESSION; } slot->flags |= P11_SESSION; /* * If we have have to login, and haven't tried before and have a * prompter or known to work pin code. * * This code is very conversative and only uses the prompter in * the hx509_lock, the reason is that it's bad to try many * passwords on a pkcs11 token, it might lock up and have to be * unlocked by a administrator. * * XXX try harder to not use pin several times on the same card. */ if ( (slot->flags & P11_LOGIN_REQ) && (slot->flags & P11_LOGIN_DONE) == 0 && (lock || slot->pin)) { hx509_prompt prompt; char pin[20]; char *str; if (slot->pin == NULL) { memset(&prompt, 0, sizeof(prompt)); asprintf(&str, "PIN code for %s: ", slot->name); prompt.prompt = str; prompt.type = HX509_PROMPT_TYPE_PASSWORD; prompt.reply.data = pin; prompt.reply.length = sizeof(pin); ret = hx509_lock_prompt(lock, &prompt); if (ret) { free(str); if (context) hx509_set_error_string(context, 0, ret, "Failed to get pin code for slot " "id %d with error: %d", (int)slot->id, ret); return ret; } free(str); } else { strlcpy(pin, slot->pin, sizeof(pin)); } ret = P11FUNC(p, Login, (slot->session, CKU_USER, (unsigned char*)pin, strlen(pin))); if (ret != CKR_OK) { if (context) hx509_set_error_string(context, 0, HX509_PKCS11_LOGIN, "Failed to login on slot id %d " "with error: 0x%08x", (int)slot->id, ret); return HX509_PKCS11_LOGIN; } else slot->flags |= P11_LOGIN_DONE; if (slot->pin == NULL) { slot->pin = strdup(pin); if (slot->pin == NULL) { if (context) hx509_set_error_string(context, 0, ENOMEM, "out of memory"); return ENOMEM; } } } else slot->flags |= P11_LOGIN_DONE; slot->flags |= P11_SESSION_IN_USE; *psession = slot->session; return 0; } static int p11_put_session(struct p11_module *p, struct p11_slot *slot, CK_SESSION_HANDLE session) { if ((slot->flags & P11_SESSION_IN_USE) == 0) _hx509_abort("slot not in session"); slot->flags &= ~P11_SESSION_IN_USE; return 0; } static int iterate_entries(hx509_context context, struct p11_module *p, struct p11_slot *slot, CK_SESSION_HANDLE session, CK_ATTRIBUTE *search_data, int num_search_data, CK_ATTRIBUTE *query, int num_query, int (*func)(hx509_context, struct p11_module *, struct p11_slot *, CK_SESSION_HANDLE session, CK_OBJECT_HANDLE object, void *, CK_ATTRIBUTE *, int), void *ptr) { CK_OBJECT_HANDLE object; CK_ULONG object_count; int ret, ret2, i; ret = P11FUNC(p, FindObjectsInit, (session, search_data, num_search_data)); if (ret != CKR_OK) { return -1; } while (1) { ret = P11FUNC(p, FindObjects, (session, &object, 1, &object_count)); if (ret != CKR_OK) { return -1; } if (object_count == 0) break; for (i = 0; i < num_query; i++) query[i].pValue = NULL; ret = P11FUNC(p, GetAttributeValue, (session, object, query, num_query)); if (ret != CKR_OK) { return -1; } for (i = 0; i < num_query; i++) { query[i].pValue = malloc(query[i].ulValueLen); if (query[i].pValue == NULL) { ret = ENOMEM; goto out; } } ret = P11FUNC(p, GetAttributeValue, (session, object, query, num_query)); if (ret != CKR_OK) { ret = -1; goto out; } ret = (*func)(context, p, slot, session, object, ptr, query, num_query); if (ret) goto out; for (i = 0; i < num_query; i++) { if (query[i].pValue) free(query[i].pValue); query[i].pValue = NULL; } } out: for (i = 0; i < num_query; i++) { if (query[i].pValue) free(query[i].pValue); query[i].pValue = NULL; } ret2 = P11FUNC(p, FindObjectsFinal, (session)); if (ret2 != CKR_OK) { return ret2; } return ret; } static BIGNUM * getattr_bn(struct p11_module *p, struct p11_slot *slot, CK_SESSION_HANDLE session, CK_OBJECT_HANDLE object, unsigned int type) { CK_ATTRIBUTE query; BIGNUM *bn; int ret; query.type = type; query.pValue = NULL; query.ulValueLen = 0; ret = P11FUNC(p, GetAttributeValue, (session, object, &query, 1)); if (ret != CKR_OK) return NULL; query.pValue = malloc(query.ulValueLen); ret = P11FUNC(p, GetAttributeValue, (session, object, &query, 1)); if (ret != CKR_OK) { free(query.pValue); return NULL; } bn = BN_bin2bn(query.pValue, query.ulValueLen, NULL); free(query.pValue); return bn; } static int collect_private_key(hx509_context context, struct p11_module *p, struct p11_slot *slot, CK_SESSION_HANDLE session, CK_OBJECT_HANDLE object, void *ptr, CK_ATTRIBUTE *query, int num_query) { struct hx509_collector *collector = ptr; hx509_private_key key; heim_octet_string localKeyId; int ret; + const RSA_METHOD *meth; + BIGNUM *n, *e; RSA *rsa; struct p11_rsa *p11rsa; localKeyId.data = query[0].pValue; localKeyId.length = query[0].ulValueLen; ret = hx509_private_key_init(&key, NULL, NULL); if (ret) return ret; rsa = RSA_new(); if (rsa == NULL) _hx509_abort("out of memory"); /* * The exponent and modulus should always be present according to * the pkcs11 specification, but some smartcards leaves it out, * let ignore any failure to fetch it. */ - rsa->n = getattr_bn(p, slot, session, object, CKA_MODULUS); - rsa->e = getattr_bn(p, slot, session, object, CKA_PUBLIC_EXPONENT); + n = getattr_bn(p, slot, session, object, CKA_MODULUS); + e = getattr_bn(p, slot, session, object, CKA_PUBLIC_EXPONENT); + if (RSA_set0_key(rsa, n, e, NULL) != 1) { + BN_free(n); + BN_free(e); + RSA_free(rsa); + hx509_private_key_free(&key); + return EINVAL; + } p11rsa = calloc(1, sizeof(*p11rsa)); if (p11rsa == NULL) _hx509_abort("out of memory"); p11rsa->p = p; p11rsa->slot = slot; p11rsa->private_key = object; if (p->ref == 0) _hx509_abort("pkcs11 ref == 0 on alloc"); p->ref++; if (p->ref == UINT_MAX) _hx509_abort("pkcs11 ref == UINT_MAX on alloc"); - RSA_set_method(rsa, &p11_rsa_pkcs1_method); + meth = get_p11_rsa_pkcs1_method(); + if (meth == NULL) + _hx509_abort("failed to create RSA method"); + RSA_set_method(rsa, meth); ret = RSA_set_app_data(rsa, p11rsa); if (ret != 1) _hx509_abort("RSA_set_app_data"); hx509_private_key_assign_rsa(key, rsa); ret = _hx509_collector_private_key_add(context, collector, hx509_signature_rsa(), key, NULL, &localKeyId); if (ret) { hx509_private_key_free(&key); return ret; } return 0; } static void p11_cert_release(hx509_cert cert, void *ctx) { struct p11_module *p = ctx; p11_release_module(p); } static int collect_cert(hx509_context context, struct p11_module *p, struct p11_slot *slot, CK_SESSION_HANDLE session, CK_OBJECT_HANDLE object, void *ptr, CK_ATTRIBUTE *query, int num_query) { struct hx509_collector *collector = ptr; hx509_cert cert; int ret; if ((CK_LONG)query[0].ulValueLen == -1 || (CK_LONG)query[1].ulValueLen == -1) { return 0; } ret = hx509_cert_init_data(context, query[1].pValue, query[1].ulValueLen, &cert); if (ret) return ret; if (p->ref == 0) _hx509_abort("pkcs11 ref == 0 on alloc"); p->ref++; if (p->ref == UINT_MAX) _hx509_abort("pkcs11 ref to high"); _hx509_cert_set_release(cert, p11_cert_release, p); { heim_octet_string data; data.data = query[0].pValue; data.length = query[0].ulValueLen; _hx509_set_cert_attribute(context, cert, &asn1_oid_id_pkcs_9_at_localKeyId, &data); } if ((CK_LONG)query[2].ulValueLen != -1) { char *str; asprintf(&str, "%.*s", (int)query[2].ulValueLen, (char *)query[2].pValue); if (str) { hx509_cert_set_friendly_name(cert, str); free(str); } } ret = _hx509_collector_certs_add(context, collector, cert); hx509_cert_free(cert); return ret; } static int p11_list_keys(hx509_context context, struct p11_module *p, struct p11_slot *slot, CK_SESSION_HANDLE session, hx509_lock lock, hx509_certs *certs) { struct hx509_collector *collector; CK_OBJECT_CLASS key_class; CK_ATTRIBUTE search_data[] = { {CKA_CLASS, NULL, 0}, }; CK_ATTRIBUTE query_data[3] = { {CKA_ID, NULL, 0}, {CKA_VALUE, NULL, 0}, {CKA_LABEL, NULL, 0} }; int ret; search_data[0].pValue = &key_class; search_data[0].ulValueLen = sizeof(key_class); if (lock == NULL) lock = _hx509_empty_lock; ret = _hx509_collector_alloc(context, lock, &collector); if (ret) return ret; key_class = CKO_PRIVATE_KEY; ret = iterate_entries(context, p, slot, session, search_data, 1, query_data, 1, collect_private_key, collector); if (ret) goto out; key_class = CKO_CERTIFICATE; ret = iterate_entries(context, p, slot, session, search_data, 1, query_data, 3, collect_cert, collector); if (ret) goto out; ret = _hx509_collector_collect_certs(context, collector, &slot->certs); out: _hx509_collector_free(collector); return ret; } static int p11_init(hx509_context context, hx509_certs certs, void **data, int flags, const char *residue, hx509_lock lock) { CK_C_GetFunctionList getFuncs; struct p11_module *p; char *list, *str; int ret; *data = NULL; list = strdup(residue); if (list == NULL) return ENOMEM; p = calloc(1, sizeof(*p)); if (p == NULL) { free(list); return ENOMEM; } p->ref = 1; str = strchr(list, ','); if (str) *str++ = '\0'; while (str) { char *strnext; strnext = strchr(str, ','); if (strnext) *strnext++ = '\0'; #if 0 if (strncasecmp(str, "slot=", 5) == 0) p->selected_slot = atoi(str + 5); #endif str = strnext; } p->dl_handle = dlopen(list, RTLD_NOW); free(list); if (p->dl_handle == NULL) { ret = HX509_PKCS11_LOAD; hx509_set_error_string(context, 0, ret, "Failed to open %s: %s", list, dlerror()); goto out; } getFuncs = (CK_C_GetFunctionList) dlsym(p->dl_handle, "C_GetFunctionList"); if (getFuncs == NULL) { ret = HX509_PKCS11_LOAD; hx509_set_error_string(context, 0, ret, "C_GetFunctionList missing in %s: %s", list, dlerror()); goto out; } ret = (*getFuncs)(&p->funcs); if (ret) { ret = HX509_PKCS11_LOAD; hx509_set_error_string(context, 0, ret, "C_GetFunctionList failed in %s", list); goto out; } ret = P11FUNC(p, Initialize, (NULL_PTR)); if (ret != CKR_OK) { ret = HX509_PKCS11_TOKEN_CONFUSED; hx509_set_error_string(context, 0, ret, "Failed initialize the PKCS11 module"); goto out; } ret = P11FUNC(p, GetSlotList, (FALSE, NULL, &p->num_slots)); if (ret) { ret = HX509_PKCS11_TOKEN_CONFUSED; hx509_set_error_string(context, 0, ret, "Failed to get number of PKCS11 slots"); goto out; } if (p->num_slots == 0) { ret = HX509_PKCS11_NO_SLOT; hx509_set_error_string(context, 0, ret, "Selected PKCS11 module have no slots"); goto out; } { CK_SLOT_ID_PTR slot_ids; int num_tokens = 0; size_t i; slot_ids = malloc(p->num_slots * sizeof(*slot_ids)); if (slot_ids == NULL) { hx509_clear_error_string(context); ret = ENOMEM; goto out; } ret = P11FUNC(p, GetSlotList, (FALSE, slot_ids, &p->num_slots)); if (ret) { free(slot_ids); hx509_set_error_string(context, 0, HX509_PKCS11_TOKEN_CONFUSED, "Failed getting slot-list from " "PKCS11 module"); ret = HX509_PKCS11_TOKEN_CONFUSED; goto out; } p->slot = calloc(p->num_slots, sizeof(p->slot[0])); if (p->slot == NULL) { free(slot_ids); hx509_set_error_string(context, 0, ENOMEM, "Failed to get memory for slot-list"); ret = ENOMEM; goto out; } for (i = 0; i < p->num_slots; i++) { ret = p11_init_slot(context, p, lock, slot_ids[i], i, &p->slot[i]); if (ret) break; if (p->slot[i].flags & P11_TOKEN_PRESENT) num_tokens++; } free(slot_ids); if (ret) goto out; if (num_tokens == 0) { ret = HX509_PKCS11_NO_TOKEN; goto out; } } *data = p; return 0; out: p11_release_module(p); return ret; } static void p11_release_module(struct p11_module *p) { size_t i; if (p->ref == 0) _hx509_abort("pkcs11 ref to low"); if (--p->ref > 0) return; for (i = 0; i < p->num_slots; i++) { if (p->slot[i].flags & P11_SESSION_IN_USE) _hx509_abort("pkcs11 module release while session in use"); if (p->slot[i].flags & P11_SESSION) { P11FUNC(p, CloseSession, (p->slot[i].session)); } if (p->slot[i].name) free(p->slot[i].name); if (p->slot[i].pin) { memset(p->slot[i].pin, 0, strlen(p->slot[i].pin)); free(p->slot[i].pin); } if (p->slot[i].mechs.num) { free(p->slot[i].mechs.list); if (p->slot[i].mechs.infos) { size_t j; for (j = 0 ; j < p->slot[i].mechs.num ; j++) free(p->slot[i].mechs.infos[j]); free(p->slot[i].mechs.infos); } } } free(p->slot); if (p->funcs) P11FUNC(p, Finalize, (NULL)); if (p->dl_handle) dlclose(p->dl_handle); memset(p, 0, sizeof(*p)); free(p); } static int p11_free(hx509_certs certs, void *data) { struct p11_module *p = data; size_t i; for (i = 0; i < p->num_slots; i++) { if (p->slot[i].certs) hx509_certs_free(&p->slot[i].certs); } p11_release_module(p); return 0; } struct p11_cursor { hx509_certs certs; void *cursor; }; static int p11_iter_start(hx509_context context, hx509_certs certs, void *data, void **cursor) { struct p11_module *p = data; struct p11_cursor *c; int ret; size_t i; c = malloc(sizeof(*c)); if (c == NULL) { hx509_clear_error_string(context); return ENOMEM; } ret = hx509_certs_init(context, "MEMORY:pkcs11-iter", 0, NULL, &c->certs); if (ret) { free(c); return ret; } for (i = 0 ; i < p->num_slots; i++) { if (p->slot[i].certs == NULL) continue; ret = hx509_certs_merge(context, c->certs, p->slot[i].certs); if (ret) { hx509_certs_free(&c->certs); free(c); return ret; } } ret = hx509_certs_start_seq(context, c->certs, &c->cursor); if (ret) { hx509_certs_free(&c->certs); free(c); return 0; } *cursor = c; return 0; } static int p11_iter(hx509_context context, hx509_certs certs, void *data, void *cursor, hx509_cert *cert) { struct p11_cursor *c = cursor; return hx509_certs_next_cert(context, c->certs, c->cursor, cert); } static int p11_iter_end(hx509_context context, hx509_certs certs, void *data, void *cursor) { struct p11_cursor *c = cursor; int ret; ret = hx509_certs_end_seq(context, c->certs, c->cursor); hx509_certs_free(&c->certs); free(c); return ret; } #define MECHFLAG(x) { "unknown-flag-" #x, x } static struct units mechflags[] = { MECHFLAG(0x80000000), MECHFLAG(0x40000000), MECHFLAG(0x20000000), MECHFLAG(0x10000000), MECHFLAG(0x08000000), MECHFLAG(0x04000000), {"ec-compress", 0x2000000 }, {"ec-uncompress", 0x1000000 }, {"ec-namedcurve", 0x0800000 }, {"ec-ecparameters", 0x0400000 }, {"ec-f-2m", 0x0200000 }, {"ec-f-p", 0x0100000 }, {"derive", 0x0080000 }, {"unwrap", 0x0040000 }, {"wrap", 0x0020000 }, {"genereate-key-pair", 0x0010000 }, {"generate", 0x0008000 }, {"verify-recover", 0x0004000 }, {"verify", 0x0002000 }, {"sign-recover", 0x0001000 }, {"sign", 0x0000800 }, {"digest", 0x0000400 }, {"decrypt", 0x0000200 }, {"encrypt", 0x0000100 }, MECHFLAG(0x00080), MECHFLAG(0x00040), MECHFLAG(0x00020), MECHFLAG(0x00010), MECHFLAG(0x00008), MECHFLAG(0x00004), MECHFLAG(0x00002), {"hw", 0x0000001 }, { NULL, 0x0000000 } }; #undef MECHFLAG static int p11_printinfo(hx509_context context, hx509_certs certs, void *data, int (*func)(void *, const char *), void *ctx) { struct p11_module *p = data; size_t i, j; _hx509_pi_printf(func, ctx, "pkcs11 driver with %d slot%s", p->num_slots, p->num_slots > 1 ? "s" : ""); for (i = 0; i < p->num_slots; i++) { struct p11_slot *s = &p->slot[i]; _hx509_pi_printf(func, ctx, "slot %d: id: %d name: %s flags: %08x", i, (int)s->id, s->name, s->flags); _hx509_pi_printf(func, ctx, "number of supported mechanisms: %lu", (unsigned long)s->mechs.num); for (j = 0; j < s->mechs.num; j++) { const char *mechname = "unknown"; char flags[256], unknownname[40]; #define MECHNAME(s,n) case s: mechname = n; break switch(s->mechs.list[j]) { MECHNAME(CKM_RSA_PKCS_KEY_PAIR_GEN, "rsa-pkcs-key-pair-gen"); MECHNAME(CKM_RSA_PKCS, "rsa-pkcs"); MECHNAME(CKM_RSA_X_509, "rsa-x-509"); MECHNAME(CKM_MD5_RSA_PKCS, "md5-rsa-pkcs"); MECHNAME(CKM_SHA1_RSA_PKCS, "sha1-rsa-pkcs"); MECHNAME(CKM_SHA256_RSA_PKCS, "sha256-rsa-pkcs"); MECHNAME(CKM_SHA384_RSA_PKCS, "sha384-rsa-pkcs"); MECHNAME(CKM_SHA512_RSA_PKCS, "sha512-rsa-pkcs"); MECHNAME(CKM_RIPEMD160_RSA_PKCS, "ripemd160-rsa-pkcs"); MECHNAME(CKM_RSA_PKCS_OAEP, "rsa-pkcs-oaep"); MECHNAME(CKM_SHA512_HMAC, "sha512-hmac"); MECHNAME(CKM_SHA512, "sha512"); MECHNAME(CKM_SHA384_HMAC, "sha384-hmac"); MECHNAME(CKM_SHA384, "sha384"); MECHNAME(CKM_SHA256_HMAC, "sha256-hmac"); MECHNAME(CKM_SHA256, "sha256"); MECHNAME(CKM_SHA_1, "sha1"); MECHNAME(CKM_MD5, "md5"); MECHNAME(CKM_RIPEMD160, "ripemd-160"); MECHNAME(CKM_DES_ECB, "des-ecb"); MECHNAME(CKM_DES_CBC, "des-cbc"); MECHNAME(CKM_AES_ECB, "aes-ecb"); MECHNAME(CKM_AES_CBC, "aes-cbc"); MECHNAME(CKM_DH_PKCS_PARAMETER_GEN, "dh-pkcs-parameter-gen"); default: snprintf(unknownname, sizeof(unknownname), "unknown-mech-%lu", (unsigned long)s->mechs.list[j]); mechname = unknownname; break; } #undef MECHNAME unparse_flags(s->mechs.infos[j]->flags, mechflags, flags, sizeof(flags)); _hx509_pi_printf(func, ctx, " %s: %s", mechname, flags); } } return 0; } static struct hx509_keyset_ops keyset_pkcs11 = { "PKCS11", 0, p11_init, NULL, p11_free, NULL, NULL, p11_iter_start, p11_iter, p11_iter_end, p11_printinfo }; #endif /* HAVE_DLOPEN */ void _hx509_ks_pkcs11_register(hx509_context context) { #ifdef HAVE_DLOPEN _hx509_ks_register(context, &keyset_pkcs11); #endif } Index: projects/openssl111/crypto/heimdal/lib/krb5/crypto-aes.c =================================================================== --- projects/openssl111/crypto/heimdal/lib/krb5/crypto-aes.c (revision 339197) +++ projects/openssl111/crypto/heimdal/lib/krb5/crypto-aes.c (revision 339198) @@ -1,170 +1,172 @@ /* * Copyright (c) 1997 - 2008 Kungliga Tekniska Högskolan * (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 "krb5_locl.h" /* * AES */ static struct _krb5_key_type keytype_aes128 = { ENCTYPE_AES128_CTS_HMAC_SHA1_96, "aes-128", 128, 16, sizeof(struct _krb5_evp_schedule), NULL, _krb5_evp_schedule, _krb5_AES_salt, NULL, _krb5_evp_cleanup, EVP_aes_128_cbc }; static struct _krb5_key_type keytype_aes256 = { ENCTYPE_AES256_CTS_HMAC_SHA1_96, "aes-256", 256, 32, sizeof(struct _krb5_evp_schedule), NULL, _krb5_evp_schedule, _krb5_AES_salt, NULL, _krb5_evp_cleanup, EVP_aes_256_cbc }; struct _krb5_checksum_type _krb5_checksum_hmac_sha1_aes128 = { CKSUMTYPE_HMAC_SHA1_96_AES_128, "hmac-sha1-96-aes128", 64, 12, F_KEYED | F_CPROOF | F_DERIVED, _krb5_SP_HMAC_SHA1_checksum, NULL }; struct _krb5_checksum_type _krb5_checksum_hmac_sha1_aes256 = { CKSUMTYPE_HMAC_SHA1_96_AES_256, "hmac-sha1-96-aes256", 64, 12, F_KEYED | F_CPROOF | F_DERIVED, _krb5_SP_HMAC_SHA1_checksum, NULL }; static krb5_error_code AES_PRF(krb5_context context, krb5_crypto crypto, const krb5_data *in, krb5_data *out) { struct _krb5_checksum_type *ct = crypto->et->checksum; krb5_error_code ret; Checksum result; krb5_keyblock *derived; result.cksumtype = ct->type; ret = krb5_data_alloc(&result.checksum, ct->checksumsize); if (ret) { krb5_set_error_message(context, ret, N_("malloc: out memory", "")); return ret; } ret = (*ct->checksum)(context, NULL, in->data, in->length, 0, &result); if (ret) { krb5_data_free(&result.checksum); return ret; } if (result.checksum.length < crypto->et->blocksize) krb5_abortx(context, "internal prf error"); derived = NULL; ret = krb5_derive_key(context, crypto->key.key, crypto->et->type, "prf", 3, &derived); if (ret) krb5_abortx(context, "krb5_derive_key"); ret = krb5_data_alloc(out, crypto->et->blocksize); if (ret) krb5_abortx(context, "malloc failed"); { const EVP_CIPHER *c = (*crypto->et->keytype->evp)(); - EVP_CIPHER_CTX ctx; + EVP_CIPHER_CTX *ctx; - EVP_CIPHER_CTX_init(&ctx); /* ivec all zero */ - EVP_CipherInit_ex(&ctx, c, NULL, derived->keyvalue.data, NULL, 1); - EVP_Cipher(&ctx, out->data, result.checksum.data, + ctx = EVP_CIPHER_CTX_new(); /* ivec all zero */ + if (ctx == NULL) + krb5_abortx(context, "malloc failed"); + EVP_CipherInit_ex(ctx, c, NULL, derived->keyvalue.data, NULL, 1); + EVP_Cipher(ctx, out->data, result.checksum.data, crypto->et->blocksize); - EVP_CIPHER_CTX_cleanup(&ctx); + EVP_CIPHER_CTX_free(ctx); } krb5_data_free(&result.checksum); krb5_free_keyblock(context, derived); return ret; } struct _krb5_encryption_type _krb5_enctype_aes128_cts_hmac_sha1 = { ETYPE_AES128_CTS_HMAC_SHA1_96, "aes128-cts-hmac-sha1-96", 16, 1, 16, &keytype_aes128, &_krb5_checksum_sha1, &_krb5_checksum_hmac_sha1_aes128, F_DERIVED, _krb5_evp_encrypt_cts, 16, AES_PRF }; struct _krb5_encryption_type _krb5_enctype_aes256_cts_hmac_sha1 = { ETYPE_AES256_CTS_HMAC_SHA1_96, "aes256-cts-hmac-sha1-96", 16, 1, 16, &keytype_aes256, &_krb5_checksum_sha1, &_krb5_checksum_hmac_sha1_aes256, F_DERIVED, _krb5_evp_encrypt_cts, 16, AES_PRF }; Index: projects/openssl111/crypto/heimdal/lib/krb5/crypto-arcfour.c =================================================================== --- projects/openssl111/crypto/heimdal/lib/krb5/crypto-arcfour.c (revision 339197) +++ projects/openssl111/crypto/heimdal/lib/krb5/crypto-arcfour.c (revision 339198) @@ -1,325 +1,329 @@ /* * Copyright (c) 1997 - 2008 Kungliga Tekniska Högskolan * (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. */ /* * ARCFOUR */ #include "krb5_locl.h" static struct _krb5_key_type keytype_arcfour = { ENCTYPE_ARCFOUR_HMAC_MD5, "arcfour", 128, 16, sizeof(struct _krb5_evp_schedule), NULL, _krb5_evp_schedule, _krb5_arcfour_salt, NULL, _krb5_evp_cleanup, EVP_rc4 }; /* * checksum according to section 5. of draft-brezak-win2k-krb-rc4-hmac-03.txt */ krb5_error_code _krb5_HMAC_MD5_checksum(krb5_context context, struct _krb5_key_data *key, const void *data, size_t len, unsigned usage, Checksum *result) { EVP_MD_CTX *m; struct _krb5_checksum_type *c = _krb5_find_checksum (CKSUMTYPE_RSA_MD5); const char signature[] = "signaturekey"; Checksum ksign_c; struct _krb5_key_data ksign; krb5_keyblock kb; unsigned char t[4]; unsigned char tmp[16]; unsigned char ksign_c_data[16]; krb5_error_code ret; m = EVP_MD_CTX_create(); if (m == NULL) { krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", "")); return ENOMEM; } ksign_c.checksum.length = sizeof(ksign_c_data); ksign_c.checksum.data = ksign_c_data; ret = _krb5_internal_hmac(context, c, signature, sizeof(signature), 0, key, &ksign_c); if (ret) { EVP_MD_CTX_destroy(m); return ret; } ksign.key = &kb; kb.keyvalue = ksign_c.checksum; EVP_DigestInit_ex(m, EVP_md5(), NULL); t[0] = (usage >> 0) & 0xFF; t[1] = (usage >> 8) & 0xFF; t[2] = (usage >> 16) & 0xFF; t[3] = (usage >> 24) & 0xFF; EVP_DigestUpdate(m, t, 4); EVP_DigestUpdate(m, data, len); EVP_DigestFinal_ex (m, tmp, NULL); EVP_MD_CTX_destroy(m); ret = _krb5_internal_hmac(context, c, tmp, sizeof(tmp), 0, &ksign, result); if (ret) return ret; return 0; } struct _krb5_checksum_type _krb5_checksum_hmac_md5 = { CKSUMTYPE_HMAC_MD5, "hmac-md5", 64, 16, F_KEYED | F_CPROOF, _krb5_HMAC_MD5_checksum, NULL }; /* * section 6 of draft-brezak-win2k-krb-rc4-hmac-03 * * warning: not for small children */ static krb5_error_code ARCFOUR_subencrypt(krb5_context context, struct _krb5_key_data *key, void *data, size_t len, unsigned usage, void *ivec) { - EVP_CIPHER_CTX ctx; + EVP_CIPHER_CTX *ctx; struct _krb5_checksum_type *c = _krb5_find_checksum (CKSUMTYPE_RSA_MD5); Checksum k1_c, k2_c, k3_c, cksum; struct _krb5_key_data ke; krb5_keyblock kb; unsigned char t[4]; unsigned char *cdata = data; unsigned char k1_c_data[16], k2_c_data[16], k3_c_data[16]; krb5_error_code ret; t[0] = (usage >> 0) & 0xFF; t[1] = (usage >> 8) & 0xFF; t[2] = (usage >> 16) & 0xFF; t[3] = (usage >> 24) & 0xFF; k1_c.checksum.length = sizeof(k1_c_data); k1_c.checksum.data = k1_c_data; ret = _krb5_internal_hmac(NULL, c, t, sizeof(t), 0, key, &k1_c); if (ret) krb5_abortx(context, "hmac failed"); memcpy (k2_c_data, k1_c_data, sizeof(k1_c_data)); k2_c.checksum.length = sizeof(k2_c_data); k2_c.checksum.data = k2_c_data; ke.key = &kb; kb.keyvalue = k2_c.checksum; cksum.checksum.length = 16; cksum.checksum.data = data; ret = _krb5_internal_hmac(NULL, c, cdata + 16, len - 16, 0, &ke, &cksum); if (ret) krb5_abortx(context, "hmac failed"); ke.key = &kb; kb.keyvalue = k1_c.checksum; k3_c.checksum.length = sizeof(k3_c_data); k3_c.checksum.data = k3_c_data; ret = _krb5_internal_hmac(NULL, c, data, 16, 0, &ke, &k3_c); if (ret) krb5_abortx(context, "hmac failed"); - EVP_CIPHER_CTX_init(&ctx); + ctx = EVP_CIPHER_CTX_new(); + if (ctx == NULL) + krb5_abortx(context, "malloc failed"); - EVP_CipherInit_ex(&ctx, EVP_rc4(), NULL, k3_c.checksum.data, NULL, 1); - EVP_Cipher(&ctx, cdata + 16, cdata + 16, len - 16); - EVP_CIPHER_CTX_cleanup(&ctx); + EVP_CipherInit_ex(ctx, EVP_rc4(), NULL, k3_c.checksum.data, NULL, 1); + EVP_Cipher(ctx, cdata + 16, cdata + 16, len - 16); + EVP_CIPHER_CTX_free(ctx); memset (k1_c_data, 0, sizeof(k1_c_data)); memset (k2_c_data, 0, sizeof(k2_c_data)); memset (k3_c_data, 0, sizeof(k3_c_data)); return 0; } static krb5_error_code ARCFOUR_subdecrypt(krb5_context context, struct _krb5_key_data *key, void *data, size_t len, unsigned usage, void *ivec) { - EVP_CIPHER_CTX ctx; + EVP_CIPHER_CTX *ctx; struct _krb5_checksum_type *c = _krb5_find_checksum (CKSUMTYPE_RSA_MD5); Checksum k1_c, k2_c, k3_c, cksum; struct _krb5_key_data ke; krb5_keyblock kb; unsigned char t[4]; unsigned char *cdata = data; unsigned char k1_c_data[16], k2_c_data[16], k3_c_data[16]; unsigned char cksum_data[16]; krb5_error_code ret; t[0] = (usage >> 0) & 0xFF; t[1] = (usage >> 8) & 0xFF; t[2] = (usage >> 16) & 0xFF; t[3] = (usage >> 24) & 0xFF; k1_c.checksum.length = sizeof(k1_c_data); k1_c.checksum.data = k1_c_data; ret = _krb5_internal_hmac(NULL, c, t, sizeof(t), 0, key, &k1_c); if (ret) krb5_abortx(context, "hmac failed"); memcpy (k2_c_data, k1_c_data, sizeof(k1_c_data)); k2_c.checksum.length = sizeof(k2_c_data); k2_c.checksum.data = k2_c_data; ke.key = &kb; kb.keyvalue = k1_c.checksum; k3_c.checksum.length = sizeof(k3_c_data); k3_c.checksum.data = k3_c_data; ret = _krb5_internal_hmac(NULL, c, cdata, 16, 0, &ke, &k3_c); if (ret) krb5_abortx(context, "hmac failed"); - EVP_CIPHER_CTX_init(&ctx); - EVP_CipherInit_ex(&ctx, EVP_rc4(), NULL, k3_c.checksum.data, NULL, 0); - EVP_Cipher(&ctx, cdata + 16, cdata + 16, len - 16); - EVP_CIPHER_CTX_cleanup(&ctx); + ctx = EVP_CIPHER_CTX_new(); + if (ctx == NULL) + krb5_abortx(context, "malloc failed"); + EVP_CipherInit_ex(ctx, EVP_rc4(), NULL, k3_c.checksum.data, NULL, 0); + EVP_Cipher(ctx, cdata + 16, cdata + 16, len - 16); + EVP_CIPHER_CTX_free(ctx); ke.key = &kb; kb.keyvalue = k2_c.checksum; cksum.checksum.length = 16; cksum.checksum.data = cksum_data; ret = _krb5_internal_hmac(NULL, c, cdata + 16, len - 16, 0, &ke, &cksum); if (ret) krb5_abortx(context, "hmac failed"); memset (k1_c_data, 0, sizeof(k1_c_data)); memset (k2_c_data, 0, sizeof(k2_c_data)); memset (k3_c_data, 0, sizeof(k3_c_data)); if (ct_memcmp (cksum.checksum.data, data, 16) != 0) { krb5_clear_error_message (context); return KRB5KRB_AP_ERR_BAD_INTEGRITY; } else { return 0; } } /* * convert the usage numbers used in * draft-ietf-cat-kerb-key-derivation-00.txt to the ones in * draft-brezak-win2k-krb-rc4-hmac-04.txt */ krb5_error_code _krb5_usage2arcfour(krb5_context context, unsigned *usage) { switch (*usage) { case KRB5_KU_AS_REP_ENC_PART : /* 3 */ *usage = 8; return 0; case KRB5_KU_USAGE_SEAL : /* 22 */ *usage = 13; return 0; case KRB5_KU_USAGE_SIGN : /* 23 */ *usage = 15; return 0; case KRB5_KU_USAGE_SEQ: /* 24 */ *usage = 0; return 0; default : return 0; } } static krb5_error_code ARCFOUR_encrypt(krb5_context context, struct _krb5_key_data *key, void *data, size_t len, krb5_boolean encryptp, int usage, void *ivec) { krb5_error_code ret; unsigned keyusage = usage; if((ret = _krb5_usage2arcfour (context, &keyusage)) != 0) return ret; if (encryptp) return ARCFOUR_subencrypt (context, key, data, len, keyusage, ivec); else return ARCFOUR_subdecrypt (context, key, data, len, keyusage, ivec); } struct _krb5_encryption_type _krb5_enctype_arcfour_hmac_md5 = { ETYPE_ARCFOUR_HMAC_MD5, "arcfour-hmac-md5", 1, 1, 8, &keytype_arcfour, &_krb5_checksum_hmac_md5, &_krb5_checksum_hmac_md5, F_SPECIAL, ARCFOUR_encrypt, 0, NULL }; Index: projects/openssl111/crypto/heimdal/lib/krb5/crypto-des-common.c =================================================================== --- projects/openssl111/crypto/heimdal/lib/krb5/crypto-des-common.c (revision 339197) +++ projects/openssl111/crypto/heimdal/lib/krb5/crypto-des-common.c (revision 339198) @@ -1,152 +1,152 @@ /* * Copyright (c) 1997 - 2008 Kungliga Tekniska Högskolan * (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. */ /* Functions which are used by both single and triple DES enctypes */ #include "krb5_locl.h" /* * A = A xor B. A & B are 8 bytes. */ void _krb5_xor (DES_cblock *key, const unsigned char *b) { unsigned char *a = (unsigned char*)key; a[0] ^= b[0]; a[1] ^= b[1]; a[2] ^= b[2]; a[3] ^= b[3]; a[4] ^= b[4]; a[5] ^= b[5]; a[6] ^= b[6]; a[7] ^= b[7]; } #if defined(DES3_OLD_ENCTYPE) || defined(HEIM_WEAK_CRYPTO) krb5_error_code _krb5_des_checksum(krb5_context context, const EVP_MD *evp_md, struct _krb5_key_data *key, const void *data, size_t len, Checksum *cksum) { struct _krb5_evp_schedule *ctx = key->schedule->data; EVP_MD_CTX *m; DES_cblock ivec; unsigned char *p = cksum->checksum.data; krb5_generate_random_block(p, 8); m = EVP_MD_CTX_create(); if (m == NULL) { krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", "")); return ENOMEM; } EVP_DigestInit_ex(m, evp_md, NULL); EVP_DigestUpdate(m, p, 8); EVP_DigestUpdate(m, data, len); EVP_DigestFinal_ex (m, p + 8, NULL); EVP_MD_CTX_destroy(m); memset (&ivec, 0, sizeof(ivec)); - EVP_CipherInit_ex(&ctx->ectx, NULL, NULL, NULL, (void *)&ivec, -1); - EVP_Cipher(&ctx->ectx, p, p, 24); + EVP_CipherInit_ex(ctx->ectx, NULL, NULL, NULL, (void *)&ivec, -1); + EVP_Cipher(ctx->ectx, p, p, 24); return 0; } krb5_error_code _krb5_des_verify(krb5_context context, const EVP_MD *evp_md, struct _krb5_key_data *key, const void *data, size_t len, Checksum *C) { struct _krb5_evp_schedule *ctx = key->schedule->data; EVP_MD_CTX *m; unsigned char tmp[24]; unsigned char res[16]; DES_cblock ivec; krb5_error_code ret = 0; m = EVP_MD_CTX_create(); if (m == NULL) { krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", "")); return ENOMEM; } memset(&ivec, 0, sizeof(ivec)); - EVP_CipherInit_ex(&ctx->dctx, NULL, NULL, NULL, (void *)&ivec, -1); - EVP_Cipher(&ctx->dctx, tmp, C->checksum.data, 24); + EVP_CipherInit_ex(ctx->dctx, NULL, NULL, NULL, (void *)&ivec, -1); + EVP_Cipher(ctx->dctx, tmp, C->checksum.data, 24); EVP_DigestInit_ex(m, evp_md, NULL); EVP_DigestUpdate(m, tmp, 8); /* confounder */ EVP_DigestUpdate(m, data, len); EVP_DigestFinal_ex (m, res, NULL); EVP_MD_CTX_destroy(m); if(ct_memcmp(res, tmp + 8, sizeof(res)) != 0) { krb5_clear_error_message (context); ret = KRB5KRB_AP_ERR_BAD_INTEGRITY; } memset(tmp, 0, sizeof(tmp)); memset(res, 0, sizeof(res)); return ret; } #endif static krb5_error_code RSA_MD5_checksum(krb5_context context, struct _krb5_key_data *key, const void *data, size_t len, unsigned usage, Checksum *C) { if (EVP_Digest(data, len, C->checksum.data, NULL, EVP_md5(), NULL) != 1) krb5_abortx(context, "md5 checksum failed"); return 0; } struct _krb5_checksum_type _krb5_checksum_rsa_md5 = { CKSUMTYPE_RSA_MD5, "rsa-md5", 64, 16, F_CPROOF, RSA_MD5_checksum, NULL }; Index: projects/openssl111/crypto/heimdal/lib/krb5/crypto-des.c =================================================================== --- projects/openssl111/crypto/heimdal/lib/krb5/crypto-des.c (revision 339197) +++ projects/openssl111/crypto/heimdal/lib/krb5/crypto-des.c (revision 339198) @@ -1,377 +1,377 @@ /* * Copyright (c) 1997 - 2008 Kungliga Tekniska Högskolan * (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 "krb5_locl.h" #ifdef HEIM_WEAK_CRYPTO static void krb5_DES_random_key(krb5_context context, krb5_keyblock *key) { DES_cblock *k = key->keyvalue.data; do { krb5_generate_random_block(k, sizeof(DES_cblock)); DES_set_odd_parity(k); } while(DES_is_weak_key(k)); } static void krb5_DES_schedule_old(krb5_context context, struct _krb5_key_type *kt, struct _krb5_key_data *key) { DES_set_key_unchecked(key->key->keyvalue.data, key->schedule->data); } static void krb5_DES_random_to_key(krb5_context context, krb5_keyblock *key, const void *data, size_t size) { DES_cblock *k = key->keyvalue.data; memcpy(k, data, key->keyvalue.length); DES_set_odd_parity(k); if(DES_is_weak_key(k)) _krb5_xor(k, (const unsigned char*)"\0\0\0\0\0\0\0\xf0"); } static struct _krb5_key_type keytype_des_old = { ETYPE_DES_CBC_CRC, "des-old", 56, 8, sizeof(DES_key_schedule), krb5_DES_random_key, krb5_DES_schedule_old, _krb5_des_salt, krb5_DES_random_to_key, NULL, NULL }; static struct _krb5_key_type keytype_des = { ETYPE_DES_CBC_CRC, "des", 56, 8, sizeof(struct _krb5_evp_schedule), krb5_DES_random_key, _krb5_evp_schedule, _krb5_des_salt, krb5_DES_random_to_key, _krb5_evp_cleanup, EVP_des_cbc }; static krb5_error_code CRC32_checksum(krb5_context context, struct _krb5_key_data *key, const void *data, size_t len, unsigned usage, Checksum *C) { uint32_t crc; unsigned char *r = C->checksum.data; _krb5_crc_init_table (); crc = _krb5_crc_update (data, len, 0); r[0] = crc & 0xff; r[1] = (crc >> 8) & 0xff; r[2] = (crc >> 16) & 0xff; r[3] = (crc >> 24) & 0xff; return 0; } static krb5_error_code RSA_MD4_checksum(krb5_context context, struct _krb5_key_data *key, const void *data, size_t len, unsigned usage, Checksum *C) { if (EVP_Digest(data, len, C->checksum.data, NULL, EVP_md4(), NULL) != 1) krb5_abortx(context, "md4 checksum failed"); return 0; } static krb5_error_code RSA_MD4_DES_checksum(krb5_context context, struct _krb5_key_data *key, const void *data, size_t len, unsigned usage, Checksum *cksum) { return _krb5_des_checksum(context, EVP_md4(), key, data, len, cksum); } static krb5_error_code RSA_MD4_DES_verify(krb5_context context, struct _krb5_key_data *key, const void *data, size_t len, unsigned usage, Checksum *C) { return _krb5_des_verify(context, EVP_md4(), key, data, len, C); } static krb5_error_code RSA_MD5_DES_checksum(krb5_context context, struct _krb5_key_data *key, const void *data, size_t len, unsigned usage, Checksum *C) { return _krb5_des_checksum(context, EVP_md5(), key, data, len, C); } static krb5_error_code RSA_MD5_DES_verify(krb5_context context, struct _krb5_key_data *key, const void *data, size_t len, unsigned usage, Checksum *C) { return _krb5_des_verify(context, EVP_md5(), key, data, len, C); } struct _krb5_checksum_type _krb5_checksum_crc32 = { CKSUMTYPE_CRC32, "crc32", 1, 4, 0, CRC32_checksum, NULL }; struct _krb5_checksum_type _krb5_checksum_rsa_md4 = { CKSUMTYPE_RSA_MD4, "rsa-md4", 64, 16, F_CPROOF, RSA_MD4_checksum, NULL }; struct _krb5_checksum_type _krb5_checksum_rsa_md4_des = { CKSUMTYPE_RSA_MD4_DES, "rsa-md4-des", 64, 24, F_KEYED | F_CPROOF | F_VARIANT, RSA_MD4_DES_checksum, RSA_MD4_DES_verify }; struct _krb5_checksum_type _krb5_checksum_rsa_md5_des = { CKSUMTYPE_RSA_MD5_DES, "rsa-md5-des", 64, 24, F_KEYED | F_CPROOF | F_VARIANT, RSA_MD5_DES_checksum, RSA_MD5_DES_verify }; static krb5_error_code evp_des_encrypt_null_ivec(krb5_context context, struct _krb5_key_data *key, void *data, size_t len, krb5_boolean encryptp, int usage, void *ignore_ivec) { struct _krb5_evp_schedule *ctx = key->schedule->data; EVP_CIPHER_CTX *c; DES_cblock ivec; memset(&ivec, 0, sizeof(ivec)); - c = encryptp ? &ctx->ectx : &ctx->dctx; + c = encryptp ? ctx->ectx : ctx->dctx; EVP_CipherInit_ex(c, NULL, NULL, NULL, (void *)&ivec, -1); EVP_Cipher(c, data, data, len); return 0; } static krb5_error_code evp_des_encrypt_key_ivec(krb5_context context, struct _krb5_key_data *key, void *data, size_t len, krb5_boolean encryptp, int usage, void *ignore_ivec) { struct _krb5_evp_schedule *ctx = key->schedule->data; EVP_CIPHER_CTX *c; DES_cblock ivec; memcpy(&ivec, key->key->keyvalue.data, sizeof(ivec)); - c = encryptp ? &ctx->ectx : &ctx->dctx; + c = encryptp ? ctx->ectx : ctx->dctx; EVP_CipherInit_ex(c, NULL, NULL, NULL, (void *)&ivec, -1); EVP_Cipher(c, data, data, len); return 0; } static krb5_error_code DES_CFB64_encrypt_null_ivec(krb5_context context, struct _krb5_key_data *key, void *data, size_t len, krb5_boolean encryptp, int usage, void *ignore_ivec) { DES_cblock ivec; int num = 0; DES_key_schedule *s = key->schedule->data; memset(&ivec, 0, sizeof(ivec)); DES_cfb64_encrypt(data, data, len, s, &ivec, &num, encryptp); return 0; } static krb5_error_code DES_PCBC_encrypt_key_ivec(krb5_context context, struct _krb5_key_data *key, void *data, size_t len, krb5_boolean encryptp, int usage, void *ignore_ivec) { DES_cblock ivec; DES_key_schedule *s = key->schedule->data; memcpy(&ivec, key->key->keyvalue.data, sizeof(ivec)); DES_pcbc_encrypt(data, data, len, s, &ivec, encryptp); return 0; } struct _krb5_encryption_type _krb5_enctype_des_cbc_crc = { ETYPE_DES_CBC_CRC, "des-cbc-crc", 8, 8, 8, &keytype_des, &_krb5_checksum_crc32, NULL, F_DISABLED|F_WEAK, evp_des_encrypt_key_ivec, 0, NULL }; struct _krb5_encryption_type _krb5_enctype_des_cbc_md4 = { ETYPE_DES_CBC_MD4, "des-cbc-md4", 8, 8, 8, &keytype_des, &_krb5_checksum_rsa_md4, &_krb5_checksum_rsa_md4_des, F_DISABLED|F_WEAK, evp_des_encrypt_null_ivec, 0, NULL }; struct _krb5_encryption_type _krb5_enctype_des_cbc_md5 = { ETYPE_DES_CBC_MD5, "des-cbc-md5", 8, 8, 8, &keytype_des, &_krb5_checksum_rsa_md5, &_krb5_checksum_rsa_md5_des, F_DISABLED|F_WEAK, evp_des_encrypt_null_ivec, 0, NULL }; struct _krb5_encryption_type _krb5_enctype_des_cbc_none = { ETYPE_DES_CBC_NONE, "des-cbc-none", 8, 8, 0, &keytype_des, &_krb5_checksum_none, NULL, F_PSEUDO|F_DISABLED|F_WEAK, evp_des_encrypt_null_ivec, 0, NULL }; struct _krb5_encryption_type _krb5_enctype_des_cfb64_none = { ETYPE_DES_CFB64_NONE, "des-cfb64-none", 1, 1, 0, &keytype_des_old, &_krb5_checksum_none, NULL, F_PSEUDO|F_DISABLED|F_WEAK, DES_CFB64_encrypt_null_ivec, 0, NULL }; struct _krb5_encryption_type _krb5_enctype_des_pcbc_none = { ETYPE_DES_PCBC_NONE, "des-pcbc-none", 8, 8, 0, &keytype_des_old, &_krb5_checksum_none, NULL, F_PSEUDO|F_DISABLED|F_WEAK, DES_PCBC_encrypt_key_ivec, 0, NULL }; #endif /* HEIM_WEAK_CRYPTO */ Index: projects/openssl111/crypto/heimdal/lib/krb5/crypto-evp.c =================================================================== --- projects/openssl111/crypto/heimdal/lib/krb5/crypto-evp.c (revision 339197) +++ projects/openssl111/crypto/heimdal/lib/krb5/crypto-evp.c (revision 339198) @@ -1,182 +1,184 @@ /* * Copyright (c) 1997 - 2008 Kungliga Tekniska Högskolan * (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 "krb5_locl.h" void _krb5_evp_schedule(krb5_context context, struct _krb5_key_type *kt, struct _krb5_key_data *kd) { struct _krb5_evp_schedule *key = kd->schedule->data; const EVP_CIPHER *c = (*kt->evp)(); - EVP_CIPHER_CTX_init(&key->ectx); - EVP_CIPHER_CTX_init(&key->dctx); + key->ectx = EVP_CIPHER_CTX_new(); + key->dctx = EVP_CIPHER_CTX_new(); + if (key->ectx == NULL || key->dctx == NULL) + krb5_abort(context, ENOMEM, "malloc failed"); - EVP_CipherInit_ex(&key->ectx, c, NULL, kd->key->keyvalue.data, NULL, 1); - EVP_CipherInit_ex(&key->dctx, c, NULL, kd->key->keyvalue.data, NULL, 0); + EVP_CipherInit_ex(key->ectx, c, NULL, kd->key->keyvalue.data, NULL, 1); + EVP_CipherInit_ex(key->dctx, c, NULL, kd->key->keyvalue.data, NULL, 0); } void _krb5_evp_cleanup(krb5_context context, struct _krb5_key_data *kd) { struct _krb5_evp_schedule *key = kd->schedule->data; - EVP_CIPHER_CTX_cleanup(&key->ectx); - EVP_CIPHER_CTX_cleanup(&key->dctx); + EVP_CIPHER_CTX_free(key->ectx); + EVP_CIPHER_CTX_free(key->dctx); } krb5_error_code _krb5_evp_encrypt(krb5_context context, struct _krb5_key_data *key, void *data, size_t len, krb5_boolean encryptp, int usage, void *ivec) { struct _krb5_evp_schedule *ctx = key->schedule->data; EVP_CIPHER_CTX *c; - c = encryptp ? &ctx->ectx : &ctx->dctx; + c = encryptp ? ctx->ectx : ctx->dctx; if (ivec == NULL) { /* alloca ? */ size_t len2 = EVP_CIPHER_CTX_iv_length(c); void *loiv = malloc(len2); if (loiv == NULL) { krb5_clear_error_message(context); return ENOMEM; } memset(loiv, 0, len2); EVP_CipherInit_ex(c, NULL, NULL, NULL, loiv, -1); free(loiv); } else EVP_CipherInit_ex(c, NULL, NULL, NULL, ivec, -1); EVP_Cipher(c, data, data, len); return 0; } static const unsigned char zero_ivec[EVP_MAX_BLOCK_LENGTH] = { 0 }; krb5_error_code _krb5_evp_encrypt_cts(krb5_context context, struct _krb5_key_data *key, void *data, size_t len, krb5_boolean encryptp, int usage, void *ivec) { size_t i, blocksize; struct _krb5_evp_schedule *ctx = key->schedule->data; unsigned char tmp[EVP_MAX_BLOCK_LENGTH], ivec2[EVP_MAX_BLOCK_LENGTH]; EVP_CIPHER_CTX *c; unsigned char *p; - c = encryptp ? &ctx->ectx : &ctx->dctx; + c = encryptp ? ctx->ectx : ctx->dctx; blocksize = EVP_CIPHER_CTX_block_size(c); if (len < blocksize) { krb5_set_error_message(context, EINVAL, "message block too short"); return EINVAL; } else if (len == blocksize) { EVP_CipherInit_ex(c, NULL, NULL, NULL, zero_ivec, -1); EVP_Cipher(c, data, data, len); return 0; } if (ivec) EVP_CipherInit_ex(c, NULL, NULL, NULL, ivec, -1); else EVP_CipherInit_ex(c, NULL, NULL, NULL, zero_ivec, -1); if (encryptp) { p = data; i = ((len - 1) / blocksize) * blocksize; EVP_Cipher(c, p, p, i); p += i - blocksize; len -= i; memcpy(ivec2, p, blocksize); for (i = 0; i < len; i++) tmp[i] = p[i + blocksize] ^ ivec2[i]; for (; i < blocksize; i++) tmp[i] = 0 ^ ivec2[i]; EVP_CipherInit_ex(c, NULL, NULL, NULL, zero_ivec, -1); EVP_Cipher(c, p, tmp, blocksize); memcpy(p + blocksize, ivec2, len); if (ivec) memcpy(ivec, p, blocksize); } else { unsigned char tmp2[EVP_MAX_BLOCK_LENGTH], tmp3[EVP_MAX_BLOCK_LENGTH]; p = data; if (len > blocksize * 2) { /* remove last two blocks and round up, decrypt this with cbc, then do cts dance */ i = ((((len - blocksize * 2) + blocksize - 1) / blocksize) * blocksize); memcpy(ivec2, p + i - blocksize, blocksize); EVP_Cipher(c, p, p, i); p += i; len -= i + blocksize; } else { if (ivec) memcpy(ivec2, ivec, blocksize); else memcpy(ivec2, zero_ivec, blocksize); len -= blocksize; } memcpy(tmp, p, blocksize); EVP_CipherInit_ex(c, NULL, NULL, NULL, zero_ivec, -1); EVP_Cipher(c, tmp2, p, blocksize); memcpy(tmp3, p + blocksize, len); memcpy(tmp3 + len, tmp2 + len, blocksize - len); /* xor 0 */ for (i = 0; i < len; i++) p[i + blocksize] = tmp2[i] ^ tmp3[i]; EVP_CipherInit_ex(c, NULL, NULL, NULL, zero_ivec, -1); EVP_Cipher(c, p, tmp3, blocksize); for (i = 0; i < blocksize; i++) p[i] ^= ivec2[i]; if (ivec) memcpy(ivec, tmp, blocksize); } return 0; } Index: projects/openssl111/crypto/heimdal/lib/krb5/crypto-rand.c =================================================================== --- projects/openssl111/crypto/heimdal/lib/krb5/crypto-rand.c (revision 339197) +++ projects/openssl111/crypto/heimdal/lib/krb5/crypto-rand.c (revision 339198) @@ -1,109 +1,111 @@ /* * Copyright (c) 1997 - 2008 Kungliga Tekniska Högskolan * (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 "krb5_locl.h" #define ENTROPY_NEEDED 128 static HEIMDAL_MUTEX crypto_mutex = HEIMDAL_MUTEX_INITIALIZER; static int seed_something(void) { char buf[1024], seedfile[256]; /* If there is a seed file, load it. But such a file cannot be trusted, so use 0 for the entropy estimate */ if (RAND_file_name(seedfile, sizeof(seedfile))) { int fd; fd = open(seedfile, O_RDONLY | O_BINARY | O_CLOEXEC); if (fd >= 0) { ssize_t ret; rk_cloexec(fd); ret = read(fd, buf, sizeof(buf)); if (ret > 0) RAND_add(buf, ret, 0.0); close(fd); } else seedfile[0] = '\0'; } else seedfile[0] = '\0'; /* Calling RAND_status() will try to use /dev/urandom if it exists so we do not have to deal with it. */ if (RAND_status() != 1) { #ifndef _WIN32 +#ifndef OPENSSL_NO_EGD krb5_context context; const char *p; /* Try using egd */ if (!krb5_init_context(&context)) { p = krb5_config_get_string(context, NULL, "libdefaults", "egd_socket", NULL); if (p != NULL) RAND_egd_bytes(p, ENTROPY_NEEDED); krb5_free_context(context); } +#endif #else /* TODO: Once a Windows CryptoAPI RAND method is defined, we can use that and failover to another method. */ #endif } if (RAND_status() == 1) { /* Update the seed file */ if (seedfile[0]) RAND_write_file(seedfile); return 0; } else return -1; } KRB5_LIB_FUNCTION void KRB5_LIB_CALL krb5_generate_random_block(void *buf, size_t len) { static int rng_initialized = 0; HEIMDAL_MUTEX_lock(&crypto_mutex); if (!rng_initialized) { if (seed_something()) krb5_abortx(NULL, "Fatal: could not seed the " "random number generator"); rng_initialized = 1; } HEIMDAL_MUTEX_unlock(&crypto_mutex); if (RAND_bytes(buf, len) <= 0) krb5_abortx(NULL, "Failed to generate random block"); } Index: projects/openssl111/crypto/heimdal/lib/krb5/crypto.h =================================================================== --- projects/openssl111/crypto/heimdal/lib/krb5/crypto.h (revision 339197) +++ projects/openssl111/crypto/heimdal/lib/krb5/crypto.h (revision 339198) @@ -1,179 +1,179 @@ /* * Copyright (c) 1997 - 2008 Kungliga Tekniska Högskolan * (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. */ #ifndef HEIMDAL_SMALLER #define DES3_OLD_ENCTYPE 1 #endif struct _krb5_key_data { krb5_keyblock *key; krb5_data *schedule; }; struct _krb5_key_usage; struct krb5_crypto_data { struct _krb5_encryption_type *et; struct _krb5_key_data key; int num_key_usage; struct _krb5_key_usage *key_usage; }; #define CRYPTO_ETYPE(C) ((C)->et->type) /* bits for `flags' below */ #define F_KEYED 1 /* checksum is keyed */ #define F_CPROOF 2 /* checksum is collision proof */ #define F_DERIVED 4 /* uses derived keys */ #define F_VARIANT 8 /* uses `variant' keys (6.4.3) */ #define F_PSEUDO 16 /* not a real protocol type */ #define F_SPECIAL 32 /* backwards */ #define F_DISABLED 64 /* enctype/checksum disabled */ #define F_WEAK 128 /* enctype is considered weak */ struct salt_type { krb5_salttype type; const char *name; krb5_error_code (*string_to_key)(krb5_context, krb5_enctype, krb5_data, krb5_salt, krb5_data, krb5_keyblock*); }; struct _krb5_key_type { krb5_enctype type; const char *name; size_t bits; size_t size; size_t schedule_size; void (*random_key)(krb5_context, krb5_keyblock*); void (*schedule)(krb5_context, struct _krb5_key_type *, struct _krb5_key_data *); struct salt_type *string_to_key; void (*random_to_key)(krb5_context, krb5_keyblock*, const void*, size_t); void (*cleanup)(krb5_context, struct _krb5_key_data *); const EVP_CIPHER *(*evp)(void); }; struct _krb5_checksum_type { krb5_cksumtype type; const char *name; size_t blocksize; size_t checksumsize; unsigned flags; krb5_error_code (*checksum)(krb5_context context, struct _krb5_key_data *key, const void *buf, size_t len, unsigned usage, Checksum *csum); krb5_error_code (*verify)(krb5_context context, struct _krb5_key_data *key, const void *buf, size_t len, unsigned usage, Checksum *csum); }; struct _krb5_encryption_type { krb5_enctype type; const char *name; size_t blocksize; size_t padsize; size_t confoundersize; struct _krb5_key_type *keytype; struct _krb5_checksum_type *checksum; struct _krb5_checksum_type *keyed_checksum; unsigned flags; krb5_error_code (*encrypt)(krb5_context context, struct _krb5_key_data *key, void *data, size_t len, krb5_boolean encryptp, int usage, void *ivec); size_t prf_length; krb5_error_code (*prf)(krb5_context, krb5_crypto, const krb5_data *, krb5_data *); }; #define ENCRYPTION_USAGE(U) (((U) << 8) | 0xAA) #define INTEGRITY_USAGE(U) (((U) << 8) | 0x55) #define CHECKSUM_USAGE(U) (((U) << 8) | 0x99) /* Checksums */ extern struct _krb5_checksum_type _krb5_checksum_none; extern struct _krb5_checksum_type _krb5_checksum_crc32; extern struct _krb5_checksum_type _krb5_checksum_rsa_md4; extern struct _krb5_checksum_type _krb5_checksum_rsa_md4_des; extern struct _krb5_checksum_type _krb5_checksum_rsa_md5_des; extern struct _krb5_checksum_type _krb5_checksum_rsa_md5_des3; extern struct _krb5_checksum_type _krb5_checksum_rsa_md5; extern struct _krb5_checksum_type _krb5_checksum_hmac_sha1_des3; extern struct _krb5_checksum_type _krb5_checksum_hmac_sha1_aes128; extern struct _krb5_checksum_type _krb5_checksum_hmac_sha1_aes256; extern struct _krb5_checksum_type _krb5_checksum_hmac_md5; extern struct _krb5_checksum_type _krb5_checksum_sha1; extern struct _krb5_checksum_type *_krb5_checksum_types[]; extern int _krb5_num_checksums; /* Salts */ extern struct salt_type _krb5_AES_salt[]; extern struct salt_type _krb5_arcfour_salt[]; extern struct salt_type _krb5_des_salt[]; extern struct salt_type _krb5_des3_salt[]; extern struct salt_type _krb5_des3_salt_derived[]; /* Encryption types */ extern struct _krb5_encryption_type _krb5_enctype_aes256_cts_hmac_sha1; extern struct _krb5_encryption_type _krb5_enctype_aes128_cts_hmac_sha1; extern struct _krb5_encryption_type _krb5_enctype_des3_cbc_sha1; extern struct _krb5_encryption_type _krb5_enctype_des3_cbc_md5; extern struct _krb5_encryption_type _krb5_enctype_des3_cbc_none; extern struct _krb5_encryption_type _krb5_enctype_arcfour_hmac_md5; extern struct _krb5_encryption_type _krb5_enctype_des_cbc_md5; extern struct _krb5_encryption_type _krb5_enctype_old_des3_cbc_sha1; extern struct _krb5_encryption_type _krb5_enctype_des_cbc_crc; extern struct _krb5_encryption_type _krb5_enctype_des_cbc_md4; extern struct _krb5_encryption_type _krb5_enctype_des_cbc_md5; extern struct _krb5_encryption_type _krb5_enctype_des_cbc_none; extern struct _krb5_encryption_type _krb5_enctype_des_cfb64_none; extern struct _krb5_encryption_type _krb5_enctype_des_pcbc_none; extern struct _krb5_encryption_type _krb5_enctype_null; extern struct _krb5_encryption_type *_krb5_etypes[]; extern int _krb5_num_etypes; /* Interface to the EVP crypto layer provided by hcrypto */ struct _krb5_evp_schedule { - EVP_CIPHER_CTX ectx; - EVP_CIPHER_CTX dctx; + EVP_CIPHER_CTX *ectx; + EVP_CIPHER_CTX *dctx; }; Index: projects/openssl111/crypto/heimdal/lib/krb5/pkinit.c =================================================================== --- projects/openssl111/crypto/heimdal/lib/krb5/pkinit.c (revision 339197) +++ projects/openssl111/crypto/heimdal/lib/krb5/pkinit.c (revision 339198) @@ -1,2641 +1,2652 @@ /* * Copyright (c) 2003 - 2007 Kungliga Tekniska Högskolan * (Royal Institute of Technology, Stockholm, Sweden). * All rights reserved. * * Portions Copyright (c) 2009 Apple Inc. 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 "krb5_locl.h" struct krb5_dh_moduli { char *name; unsigned long bits; heim_integer p; heim_integer g; heim_integer q; }; #ifdef PKINIT #include #include #include #include #include #include #include struct krb5_pk_cert { hx509_cert cert; }; struct krb5_pk_init_ctx_data { struct krb5_pk_identity *id; enum { USE_RSA, USE_DH, USE_ECDH } keyex; union { DH *dh; #ifdef HAVE_OPENSSL EC_KEY *eckey; #endif } u; krb5_data *clientDHNonce; struct krb5_dh_moduli **m; hx509_peer_info peer; enum krb5_pk_type type; unsigned int require_binding:1; unsigned int require_eku:1; unsigned int require_krbtgt_otherName:1; unsigned int require_hostname_match:1; unsigned int trustedCertifiers:1; unsigned int anonymous:1; }; static void pk_copy_error(krb5_context context, hx509_context hx509ctx, int hxret, const char *fmt, ...) __attribute__ ((format (printf, 4, 5))); /* * */ KRB5_LIB_FUNCTION void KRB5_LIB_CALL _krb5_pk_cert_free(struct krb5_pk_cert *cert) { if (cert->cert) { hx509_cert_free(cert->cert); } free(cert); } static krb5_error_code -BN_to_integer(krb5_context context, BIGNUM *bn, heim_integer *integer) +BN_to_integer(krb5_context context, const BIGNUM *bn, heim_integer *integer) { integer->length = BN_num_bytes(bn); integer->data = malloc(integer->length); if (integer->data == NULL) { krb5_clear_error_message(context); return ENOMEM; } BN_bn2bin(bn, integer->data); integer->negative = BN_is_negative(bn); return 0; } static BIGNUM * integer_to_BN(krb5_context context, const char *field, const heim_integer *f) { BIGNUM *bn; bn = BN_bin2bn((const unsigned char *)f->data, f->length, NULL); if (bn == NULL) { krb5_set_error_message(context, ENOMEM, N_("PKINIT: parsing BN failed %s", ""), field); return NULL; } BN_set_negative(bn, f->negative); return bn; } static krb5_error_code select_dh_group(krb5_context context, DH *dh, unsigned long bits, struct krb5_dh_moduli **moduli) { const struct krb5_dh_moduli *m; + BIGNUM *p, *g, *q; if (bits == 0) { m = moduli[1]; /* XXX */ if (m == NULL) m = moduli[0]; /* XXX */ } else { int i; for (i = 0; moduli[i] != NULL; i++) { if (bits < moduli[i]->bits) break; } if (moduli[i] == NULL) { krb5_set_error_message(context, EINVAL, N_("Did not find a DH group parameter " "matching requirement of %lu bits", ""), bits); return EINVAL; } m = moduli[i]; } - dh->p = integer_to_BN(context, "p", &m->p); - if (dh->p == NULL) + p = integer_to_BN(context, "p", &m->p); + g = integer_to_BN(context, "g", &m->g); + q = integer_to_BN(context, "q", &m->q); + if (p == NULL || g == NULL || q == NULL) { + BN_free(p); + BN_free(g); + BN_free(q); return ENOMEM; - dh->g = integer_to_BN(context, "g", &m->g); - if (dh->g == NULL) - return ENOMEM; - dh->q = integer_to_BN(context, "q", &m->q); - if (dh->q == NULL) - return ENOMEM; + } + if (DH_set0_pqg(dh, p, q, g) != 1) { + BN_free(p); + BN_free(g); + BN_free(q); + return EINVAL; + } + return 0; } struct certfind { const char *type; const heim_oid *oid; }; /* * Try searchin the key by to use by first looking for for PK-INIT * EKU, then the Microsoft smart card EKU and last, no special EKU at all. */ static krb5_error_code find_cert(krb5_context context, struct krb5_pk_identity *id, hx509_query *q, hx509_cert *cert) { struct certfind cf[4] = { { "MobileMe EKU" }, { "PKINIT EKU" }, { "MS EKU" }, { "any (or no)" } }; int ret = HX509_CERT_NOT_FOUND; size_t i, start = 1; unsigned oids[] = { 1, 2, 840, 113635, 100, 3, 2, 1 }; const heim_oid mobileMe = { sizeof(oids)/sizeof(oids[0]), oids }; if (id->flags & PKINIT_BTMM) start = 0; cf[0].oid = &mobileMe; cf[1].oid = &asn1_oid_id_pkekuoid; cf[2].oid = &asn1_oid_id_pkinit_ms_eku; cf[3].oid = NULL; for (i = start; i < sizeof(cf)/sizeof(cf[0]); i++) { ret = hx509_query_match_eku(q, cf[i].oid); if (ret) { pk_copy_error(context, context->hx509ctx, ret, "Failed setting %s OID", cf[i].type); return ret; } ret = hx509_certs_find(context->hx509ctx, id->certs, q, cert); if (ret == 0) break; pk_copy_error(context, context->hx509ctx, ret, "Failed finding certificate with %s OID", cf[i].type); } return ret; } static krb5_error_code create_signature(krb5_context context, const heim_oid *eContentType, krb5_data *eContent, struct krb5_pk_identity *id, hx509_peer_info peer, krb5_data *sd_data) { int ret, flags = 0; if (id->cert == NULL) flags |= HX509_CMS_SIGNATURE_NO_SIGNER; ret = hx509_cms_create_signed_1(context->hx509ctx, flags, eContentType, eContent->data, eContent->length, NULL, id->cert, peer, NULL, id->certs, sd_data); if (ret) { pk_copy_error(context, context->hx509ctx, ret, "Create CMS signedData"); return ret; } return 0; } static int cert2epi(hx509_context context, void *ctx, hx509_cert c) { ExternalPrincipalIdentifiers *ids = ctx; ExternalPrincipalIdentifier id; hx509_name subject = NULL; void *p; int ret; if (ids->len > 10) return 0; memset(&id, 0, sizeof(id)); ret = hx509_cert_get_subject(c, &subject); if (ret) return ret; if (hx509_name_is_null_p(subject) != 0) { id.subjectName = calloc(1, sizeof(*id.subjectName)); if (id.subjectName == NULL) { hx509_name_free(&subject); free_ExternalPrincipalIdentifier(&id); return ENOMEM; } ret = hx509_name_binary(subject, id.subjectName); if (ret) { hx509_name_free(&subject); free_ExternalPrincipalIdentifier(&id); return ret; } } hx509_name_free(&subject); id.issuerAndSerialNumber = calloc(1, sizeof(*id.issuerAndSerialNumber)); if (id.issuerAndSerialNumber == NULL) { free_ExternalPrincipalIdentifier(&id); return ENOMEM; } { IssuerAndSerialNumber iasn; hx509_name issuer; size_t size = 0; memset(&iasn, 0, sizeof(iasn)); ret = hx509_cert_get_issuer(c, &issuer); if (ret) { free_ExternalPrincipalIdentifier(&id); return ret; } ret = hx509_name_to_Name(issuer, &iasn.issuer); hx509_name_free(&issuer); if (ret) { free_ExternalPrincipalIdentifier(&id); return ret; } ret = hx509_cert_get_serialnumber(c, &iasn.serialNumber); if (ret) { free_IssuerAndSerialNumber(&iasn); free_ExternalPrincipalIdentifier(&id); return ret; } ASN1_MALLOC_ENCODE(IssuerAndSerialNumber, id.issuerAndSerialNumber->data, id.issuerAndSerialNumber->length, &iasn, &size, ret); free_IssuerAndSerialNumber(&iasn); if (ret) return ret; if (id.issuerAndSerialNumber->length != size) abort(); } id.subjectKeyIdentifier = NULL; p = realloc(ids->val, sizeof(ids->val[0]) * (ids->len + 1)); if (p == NULL) { free_ExternalPrincipalIdentifier(&id); return ENOMEM; } ids->val = p; ids->val[ids->len] = id; ids->len++; return 0; } static krb5_error_code build_edi(krb5_context context, hx509_context hx509ctx, hx509_certs certs, ExternalPrincipalIdentifiers *ids) { return hx509_certs_iter_f(hx509ctx, certs, cert2epi, ids); } static krb5_error_code build_auth_pack(krb5_context context, unsigned nonce, krb5_pk_init_ctx ctx, const KDC_REQ_BODY *body, AuthPack *a) { size_t buf_size, len = 0; krb5_error_code ret; void *buf; krb5_timestamp sec; int32_t usec; Checksum checksum; krb5_clear_error_message(context); memset(&checksum, 0, sizeof(checksum)); krb5_us_timeofday(context, &sec, &usec); a->pkAuthenticator.ctime = sec; a->pkAuthenticator.nonce = nonce; ASN1_MALLOC_ENCODE(KDC_REQ_BODY, buf, buf_size, body, &len, ret); if (ret) return ret; if (buf_size != len) krb5_abortx(context, "internal error in ASN.1 encoder"); ret = krb5_create_checksum(context, NULL, 0, CKSUMTYPE_SHA1, buf, len, &checksum); free(buf); if (ret) return ret; ALLOC(a->pkAuthenticator.paChecksum, 1); if (a->pkAuthenticator.paChecksum == NULL) { krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", "")); return ENOMEM; } ret = krb5_data_copy(a->pkAuthenticator.paChecksum, checksum.checksum.data, checksum.checksum.length); free_Checksum(&checksum); if (ret) return ret; if (ctx->keyex == USE_DH || ctx->keyex == USE_ECDH) { const char *moduli_file; unsigned long dh_min_bits; krb5_data dhbuf; size_t size = 0; krb5_data_zero(&dhbuf); moduli_file = krb5_config_get_string(context, NULL, "libdefaults", "moduli", NULL); dh_min_bits = krb5_config_get_int_default(context, NULL, 0, "libdefaults", "pkinit_dh_min_bits", NULL); ret = _krb5_parse_moduli(context, moduli_file, &ctx->m); if (ret) return ret; ctx->u.dh = DH_new(); if (ctx->u.dh == NULL) { krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", "")); return ENOMEM; } ret = select_dh_group(context, ctx->u.dh, dh_min_bits, ctx->m); if (ret) return ret; if (DH_generate_key(ctx->u.dh) != 1) { krb5_set_error_message(context, ENOMEM, N_("pkinit: failed to generate DH key", "")); return ENOMEM; } if (1 /* support_cached_dh */) { ALLOC(a->clientDHNonce, 1); if (a->clientDHNonce == NULL) { krb5_clear_error_message(context); return ENOMEM; } ret = krb5_data_alloc(a->clientDHNonce, 40); if (a->clientDHNonce == NULL) { krb5_clear_error_message(context); return ret; } RAND_bytes(a->clientDHNonce->data, a->clientDHNonce->length); ret = krb5_copy_data(context, a->clientDHNonce, &ctx->clientDHNonce); if (ret) return ret; } ALLOC(a->clientPublicValue, 1); if (a->clientPublicValue == NULL) return ENOMEM; if (ctx->keyex == USE_DH) { DH *dh = ctx->u.dh; + const BIGNUM *p, *g, *q, *pub_key; DomainParameters dp; heim_integer dh_pub_key; ret = der_copy_oid(&asn1_oid_id_dhpublicnumber, &a->clientPublicValue->algorithm.algorithm); if (ret) return ret; memset(&dp, 0, sizeof(dp)); - ret = BN_to_integer(context, dh->p, &dp.p); + DH_get0_pqg(dh, &p, &q, &g); + ret = BN_to_integer(context, p, &dp.p); if (ret) { free_DomainParameters(&dp); return ret; } - ret = BN_to_integer(context, dh->g, &dp.g); + ret = BN_to_integer(context, g, &dp.g); if (ret) { free_DomainParameters(&dp); return ret; } - ret = BN_to_integer(context, dh->q, &dp.q); + ret = BN_to_integer(context, q, &dp.q); if (ret) { free_DomainParameters(&dp); return ret; } dp.j = NULL; dp.validationParms = NULL; a->clientPublicValue->algorithm.parameters = malloc(sizeof(*a->clientPublicValue->algorithm.parameters)); if (a->clientPublicValue->algorithm.parameters == NULL) { free_DomainParameters(&dp); return ret; } ASN1_MALLOC_ENCODE(DomainParameters, a->clientPublicValue->algorithm.parameters->data, a->clientPublicValue->algorithm.parameters->length, &dp, &size, ret); free_DomainParameters(&dp); if (ret) return ret; if (size != a->clientPublicValue->algorithm.parameters->length) krb5_abortx(context, "Internal ASN1 encoder error"); - ret = BN_to_integer(context, dh->pub_key, &dh_pub_key); + DH_get0_key(dh, &pub_key, NULL); + ret = BN_to_integer(context, pub_key, &dh_pub_key); if (ret) return ret; ASN1_MALLOC_ENCODE(DHPublicKey, dhbuf.data, dhbuf.length, &dh_pub_key, &size, ret); der_free_heim_integer(&dh_pub_key); if (ret) return ret; if (size != dhbuf.length) krb5_abortx(context, "asn1 internal error"); } else if (ctx->keyex == USE_ECDH) { #ifdef HAVE_OPENSSL ECParameters ecp; unsigned char *p; int xlen; /* copy in public key, XXX find the best curve that the server support or use the clients curve if possible */ ecp.element = choice_ECParameters_namedCurve; ret = der_copy_oid(&asn1_oid_id_ec_group_secp256r1, &ecp.u.namedCurve); if (ret) return ret; ALLOC(a->clientPublicValue->algorithm.parameters, 1); if (a->clientPublicValue->algorithm.parameters == NULL) { free_ECParameters(&ecp); return ENOMEM; } ASN1_MALLOC_ENCODE(ECParameters, p, xlen, &ecp, &size, ret); free_ECParameters(&ecp); if (ret) return ret; if ((int)size != xlen) krb5_abortx(context, "asn1 internal error"); a->clientPublicValue->algorithm.parameters->data = p; a->clientPublicValue->algorithm.parameters->length = size; /* copy in public key */ ret = der_copy_oid(&asn1_oid_id_ecPublicKey, &a->clientPublicValue->algorithm.algorithm); if (ret) return ret; ctx->u.eckey = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1); if (ctx->u.eckey == NULL) return ENOMEM; ret = EC_KEY_generate_key(ctx->u.eckey); if (ret != 1) return EINVAL; /* encode onto dhkey */ xlen = i2o_ECPublicKey(ctx->u.eckey, NULL); if (xlen <= 0) abort(); dhbuf.data = malloc(xlen); if (dhbuf.data == NULL) abort(); dhbuf.length = xlen; p = dhbuf.data; xlen = i2o_ECPublicKey(ctx->u.eckey, &p); if (xlen <= 0) abort(); /* XXX verify that this is right with RFC3279 */ #else return EINVAL; #endif } else krb5_abortx(context, "internal error"); a->clientPublicValue->subjectPublicKey.length = dhbuf.length * 8; a->clientPublicValue->subjectPublicKey.data = dhbuf.data; } { a->supportedCMSTypes = calloc(1, sizeof(*a->supportedCMSTypes)); if (a->supportedCMSTypes == NULL) return ENOMEM; ret = hx509_crypto_available(context->hx509ctx, HX509_SELECT_ALL, ctx->id->cert, &a->supportedCMSTypes->val, &a->supportedCMSTypes->len); if (ret) return ret; } return ret; } KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL _krb5_pk_mk_ContentInfo(krb5_context context, const krb5_data *buf, const heim_oid *oid, struct ContentInfo *content_info) { krb5_error_code ret; ret = der_copy_oid(oid, &content_info->contentType); if (ret) return ret; ALLOC(content_info->content, 1); if (content_info->content == NULL) return ENOMEM; content_info->content->data = malloc(buf->length); if (content_info->content->data == NULL) return ENOMEM; memcpy(content_info->content->data, buf->data, buf->length); content_info->content->length = buf->length; return 0; } static krb5_error_code pk_mk_padata(krb5_context context, krb5_pk_init_ctx ctx, const KDC_REQ_BODY *req_body, unsigned nonce, METHOD_DATA *md) { struct ContentInfo content_info; krb5_error_code ret; const heim_oid *oid = NULL; size_t size = 0; krb5_data buf, sd_buf; int pa_type = -1; krb5_data_zero(&buf); krb5_data_zero(&sd_buf); memset(&content_info, 0, sizeof(content_info)); if (ctx->type == PKINIT_WIN2K) { AuthPack_Win2k ap; krb5_timestamp sec; int32_t usec; memset(&ap, 0, sizeof(ap)); /* fill in PKAuthenticator */ ret = copy_PrincipalName(req_body->sname, &ap.pkAuthenticator.kdcName); if (ret) { free_AuthPack_Win2k(&ap); krb5_clear_error_message(context); goto out; } ret = copy_Realm(&req_body->realm, &ap.pkAuthenticator.kdcRealm); if (ret) { free_AuthPack_Win2k(&ap); krb5_clear_error_message(context); goto out; } krb5_us_timeofday(context, &sec, &usec); ap.pkAuthenticator.ctime = sec; ap.pkAuthenticator.cusec = usec; ap.pkAuthenticator.nonce = nonce; ASN1_MALLOC_ENCODE(AuthPack_Win2k, buf.data, buf.length, &ap, &size, ret); free_AuthPack_Win2k(&ap); if (ret) { krb5_set_error_message(context, ret, N_("Failed encoding AuthPackWin: %d", ""), (int)ret); goto out; } if (buf.length != size) krb5_abortx(context, "internal ASN1 encoder error"); oid = &asn1_oid_id_pkcs7_data; } else if (ctx->type == PKINIT_27) { AuthPack ap; memset(&ap, 0, sizeof(ap)); ret = build_auth_pack(context, nonce, ctx, req_body, &ap); if (ret) { free_AuthPack(&ap); goto out; } ASN1_MALLOC_ENCODE(AuthPack, buf.data, buf.length, &ap, &size, ret); free_AuthPack(&ap); if (ret) { krb5_set_error_message(context, ret, N_("Failed encoding AuthPack: %d", ""), (int)ret); goto out; } if (buf.length != size) krb5_abortx(context, "internal ASN1 encoder error"); oid = &asn1_oid_id_pkauthdata; } else krb5_abortx(context, "internal pkinit error"); ret = create_signature(context, oid, &buf, ctx->id, ctx->peer, &sd_buf); krb5_data_free(&buf); if (ret) goto out; ret = hx509_cms_wrap_ContentInfo(&asn1_oid_id_pkcs7_signedData, &sd_buf, &buf); krb5_data_free(&sd_buf); if (ret) { krb5_set_error_message(context, ret, N_("ContentInfo wrapping of signedData failed","")); goto out; } if (ctx->type == PKINIT_WIN2K) { PA_PK_AS_REQ_Win2k winreq; pa_type = KRB5_PADATA_PK_AS_REQ_WIN; memset(&winreq, 0, sizeof(winreq)); winreq.signed_auth_pack = buf; ASN1_MALLOC_ENCODE(PA_PK_AS_REQ_Win2k, buf.data, buf.length, &winreq, &size, ret); free_PA_PK_AS_REQ_Win2k(&winreq); } else if (ctx->type == PKINIT_27) { PA_PK_AS_REQ req; pa_type = KRB5_PADATA_PK_AS_REQ; memset(&req, 0, sizeof(req)); req.signedAuthPack = buf; if (ctx->trustedCertifiers) { req.trustedCertifiers = calloc(1, sizeof(*req.trustedCertifiers)); if (req.trustedCertifiers == NULL) { ret = ENOMEM; krb5_set_error_message(context, ret, N_("malloc: out of memory", "")); free_PA_PK_AS_REQ(&req); goto out; } ret = build_edi(context, context->hx509ctx, ctx->id->anchors, req.trustedCertifiers); if (ret) { krb5_set_error_message(context, ret, N_("pk-init: failed to build " "trustedCertifiers", "")); free_PA_PK_AS_REQ(&req); goto out; } } req.kdcPkId = NULL; ASN1_MALLOC_ENCODE(PA_PK_AS_REQ, buf.data, buf.length, &req, &size, ret); free_PA_PK_AS_REQ(&req); } else krb5_abortx(context, "internal pkinit error"); if (ret) { krb5_set_error_message(context, ret, "PA-PK-AS-REQ %d", (int)ret); goto out; } if (buf.length != size) krb5_abortx(context, "Internal ASN1 encoder error"); ret = krb5_padata_add(context, md, pa_type, buf.data, buf.length); if (ret) free(buf.data); if (ret == 0) krb5_padata_add(context, md, KRB5_PADATA_PK_AS_09_BINDING, NULL, 0); out: free_ContentInfo(&content_info); return ret; } KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL _krb5_pk_mk_padata(krb5_context context, void *c, int ic_flags, int win2k, const KDC_REQ_BODY *req_body, unsigned nonce, METHOD_DATA *md) { krb5_pk_init_ctx ctx = c; int win2k_compat; if (ctx->id->certs == NULL && ctx->anonymous == 0) { krb5_set_error_message(context, HEIM_PKINIT_NO_PRIVATE_KEY, N_("PKINIT: No user certificate given", "")); return HEIM_PKINIT_NO_PRIVATE_KEY; } win2k_compat = krb5_config_get_bool_default(context, NULL, win2k, "realms", req_body->realm, "pkinit_win2k", NULL); if (win2k_compat) { ctx->require_binding = krb5_config_get_bool_default(context, NULL, TRUE, "realms", req_body->realm, "pkinit_win2k_require_binding", NULL); ctx->type = PKINIT_WIN2K; } else ctx->type = PKINIT_27; ctx->require_eku = krb5_config_get_bool_default(context, NULL, TRUE, "realms", req_body->realm, "pkinit_require_eku", NULL); if (ic_flags & KRB5_INIT_CREDS_NO_C_NO_EKU_CHECK) ctx->require_eku = 0; if (ctx->id->flags & PKINIT_BTMM) ctx->require_eku = 0; ctx->require_krbtgt_otherName = krb5_config_get_bool_default(context, NULL, TRUE, "realms", req_body->realm, "pkinit_require_krbtgt_otherName", NULL); ctx->require_hostname_match = krb5_config_get_bool_default(context, NULL, FALSE, "realms", req_body->realm, "pkinit_require_hostname_match", NULL); ctx->trustedCertifiers = krb5_config_get_bool_default(context, NULL, TRUE, "realms", req_body->realm, "pkinit_trustedCertifiers", NULL); return pk_mk_padata(context, ctx, req_body, nonce, md); } static krb5_error_code pk_verify_sign(krb5_context context, const void *data, size_t length, struct krb5_pk_identity *id, heim_oid *contentType, krb5_data *content, struct krb5_pk_cert **signer) { hx509_certs signer_certs; int ret, flags = 0; /* BTMM is broken in Leo and SnowLeo */ if (id->flags & PKINIT_BTMM) { flags |= HX509_CMS_VS_ALLOW_DATA_OID_MISMATCH; flags |= HX509_CMS_VS_NO_KU_CHECK; flags |= HX509_CMS_VS_NO_VALIDATE; } *signer = NULL; ret = hx509_cms_verify_signed(context->hx509ctx, id->verify_ctx, flags, data, length, NULL, id->certpool, contentType, content, &signer_certs); if (ret) { pk_copy_error(context, context->hx509ctx, ret, "CMS verify signed failed"); return ret; } *signer = calloc(1, sizeof(**signer)); if (*signer == NULL) { krb5_clear_error_message(context); ret = ENOMEM; goto out; } ret = hx509_get_one_cert(context->hx509ctx, signer_certs, &(*signer)->cert); if (ret) { pk_copy_error(context, context->hx509ctx, ret, "Failed to get on of the signer certs"); goto out; } out: hx509_certs_free(&signer_certs); if (ret) { if (*signer) { hx509_cert_free((*signer)->cert); free(*signer); *signer = NULL; } } return ret; } static krb5_error_code get_reply_key_win(krb5_context context, const krb5_data *content, unsigned nonce, krb5_keyblock **key) { ReplyKeyPack_Win2k key_pack; krb5_error_code ret; size_t size; ret = decode_ReplyKeyPack_Win2k(content->data, content->length, &key_pack, &size); if (ret) { krb5_set_error_message(context, ret, N_("PKINIT decoding reply key failed", "")); free_ReplyKeyPack_Win2k(&key_pack); return ret; } if ((unsigned)key_pack.nonce != nonce) { krb5_set_error_message(context, ret, N_("PKINIT enckey nonce is wrong", "")); free_ReplyKeyPack_Win2k(&key_pack); return KRB5KRB_AP_ERR_MODIFIED; } *key = malloc (sizeof (**key)); if (*key == NULL) { free_ReplyKeyPack_Win2k(&key_pack); krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", "")); return ENOMEM; } ret = copy_EncryptionKey(&key_pack.replyKey, *key); free_ReplyKeyPack_Win2k(&key_pack); if (ret) { krb5_set_error_message(context, ret, N_("PKINIT failed copying reply key", "")); free(*key); *key = NULL; } return ret; } static krb5_error_code get_reply_key(krb5_context context, const krb5_data *content, const krb5_data *req_buffer, krb5_keyblock **key) { ReplyKeyPack key_pack; krb5_error_code ret; size_t size; ret = decode_ReplyKeyPack(content->data, content->length, &key_pack, &size); if (ret) { krb5_set_error_message(context, ret, N_("PKINIT decoding reply key failed", "")); free_ReplyKeyPack(&key_pack); return ret; } { krb5_crypto crypto; /* * XXX Verify kp.replyKey is a allowed enctype in the * configuration file */ ret = krb5_crypto_init(context, &key_pack.replyKey, 0, &crypto); if (ret) { free_ReplyKeyPack(&key_pack); return ret; } ret = krb5_verify_checksum(context, crypto, 6, req_buffer->data, req_buffer->length, &key_pack.asChecksum); krb5_crypto_destroy(context, crypto); if (ret) { free_ReplyKeyPack(&key_pack); return ret; } } *key = malloc (sizeof (**key)); if (*key == NULL) { free_ReplyKeyPack(&key_pack); krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", "")); return ENOMEM; } ret = copy_EncryptionKey(&key_pack.replyKey, *key); free_ReplyKeyPack(&key_pack); if (ret) { krb5_set_error_message(context, ret, N_("PKINIT failed copying reply key", "")); free(*key); *key = NULL; } return ret; } static krb5_error_code pk_verify_host(krb5_context context, const char *realm, const krb5_krbhst_info *hi, struct krb5_pk_init_ctx_data *ctx, struct krb5_pk_cert *host) { krb5_error_code ret = 0; if (ctx->require_eku) { ret = hx509_cert_check_eku(context->hx509ctx, host->cert, &asn1_oid_id_pkkdcekuoid, 0); if (ret) { krb5_set_error_message(context, ret, N_("No PK-INIT KDC EKU in kdc certificate", "")); return ret; } } if (ctx->require_krbtgt_otherName) { hx509_octet_string_list list; size_t i; ret = hx509_cert_find_subjectAltName_otherName(context->hx509ctx, host->cert, &asn1_oid_id_pkinit_san, &list); if (ret) { krb5_set_error_message(context, ret, N_("Failed to find the PK-INIT " "subjectAltName in the KDC " "certificate", "")); return ret; } for (i = 0; i < list.len; i++) { KRB5PrincipalName r; ret = decode_KRB5PrincipalName(list.val[i].data, list.val[i].length, &r, NULL); if (ret) { krb5_set_error_message(context, ret, N_("Failed to decode the PK-INIT " "subjectAltName in the " "KDC certificate", "")); break; } if (r.principalName.name_string.len != 2 || strcmp(r.principalName.name_string.val[0], KRB5_TGS_NAME) != 0 || strcmp(r.principalName.name_string.val[1], realm) != 0 || strcmp(r.realm, realm) != 0) { ret = KRB5_KDC_ERR_INVALID_CERTIFICATE; krb5_set_error_message(context, ret, N_("KDC have wrong realm name in " "the certificate", "")); } free_KRB5PrincipalName(&r); if (ret) break; } hx509_free_octet_string_list(&list); } if (ret) return ret; if (hi) { ret = hx509_verify_hostname(context->hx509ctx, host->cert, ctx->require_hostname_match, HX509_HN_HOSTNAME, hi->hostname, hi->ai->ai_addr, hi->ai->ai_addrlen); if (ret) krb5_set_error_message(context, ret, N_("Address mismatch in " "the KDC certificate", "")); } return ret; } static krb5_error_code pk_rd_pa_reply_enckey(krb5_context context, int type, const heim_octet_string *indata, const heim_oid *dataType, const char *realm, krb5_pk_init_ctx ctx, krb5_enctype etype, const krb5_krbhst_info *hi, unsigned nonce, const krb5_data *req_buffer, PA_DATA *pa, krb5_keyblock **key) { krb5_error_code ret; struct krb5_pk_cert *host = NULL; krb5_data content; heim_oid contentType = { 0, NULL }; int flags = HX509_CMS_UE_DONT_REQUIRE_KU_ENCIPHERMENT; if (der_heim_oid_cmp(&asn1_oid_id_pkcs7_envelopedData, dataType)) { krb5_set_error_message(context, EINVAL, N_("PKINIT: Invalid content type", "")); return EINVAL; } if (ctx->type == PKINIT_WIN2K) flags |= HX509_CMS_UE_ALLOW_WEAK; ret = hx509_cms_unenvelope(context->hx509ctx, ctx->id->certs, flags, indata->data, indata->length, NULL, 0, &contentType, &content); if (ret) { pk_copy_error(context, context->hx509ctx, ret, "Failed to unenvelope CMS data in PK-INIT reply"); return ret; } der_free_oid(&contentType); /* win2k uses ContentInfo */ if (type == PKINIT_WIN2K) { heim_oid type2; heim_octet_string out; ret = hx509_cms_unwrap_ContentInfo(&content, &type2, &out, NULL); if (ret) { /* windows LH with interesting CMS packets */ size_t ph = 1 + der_length_len(content.length); unsigned char *ptr = malloc(content.length + ph); size_t l; memcpy(ptr + ph, content.data, content.length); ret = der_put_length_and_tag (ptr + ph - 1, ph, content.length, ASN1_C_UNIV, CONS, UT_Sequence, &l); if (ret) return ret; free(content.data); content.data = ptr; content.length += ph; ret = hx509_cms_unwrap_ContentInfo(&content, &type2, &out, NULL); if (ret) goto out; } if (der_heim_oid_cmp(&type2, &asn1_oid_id_pkcs7_signedData)) { ret = EINVAL; /* XXX */ krb5_set_error_message(context, ret, N_("PKINIT: Invalid content type", "")); der_free_oid(&type2); der_free_octet_string(&out); goto out; } der_free_oid(&type2); krb5_data_free(&content); ret = krb5_data_copy(&content, out.data, out.length); der_free_octet_string(&out); if (ret) { krb5_set_error_message(context, ret, N_("malloc: out of memory", "")); goto out; } } ret = pk_verify_sign(context, content.data, content.length, ctx->id, &contentType, &content, &host); if (ret) goto out; /* make sure that it is the kdc's certificate */ ret = pk_verify_host(context, realm, hi, ctx, host); if (ret) { goto out; } #if 0 if (type == PKINIT_WIN2K) { if (der_heim_oid_cmp(&contentType, &asn1_oid_id_pkcs7_data) != 0) { ret = KRB5KRB_AP_ERR_MSG_TYPE; krb5_set_error_message(context, ret, "PKINIT: reply key, wrong oid"); goto out; } } else { if (der_heim_oid_cmp(&contentType, &asn1_oid_id_pkrkeydata) != 0) { ret = KRB5KRB_AP_ERR_MSG_TYPE; krb5_set_error_message(context, ret, "PKINIT: reply key, wrong oid"); goto out; } } #endif switch(type) { case PKINIT_WIN2K: ret = get_reply_key(context, &content, req_buffer, key); if (ret != 0 && ctx->require_binding == 0) ret = get_reply_key_win(context, &content, nonce, key); break; case PKINIT_27: ret = get_reply_key(context, &content, req_buffer, key); break; } if (ret) goto out; /* XXX compare given etype with key->etype */ out: if (host) _krb5_pk_cert_free(host); der_free_oid(&contentType); krb5_data_free(&content); return ret; } static krb5_error_code pk_rd_pa_reply_dh(krb5_context context, const heim_octet_string *indata, const heim_oid *dataType, const char *realm, krb5_pk_init_ctx ctx, krb5_enctype etype, const krb5_krbhst_info *hi, const DHNonce *c_n, const DHNonce *k_n, unsigned nonce, PA_DATA *pa, krb5_keyblock **key) { const unsigned char *p; unsigned char *dh_gen_key = NULL; struct krb5_pk_cert *host = NULL; BIGNUM *kdc_dh_pubkey = NULL; KDCDHKeyInfo kdc_dh_info; heim_oid contentType = { 0, NULL }; krb5_data content; krb5_error_code ret; int dh_gen_keylen = 0; size_t size; krb5_data_zero(&content); memset(&kdc_dh_info, 0, sizeof(kdc_dh_info)); if (der_heim_oid_cmp(&asn1_oid_id_pkcs7_signedData, dataType)) { krb5_set_error_message(context, EINVAL, N_("PKINIT: Invalid content type", "")); return EINVAL; } ret = pk_verify_sign(context, indata->data, indata->length, ctx->id, &contentType, &content, &host); if (ret) goto out; /* make sure that it is the kdc's certificate */ ret = pk_verify_host(context, realm, hi, ctx, host); if (ret) goto out; if (der_heim_oid_cmp(&contentType, &asn1_oid_id_pkdhkeydata)) { ret = KRB5KRB_AP_ERR_MSG_TYPE; krb5_set_error_message(context, ret, N_("pkinit - dh reply contains wrong oid", "")); goto out; } ret = decode_KDCDHKeyInfo(content.data, content.length, &kdc_dh_info, &size); if (ret) { krb5_set_error_message(context, ret, N_("pkinit - failed to decode " "KDC DH Key Info", "")); goto out; } if (kdc_dh_info.nonce != nonce) { ret = KRB5KRB_AP_ERR_MODIFIED; krb5_set_error_message(context, ret, N_("PKINIT: DH nonce is wrong", "")); goto out; } if (kdc_dh_info.dhKeyExpiration) { if (k_n == NULL) { ret = KRB5KRB_ERR_GENERIC; krb5_set_error_message(context, ret, N_("pkinit; got key expiration " "without server nonce", "")); goto out; } if (c_n == NULL) { ret = KRB5KRB_ERR_GENERIC; krb5_set_error_message(context, ret, N_("pkinit; got DH reuse but no " "client nonce", "")); goto out; } } else { if (k_n) { ret = KRB5KRB_ERR_GENERIC; krb5_set_error_message(context, ret, N_("pkinit: got server nonce " "without key expiration", "")); goto out; } c_n = NULL; } p = kdc_dh_info.subjectPublicKey.data; size = (kdc_dh_info.subjectPublicKey.length + 7) / 8; if (ctx->keyex == USE_DH) { DHPublicKey k; ret = decode_DHPublicKey(p, size, &k, NULL); if (ret) { krb5_set_error_message(context, ret, N_("pkinit: can't decode " "without key expiration", "")); goto out; } kdc_dh_pubkey = integer_to_BN(context, "DHPublicKey", &k); free_DHPublicKey(&k); if (kdc_dh_pubkey == NULL) { ret = ENOMEM; goto out; } size = DH_size(ctx->u.dh); dh_gen_key = malloc(size); if (dh_gen_key == NULL) { ret = ENOMEM; krb5_set_error_message(context, ret, N_("malloc: out of memory", "")); goto out; } dh_gen_keylen = DH_compute_key(dh_gen_key, kdc_dh_pubkey, ctx->u.dh); if (dh_gen_keylen == -1) { ret = KRB5KRB_ERR_GENERIC; dh_gen_keylen = 0; krb5_set_error_message(context, ret, N_("PKINIT: Can't compute Diffie-Hellman key", "")); goto out; } if (dh_gen_keylen < (int)size) { size -= dh_gen_keylen; memmove(dh_gen_key + size, dh_gen_key, dh_gen_keylen); memset(dh_gen_key, 0, size); } } else { #ifdef HAVE_OPENSSL const EC_GROUP *group; EC_KEY *public = NULL; group = EC_KEY_get0_group(ctx->u.eckey); public = EC_KEY_new(); if (public == NULL) { ret = ENOMEM; goto out; } if (EC_KEY_set_group(public, group) != 1) { EC_KEY_free(public); ret = ENOMEM; goto out; } if (o2i_ECPublicKey(&public, &p, size) == NULL) { EC_KEY_free(public); ret = KRB5KRB_ERR_GENERIC; krb5_set_error_message(context, ret, N_("PKINIT: Can't parse ECDH public key", "")); goto out; } size = (EC_GROUP_get_degree(group) + 7) / 8; dh_gen_key = malloc(size); if (dh_gen_key == NULL) { EC_KEY_free(public); ret = ENOMEM; krb5_set_error_message(context, ret, N_("malloc: out of memory", "")); goto out; } dh_gen_keylen = ECDH_compute_key(dh_gen_key, size, EC_KEY_get0_public_key(public), ctx->u.eckey, NULL); EC_KEY_free(public); if (dh_gen_keylen == -1) { ret = KRB5KRB_ERR_GENERIC; dh_gen_keylen = 0; krb5_set_error_message(context, ret, N_("PKINIT: Can't compute ECDH public key", "")); goto out; } #else ret = EINVAL; #endif } if (dh_gen_keylen <= 0) { ret = EINVAL; krb5_set_error_message(context, ret, N_("PKINIT: resulting DH key <= 0", "")); dh_gen_keylen = 0; goto out; } *key = malloc (sizeof (**key)); if (*key == NULL) { ret = ENOMEM; krb5_set_error_message(context, ret, N_("malloc: out of memory", "")); goto out; } ret = _krb5_pk_octetstring2key(context, etype, dh_gen_key, dh_gen_keylen, c_n, k_n, *key); if (ret) { krb5_set_error_message(context, ret, N_("PKINIT: can't create key from DH key", "")); free(*key); *key = NULL; goto out; } out: if (kdc_dh_pubkey) BN_free(kdc_dh_pubkey); if (dh_gen_key) { memset(dh_gen_key, 0, dh_gen_keylen); free(dh_gen_key); } if (host) _krb5_pk_cert_free(host); if (content.data) krb5_data_free(&content); der_free_oid(&contentType); free_KDCDHKeyInfo(&kdc_dh_info); return ret; } KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL _krb5_pk_rd_pa_reply(krb5_context context, const char *realm, void *c, krb5_enctype etype, const krb5_krbhst_info *hi, unsigned nonce, const krb5_data *req_buffer, PA_DATA *pa, krb5_keyblock **key) { krb5_pk_init_ctx ctx = c; krb5_error_code ret; size_t size; /* Check for IETF PK-INIT first */ if (ctx->type == PKINIT_27) { PA_PK_AS_REP rep; heim_octet_string os, data; heim_oid oid; if (pa->padata_type != KRB5_PADATA_PK_AS_REP) { krb5_set_error_message(context, EINVAL, N_("PKINIT: wrong padata recv", "")); return EINVAL; } ret = decode_PA_PK_AS_REP(pa->padata_value.data, pa->padata_value.length, &rep, &size); if (ret) { krb5_set_error_message(context, ret, N_("Failed to decode pkinit AS rep", "")); return ret; } switch (rep.element) { case choice_PA_PK_AS_REP_dhInfo: _krb5_debug(context, 5, "krb5_get_init_creds: using pkinit dh"); os = rep.u.dhInfo.dhSignedData; break; case choice_PA_PK_AS_REP_encKeyPack: _krb5_debug(context, 5, "krb5_get_init_creds: using kinit enc reply key"); os = rep.u.encKeyPack; break; default: { PA_PK_AS_REP_BTMM btmm; free_PA_PK_AS_REP(&rep); memset(&rep, 0, sizeof(rep)); _krb5_debug(context, 5, "krb5_get_init_creds: using BTMM kinit enc reply key"); ret = decode_PA_PK_AS_REP_BTMM(pa->padata_value.data, pa->padata_value.length, &btmm, &size); if (ret) { krb5_set_error_message(context, EINVAL, N_("PKINIT: -27 reply " "invalid content type", "")); return EINVAL; } if (btmm.dhSignedData || btmm.encKeyPack == NULL) { free_PA_PK_AS_REP_BTMM(&btmm); ret = EINVAL; krb5_set_error_message(context, ret, N_("DH mode not supported for BTMM mode", "")); return ret; } /* * Transform to IETF style PK-INIT reply so that free works below */ rep.element = choice_PA_PK_AS_REP_encKeyPack; rep.u.encKeyPack.data = btmm.encKeyPack->data; rep.u.encKeyPack.length = btmm.encKeyPack->length; btmm.encKeyPack->data = NULL; btmm.encKeyPack->length = 0; free_PA_PK_AS_REP_BTMM(&btmm); os = rep.u.encKeyPack; } } ret = hx509_cms_unwrap_ContentInfo(&os, &oid, &data, NULL); if (ret) { free_PA_PK_AS_REP(&rep); krb5_set_error_message(context, ret, N_("PKINIT: failed to unwrap CI", "")); return ret; } switch (rep.element) { case choice_PA_PK_AS_REP_dhInfo: ret = pk_rd_pa_reply_dh(context, &data, &oid, realm, ctx, etype, hi, ctx->clientDHNonce, rep.u.dhInfo.serverDHNonce, nonce, pa, key); break; case choice_PA_PK_AS_REP_encKeyPack: ret = pk_rd_pa_reply_enckey(context, PKINIT_27, &data, &oid, realm, ctx, etype, hi, nonce, req_buffer, pa, key); break; default: krb5_abortx(context, "pk-init as-rep case not possible to happen"); } der_free_octet_string(&data); der_free_oid(&oid); free_PA_PK_AS_REP(&rep); } else if (ctx->type == PKINIT_WIN2K) { PA_PK_AS_REP_Win2k w2krep; /* Check for Windows encoding of the AS-REP pa data */ #if 0 /* should this be ? */ if (pa->padata_type != KRB5_PADATA_PK_AS_REP) { krb5_set_error_message(context, EINVAL, "PKINIT: wrong padata recv"); return EINVAL; } #endif memset(&w2krep, 0, sizeof(w2krep)); ret = decode_PA_PK_AS_REP_Win2k(pa->padata_value.data, pa->padata_value.length, &w2krep, &size); if (ret) { krb5_set_error_message(context, ret, N_("PKINIT: Failed decoding windows " "pkinit reply %d", ""), (int)ret); return ret; } krb5_clear_error_message(context); switch (w2krep.element) { case choice_PA_PK_AS_REP_Win2k_encKeyPack: { heim_octet_string data; heim_oid oid; ret = hx509_cms_unwrap_ContentInfo(&w2krep.u.encKeyPack, &oid, &data, NULL); free_PA_PK_AS_REP_Win2k(&w2krep); if (ret) { krb5_set_error_message(context, ret, N_("PKINIT: failed to unwrap CI", "")); return ret; } ret = pk_rd_pa_reply_enckey(context, PKINIT_WIN2K, &data, &oid, realm, ctx, etype, hi, nonce, req_buffer, pa, key); der_free_octet_string(&data); der_free_oid(&oid); break; } default: free_PA_PK_AS_REP_Win2k(&w2krep); ret = EINVAL; krb5_set_error_message(context, ret, N_("PKINIT: win2k reply invalid " "content type", "")); break; } } else { ret = EINVAL; krb5_set_error_message(context, ret, N_("PKINIT: unknown reply type", "")); } return ret; } struct prompter { krb5_context context; krb5_prompter_fct prompter; void *prompter_data; }; static int hx_pass_prompter(void *data, const hx509_prompt *prompter) { krb5_error_code ret; krb5_prompt prompt; krb5_data password_data; struct prompter *p = data; password_data.data = prompter->reply.data; password_data.length = prompter->reply.length; prompt.prompt = prompter->prompt; prompt.hidden = hx509_prompt_hidden(prompter->type); prompt.reply = &password_data; switch (prompter->type) { case HX509_PROMPT_TYPE_INFO: prompt.type = KRB5_PROMPT_TYPE_INFO; break; case HX509_PROMPT_TYPE_PASSWORD: case HX509_PROMPT_TYPE_QUESTION: default: prompt.type = KRB5_PROMPT_TYPE_PASSWORD; break; } ret = (*p->prompter)(p->context, p->prompter_data, NULL, NULL, 1, &prompt); if (ret) { memset (prompter->reply.data, 0, prompter->reply.length); return 1; } return 0; } static krb5_error_code _krb5_pk_set_user_id(krb5_context context, krb5_principal principal, krb5_pk_init_ctx ctx, struct hx509_certs_data *certs) { hx509_certs c = hx509_certs_ref(certs); hx509_query *q = NULL; int ret; if (ctx->id->certs) hx509_certs_free(&ctx->id->certs); if (ctx->id->cert) { hx509_cert_free(ctx->id->cert); ctx->id->cert = NULL; } ctx->id->certs = c; ctx->anonymous = 0; ret = hx509_query_alloc(context->hx509ctx, &q); if (ret) { pk_copy_error(context, context->hx509ctx, ret, "Allocate query to find signing certificate"); return ret; } hx509_query_match_option(q, HX509_QUERY_OPTION_PRIVATE_KEY); hx509_query_match_option(q, HX509_QUERY_OPTION_KU_DIGITALSIGNATURE); if (principal && strncmp("LKDC:SHA1.", krb5_principal_get_realm(context, principal), 9) == 0) { ctx->id->flags |= PKINIT_BTMM; } ret = find_cert(context, ctx->id, q, &ctx->id->cert); hx509_query_free(context->hx509ctx, q); if (ret == 0 && _krb5_have_debug(context, 2)) { hx509_name name; char *str, *sn; heim_integer i; ret = hx509_cert_get_subject(ctx->id->cert, &name); if (ret) goto out; ret = hx509_name_to_string(name, &str); hx509_name_free(&name); if (ret) goto out; ret = hx509_cert_get_serialnumber(ctx->id->cert, &i); if (ret) { free(str); goto out; } ret = der_print_hex_heim_integer(&i, &sn); der_free_heim_integer(&i); if (ret) { free(name); goto out; } _krb5_debug(context, 2, "using cert: subject: %s sn: %s", str, sn); free(str); free(sn); } out: return ret; } KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL _krb5_pk_load_id(krb5_context context, struct krb5_pk_identity **ret_id, const char *user_id, const char *anchor_id, char * const *chain_list, char * const *revoke_list, krb5_prompter_fct prompter, void *prompter_data, char *password) { struct krb5_pk_identity *id = NULL; struct prompter p; int ret; *ret_id = NULL; if (anchor_id == NULL) { krb5_set_error_message(context, HEIM_PKINIT_NO_VALID_CA, N_("PKINIT: No anchor given", "")); return HEIM_PKINIT_NO_VALID_CA; } /* load cert */ id = calloc(1, sizeof(*id)); if (id == NULL) { krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", "")); return ENOMEM; } if (user_id) { hx509_lock lock; ret = hx509_lock_init(context->hx509ctx, &lock); if (ret) { pk_copy_error(context, context->hx509ctx, ret, "Failed init lock"); goto out; } if (password && password[0]) hx509_lock_add_password(lock, password); if (prompter) { p.context = context; p.prompter = prompter; p.prompter_data = prompter_data; ret = hx509_lock_set_prompter(lock, hx_pass_prompter, &p); if (ret) { hx509_lock_free(lock); goto out; } } ret = hx509_certs_init(context->hx509ctx, user_id, 0, lock, &id->certs); hx509_lock_free(lock); if (ret) { pk_copy_error(context, context->hx509ctx, ret, "Failed to init cert certs"); goto out; } } else { id->certs = NULL; } ret = hx509_certs_init(context->hx509ctx, anchor_id, 0, NULL, &id->anchors); if (ret) { pk_copy_error(context, context->hx509ctx, ret, "Failed to init anchors"); goto out; } ret = hx509_certs_init(context->hx509ctx, "MEMORY:pkinit-cert-chain", 0, NULL, &id->certpool); if (ret) { pk_copy_error(context, context->hx509ctx, ret, "Failed to init chain"); goto out; } while (chain_list && *chain_list) { ret = hx509_certs_append(context->hx509ctx, id->certpool, NULL, *chain_list); if (ret) { pk_copy_error(context, context->hx509ctx, ret, "Failed to laod chain %s", *chain_list); goto out; } chain_list++; } if (revoke_list) { ret = hx509_revoke_init(context->hx509ctx, &id->revokectx); if (ret) { pk_copy_error(context, context->hx509ctx, ret, "Failed init revoke list"); goto out; } while (*revoke_list) { ret = hx509_revoke_add_crl(context->hx509ctx, id->revokectx, *revoke_list); if (ret) { pk_copy_error(context, context->hx509ctx, ret, "Failed load revoke list"); goto out; } revoke_list++; } } else hx509_context_set_missing_revoke(context->hx509ctx, 1); ret = hx509_verify_init_ctx(context->hx509ctx, &id->verify_ctx); if (ret) { pk_copy_error(context, context->hx509ctx, ret, "Failed init verify context"); goto out; } hx509_verify_attach_anchors(id->verify_ctx, id->anchors); hx509_verify_attach_revoke(id->verify_ctx, id->revokectx); out: if (ret) { hx509_verify_destroy_ctx(id->verify_ctx); hx509_certs_free(&id->certs); hx509_certs_free(&id->anchors); hx509_certs_free(&id->certpool); hx509_revoke_free(&id->revokectx); free(id); } else *ret_id = id; return ret; } /* * */ static void pk_copy_error(krb5_context context, hx509_context hx509ctx, int hxret, const char *fmt, ...) { va_list va; char *s, *f; int ret; va_start(va, fmt); ret = vasprintf(&f, fmt, va); va_end(va); if (ret == -1 || f == NULL) { krb5_clear_error_message(context); return; } s = hx509_get_error_string(hx509ctx, hxret); if (s == NULL) { krb5_clear_error_message(context); free(f); return; } krb5_set_error_message(context, hxret, "%s: %s", f, s); free(s); free(f); } static int parse_integer(krb5_context context, char **p, const char *file, int lineno, const char *name, heim_integer *integer) { int ret; char *p1; p1 = strsep(p, " \t"); if (p1 == NULL) { krb5_set_error_message(context, EINVAL, N_("moduli file %s missing %s on line %d", ""), file, name, lineno); return EINVAL; } ret = der_parse_hex_heim_integer(p1, integer); if (ret) { krb5_set_error_message(context, ret, N_("moduli file %s failed parsing %s " "on line %d", ""), file, name, lineno); return ret; } return 0; } krb5_error_code _krb5_parse_moduli_line(krb5_context context, const char *file, int lineno, char *p, struct krb5_dh_moduli **m) { struct krb5_dh_moduli *m1; char *p1; int ret; *m = NULL; m1 = calloc(1, sizeof(*m1)); if (m1 == NULL) { krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", "")); return ENOMEM; } while (isspace((unsigned char)*p)) p++; if (*p == '#') { free(m1); return 0; } ret = EINVAL; p1 = strsep(&p, " \t"); if (p1 == NULL) { krb5_set_error_message(context, ret, N_("moduli file %s missing name on line %d", ""), file, lineno); goto out; } m1->name = strdup(p1); if (m1->name == NULL) { ret = ENOMEM; krb5_set_error_message(context, ret, N_("malloc: out of memeory", "")); goto out; } p1 = strsep(&p, " \t"); if (p1 == NULL) { krb5_set_error_message(context, ret, N_("moduli file %s missing bits on line %d", ""), file, lineno); goto out; } m1->bits = atoi(p1); if (m1->bits == 0) { krb5_set_error_message(context, ret, N_("moduli file %s have un-parsable " "bits on line %d", ""), file, lineno); goto out; } ret = parse_integer(context, &p, file, lineno, "p", &m1->p); if (ret) goto out; ret = parse_integer(context, &p, file, lineno, "g", &m1->g); if (ret) goto out; ret = parse_integer(context, &p, file, lineno, "q", &m1->q); if (ret) goto out; *m = m1; return 0; out: free(m1->name); der_free_heim_integer(&m1->p); der_free_heim_integer(&m1->g); der_free_heim_integer(&m1->q); free(m1); return ret; } void _krb5_free_moduli(struct krb5_dh_moduli **moduli) { int i; for (i = 0; moduli[i] != NULL; i++) { free(moduli[i]->name); der_free_heim_integer(&moduli[i]->p); der_free_heim_integer(&moduli[i]->g); der_free_heim_integer(&moduli[i]->q); free(moduli[i]); } free(moduli); } static const char *default_moduli_RFC2412_MODP_group2 = /* name */ "RFC2412-MODP-group2 " /* bits */ "1024 " /* p */ "FFFFFFFF" "FFFFFFFF" "C90FDAA2" "2168C234" "C4C6628B" "80DC1CD1" "29024E08" "8A67CC74" "020BBEA6" "3B139B22" "514A0879" "8E3404DD" "EF9519B3" "CD3A431B" "302B0A6D" "F25F1437" "4FE1356D" "6D51C245" "E485B576" "625E7EC6" "F44C42E9" "A637ED6B" "0BFF5CB6" "F406B7ED" "EE386BFB" "5A899FA5" "AE9F2411" "7C4B1FE6" "49286651" "ECE65381" "FFFFFFFF" "FFFFFFFF " /* g */ "02 " /* q */ "7FFFFFFF" "FFFFFFFF" "E487ED51" "10B4611A" "62633145" "C06E0E68" "94812704" "4533E63A" "0105DF53" "1D89CD91" "28A5043C" "C71A026E" "F7CA8CD9" "E69D218D" "98158536" "F92F8A1B" "A7F09AB6" "B6A8E122" "F242DABB" "312F3F63" "7A262174" "D31BF6B5" "85FFAE5B" "7A035BF6" "F71C35FD" "AD44CFD2" "D74F9208" "BE258FF3" "24943328" "F67329C0" "FFFFFFFF" "FFFFFFFF"; static const char *default_moduli_rfc3526_MODP_group14 = /* name */ "rfc3526-MODP-group14 " /* bits */ "1760 " /* p */ "FFFFFFFF" "FFFFFFFF" "C90FDAA2" "2168C234" "C4C6628B" "80DC1CD1" "29024E08" "8A67CC74" "020BBEA6" "3B139B22" "514A0879" "8E3404DD" "EF9519B3" "CD3A431B" "302B0A6D" "F25F1437" "4FE1356D" "6D51C245" "E485B576" "625E7EC6" "F44C42E9" "A637ED6B" "0BFF5CB6" "F406B7ED" "EE386BFB" "5A899FA5" "AE9F2411" "7C4B1FE6" "49286651" "ECE45B3D" "C2007CB8" "A163BF05" "98DA4836" "1C55D39A" "69163FA8" "FD24CF5F" "83655D23" "DCA3AD96" "1C62F356" "208552BB" "9ED52907" "7096966D" "670C354E" "4ABC9804" "F1746C08" "CA18217C" "32905E46" "2E36CE3B" "E39E772C" "180E8603" "9B2783A2" "EC07A28F" "B5C55DF0" "6F4C52C9" "DE2BCBF6" "95581718" "3995497C" "EA956AE5" "15D22618" "98FA0510" "15728E5A" "8AACAA68" "FFFFFFFF" "FFFFFFFF " /* g */ "02 " /* q */ "7FFFFFFF" "FFFFFFFF" "E487ED51" "10B4611A" "62633145" "C06E0E68" "94812704" "4533E63A" "0105DF53" "1D89CD91" "28A5043C" "C71A026E" "F7CA8CD9" "E69D218D" "98158536" "F92F8A1B" "A7F09AB6" "B6A8E122" "F242DABB" "312F3F63" "7A262174" "D31BF6B5" "85FFAE5B" "7A035BF6" "F71C35FD" "AD44CFD2" "D74F9208" "BE258FF3" "24943328" "F6722D9E" "E1003E5C" "50B1DF82" "CC6D241B" "0E2AE9CD" "348B1FD4" "7E9267AF" "C1B2AE91" "EE51D6CB" "0E3179AB" "1042A95D" "CF6A9483" "B84B4B36" "B3861AA7" "255E4C02" "78BA3604" "650C10BE" "19482F23" "171B671D" "F1CF3B96" "0C074301" "CD93C1D1" "7603D147" "DAE2AEF8" "37A62964" "EF15E5FB" "4AAC0B8C" "1CCAA4BE" "754AB572" "8AE9130C" "4C7D0288" "0AB9472D" "45565534" "7FFFFFFF" "FFFFFFFF"; krb5_error_code _krb5_parse_moduli(krb5_context context, const char *file, struct krb5_dh_moduli ***moduli) { /* name bits P G Q */ krb5_error_code ret; struct krb5_dh_moduli **m = NULL, **m2; char buf[4096]; FILE *f; int lineno = 0, n = 0; *moduli = NULL; m = calloc(1, sizeof(m[0]) * 3); if (m == NULL) { krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", "")); return ENOMEM; } strlcpy(buf, default_moduli_rfc3526_MODP_group14, sizeof(buf)); ret = _krb5_parse_moduli_line(context, "builtin", 1, buf, &m[0]); if (ret) { _krb5_free_moduli(m); return ret; } n++; strlcpy(buf, default_moduli_RFC2412_MODP_group2, sizeof(buf)); ret = _krb5_parse_moduli_line(context, "builtin", 1, buf, &m[1]); if (ret) { _krb5_free_moduli(m); return ret; } n++; if (file == NULL) file = MODULI_FILE; #ifdef KRB5_USE_PATH_TOKENS { char * exp_file; if (_krb5_expand_path_tokens(context, file, &exp_file) == 0) { f = fopen(exp_file, "r"); krb5_xfree(exp_file); } else { f = NULL; } } #else f = fopen(file, "r"); #endif if (f == NULL) { *moduli = m; return 0; } rk_cloexec_file(f); while(fgets(buf, sizeof(buf), f) != NULL) { struct krb5_dh_moduli *element; buf[strcspn(buf, "\n")] = '\0'; lineno++; m2 = realloc(m, (n + 2) * sizeof(m[0])); if (m2 == NULL) { _krb5_free_moduli(m); krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", "")); return ENOMEM; } m = m2; m[n] = NULL; ret = _krb5_parse_moduli_line(context, file, lineno, buf, &element); if (ret) { _krb5_free_moduli(m); return ret; } if (element == NULL) continue; m[n] = element; m[n + 1] = NULL; n++; } *moduli = m; return 0; } krb5_error_code _krb5_dh_group_ok(krb5_context context, unsigned long bits, heim_integer *p, heim_integer *g, heim_integer *q, struct krb5_dh_moduli **moduli, char **name) { int i; if (name) *name = NULL; for (i = 0; moduli[i] != NULL; i++) { if (der_heim_integer_cmp(&moduli[i]->g, g) == 0 && der_heim_integer_cmp(&moduli[i]->p, p) == 0 && (q == NULL || der_heim_integer_cmp(&moduli[i]->q, q) == 0)) { if (bits && bits > moduli[i]->bits) { krb5_set_error_message(context, KRB5_KDC_ERR_DH_KEY_PARAMETERS_NOT_ACCEPTED, N_("PKINIT: DH group parameter %s " "no accepted, not enough bits " "generated", ""), moduli[i]->name); return KRB5_KDC_ERR_DH_KEY_PARAMETERS_NOT_ACCEPTED; } if (name) *name = strdup(moduli[i]->name); return 0; } } krb5_set_error_message(context, KRB5_KDC_ERR_DH_KEY_PARAMETERS_NOT_ACCEPTED, N_("PKINIT: DH group parameter no ok", "")); return KRB5_KDC_ERR_DH_KEY_PARAMETERS_NOT_ACCEPTED; } #endif /* PKINIT */ KRB5_LIB_FUNCTION void KRB5_LIB_CALL _krb5_get_init_creds_opt_free_pkinit(krb5_get_init_creds_opt *opt) { #ifdef PKINIT krb5_pk_init_ctx ctx; if (opt->opt_private == NULL || opt->opt_private->pk_init_ctx == NULL) return; ctx = opt->opt_private->pk_init_ctx; switch (ctx->keyex) { case USE_DH: if (ctx->u.dh) DH_free(ctx->u.dh); break; case USE_RSA: break; case USE_ECDH: #ifdef HAVE_OPENSSL if (ctx->u.eckey) EC_KEY_free(ctx->u.eckey); #endif break; } if (ctx->id) { hx509_verify_destroy_ctx(ctx->id->verify_ctx); hx509_certs_free(&ctx->id->certs); hx509_cert_free(ctx->id->cert); hx509_certs_free(&ctx->id->anchors); hx509_certs_free(&ctx->id->certpool); if (ctx->clientDHNonce) { krb5_free_data(NULL, ctx->clientDHNonce); ctx->clientDHNonce = NULL; } if (ctx->m) _krb5_free_moduli(ctx->m); free(ctx->id); ctx->id = NULL; } free(opt->opt_private->pk_init_ctx); opt->opt_private->pk_init_ctx = NULL; #endif } KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL krb5_get_init_creds_opt_set_pkinit(krb5_context context, krb5_get_init_creds_opt *opt, krb5_principal principal, const char *user_id, const char *x509_anchors, char * const * pool, char * const * pki_revoke, int flags, krb5_prompter_fct prompter, void *prompter_data, char *password) { #ifdef PKINIT krb5_error_code ret; char *anchors = NULL; if (opt->opt_private == NULL) { krb5_set_error_message(context, EINVAL, N_("PKINIT: on non extendable opt", "")); return EINVAL; } opt->opt_private->pk_init_ctx = calloc(1, sizeof(*opt->opt_private->pk_init_ctx)); if (opt->opt_private->pk_init_ctx == NULL) { krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", "")); return ENOMEM; } opt->opt_private->pk_init_ctx->require_binding = 0; opt->opt_private->pk_init_ctx->require_eku = 1; opt->opt_private->pk_init_ctx->require_krbtgt_otherName = 1; opt->opt_private->pk_init_ctx->peer = NULL; /* XXX implement krb5_appdefault_strings */ if (pool == NULL) pool = krb5_config_get_strings(context, NULL, "appdefaults", "pkinit_pool", NULL); if (pki_revoke == NULL) pki_revoke = krb5_config_get_strings(context, NULL, "appdefaults", "pkinit_revoke", NULL); if (x509_anchors == NULL) { krb5_appdefault_string(context, "kinit", krb5_principal_get_realm(context, principal), "pkinit_anchors", NULL, &anchors); x509_anchors = anchors; } if (flags & 4) opt->opt_private->pk_init_ctx->anonymous = 1; ret = _krb5_pk_load_id(context, &opt->opt_private->pk_init_ctx->id, user_id, x509_anchors, pool, pki_revoke, prompter, prompter_data, password); if (ret) { free(opt->opt_private->pk_init_ctx); opt->opt_private->pk_init_ctx = NULL; return ret; } if (opt->opt_private->pk_init_ctx->id->certs) { _krb5_pk_set_user_id(context, principal, opt->opt_private->pk_init_ctx, opt->opt_private->pk_init_ctx->id->certs); } else opt->opt_private->pk_init_ctx->id->cert = NULL; if ((flags & 2) == 0) { hx509_context hx509ctx = context->hx509ctx; hx509_cert cert = opt->opt_private->pk_init_ctx->id->cert; opt->opt_private->pk_init_ctx->keyex = USE_DH; /* * If its a ECDSA certs, lets select ECDSA as the keyex algorithm. */ if (cert) { AlgorithmIdentifier alg; ret = hx509_cert_get_SPKI_AlgorithmIdentifier(hx509ctx, cert, &alg); if (ret == 0) { if (der_heim_oid_cmp(&alg.algorithm, &asn1_oid_id_ecPublicKey) == 0) opt->opt_private->pk_init_ctx->keyex = USE_ECDH; free_AlgorithmIdentifier(&alg); } } } else { opt->opt_private->pk_init_ctx->keyex = USE_RSA; if (opt->opt_private->pk_init_ctx->id->certs == NULL) { krb5_set_error_message(context, EINVAL, N_("No anonymous pkinit support in RSA mode", "")); return EINVAL; } } return 0; #else krb5_set_error_message(context, EINVAL, N_("no support for PKINIT compiled in", "")); return EINVAL; #endif } krb5_error_code KRB5_LIB_FUNCTION krb5_get_init_creds_opt_set_pkinit_user_certs(krb5_context context, krb5_get_init_creds_opt *opt, struct hx509_certs_data *certs) { #ifdef PKINIT if (opt->opt_private == NULL) { krb5_set_error_message(context, EINVAL, N_("PKINIT: on non extendable opt", "")); return EINVAL; } if (opt->opt_private->pk_init_ctx == NULL) { krb5_set_error_message(context, EINVAL, N_("PKINIT: on pkinit context", "")); return EINVAL; } _krb5_pk_set_user_id(context, NULL, opt->opt_private->pk_init_ctx, certs); return 0; #else krb5_set_error_message(context, EINVAL, N_("no support for PKINIT compiled in", "")); return EINVAL; #endif } #ifdef PKINIT static int get_ms_san(hx509_context context, hx509_cert cert, char **upn) { hx509_octet_string_list list; int ret; *upn = NULL; ret = hx509_cert_find_subjectAltName_otherName(context, cert, &asn1_oid_id_pkinit_ms_san, &list); if (ret) return 0; if (list.len > 0 && list.val[0].length > 0) ret = decode_MS_UPN_SAN(list.val[0].data, list.val[0].length, upn, NULL); else ret = 1; hx509_free_octet_string_list(&list); return ret; } static int find_ms_san(hx509_context context, hx509_cert cert, void *ctx) { char *upn; int ret; ret = get_ms_san(context, cert, &upn); if (ret == 0) free(upn); return ret; } #endif /* * Private since it need to be redesigned using krb5_get_init_creds() */ KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL krb5_pk_enterprise_cert(krb5_context context, const char *user_id, krb5_const_realm realm, krb5_principal *principal, struct hx509_certs_data **res) { #ifdef PKINIT krb5_error_code ret; hx509_certs certs, result; hx509_cert cert = NULL; hx509_query *q; char *name; *principal = NULL; if (res) *res = NULL; if (user_id == NULL) { krb5_set_error_message(context, ENOENT, "no user id"); return ENOENT; } ret = hx509_certs_init(context->hx509ctx, user_id, 0, NULL, &certs); if (ret) { pk_copy_error(context, context->hx509ctx, ret, "Failed to init cert certs"); goto out; } ret = hx509_query_alloc(context->hx509ctx, &q); if (ret) { krb5_set_error_message(context, ret, "out of memory"); hx509_certs_free(&certs); goto out; } hx509_query_match_option(q, HX509_QUERY_OPTION_PRIVATE_KEY); hx509_query_match_option(q, HX509_QUERY_OPTION_KU_DIGITALSIGNATURE); hx509_query_match_eku(q, &asn1_oid_id_pkinit_ms_eku); hx509_query_match_cmp_func(q, find_ms_san, NULL); ret = hx509_certs_filter(context->hx509ctx, certs, q, &result); hx509_query_free(context->hx509ctx, q); hx509_certs_free(&certs); if (ret) { pk_copy_error(context, context->hx509ctx, ret, "Failed to find PKINIT certificate"); return ret; } ret = hx509_get_one_cert(context->hx509ctx, result, &cert); hx509_certs_free(&result); if (ret) { pk_copy_error(context, context->hx509ctx, ret, "Failed to get one cert"); goto out; } ret = get_ms_san(context->hx509ctx, cert, &name); if (ret) { pk_copy_error(context, context->hx509ctx, ret, "Failed to get MS SAN"); goto out; } ret = krb5_make_principal(context, principal, realm, name, NULL); free(name); if (ret) goto out; krb5_principal_set_type(context, *principal, KRB5_NT_ENTERPRISE_PRINCIPAL); if (res) { ret = hx509_certs_init(context->hx509ctx, "MEMORY:", 0, NULL, res); if (ret) goto out; ret = hx509_certs_add(context->hx509ctx, *res, cert); if (ret) { hx509_certs_free(res); goto out; } } out: hx509_cert_free(cert); return ret; #else krb5_set_error_message(context, EINVAL, N_("no support for PKINIT compiled in", "")); return EINVAL; #endif } Index: projects/openssl111/crypto/heimdal/lib/ntlm/heimntlm-protos.h =================================================================== --- projects/openssl111/crypto/heimdal/lib/ntlm/heimntlm-protos.h (revision 339197) +++ projects/openssl111/crypto/heimdal/lib/ntlm/heimntlm-protos.h (revision 339198) @@ -1,194 +1,194 @@ /* This is a generated file */ #ifndef __heimntlm_protos_h__ #define __heimntlm_protos_h__ #include #ifdef __cplusplus extern "C" { #endif int heim_ntlm_build_ntlm1_master ( void */*key*/, size_t /*len*/, struct ntlm_buf */*session*/, struct ntlm_buf */*master*/); int heim_ntlm_build_ntlm2_master ( void */*key*/, size_t /*len*/, struct ntlm_buf */*blob*/, struct ntlm_buf */*session*/, struct ntlm_buf */*master*/); int heim_ntlm_calculate_lm2 ( const void */*key*/, size_t /*len*/, const char */*username*/, const char */*target*/, const unsigned char serverchallenge[8], unsigned char ntlmv2[16], struct ntlm_buf */*answer*/); int heim_ntlm_calculate_ntlm1 ( void */*key*/, size_t /*len*/, unsigned char challenge[8], struct ntlm_buf */*answer*/); int heim_ntlm_calculate_ntlm2 ( const void */*key*/, size_t /*len*/, const char */*username*/, const char */*target*/, const unsigned char serverchallenge[8], const struct ntlm_buf */*infotarget*/, unsigned char ntlmv2[16], struct ntlm_buf */*answer*/); int heim_ntlm_calculate_ntlm2_sess ( const unsigned char clnt_nonce[8], const unsigned char svr_chal[8], const unsigned char ntlm_hash[16], struct ntlm_buf */*lm*/, struct ntlm_buf */*ntlm*/); int heim_ntlm_calculate_ntlm2_sess_hash ( const unsigned char clnt_nonce[8], const unsigned char svr_chal[8], unsigned char verifier[8]); int heim_ntlm_decode_targetinfo ( const struct ntlm_buf */*data*/, int /*ucs2*/, struct ntlm_targetinfo */*ti*/); int heim_ntlm_decode_type1 ( const struct ntlm_buf */*buf*/, struct ntlm_type1 */*data*/); int heim_ntlm_decode_type2 ( const struct ntlm_buf */*buf*/, struct ntlm_type2 */*type2*/); int heim_ntlm_decode_type3 ( const struct ntlm_buf */*buf*/, int /*ucs2*/, struct ntlm_type3 */*type3*/); -void +int heim_ntlm_derive_ntlm2_sess ( const unsigned char sessionkey[16], const unsigned char */*clnt_nonce*/, size_t /*clnt_nonce_length*/, const unsigned char svr_chal[8], unsigned char derivedkey[16]); int heim_ntlm_encode_targetinfo ( const struct ntlm_targetinfo */*ti*/, int /*ucs2*/, struct ntlm_buf */*data*/); int heim_ntlm_encode_type1 ( const struct ntlm_type1 */*type1*/, struct ntlm_buf */*data*/); int heim_ntlm_encode_type2 ( const struct ntlm_type2 */*type2*/, struct ntlm_buf */*data*/); int heim_ntlm_encode_type3 ( const struct ntlm_type3 */*type3*/, struct ntlm_buf */*data*/); void heim_ntlm_free_buf (struct ntlm_buf */*p*/); void heim_ntlm_free_targetinfo (struct ntlm_targetinfo */*ti*/); void heim_ntlm_free_type1 (struct ntlm_type1 */*data*/); void heim_ntlm_free_type2 (struct ntlm_type2 */*data*/); void heim_ntlm_free_type3 (struct ntlm_type3 */*data*/); int heim_ntlm_keyex_unwrap ( struct ntlm_buf */*baseKey*/, struct ntlm_buf */*encryptedSession*/, struct ntlm_buf */*session*/); int heim_ntlm_keyex_wrap ( struct ntlm_buf */*base_session*/, struct ntlm_buf */*session*/, struct ntlm_buf */*encryptedSession*/); int heim_ntlm_nt_key ( const char */*password*/, struct ntlm_buf */*key*/); int heim_ntlm_ntlmv2_key ( const void */*key*/, size_t /*len*/, const char */*username*/, const char */*target*/, unsigned char ntlmv2[16]); size_t heim_ntlm_unparse_flags ( uint32_t /*flags*/, char */*s*/, size_t /*len*/); int heim_ntlm_v1_base_session ( void */*key*/, size_t /*len*/, struct ntlm_buf */*session*/); int heim_ntlm_v2_base_session ( void */*key*/, size_t /*len*/, struct ntlm_buf */*ntlmResponse*/, struct ntlm_buf */*session*/); int heim_ntlm_verify_ntlm2 ( const void */*key*/, size_t /*len*/, const char */*username*/, const char */*target*/, time_t /*now*/, const unsigned char serverchallenge[8], const struct ntlm_buf */*answer*/, struct ntlm_buf */*infotarget*/, unsigned char ntlmv2[16]); #ifdef __cplusplus } #endif #endif /* __heimntlm_protos_h__ */ Index: projects/openssl111/crypto/heimdal/lib/ntlm/ntlm.c =================================================================== --- projects/openssl111/crypto/heimdal/lib/ntlm/ntlm.c (revision 339197) +++ projects/openssl111/crypto/heimdal/lib/ntlm/ntlm.c (revision 339198) @@ -1,1803 +1,1857 @@ /* * Copyright (c) 2006 - 2008 Kungliga Tekniska Högskolan * (Royal Institute of Technology, Stockholm, Sweden). * All rights reserved. * * Portions Copyright (c) 2010 Apple Inc. 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 #include #include #include #include #include #include #include #include #include #include #define HC_DEPRECATED_CRYPTO #include "krb5-types.h" #include "crypto-headers.h" #include /*! \mainpage Heimdal NTLM library * * \section intro Introduction * * Heimdal libheimntlm library is a implementation of the NTLM * protocol, both version 1 and 2. The GSS-API mech that uses this * library adds support for transport encryption and integrity * checking. * * NTLM is a protocol for mutual authentication, its still used in * many protocol where Kerberos is not support, one example is * EAP/X802.1x mechanism LEAP from Microsoft and Cisco. * * This is a support library for the core protocol, its used in * Heimdal to implement and GSS-API mechanism. There is also support * in the KDC to do remote digest authenticiation, this to allow * services to authenticate users w/o direct access to the users ntlm * hashes (same as Kerberos arcfour enctype keys). * * More information about the NTLM protocol can found here * http://davenport.sourceforge.net/ntlm.html . * * The Heimdal projects web page: http://www.h5l.org/ * * @section ntlm_example NTLM Example * * Example to to use @ref test_ntlm.c . * * @example test_ntlm.c * * Example how to use the NTLM primitives. * */ /** @defgroup ntlm_core Heimdal NTLM library * * The NTLM core functions implement the string2key generation * function, message encode and decode function, and the hash function * functions. */ struct sec_buffer { uint16_t length; uint16_t allocated; uint32_t offset; }; static const unsigned char ntlmsigature[8] = "NTLMSSP\x00"; /* * */ #define CHECK(f, e) \ do { \ ret = f; \ if (ret != (ssize_t)(e)) { \ ret = HNTLM_ERR_DECODE; \ goto out; \ } \ } while(/*CONSTCOND*/0) static struct units ntlm_flag_units[] = { #define ntlm_flag(x) { #x, NTLM_##x } ntlm_flag(ENC_56), ntlm_flag(NEG_KEYEX), ntlm_flag(ENC_128), ntlm_flag(MBZ1), ntlm_flag(MBZ2), ntlm_flag(MBZ3), ntlm_flag(NEG_VERSION), ntlm_flag(MBZ4), ntlm_flag(NEG_TARGET_INFO), ntlm_flag(NON_NT_SESSION_KEY), ntlm_flag(MBZ5), ntlm_flag(NEG_IDENTIFY), ntlm_flag(NEG_NTLM2), ntlm_flag(TARGET_SHARE), ntlm_flag(TARGET_SERVER), ntlm_flag(TARGET_DOMAIN), ntlm_flag(NEG_ALWAYS_SIGN), ntlm_flag(MBZ6), ntlm_flag(OEM_SUPPLIED_WORKSTATION), ntlm_flag(OEM_SUPPLIED_DOMAIN), ntlm_flag(NEG_ANONYMOUS), ntlm_flag(NEG_NT_ONLY), ntlm_flag(NEG_NTLM), ntlm_flag(MBZ8), ntlm_flag(NEG_LM_KEY), ntlm_flag(NEG_DATAGRAM), ntlm_flag(NEG_SEAL), ntlm_flag(NEG_SIGN), ntlm_flag(MBZ9), ntlm_flag(NEG_TARGET), ntlm_flag(NEG_OEM), ntlm_flag(NEG_UNICODE), #undef ntlm_flag {NULL, 0} }; size_t heim_ntlm_unparse_flags(uint32_t flags, char *s, size_t len) { return unparse_flags(flags, ntlm_flag_units, s, len); } /** * heim_ntlm_free_buf frees the ntlm buffer * * @param p buffer to be freed * * @ingroup ntlm_core */ void heim_ntlm_free_buf(struct ntlm_buf *p) { if (p->data) free(p->data); p->data = NULL; p->length = 0; } static int ascii2ucs2le(const char *string, int up, struct ntlm_buf *buf) { unsigned char *p; size_t len, i; len = strlen(string); if (len / 2 > UINT_MAX) return ERANGE; buf->length = len * 2; buf->data = malloc(buf->length); if (buf->data == NULL && len != 0) { heim_ntlm_free_buf(buf); return ENOMEM; } p = buf->data; for (i = 0; i < len; i++) { unsigned char t = (unsigned char)string[i]; if (t & 0x80) { heim_ntlm_free_buf(buf); return EINVAL; } if (up) t = toupper(t); p[(i * 2) + 0] = t; p[(i * 2) + 1] = 0; } return 0; } /* * */ static krb5_error_code ret_sec_buffer(krb5_storage *sp, struct sec_buffer *buf) { krb5_error_code ret; CHECK(krb5_ret_uint16(sp, &buf->length), 0); CHECK(krb5_ret_uint16(sp, &buf->allocated), 0); CHECK(krb5_ret_uint32(sp, &buf->offset), 0); out: return ret; } static krb5_error_code store_sec_buffer(krb5_storage *sp, const struct sec_buffer *buf) { krb5_error_code ret; CHECK(krb5_store_uint16(sp, buf->length), 0); CHECK(krb5_store_uint16(sp, buf->allocated), 0); CHECK(krb5_store_uint32(sp, buf->offset), 0); out: return ret; } /* * Strings are either OEM or UNICODE. The later is encoded as ucs2 on * wire, but using utf8 in memory. */ static krb5_error_code len_string(int ucs2, const char *s) { size_t len = strlen(s); if (ucs2) len *= 2; return len; } /* * */ static krb5_error_code ret_string(krb5_storage *sp, int ucs2, size_t len, char **s) { krb5_error_code ret; *s = malloc(len + 1); if (*s == NULL) return ENOMEM; CHECK(krb5_storage_read(sp, *s, len), len); (*s)[len] = '\0'; if (ucs2) { size_t i; for (i = 0; i < len / 2; i++) { (*s)[i] = (*s)[i * 2]; if ((*s)[i * 2 + 1]) { free(*s); *s = NULL; return EINVAL; } } (*s)[i] = '\0'; } ret = 0; out: return ret; } static krb5_error_code ret_sec_string(krb5_storage *sp, int ucs2, struct sec_buffer *desc, char **s) { krb5_error_code ret = 0; CHECK(krb5_storage_seek(sp, desc->offset, SEEK_SET), desc->offset); CHECK(ret_string(sp, ucs2, desc->length, s), 0); out: return ret; } static krb5_error_code put_string(krb5_storage *sp, int ucs2, const char *s) { krb5_error_code ret; struct ntlm_buf buf; if (ucs2) { ret = ascii2ucs2le(s, 0, &buf); if (ret) return ret; } else { buf.data = rk_UNCONST(s); buf.length = strlen(s); } CHECK(krb5_storage_write(sp, buf.data, buf.length), buf.length); if (ucs2) heim_ntlm_free_buf(&buf); ret = 0; out: return ret; } /* * */ static krb5_error_code ret_buf(krb5_storage *sp, struct sec_buffer *desc, struct ntlm_buf *buf) { krb5_error_code ret; buf->data = malloc(desc->length); buf->length = desc->length; CHECK(krb5_storage_seek(sp, desc->offset, SEEK_SET), desc->offset); CHECK(krb5_storage_read(sp, buf->data, buf->length), buf->length); ret = 0; out: return ret; } static krb5_error_code put_buf(krb5_storage *sp, const struct ntlm_buf *buf) { krb5_error_code ret; CHECK(krb5_storage_write(sp, buf->data, buf->length), buf->length); ret = 0; out: return ret; } /** * Frees the ntlm_targetinfo message * * @param ti targetinfo to be freed * * @ingroup ntlm_core */ void heim_ntlm_free_targetinfo(struct ntlm_targetinfo *ti) { free(ti->servername); free(ti->domainname); free(ti->dnsdomainname); free(ti->dnsservername); free(ti->dnstreename); memset(ti, 0, sizeof(*ti)); } static int encode_ti_string(krb5_storage *out, uint16_t type, int ucs2, char *s) { krb5_error_code ret; CHECK(krb5_store_uint16(out, type), 0); CHECK(krb5_store_uint16(out, len_string(ucs2, s)), 0); CHECK(put_string(out, ucs2, s), 0); out: return ret; } /** * Encodes a ntlm_targetinfo message. * * @param ti the ntlm_targetinfo message to encode. * @param ucs2 ignored * @param data is the return buffer with the encoded message, should be * freed with heim_ntlm_free_buf(). * * @return In case of success 0 is return, an errors, a errno in what * went wrong. * * @ingroup ntlm_core */ int heim_ntlm_encode_targetinfo(const struct ntlm_targetinfo *ti, int ucs2, struct ntlm_buf *data) { krb5_error_code ret; krb5_storage *out; data->data = NULL; data->length = 0; out = krb5_storage_emem(); if (out == NULL) return ENOMEM; krb5_storage_set_byteorder(out, KRB5_STORAGE_BYTEORDER_LE); if (ti->servername) CHECK(encode_ti_string(out, 1, ucs2, ti->servername), 0); if (ti->domainname) CHECK(encode_ti_string(out, 2, ucs2, ti->domainname), 0); if (ti->dnsservername) CHECK(encode_ti_string(out, 3, ucs2, ti->dnsservername), 0); if (ti->dnsdomainname) CHECK(encode_ti_string(out, 4, ucs2, ti->dnsdomainname), 0); if (ti->dnstreename) CHECK(encode_ti_string(out, 5, ucs2, ti->dnstreename), 0); if (ti->avflags) { CHECK(krb5_store_uint16(out, 6), 0); CHECK(krb5_store_uint16(out, 4), 0); CHECK(krb5_store_uint32(out, ti->avflags), 0); } /* end tag */ CHECK(krb5_store_int16(out, 0), 0); CHECK(krb5_store_int16(out, 0), 0); { krb5_data d; ret = krb5_storage_to_data(out, &d); data->data = d.data; data->length = d.length; } out: krb5_storage_free(out); return ret; } /** * Decodes an NTLM targetinfo message * * @param data input data buffer with the encode NTLM targetinfo message * @param ucs2 if the strings should be encoded with ucs2 (selected by flag in message). * @param ti the decoded target info, should be freed with heim_ntlm_free_targetinfo(). * * @return In case of success 0 is return, an errors, a errno in what * went wrong. * * @ingroup ntlm_core */ int heim_ntlm_decode_targetinfo(const struct ntlm_buf *data, int ucs2, struct ntlm_targetinfo *ti) { uint16_t type, len; krb5_storage *in; int ret = 0, done = 0; memset(ti, 0, sizeof(*ti)); if (data->length == 0) return 0; in = krb5_storage_from_readonly_mem(data->data, data->length); if (in == NULL) return ENOMEM; krb5_storage_set_byteorder(in, KRB5_STORAGE_BYTEORDER_LE); while (!done) { CHECK(krb5_ret_uint16(in, &type), 0); CHECK(krb5_ret_uint16(in, &len), 0); switch (type) { case 0: done = 1; break; case 1: CHECK(ret_string(in, ucs2, len, &ti->servername), 0); break; case 2: CHECK(ret_string(in, ucs2, len, &ti->domainname), 0); break; case 3: CHECK(ret_string(in, ucs2, len, &ti->dnsservername), 0); break; case 4: CHECK(ret_string(in, ucs2, len, &ti->dnsdomainname), 0); break; case 5: CHECK(ret_string(in, ucs2, len, &ti->dnstreename), 0); break; case 6: CHECK(krb5_ret_uint32(in, &ti->avflags), 0); break; default: krb5_storage_seek(in, len, SEEK_CUR); break; } } out: if (in) krb5_storage_free(in); return ret; } /** * Frees the ntlm_type1 message * * @param data message to be freed * * @ingroup ntlm_core */ void heim_ntlm_free_type1(struct ntlm_type1 *data) { if (data->domain) free(data->domain); if (data->hostname) free(data->hostname); memset(data, 0, sizeof(*data)); } int heim_ntlm_decode_type1(const struct ntlm_buf *buf, struct ntlm_type1 *data) { krb5_error_code ret; unsigned char sig[8]; uint32_t type; struct sec_buffer domain, hostname; krb5_storage *in; memset(data, 0, sizeof(*data)); in = krb5_storage_from_readonly_mem(buf->data, buf->length); if (in == NULL) { ret = ENOMEM; goto out; } krb5_storage_set_byteorder(in, KRB5_STORAGE_BYTEORDER_LE); CHECK(krb5_storage_read(in, sig, sizeof(sig)), sizeof(sig)); CHECK(memcmp(ntlmsigature, sig, sizeof(ntlmsigature)), 0); CHECK(krb5_ret_uint32(in, &type), 0); CHECK(type, 1); CHECK(krb5_ret_uint32(in, &data->flags), 0); if (data->flags & NTLM_OEM_SUPPLIED_DOMAIN) CHECK(ret_sec_buffer(in, &domain), 0); if (data->flags & NTLM_OEM_SUPPLIED_WORKSTATION) CHECK(ret_sec_buffer(in, &hostname), 0); #if 0 if (domain.offset > 32) { CHECK(krb5_ret_uint32(in, &data->os[0]), 0); CHECK(krb5_ret_uint32(in, &data->os[1]), 0); } #endif if (data->flags & NTLM_OEM_SUPPLIED_DOMAIN) CHECK(ret_sec_string(in, 0, &domain, &data->domain), 0); if (data->flags & NTLM_OEM_SUPPLIED_WORKSTATION) CHECK(ret_sec_string(in, 0, &hostname, &data->hostname), 0); out: if (in) krb5_storage_free(in); if (ret) heim_ntlm_free_type1(data); return ret; } /** * Encodes an ntlm_type1 message. * * @param type1 the ntlm_type1 message to encode. * @param data is the return buffer with the encoded message, should be * freed with heim_ntlm_free_buf(). * * @return In case of success 0 is return, an errors, a errno in what * went wrong. * * @ingroup ntlm_core */ int heim_ntlm_encode_type1(const struct ntlm_type1 *type1, struct ntlm_buf *data) { krb5_error_code ret; struct sec_buffer domain, hostname; krb5_storage *out; uint32_t base, flags; flags = type1->flags; base = 16; if (type1->domain) { base += 8; flags |= NTLM_OEM_SUPPLIED_DOMAIN; } if (type1->hostname) { base += 8; flags |= NTLM_OEM_SUPPLIED_WORKSTATION; } if (type1->os[0]) base += 8; domain.offset = base; if (type1->domain) { domain.length = len_string(0, type1->domain); domain.allocated = domain.length; } else { domain.length = 0; domain.allocated = 0; } hostname.offset = domain.allocated + domain.offset; if (type1->hostname) { hostname.length = len_string(0, type1->hostname); hostname.allocated = hostname.length; } else { hostname.length = 0; hostname.allocated = 0; } out = krb5_storage_emem(); if (out == NULL) return ENOMEM; krb5_storage_set_byteorder(out, KRB5_STORAGE_BYTEORDER_LE); CHECK(krb5_storage_write(out, ntlmsigature, sizeof(ntlmsigature)), sizeof(ntlmsigature)); CHECK(krb5_store_uint32(out, 1), 0); CHECK(krb5_store_uint32(out, flags), 0); CHECK(store_sec_buffer(out, &domain), 0); CHECK(store_sec_buffer(out, &hostname), 0); #if 0 CHECK(krb5_store_uint32(out, type1->os[0]), 0); CHECK(krb5_store_uint32(out, type1->os[1]), 0); #endif if (type1->domain) CHECK(put_string(out, 0, type1->domain), 0); if (type1->hostname) CHECK(put_string(out, 0, type1->hostname), 0); { krb5_data d; ret = krb5_storage_to_data(out, &d); data->data = d.data; data->length = d.length; } out: krb5_storage_free(out); return ret; } /** * Frees the ntlm_type2 message * * @param data message to be freed * * @ingroup ntlm_core */ void heim_ntlm_free_type2(struct ntlm_type2 *data) { if (data->targetname) free(data->targetname); heim_ntlm_free_buf(&data->targetinfo); memset(data, 0, sizeof(*data)); } int heim_ntlm_decode_type2(const struct ntlm_buf *buf, struct ntlm_type2 *type2) { krb5_error_code ret; unsigned char sig[8]; uint32_t type, ctx[2]; struct sec_buffer targetname, targetinfo; krb5_storage *in; int ucs2 = 0; memset(type2, 0, sizeof(*type2)); in = krb5_storage_from_readonly_mem(buf->data, buf->length); if (in == NULL) { ret = ENOMEM; goto out; } krb5_storage_set_byteorder(in, KRB5_STORAGE_BYTEORDER_LE); CHECK(krb5_storage_read(in, sig, sizeof(sig)), sizeof(sig)); CHECK(memcmp(ntlmsigature, sig, sizeof(ntlmsigature)), 0); CHECK(krb5_ret_uint32(in, &type), 0); CHECK(type, 2); CHECK(ret_sec_buffer(in, &targetname), 0); CHECK(krb5_ret_uint32(in, &type2->flags), 0); if (type2->flags & NTLM_NEG_UNICODE) ucs2 = 1; CHECK(krb5_storage_read(in, type2->challenge, sizeof(type2->challenge)), sizeof(type2->challenge)); CHECK(krb5_ret_uint32(in, &ctx[0]), 0); /* context */ CHECK(krb5_ret_uint32(in, &ctx[1]), 0); CHECK(ret_sec_buffer(in, &targetinfo), 0); /* os version */ if (type2->flags & NTLM_NEG_VERSION) { CHECK(krb5_ret_uint32(in, &type2->os[0]), 0); CHECK(krb5_ret_uint32(in, &type2->os[1]), 0); } CHECK(ret_sec_string(in, ucs2, &targetname, &type2->targetname), 0); CHECK(ret_buf(in, &targetinfo, &type2->targetinfo), 0); ret = 0; out: if (in) krb5_storage_free(in); if (ret) heim_ntlm_free_type2(type2); return ret; } /** * Encodes an ntlm_type2 message. * * @param type2 the ntlm_type2 message to encode. * @param data is the return buffer with the encoded message, should be * freed with heim_ntlm_free_buf(). * * @return In case of success 0 is return, an errors, a errno in what * went wrong. * * @ingroup ntlm_core */ int heim_ntlm_encode_type2(const struct ntlm_type2 *type2, struct ntlm_buf *data) { struct sec_buffer targetname, targetinfo; krb5_error_code ret; krb5_storage *out = NULL; uint32_t base; int ucs2 = 0; base = 48; if (type2->flags & NTLM_NEG_VERSION) base += 8; if (type2->flags & NTLM_NEG_UNICODE) ucs2 = 1; targetname.offset = base; targetname.length = len_string(ucs2, type2->targetname); targetname.allocated = targetname.length; targetinfo.offset = targetname.allocated + targetname.offset; targetinfo.length = type2->targetinfo.length; targetinfo.allocated = type2->targetinfo.length; out = krb5_storage_emem(); if (out == NULL) return ENOMEM; krb5_storage_set_byteorder(out, KRB5_STORAGE_BYTEORDER_LE); CHECK(krb5_storage_write(out, ntlmsigature, sizeof(ntlmsigature)), sizeof(ntlmsigature)); CHECK(krb5_store_uint32(out, 2), 0); CHECK(store_sec_buffer(out, &targetname), 0); CHECK(krb5_store_uint32(out, type2->flags), 0); CHECK(krb5_storage_write(out, type2->challenge, sizeof(type2->challenge)), sizeof(type2->challenge)); CHECK(krb5_store_uint32(out, 0), 0); /* context */ CHECK(krb5_store_uint32(out, 0), 0); CHECK(store_sec_buffer(out, &targetinfo), 0); /* os version */ if (type2->flags & NTLM_NEG_VERSION) { CHECK(krb5_store_uint32(out, type2->os[0]), 0); CHECK(krb5_store_uint32(out, type2->os[1]), 0); } CHECK(put_string(out, ucs2, type2->targetname), 0); CHECK(krb5_storage_write(out, type2->targetinfo.data, type2->targetinfo.length), type2->targetinfo.length); { krb5_data d; ret = krb5_storage_to_data(out, &d); data->data = d.data; data->length = d.length; } out: krb5_storage_free(out); return ret; } /** * Frees the ntlm_type3 message * * @param data message to be freed * * @ingroup ntlm_core */ void heim_ntlm_free_type3(struct ntlm_type3 *data) { heim_ntlm_free_buf(&data->lm); heim_ntlm_free_buf(&data->ntlm); if (data->targetname) free(data->targetname); if (data->username) free(data->username); if (data->ws) free(data->ws); heim_ntlm_free_buf(&data->sessionkey); memset(data, 0, sizeof(*data)); } /* * */ int heim_ntlm_decode_type3(const struct ntlm_buf *buf, int ucs2, struct ntlm_type3 *type3) { krb5_error_code ret; unsigned char sig[8]; uint32_t type; krb5_storage *in; struct sec_buffer lm, ntlm, target, username, sessionkey, ws; uint32_t min_offset = 72; memset(type3, 0, sizeof(*type3)); memset(&sessionkey, 0, sizeof(sessionkey)); in = krb5_storage_from_readonly_mem(buf->data, buf->length); if (in == NULL) { ret = ENOMEM; goto out; } krb5_storage_set_byteorder(in, KRB5_STORAGE_BYTEORDER_LE); CHECK(krb5_storage_read(in, sig, sizeof(sig)), sizeof(sig)); CHECK(memcmp(ntlmsigature, sig, sizeof(ntlmsigature)), 0); CHECK(krb5_ret_uint32(in, &type), 0); CHECK(type, 3); CHECK(ret_sec_buffer(in, &lm), 0); if (lm.allocated) min_offset = min(min_offset, lm.offset); CHECK(ret_sec_buffer(in, &ntlm), 0); if (ntlm.allocated) min_offset = min(min_offset, ntlm.offset); CHECK(ret_sec_buffer(in, &target), 0); if (target.allocated) min_offset = min(min_offset, target.offset); CHECK(ret_sec_buffer(in, &username), 0); if (username.allocated) min_offset = min(min_offset, username.offset); CHECK(ret_sec_buffer(in, &ws), 0); if (ws.allocated) min_offset = min(min_offset, ws.offset); if (min_offset > 52) { CHECK(ret_sec_buffer(in, &sessionkey), 0); min_offset = max(min_offset, sessionkey.offset); CHECK(krb5_ret_uint32(in, &type3->flags), 0); } if (min_offset > 52 + 8 + 4 + 8) { CHECK(krb5_ret_uint32(in, &type3->os[0]), 0); CHECK(krb5_ret_uint32(in, &type3->os[1]), 0); } CHECK(ret_buf(in, &lm, &type3->lm), 0); CHECK(ret_buf(in, &ntlm, &type3->ntlm), 0); CHECK(ret_sec_string(in, ucs2, &target, &type3->targetname), 0); CHECK(ret_sec_string(in, ucs2, &username, &type3->username), 0); CHECK(ret_sec_string(in, ucs2, &ws, &type3->ws), 0); if (sessionkey.offset) CHECK(ret_buf(in, &sessionkey, &type3->sessionkey), 0); out: if (in) krb5_storage_free(in); if (ret) heim_ntlm_free_type3(type3); return ret; } /** * Encodes an ntlm_type3 message. * * @param type3 the ntlm_type3 message to encode. * @param data is the return buffer with the encoded message, should be * freed with heim_ntlm_free_buf(). * * @return In case of success 0 is return, an errors, a errno in what * went wrong. * * @ingroup ntlm_core */ int heim_ntlm_encode_type3(const struct ntlm_type3 *type3, struct ntlm_buf *data) { struct sec_buffer lm, ntlm, target, username, sessionkey, ws; krb5_error_code ret; krb5_storage *out = NULL; uint32_t base; int ucs2 = 0; memset(&lm, 0, sizeof(lm)); memset(&ntlm, 0, sizeof(ntlm)); memset(&target, 0, sizeof(target)); memset(&username, 0, sizeof(username)); memset(&ws, 0, sizeof(ws)); memset(&sessionkey, 0, sizeof(sessionkey)); base = 52; base += 8; /* sessionkey sec buf */ base += 4; /* flags */ if (type3->os[0]) { base += 8; } if (type3->flags & NTLM_NEG_UNICODE) ucs2 = 1; target.offset = base; target.length = len_string(ucs2, type3->targetname); target.allocated = target.length; username.offset = target.offset + target.allocated; username.length = len_string(ucs2, type3->username); username.allocated = username.length; ws.offset = username.offset + username.allocated; ws.length = len_string(ucs2, type3->ws); ws.allocated = ws.length; lm.offset = ws.offset + ws.allocated; lm.length = type3->lm.length; lm.allocated = type3->lm.length; ntlm.offset = lm.offset + lm.allocated; ntlm.length = type3->ntlm.length; ntlm.allocated = ntlm.length; sessionkey.offset = ntlm.offset + ntlm.allocated; sessionkey.length = type3->sessionkey.length; sessionkey.allocated = type3->sessionkey.length; out = krb5_storage_emem(); if (out == NULL) return ENOMEM; krb5_storage_set_byteorder(out, KRB5_STORAGE_BYTEORDER_LE); CHECK(krb5_storage_write(out, ntlmsigature, sizeof(ntlmsigature)), sizeof(ntlmsigature)); CHECK(krb5_store_uint32(out, 3), 0); CHECK(store_sec_buffer(out, &lm), 0); CHECK(store_sec_buffer(out, &ntlm), 0); CHECK(store_sec_buffer(out, &target), 0); CHECK(store_sec_buffer(out, &username), 0); CHECK(store_sec_buffer(out, &ws), 0); CHECK(store_sec_buffer(out, &sessionkey), 0); CHECK(krb5_store_uint32(out, type3->flags), 0); #if 0 CHECK(krb5_store_uint32(out, 0), 0); /* os0 */ CHECK(krb5_store_uint32(out, 0), 0); /* os1 */ #endif CHECK(put_string(out, ucs2, type3->targetname), 0); CHECK(put_string(out, ucs2, type3->username), 0); CHECK(put_string(out, ucs2, type3->ws), 0); CHECK(put_buf(out, &type3->lm), 0); CHECK(put_buf(out, &type3->ntlm), 0); CHECK(put_buf(out, &type3->sessionkey), 0); { krb5_data d; ret = krb5_storage_to_data(out, &d); data->data = d.data; data->length = d.length; } out: krb5_storage_free(out); return ret; } /* * */ -static void +static int splitandenc(unsigned char *hash, unsigned char *challenge, unsigned char *answer) { - EVP_CIPHER_CTX ctx; + EVP_CIPHER_CTX *ctx; unsigned char key[8]; key[0] = hash[0]; key[1] = (hash[0] << 7) | (hash[1] >> 1); key[2] = (hash[1] << 6) | (hash[2] >> 2); key[3] = (hash[2] << 5) | (hash[3] >> 3); key[4] = (hash[3] << 4) | (hash[4] >> 4); key[5] = (hash[4] << 3) | (hash[5] >> 5); key[6] = (hash[5] << 2) | (hash[6] >> 6); key[7] = (hash[6] << 1); - EVP_CIPHER_CTX_init(&ctx); + ctx = EVP_CIPHER_CTX_new(); + if (ctx == NULL) + return ENOMEM; - EVP_CipherInit_ex(&ctx, EVP_des_cbc(), NULL, key, NULL, 1); - EVP_Cipher(&ctx, answer, challenge, 8); - EVP_CIPHER_CTX_cleanup(&ctx); + EVP_CipherInit_ex(ctx, EVP_des_cbc(), NULL, key, NULL, 1); + EVP_Cipher(ctx, answer, challenge, 8); + EVP_CIPHER_CTX_free(ctx); memset(key, 0, sizeof(key)); + return 0; } /** * Calculate the NTLM key, the password is assumed to be in UTF8. * * @param password password to calcute the key for. * @param key calcuted key, should be freed with heim_ntlm_free_buf(). * * @return In case of success 0 is return, an errors, a errno in what * went wrong. * * @ingroup ntlm_core */ int heim_ntlm_nt_key(const char *password, struct ntlm_buf *key) { struct ntlm_buf buf; EVP_MD_CTX *m; int ret; key->data = malloc(MD5_DIGEST_LENGTH); if (key->data == NULL) return ENOMEM; key->length = MD5_DIGEST_LENGTH; ret = ascii2ucs2le(password, 0, &buf); if (ret) { heim_ntlm_free_buf(key); return ret; } m = EVP_MD_CTX_create(); if (m == NULL) { heim_ntlm_free_buf(key); heim_ntlm_free_buf(&buf); return ENOMEM; } EVP_DigestInit_ex(m, EVP_md4(), NULL); EVP_DigestUpdate(m, buf.data, buf.length); EVP_DigestFinal_ex(m, key->data, NULL); EVP_MD_CTX_destroy(m); heim_ntlm_free_buf(&buf); return 0; } /** * Calculate NTLMv1 response hash * * @param key the ntlm v1 key * @param len length of key * @param challenge sent by the server * @param answer calculated answer, should be freed with heim_ntlm_free_buf(). * * @return In case of success 0 is return, an errors, a errno in what * went wrong. * * @ingroup ntlm_core */ int heim_ntlm_calculate_ntlm1(void *key, size_t len, unsigned char challenge[8], struct ntlm_buf *answer) { unsigned char res[21]; + int ret; if (len != MD4_DIGEST_LENGTH) return HNTLM_ERR_INVALID_LENGTH; memcpy(res, key, len); memset(&res[MD4_DIGEST_LENGTH], 0, sizeof(res) - MD4_DIGEST_LENGTH); answer->data = malloc(24); if (answer->data == NULL) return ENOMEM; answer->length = 24; - splitandenc(&res[0], challenge, ((unsigned char *)answer->data) + 0); - splitandenc(&res[7], challenge, ((unsigned char *)answer->data) + 8); - splitandenc(&res[14], challenge, ((unsigned char *)answer->data) + 16); + ret = splitandenc(&res[0], challenge, ((unsigned char *)answer->data) + 0); + if (ret) + goto out; + ret = splitandenc(&res[7], challenge, ((unsigned char *)answer->data) + 8); + if (ret) + goto out; + ret = splitandenc(&res[14], challenge, ((unsigned char *)answer->data) + 16); + if (ret) + goto out; return 0; + +out: + heim_ntlm_free_buf(answer); + return ret; } int heim_ntlm_v1_base_session(void *key, size_t len, struct ntlm_buf *session) { EVP_MD_CTX *m; session->length = MD4_DIGEST_LENGTH; session->data = malloc(session->length); if (session->data == NULL) { session->length = 0; return ENOMEM; } m = EVP_MD_CTX_create(); if (m == NULL) { heim_ntlm_free_buf(session); return ENOMEM; } EVP_DigestInit_ex(m, EVP_md4(), NULL); EVP_DigestUpdate(m, key, len); EVP_DigestFinal_ex(m, session->data, NULL); EVP_MD_CTX_destroy(m); return 0; } int heim_ntlm_v2_base_session(void *key, size_t len, struct ntlm_buf *ntlmResponse, struct ntlm_buf *session) { unsigned int hmaclen; - HMAC_CTX c; + HMAC_CTX *c; if (ntlmResponse->length <= 16) return HNTLM_ERR_INVALID_LENGTH; session->data = malloc(16); if (session->data == NULL) return ENOMEM; session->length = 16; /* Note: key is the NTLMv2 key */ - HMAC_CTX_init(&c); - HMAC_Init_ex(&c, key, len, EVP_md5(), NULL); - HMAC_Update(&c, ntlmResponse->data, 16); - HMAC_Final(&c, session->data, &hmaclen); - HMAC_CTX_cleanup(&c); + c = HMAC_CTX_new(); + if (c == NULL) { + heim_ntlm_free_buf(session); + return ENOMEM; + } + HMAC_Init_ex(c, key, len, EVP_md5(), NULL); + HMAC_Update(c, ntlmResponse->data, 16); + HMAC_Final(c, session->data, &hmaclen); + HMAC_CTX_free(c); return 0; } int heim_ntlm_keyex_wrap(struct ntlm_buf *base_session, struct ntlm_buf *session, struct ntlm_buf *encryptedSession) { - EVP_CIPHER_CTX c; + EVP_CIPHER_CTX *c; int ret; session->length = MD4_DIGEST_LENGTH; session->data = malloc(session->length); if (session->data == NULL) { session->length = 0; return ENOMEM; } encryptedSession->length = MD4_DIGEST_LENGTH; encryptedSession->data = malloc(encryptedSession->length); if (encryptedSession->data == NULL) { heim_ntlm_free_buf(session); encryptedSession->length = 0; return ENOMEM; } - EVP_CIPHER_CTX_init(&c); + c = EVP_CIPHER_CTX_new(); + if (c == NULL) { + heim_ntlm_free_buf(encryptedSession); + heim_ntlm_free_buf(session); + return ENOMEM; + } - ret = EVP_CipherInit_ex(&c, EVP_rc4(), NULL, base_session->data, NULL, 1); + ret = EVP_CipherInit_ex(c, EVP_rc4(), NULL, base_session->data, NULL, 1); if (ret != 1) { - EVP_CIPHER_CTX_cleanup(&c); + EVP_CIPHER_CTX_free(c); heim_ntlm_free_buf(encryptedSession); heim_ntlm_free_buf(session); return HNTLM_ERR_CRYPTO; } if (RAND_bytes(session->data, session->length) != 1) { - EVP_CIPHER_CTX_cleanup(&c); + EVP_CIPHER_CTX_free(c); heim_ntlm_free_buf(encryptedSession); heim_ntlm_free_buf(session); return HNTLM_ERR_RAND; } - EVP_Cipher(&c, encryptedSession->data, session->data, encryptedSession->length); - EVP_CIPHER_CTX_cleanup(&c); + EVP_Cipher(c, encryptedSession->data, session->data, encryptedSession->length); + EVP_CIPHER_CTX_free(c); return 0; } /** * Generates an NTLMv1 session random with assosited session master key. * * @param key the ntlm v1 key * @param len length of key * @param session generated session nonce, should be freed with heim_ntlm_free_buf(). * @param master calculated session master key, should be freed with heim_ntlm_free_buf(). * * @return In case of success 0 is return, an errors, a errno in what * went wrong. * * @ingroup ntlm_core */ int heim_ntlm_build_ntlm1_master(void *key, size_t len, struct ntlm_buf *session, struct ntlm_buf *master) { struct ntlm_buf sess; int ret; ret = heim_ntlm_v1_base_session(key, len, &sess); if (ret) return ret; ret = heim_ntlm_keyex_wrap(&sess, session, master); heim_ntlm_free_buf(&sess); return ret; } /** * Generates an NTLMv2 session random with associated session master key. * * @param key the NTLMv2 key * @param len length of key * @param blob the NTLMv2 "blob" * @param session generated session nonce, should be freed with heim_ntlm_free_buf(). * @param master calculated session master key, should be freed with heim_ntlm_free_buf(). * * @return In case of success 0 is return, an errors, a errno in what * went wrong. * * @ingroup ntlm_core */ int heim_ntlm_build_ntlm2_master(void *key, size_t len, struct ntlm_buf *blob, struct ntlm_buf *session, struct ntlm_buf *master) { struct ntlm_buf sess; int ret; ret = heim_ntlm_v2_base_session(key, len, blob, &sess); if (ret) return ret; ret = heim_ntlm_keyex_wrap(&sess, session, master); heim_ntlm_free_buf(&sess); return ret; } /** * Given a key and encrypted session, unwrap the session key * * @param baseKey the sessionBaseKey * @param encryptedSession encrypted session, type3.session field. * @param session generated session nonce, should be freed with heim_ntlm_free_buf(). * * @return In case of success 0 is return, an errors, a errno in what * went wrong. * * @ingroup ntlm_core */ int heim_ntlm_keyex_unwrap(struct ntlm_buf *baseKey, struct ntlm_buf *encryptedSession, struct ntlm_buf *session) { - EVP_CIPHER_CTX c; + EVP_CIPHER_CTX *c; memset(session, 0, sizeof(*session)); if (baseKey->length != MD4_DIGEST_LENGTH) return HNTLM_ERR_INVALID_LENGTH; session->length = MD4_DIGEST_LENGTH; session->data = malloc(session->length); if (session->data == NULL) { session->length = 0; return ENOMEM; } - EVP_CIPHER_CTX_init(&c); + c = EVP_CIPHER_CTX_new(); + if (c == NULL) { + heim_ntlm_free_buf(session); + return ENOMEM; + } - if (EVP_CipherInit_ex(&c, EVP_rc4(), NULL, baseKey->data, NULL, 0) != 1) { - EVP_CIPHER_CTX_cleanup(&c); + if (EVP_CipherInit_ex(c, EVP_rc4(), NULL, baseKey->data, NULL, 0) != 1) { + EVP_CIPHER_CTX_free(c); heim_ntlm_free_buf(session); return HNTLM_ERR_CRYPTO; } - EVP_Cipher(&c, session->data, encryptedSession->data, session->length); - EVP_CIPHER_CTX_cleanup(&c); + EVP_Cipher(c, session->data, encryptedSession->data, session->length); + EVP_CIPHER_CTX_free(c); return 0; } /** * Generates an NTLMv2 session key. * * @param key the ntlm key * @param len length of key * @param username name of the user, as sent in the message, assumed to be in UTF8. * @param target the name of the target, assumed to be in UTF8. * @param ntlmv2 the ntlmv2 session key * * @return 0 on success, or an error code on failure. * * @ingroup ntlm_core */ int heim_ntlm_ntlmv2_key(const void *key, size_t len, const char *username, const char *target, unsigned char ntlmv2[16]) { int ret; unsigned int hmaclen; - HMAC_CTX c; + HMAC_CTX *c; - HMAC_CTX_init(&c); - HMAC_Init_ex(&c, key, len, EVP_md5(), NULL); + c = HMAC_CTX_new(); + if (c == NULL) + return ENOMEM; + HMAC_Init_ex(c, key, len, EVP_md5(), NULL); { struct ntlm_buf buf; /* uppercase username and turn it into ucs2-le */ ret = ascii2ucs2le(username, 1, &buf); if (ret) goto out; - HMAC_Update(&c, buf.data, buf.length); + HMAC_Update(c, buf.data, buf.length); free(buf.data); /* uppercase target and turn into ucs2-le */ ret = ascii2ucs2le(target, 1, &buf); if (ret) goto out; - HMAC_Update(&c, buf.data, buf.length); + HMAC_Update(c, buf.data, buf.length); free(buf.data); } - HMAC_Final(&c, ntlmv2, &hmaclen); + HMAC_Final(c, ntlmv2, &hmaclen); out: - HMAC_CTX_cleanup(&c); + HMAC_CTX_free(c); return ret; } /* * */ #define NTTIME_EPOCH 0x019DB1DED53E8000LL static uint64_t unix2nttime(time_t unix_time) { long long wt; wt = unix_time * (uint64_t)10000000 + (uint64_t)NTTIME_EPOCH; return wt; } static time_t nt2unixtime(uint64_t t) { t = ((t - (uint64_t)NTTIME_EPOCH) / (uint64_t)10000000); if (t > (((uint64_t)(time_t)(~(uint64_t)0)) >> 1)) return 0; return (time_t)t; } /** * Calculate LMv2 response * * @param key the ntlm key * @param len length of key * @param username name of the user, as sent in the message, assumed to be in UTF8. * @param target the name of the target, assumed to be in UTF8. * @param serverchallenge challenge as sent by the server in the type2 message. * @param ntlmv2 calculated session key * @param answer ntlm response answer, should be freed with heim_ntlm_free_buf(). * * @return In case of success 0 is return, an errors, a errno in what * went wrong. * * @ingroup ntlm_core */ int heim_ntlm_calculate_lm2(const void *key, size_t len, const char *username, const char *target, const unsigned char serverchallenge[8], unsigned char ntlmv2[16], struct ntlm_buf *answer) { unsigned char clientchallenge[8]; + int ret; if (RAND_bytes(clientchallenge, sizeof(clientchallenge)) != 1) return HNTLM_ERR_RAND; /* calculate ntlmv2 key */ heim_ntlm_ntlmv2_key(key, len, username, target, ntlmv2); answer->data = malloc(24); if (answer->data == NULL) return ENOMEM; answer->length = 24; - heim_ntlm_derive_ntlm2_sess(ntlmv2, clientchallenge, 8, + ret = heim_ntlm_derive_ntlm2_sess(ntlmv2, clientchallenge, 8, serverchallenge, answer->data); + if (ret) + return ret; memcpy(((uint8_t *)answer->data) + 16, clientchallenge, 8); return 0; } /** * Calculate NTLMv2 response * * @param key the ntlm key * @param len length of key * @param username name of the user, as sent in the message, assumed to be in UTF8. * @param target the name of the target, assumed to be in UTF8. * @param serverchallenge challenge as sent by the server in the type2 message. * @param infotarget infotarget as sent by the server in the type2 message. * @param ntlmv2 calculated session key * @param answer ntlm response answer, should be freed with heim_ntlm_free_buf(). * * @return In case of success 0 is return, an errors, a errno in what * went wrong. * * @ingroup ntlm_core */ int heim_ntlm_calculate_ntlm2(const void *key, size_t len, const char *username, const char *target, const unsigned char serverchallenge[8], const struct ntlm_buf *infotarget, unsigned char ntlmv2[16], struct ntlm_buf *answer) { krb5_error_code ret; krb5_data data; unsigned char ntlmv2answer[16]; krb5_storage *sp; unsigned char clientchallenge[8]; uint64_t t; + int code; t = unix2nttime(time(NULL)); if (RAND_bytes(clientchallenge, sizeof(clientchallenge)) != 1) return HNTLM_ERR_RAND; /* calculate ntlmv2 key */ heim_ntlm_ntlmv2_key(key, len, username, target, ntlmv2); /* calculate and build ntlmv2 answer */ sp = krb5_storage_emem(); if (sp == NULL) return ENOMEM; krb5_storage_set_flags(sp, KRB5_STORAGE_BYTEORDER_LE); CHECK(krb5_store_uint32(sp, 0x00000101), 0); CHECK(krb5_store_uint32(sp, 0), 0); /* timestamp le 64 bit ts */ CHECK(krb5_store_uint32(sp, t & 0xffffffff), 0); CHECK(krb5_store_uint32(sp, t >> 32), 0); CHECK(krb5_storage_write(sp, clientchallenge, 8), 8); CHECK(krb5_store_uint32(sp, 0), 0); /* unknown but zero will work */ CHECK(krb5_storage_write(sp, infotarget->data, infotarget->length), infotarget->length); CHECK(krb5_store_uint32(sp, 0), 0); /* unknown but zero will work */ CHECK(krb5_storage_to_data(sp, &data), 0); krb5_storage_free(sp); sp = NULL; - heim_ntlm_derive_ntlm2_sess(ntlmv2, data.data, data.length, serverchallenge, ntlmv2answer); + code = heim_ntlm_derive_ntlm2_sess(ntlmv2, data.data, data.length, serverchallenge, ntlmv2answer); + if (code) { + krb5_data_free(&data); + return code; + } sp = krb5_storage_emem(); if (sp == NULL) { krb5_data_free(&data); return ENOMEM; } CHECK(krb5_storage_write(sp, ntlmv2answer, 16), 16); CHECK(krb5_storage_write(sp, data.data, data.length), data.length); krb5_data_free(&data); CHECK(krb5_storage_to_data(sp, &data), 0); krb5_storage_free(sp); sp = NULL; answer->data = data.data; answer->length = data.length; return 0; out: if (sp) krb5_storage_free(sp); return ret; } static const int authtimediff = 3600 * 2; /* 2 hours */ /** * Verify NTLMv2 response. * * @param key the ntlm key * @param len length of key * @param username name of the user, as sent in the message, assumed to be in UTF8. * @param target the name of the target, assumed to be in UTF8. * @param now the time now (0 if the library should pick it up itself) * @param serverchallenge challenge as sent by the server in the type2 message. * @param answer ntlm response answer, should be freed with heim_ntlm_free_buf(). * @param infotarget infotarget as sent by the server in the type2 message. * @param ntlmv2 calculated session key * * @return In case of success 0 is return, an errors, a errno in what * went wrong. * * @ingroup ntlm_core */ int heim_ntlm_verify_ntlm2(const void *key, size_t len, const char *username, const char *target, time_t now, const unsigned char serverchallenge[8], const struct ntlm_buf *answer, struct ntlm_buf *infotarget, unsigned char ntlmv2[16]) { krb5_error_code ret; unsigned char clientanswer[16]; unsigned char clientnonce[8]; unsigned char serveranswer[16]; krb5_storage *sp; time_t authtime; uint32_t temp; uint64_t t; + int code; infotarget->length = 0; infotarget->data = NULL; if (answer->length < 16) return HNTLM_ERR_INVALID_LENGTH; if (now == 0) now = time(NULL); /* calculate ntlmv2 key */ heim_ntlm_ntlmv2_key(key, len, username, target, ntlmv2); /* calculate and build ntlmv2 answer */ sp = krb5_storage_from_readonly_mem(answer->data, answer->length); if (sp == NULL) return ENOMEM; krb5_storage_set_flags(sp, KRB5_STORAGE_BYTEORDER_LE); CHECK(krb5_storage_read(sp, clientanswer, 16), 16); CHECK(krb5_ret_uint32(sp, &temp), 0); CHECK(temp, 0x00000101); CHECK(krb5_ret_uint32(sp, &temp), 0); CHECK(temp, 0); /* timestamp le 64 bit ts */ CHECK(krb5_ret_uint32(sp, &temp), 0); t = temp; CHECK(krb5_ret_uint32(sp, &temp), 0); t |= ((uint64_t)temp)<< 32; authtime = nt2unixtime(t); if (abs((int)(authtime - now)) > authtimediff) { ret = HNTLM_ERR_TIME_SKEW; goto out; } /* client challenge */ CHECK(krb5_storage_read(sp, clientnonce, 8), 8); CHECK(krb5_ret_uint32(sp, &temp), 0); /* unknown */ /* should really unparse the infotarget, but lets pick up everything */ infotarget->length = answer->length - krb5_storage_seek(sp, 0, SEEK_CUR); infotarget->data = malloc(infotarget->length); if (infotarget->data == NULL) { ret = ENOMEM; goto out; } CHECK(krb5_storage_read(sp, infotarget->data, infotarget->length), infotarget->length); /* XXX remove the unknown ?? */ krb5_storage_free(sp); sp = NULL; if (answer->length < 16) { ret = HNTLM_ERR_INVALID_LENGTH; goto out; } - heim_ntlm_derive_ntlm2_sess(ntlmv2, + ret = heim_ntlm_derive_ntlm2_sess(ntlmv2, ((unsigned char *)answer->data) + 16, answer->length - 16, serverchallenge, serveranswer); + if (ret) + goto out; if (memcmp(serveranswer, clientanswer, 16) != 0) { heim_ntlm_free_buf(infotarget); return HNTLM_ERR_AUTH; } return 0; out: heim_ntlm_free_buf(infotarget); if (sp) krb5_storage_free(sp); return ret; } /* * Calculate the NTLM2 Session Response * * @param clnt_nonce client nonce * @param svr_chal server challage * @param ntlm2_hash ntlm hash * @param lm The LM response, should be freed with heim_ntlm_free_buf(). * @param ntlm The NTLM response, should be freed with heim_ntlm_free_buf(). * * @return In case of success 0 is return, an errors, a errno in what * went wrong. * * @ingroup ntlm_core */ int heim_ntlm_calculate_ntlm2_sess(const unsigned char clnt_nonce[8], const unsigned char svr_chal[8], const unsigned char ntlm_hash[16], struct ntlm_buf *lm, struct ntlm_buf *ntlm) { unsigned char ntlm2_sess_hash[8]; unsigned char res[21], *resp; int code; code = heim_ntlm_calculate_ntlm2_sess_hash(clnt_nonce, svr_chal, ntlm2_sess_hash); if (code) { return code; } lm->data = malloc(24); if (lm->data == NULL) { return ENOMEM; } lm->length = 24; ntlm->data = malloc(24); if (ntlm->data == NULL) { free(lm->data); lm->data = NULL; return ENOMEM; } ntlm->length = 24; /* first setup the lm resp */ memset(lm->data, 0, 24); memcpy(lm->data, clnt_nonce, 8); memset(res, 0, sizeof(res)); memcpy(res, ntlm_hash, 16); resp = ntlm->data; - splitandenc(&res[0], ntlm2_sess_hash, resp + 0); - splitandenc(&res[7], ntlm2_sess_hash, resp + 8); - splitandenc(&res[14], ntlm2_sess_hash, resp + 16); + code = splitandenc(&res[0], ntlm2_sess_hash, resp + 0); + if (code) + goto out; + code = splitandenc(&res[7], ntlm2_sess_hash, resp + 8); + if (code) + goto out; + code = splitandenc(&res[14], ntlm2_sess_hash, resp + 16); + if (code) + goto out; return 0; + +out: + heim_ntlm_free_buf(ntlm); + heim_ntlm_free_buf(lm); + return code; } /* * Calculate the NTLM2 Session "Verifier" * * @param clnt_nonce client nonce * @param svr_chal server challage * @param hash The NTLM session verifier * * @return In case of success 0 is return, an errors, a errno in what * went wrong. * * @ingroup ntlm_core */ int heim_ntlm_calculate_ntlm2_sess_hash(const unsigned char clnt_nonce[8], const unsigned char svr_chal[8], unsigned char verifier[8]) { unsigned char ntlm2_sess_hash[MD5_DIGEST_LENGTH]; EVP_MD_CTX *m; m = EVP_MD_CTX_create(); if (m == NULL) return ENOMEM; EVP_DigestInit_ex(m, EVP_md5(), NULL); EVP_DigestUpdate(m, svr_chal, 8); /* session nonce part 1 */ EVP_DigestUpdate(m, clnt_nonce, 8); /* session nonce part 2 */ EVP_DigestFinal_ex(m, ntlm2_sess_hash, NULL); /* will only use first 8 bytes */ EVP_MD_CTX_destroy(m); memcpy(verifier, ntlm2_sess_hash, 8); return 0; } /* * Derive a NTLM2 session key * * @param sessionkey session key from domain controller * @param clnt_nonce client nonce * @param svr_chal server challenge * @param derivedkey salted session key * * @return In case of success 0 is return, an errors, a errno in what * went wrong. * * @ingroup ntlm_core */ -void +int heim_ntlm_derive_ntlm2_sess(const unsigned char sessionkey[16], const unsigned char *clnt_nonce, size_t clnt_nonce_length, const unsigned char svr_chal[8], unsigned char derivedkey[16]) { unsigned int hmaclen; - HMAC_CTX c; + HMAC_CTX *c; /* HMAC(Ksession, serverchallenge || clientchallenge) */ - HMAC_CTX_init(&c); - HMAC_Init_ex(&c, sessionkey, 16, EVP_md5(), NULL); - HMAC_Update(&c, svr_chal, 8); - HMAC_Update(&c, clnt_nonce, clnt_nonce_length); - HMAC_Final(&c, derivedkey, &hmaclen); - HMAC_CTX_cleanup(&c); + c = HMAC_CTX_new(); + if (c == NULL) + return ENOMEM; + HMAC_Init_ex(c, sessionkey, 16, EVP_md5(), NULL); + HMAC_Update(c, svr_chal, 8); + HMAC_Update(c, clnt_nonce, clnt_nonce_length); + HMAC_Final(c, derivedkey, &hmaclen); + HMAC_CTX_free(c); + return 0; } Index: projects/openssl111/crypto/heimdal/lib/roken/snprintf.c =================================================================== --- projects/openssl111/crypto/heimdal/lib/roken/snprintf.c (revision 339197) +++ projects/openssl111/crypto/heimdal/lib/roken/snprintf.c (revision 339198) @@ -1,696 +1,696 @@ /* * Copyright (c) 1995-2003 Kungliga Tekniska Högskolan * (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 #include #include #include #include #include #include "roken.h" #include enum format_flags { minus_flag = 1, plus_flag = 2, space_flag = 4, alternate_flag = 8, zero_flag = 16 }; /* * Common state */ struct snprintf_state { unsigned char *str; unsigned char *s; unsigned char *theend; size_t sz; size_t max_sz; void (*append_char)(struct snprintf_state *, unsigned char); /* XXX - methods */ }; #if !defined(HAVE_VSNPRINTF) || defined(TEST_SNPRINTF) static int sn_reserve (struct snprintf_state *state, size_t n) { return state->s + n > state->theend; } static void sn_append_char (struct snprintf_state *state, unsigned char c) { if (!sn_reserve (state, 1)) *state->s++ = c; } #endif static int as_reserve (struct snprintf_state *state, size_t n) { if (state->s + n > state->theend) { int off = state->s - state->str; unsigned char *tmp; if (state->max_sz && state->sz >= state->max_sz) return 1; state->sz = max(state->sz * 2, state->sz + n); if (state->max_sz) state->sz = min(state->sz, state->max_sz); tmp = realloc (state->str, state->sz); if (tmp == NULL) return 1; state->str = tmp; state->s = state->str + off; state->theend = state->str + state->sz - 1; } return 0; } static void as_append_char (struct snprintf_state *state, unsigned char c) { if(!as_reserve (state, 1)) *state->s++ = c; } /* longest integer types */ #ifdef HAVE_LONG_LONG typedef unsigned long long u_longest; typedef long long longest; #else typedef unsigned long u_longest; typedef long longest; #endif static size_t pad(struct snprintf_state *state, int width, char c) { size_t len = 0; while(width-- > 0){ (*state->append_char)(state, c); ++len; } return len; } /* return true if we should use alternatve hex form */ static int use_alternative (int flags, u_longest num, unsigned base) { return (flags & alternate_flag) && base == 16 && num != 0; } static int append_number(struct snprintf_state *state, u_longest num, unsigned base, const char *rep, int width, int prec, int flags, int minusp) { int len = 0; u_longest n = num; char nstr[64]; /* enough for <192 bit octal integers */ int nstart, nlen; char signchar; /* given precision, ignore zero flag */ if(prec != -1) flags &= ~zero_flag; else prec = 1; /* format number as string */ nstart = sizeof(nstr); nlen = 0; nstr[--nstart] = '\0'; do { assert(nstart > 0); nstr[--nstart] = rep[n % base]; ++nlen; n /= base; } while(n); /* zero value with zero precision should produce no digits */ if(prec == 0 && num == 0) { nlen--; nstart++; } /* figure out what char to use for sign */ if(minusp) signchar = '-'; else if((flags & plus_flag)) signchar = '+'; else if((flags & space_flag)) signchar = ' '; else signchar = '\0'; if((flags & alternate_flag) && base == 8) { /* if necessary, increase the precision to make first digit a zero */ /* XXX C99 claims (regarding # and %o) that "if the value and precision are both 0, a single 0 is printed", but there is no such wording for %x. This would mean that %#.o would output "0", but %#.x "". This does not make sense, and is also not what other printf implementations are doing. */ if(prec <= nlen && nstr[nstart] != '0' && nstr[nstart] != '\0') prec = nlen + 1; } /* possible formats: pad | sign | alt | zero | digits sign | alt | zero | digits | pad minus_flag sign | alt | zero | digits zero_flag */ /* if not right justifying or padding with zeros, we need to compute the length of the rest of the string, and then pad with spaces */ if(!(flags & (minus_flag | zero_flag))) { if(prec > nlen) width -= prec; else width -= nlen; if(use_alternative(flags, num, base)) width -= 2; if(signchar != '\0') width--; /* pad to width */ len += pad(state, width, ' '); } if(signchar != '\0') { (*state->append_char)(state, signchar); ++len; } if(use_alternative(flags, num, base)) { (*state->append_char)(state, '0'); (*state->append_char)(state, rep[10] + 23); /* XXX */ len += 2; } if(flags & zero_flag) { /* pad to width with zeros */ if(prec - nlen > width - len - nlen) len += pad(state, prec - nlen, '0'); else len += pad(state, width - len - nlen, '0'); } else /* pad to prec with zeros */ len += pad(state, prec - nlen, '0'); while(nstr[nstart] != '\0') { (*state->append_char)(state, nstr[nstart++]); ++len; } if(flags & minus_flag) len += pad(state, width - len, ' '); return len; } /* * return length */ static size_t append_string (struct snprintf_state *state, const unsigned char *arg, int width, int prec, int flags) { size_t len = 0; if(arg == NULL) arg = (const unsigned char*)"(null)"; if(prec != -1) width -= prec; else width -= strlen((const char *)arg); if(!(flags & minus_flag)) len += pad(state, width, ' '); if (prec != -1) { while (*arg && prec--) { (*state->append_char) (state, *arg++); ++len; } } else { while (*arg) { (*state->append_char) (state, *arg++); ++len; } } if(flags & minus_flag) len += pad(state, width, ' '); return len; } static int append_char(struct snprintf_state *state, unsigned char arg, int width, int flags) { int len = 0; while(!(flags & minus_flag) && --width > 0) { (*state->append_char) (state, ' ') ; ++len; } (*state->append_char) (state, arg); ++len; while((flags & minus_flag) && --width > 0) { (*state->append_char) (state, ' '); ++len; } return 0; } /* * This can't be made into a function... */ #ifdef HAVE_LONG_LONG #define PARSE_INT_FORMAT(res, arg, unsig) \ if (long_long_flag) \ res = (unsig long long)va_arg(arg, unsig long long); \ else if (long_flag) \ res = (unsig long)va_arg(arg, unsig long); \ else if (size_t_flag) \ res = (unsig long)va_arg(arg, size_t); \ else if (short_flag) \ res = (unsig short)va_arg(arg, unsig int); \ else \ res = (unsig int)va_arg(arg, unsig int) #else #define PARSE_INT_FORMAT(res, arg, unsig) \ if (long_flag) \ res = (unsig long)va_arg(arg, unsig long); \ else if (size_t_flag) \ res = (unsig long)va_arg(arg, size_t); \ else if (short_flag) \ res = (unsig short)va_arg(arg, unsig int); \ else \ res = (unsig int)va_arg(arg, unsig int) #endif /* * zyxprintf - return length, as snprintf */ static size_t xyzprintf (struct snprintf_state *state, const char *char_format, va_list ap) { const unsigned char *format = (const unsigned char *)char_format; unsigned char c; size_t len = 0; while((c = *format++)) { if (c == '%') { int flags = 0; int width = 0; int prec = -1; int size_t_flag = 0; int long_long_flag = 0; int long_flag = 0; int short_flag = 0; /* flags */ while((c = *format++)){ if(c == '-') flags |= minus_flag; else if(c == '+') flags |= plus_flag; else if(c == ' ') flags |= space_flag; else if(c == '#') flags |= alternate_flag; else if(c == '0') flags |= zero_flag; else if(c == '\'') ; /* just ignore */ else break; } if((flags & space_flag) && (flags & plus_flag)) flags ^= space_flag; if((flags & minus_flag) && (flags & zero_flag)) flags ^= zero_flag; /* width */ if (isdigit(c)) do { width = width * 10 + c - '0'; c = *format++; } while(isdigit(c)); else if(c == '*') { width = va_arg(ap, int); c = *format++; } /* precision */ if (c == '.') { prec = 0; c = *format++; if (isdigit(c)) do { prec = prec * 10 + c - '0'; c = *format++; } while(isdigit(c)); else if (c == '*') { prec = va_arg(ap, int); c = *format++; } } /* size */ if (c == 'h') { short_flag = 1; c = *format++; } else if (c == 'z') { size_t_flag = 1; c = *format++; } else if (c == 'l') { long_flag = 1; c = *format++; if (c == 'l') { long_long_flag = 1; c = *format++; } } if(c != 'd' && c != 'i') flags &= ~(plus_flag | space_flag); switch (c) { case 'c' : append_char(state, va_arg(ap, int), width, flags); ++len; break; case 's' : len += append_string(state, va_arg(ap, unsigned char*), width, prec, flags); break; case 'd' : case 'i' : { longest arg; u_longest num; int minusp = 0; PARSE_INT_FORMAT(arg, ap, signed); if (arg < 0) { minusp = 1; num = -arg; } else num = arg; len += append_number (state, num, 10, "0123456789", width, prec, flags, minusp); break; } case 'u' : { u_longest arg; PARSE_INT_FORMAT(arg, ap, unsigned); len += append_number (state, arg, 10, "0123456789", width, prec, flags, 0); break; } case 'o' : { u_longest arg; PARSE_INT_FORMAT(arg, ap, unsigned); len += append_number (state, arg, 010, "01234567", width, prec, flags, 0); break; } case 'x' : { u_longest arg; PARSE_INT_FORMAT(arg, ap, unsigned); len += append_number (state, arg, 0x10, "0123456789abcdef", width, prec, flags, 0); break; } case 'X' :{ u_longest arg; PARSE_INT_FORMAT(arg, ap, unsigned); len += append_number (state, arg, 0x10, "0123456789ABCDEF", width, prec, flags, 0); break; } case 'p' : { - u_longest arg = (u_longest)va_arg(ap, void*); + u_longest arg = (uintptr_t)va_arg(ap, void*); len += append_number (state, arg, 0x10, "0123456789ABCDEF", width, prec, flags, 0); break; } case 'n' : { int *arg = va_arg(ap, int*); *arg = state->s - state->str; break; } case '\0' : --format; /* FALLTHROUGH */ case '%' : (*state->append_char)(state, c); ++len; break; default : (*state->append_char)(state, '%'); (*state->append_char)(state, c); len += 2; break; } } else { (*state->append_char) (state, c); ++len; } } return len; } #if !defined(HAVE_SNPRINTF) || defined(TEST_SNPRINTF) ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL rk_snprintf (char *str, size_t sz, const char *format, ...) { va_list args; int ret; va_start(args, format); ret = vsnprintf (str, sz, format, args); va_end(args); #ifdef PARANOIA { int ret2; char *tmp; tmp = malloc (sz); if (tmp == NULL) abort (); va_start(args, format); ret2 = vsprintf (tmp, format, args); va_end(args); if (ret != ret2 || strcmp(str, tmp)) abort (); free (tmp); } #endif return ret; } #endif #if !defined(HAVE_ASPRINTF) || defined(TEST_SNPRINTF) ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL rk_asprintf (char **ret, const char *format, ...) { va_list args; int val; va_start(args, format); val = vasprintf (ret, format, args); va_end(args); #ifdef PARANOIA { int ret2; char *tmp; tmp = malloc (val + 1); if (tmp == NULL) abort (); va_start(args, format); ret2 = vsprintf (tmp, format, args); va_end(args); if (val != ret2 || strcmp(*ret, tmp)) abort (); free (tmp); } #endif return val; } #endif #if !defined(HAVE_ASNPRINTF) || defined(TEST_SNPRINTF) ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL rk_asnprintf (char **ret, size_t max_sz, const char *format, ...) { va_list args; int val; va_start(args, format); val = vasnprintf (ret, max_sz, format, args); #ifdef PARANOIA { int ret2; char *tmp; tmp = malloc (val + 1); if (tmp == NULL) abort (); ret2 = vsprintf (tmp, format, args); if (val != ret2 || strcmp(*ret, tmp)) abort (); free (tmp); } #endif va_end(args); return val; } #endif #if !defined(HAVE_VASPRINTF) || defined(TEST_SNPRINTF) ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL rk_vasprintf (char **ret, const char *format, va_list args) { return vasnprintf (ret, 0, format, args); } #endif #if !defined(HAVE_VASNPRINTF) || defined(TEST_SNPRINTF) ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL rk_vasnprintf (char **ret, size_t max_sz, const char *format, va_list args) { size_t st; struct snprintf_state state; state.max_sz = max_sz; state.sz = 1; state.str = malloc(state.sz); if (state.str == NULL) { *ret = NULL; return -1; } state.s = state.str; state.theend = state.s + state.sz - 1; state.append_char = as_append_char; st = xyzprintf (&state, format, args); if (st > state.sz) { free (state.str); *ret = NULL; return -1; } else { char *tmp; *state.s = '\0'; tmp = realloc (state.str, st+1); if (tmp == NULL) { free (state.str); *ret = NULL; return -1; } *ret = tmp; return st; } } #endif #if !defined(HAVE_VSNPRINTF) || defined(TEST_SNPRINTF) ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL rk_vsnprintf (char *str, size_t sz, const char *format, va_list args) { struct snprintf_state state; int ret; unsigned char *ustr = (unsigned char *)str; state.max_sz = 0; state.sz = sz; state.str = ustr; state.s = ustr; state.theend = ustr + sz - (sz > 0); state.append_char = sn_append_char; ret = xyzprintf (&state, format, args); if (state.s != NULL && sz != 0) *state.s = '\0'; return ret; } #endif Index: projects/openssl111/kerberos5/Makefile.inc =================================================================== --- projects/openssl111/kerberos5/Makefile.inc (revision 339197) +++ projects/openssl111/kerberos5/Makefile.inc (revision 339198) @@ -1,53 +1,57 @@ # $FreeBSD$ .include NO_LINT= KRB5DIR= ${SRCTOP}/crypto/heimdal CFLAGS+= -DHAVE_CONFIG_H -I${.CURDIR:H:H}/include +WARNS?= 1 +CWARNFLAGS.clang+= -Wno-error=absolute-value +CWARNFLAGS+= -Wno-error=deprecated-declarations + .if ${MK_OPENLDAP} != "no" && !defined(COMPAT_32BIT) OPENLDAPBASE?= /usr/local LDAPLDADD= -lldap -llber LDAPDPADD= ${LDAPLDADD:C;^-l(.*)$;${OPENLDAPBASE}/lib/lib\1.a;} LDAPCFLAGS= -I${OPENLDAPBASE}/include -DOPENLDAP=1 -DLDAP_DEPRECATED=1 LDAPLDFLAGS= -L${OPENLDAPBASE}/lib -rpath ${OPENLDAPBASE}/lib .endif .if defined(SRCS) ETSRCS= \ ${KRB5DIR}/lib/asn1/asn1_err.et \ ${KRB5DIR}/lib/hdb/hdb_err.et \ ${KRB5DIR}/lib/kadm5/kadm5_err.et \ ${KRB5DIR}/lib/krb5/heim_err.et \ ${KRB5DIR}/lib/krb5/k524_err.et \ ${KRB5DIR}/lib/krb5/krb5_err.et \ ${KRB5DIR}/lib/krb5/krb_err.et \ ${KRB5DIR}/lib/hx509/hx509_err.et \ ${KRB5DIR}/lib/wind/wind_err.et \ ${KRB5DIR}/lib/ntlm/ntlm_err.et .if ${MK_GSSAPI} != "no" ETSRCS+= ${KRB5DIR}/lib/gssapi/krb5/gkrb5_err.et .endif .for ET in ${ETSRCS} .for _ET in ${ET:T:R} .if ${SRCS:M${_ET}.[ch]} != "" .ORDER: ${_ET}.h ${_ET}.c ${_ET}.c: .NOMETA ${_ET}.h ${_ET}.c: ${ET} compile_et ${.ALLSRC} CLEANFILES+= ${_ET}.h ${_ET}.c .endif .endfor .endfor .endif # defined(SRCS) ASN1_COMPILE= asn1_compile MAKE_ROKEN= make-roken SLC= slc Index: projects/openssl111/kerberos5/include/crypto-headers.h =================================================================== --- projects/openssl111/kerberos5/include/crypto-headers.h (revision 339197) +++ projects/openssl111/kerberos5/include/crypto-headers.h (revision 339198) @@ -1,26 +1,22 @@ /* $FreeBSD$ */ #ifndef __crypto_headers_h__ #define __crypto_headers_h__ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include -#ifndef BN_is_negative -#define BN_set_negative(bn, flag) ((bn)->neg=(flag)?1:0) -#define BN_is_negative(bn) ((bn)->neg != 0) -#endif #endif /* __crypto_headers_h__ */