diff --git a/crypto/heimdal/admin/change.c b/crypto/heimdal/admin/change.c index c390441f23dc..1ddbded6bf77 100644 --- a/crypto/heimdal/admin/change.c +++ b/crypto/heimdal/admin/change.c @@ -1,252 +1,251 @@ /* * Copyright (c) 1997-2005 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 "ktutil_locl.h" RCSID("$Id$"); static krb5_error_code change_entry (krb5_keytab keytab, krb5_principal principal, krb5_kvno kvno, const char *realm, const char *admin_server, int server_port) { krb5_error_code ret; kadm5_config_params conf; void *kadm_handle; char *client_name; krb5_keyblock *keys; int num_keys; int i; ret = krb5_unparse_name (context, principal, &client_name); if (ret) { krb5_warn (context, ret, "krb5_unparse_name"); return ret; } memset (&conf, 0, sizeof(conf)); if(realm == NULL) realm = krb5_principal_get_realm(context, principal); conf.realm = strdup(realm); if (conf.realm == NULL) { free (client_name); krb5_set_error_message(context, ENOMEM, "malloc failed"); return ENOMEM; } conf.mask |= KADM5_CONFIG_REALM; if (admin_server) { conf.admin_server = strdup(admin_server); if (conf.admin_server == NULL) { free(client_name); free(conf.realm); krb5_set_error_message(context, ENOMEM, "malloc failed"); return ENOMEM; } conf.mask |= KADM5_CONFIG_ADMIN_SERVER; } if (server_port) { conf.kadmind_port = htons(server_port); conf.mask |= KADM5_CONFIG_KADMIND_PORT; } ret = kadm5_init_with_skey_ctx (context, client_name, keytab_string, KADM5_ADMIN_SERVICE, &conf, 0, 0, &kadm_handle); free(conf.admin_server); free(conf.realm); if (ret) { krb5_warn (context, ret, "kadm5_c_init_with_skey_ctx: %s:", client_name); free (client_name); return ret; } ret = kadm5_randkey_principal (kadm_handle, principal, &keys, &num_keys); kadm5_destroy (kadm_handle); if (ret) { krb5_warn(context, ret, "kadm5_randkey_principal: %s:", client_name); free (client_name); return ret; } free (client_name); for (i = 0; i < num_keys; ++i) { krb5_keytab_entry new_entry; new_entry.principal = principal; new_entry.timestamp = time (NULL); new_entry.vno = kvno + 1; new_entry.keyblock = keys[i]; ret = krb5_kt_add_entry (context, keytab, &new_entry); if (ret) krb5_warn (context, ret, "krb5_kt_add_entry"); krb5_free_keyblock_contents (context, &keys[i]); } return ret; } /* * loop over all the entries in the keytab (or those given) and change * their keys, writing the new keys */ struct change_set { krb5_principal principal; krb5_kvno kvno; }; int kt_change (struct change_options *opt, int argc, char **argv) { krb5_error_code ret; krb5_keytab keytab; krb5_kt_cursor cursor; krb5_keytab_entry entry; int i, j, max; struct change_set *changeset; int errors = 0; if((keytab = ktutil_open_keytab()) == NULL) return 1; j = 0; max = 0; changeset = NULL; ret = krb5_kt_start_seq_get(context, keytab, &cursor); if(ret){ krb5_warn(context, ret, "%s", keytab_string); goto out; } while((ret = krb5_kt_next_entry(context, keytab, &entry, &cursor)) == 0) { int add = 0; for (i = 0; i < j; ++i) { if (krb5_principal_compare (context, changeset[i].principal, entry.principal)) { if (changeset[i].kvno < entry.vno) changeset[i].kvno = entry.vno; break; } } if (i < j) { krb5_kt_free_entry (context, &entry); continue; } if (argc == 0) { add = 1; } else { for (i = 0; i < argc; ++i) { krb5_principal princ; ret = krb5_parse_name (context, argv[i], &princ); if (ret) { krb5_warn (context, ret, "%s", argv[i]); continue; } if (krb5_principal_compare (context, princ, entry.principal)) add = 1; krb5_free_principal (context, princ); } } if (add) { if (j >= max) { void *tmp; max = max(max * 2, 1); tmp = realloc (changeset, max * sizeof(*changeset)); if (tmp == NULL) { krb5_kt_free_entry (context, &entry); krb5_warnx (context, "realloc: out of memory"); ret = ENOMEM; break; } changeset = tmp; } ret = krb5_copy_principal (context, entry.principal, &changeset[j].principal); if (ret) { krb5_warn (context, ret, "krb5_copy_principal"); krb5_kt_free_entry (context, &entry); break; } changeset[j].kvno = entry.vno; ++j; } krb5_kt_free_entry (context, &entry); } krb5_kt_end_seq_get(context, keytab, &cursor); if (ret == KRB5_KT_END) { - ret = 0; for (i = 0; i < j; i++) { if (verbose_flag) { char *client_name; ret = krb5_unparse_name (context, changeset[i].principal, &client_name); if (ret) { krb5_warn (context, ret, "krb5_unparse_name"); } else { printf("Changing %s kvno %d\n", client_name, changeset[i].kvno); free(client_name); } } ret = change_entry (keytab, changeset[i].principal, changeset[i].kvno, opt->realm_string, opt->admin_server_string, opt->server_port_integer); if (ret != 0) errors = 1; } } else errors = 1; for (i = 0; i < j; i++) krb5_free_principal (context, changeset[i].principal); free (changeset); out: krb5_kt_close(context, keytab); return errors; } diff --git a/crypto/heimdal/appl/gssmask/gssmask.c b/crypto/heimdal/appl/gssmask/gssmask.c index 916837b42de1..6eafc9391f54 100644 --- a/crypto/heimdal/appl/gssmask/gssmask.c +++ b/crypto/heimdal/appl/gssmask/gssmask.c @@ -1,1255 +1,1257 @@ /* * 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 KTH 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 KTH AND ITS 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 KTH OR ITS 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 "common.h" RCSID("$Id$"); /* * */ enum handle_type { handle_context, handle_cred }; struct handle { int32_t idx; enum handle_type type; void *ptr; struct handle *next; }; struct client { krb5_storage *sock; krb5_storage *logging; char *moniker; int32_t nHandle; struct handle *handles; struct sockaddr_storage sa; socklen_t salen; char servername[MAXHOSTNAMELEN]; }; FILE *logfile; static char *targetname; krb5_context context; /* * */ static void logmessage(struct client *c, const char *file, unsigned int lineno, int level, const char *fmt, ...) { char *message; va_list ap; int32_t ackid; va_start(ap, fmt); vasprintf(&message, fmt, ap); va_end(ap); if (logfile) fprintf(logfile, "%s:%u: %d %s\n", file, lineno, level, message); if (c->logging) { if (krb5_store_int32(c->logging, eLogInfo) != 0) errx(1, "krb5_store_int32: log level"); if (krb5_store_string(c->logging, file) != 0) errx(1, "krb5_store_string: filename"); if (krb5_store_int32(c->logging, lineno) != 0) errx(1, "krb5_store_string: filename"); if (krb5_store_string(c->logging, message) != 0) errx(1, "krb5_store_string: message"); if (krb5_ret_int32(c->logging, &ackid) != 0) errx(1, "krb5_ret_int32: ackid"); } free(message); } /* * */ static int32_t add_handle(struct client *c, enum handle_type type, void *data) { struct handle *h; h = ecalloc(1, sizeof(*h)); h->idx = ++c->nHandle; h->type = type; h->ptr = data; h->next = c->handles; c->handles = h; return h->idx; } static void del_handle(struct handle **h, int32_t idx) { OM_uint32 min_stat; if (idx == 0) return; while (*h) { if ((*h)->idx == idx) { struct handle *p = *h; *h = (*h)->next; switch(p->type) { case handle_context: { gss_ctx_id_t c = p->ptr; gss_delete_sec_context(&min_stat, &c, NULL); break; } case handle_cred: { gss_cred_id_t c = p->ptr; gss_release_cred(&min_stat, &c); break; } } free(p); return; } h = &((*h)->next); } errx(1, "tried to delete an unexisting handle"); } static void * find_handle(struct handle *h, int32_t idx, enum handle_type type) { if (idx == 0) return NULL; while (h) { if (h->idx == idx) { if (type == h->type) return h->ptr; errx(1, "monger switched type on handle!"); } h = h->next; } return NULL; } static int32_t convert_gss_to_gsm(OM_uint32 maj_stat) { switch(maj_stat) { case 0: return GSMERR_OK; case GSS_S_CONTINUE_NEEDED: return GSMERR_CONTINUE_NEEDED; case GSS_S_DEFECTIVE_TOKEN: return GSMERR_INVALID_TOKEN; case GSS_S_BAD_MIC: return GSMERR_AP_MODIFIED; default: return GSMERR_ERROR; } } static int32_t convert_krb5_to_gsm(krb5_error_code ret) { switch(ret) { case 0: return GSMERR_OK; default: return GSMERR_ERROR; } } /* * */ static int32_t acquire_cred(struct client *c, krb5_principal principal, krb5_get_init_creds_opt *opt, int32_t *handle) { krb5_error_code ret; krb5_creds cred; krb5_ccache id; gss_cred_id_t gcred; OM_uint32 maj_stat, min_stat; *handle = 0; krb5_get_init_creds_opt_set_forwardable (opt, 1); krb5_get_init_creds_opt_set_renew_life (opt, 3600 * 24 * 30); memset(&cred, 0, sizeof(cred)); ret = krb5_get_init_creds_password (context, &cred, principal, NULL, NULL, NULL, 0, NULL, opt); if (ret) { logmessage(c, __FILE__, __LINE__, 0, "krb5_get_init_creds failed: %d", ret); return convert_krb5_to_gsm(ret); } ret = krb5_cc_new_unique(context, "MEMORY", NULL, &id); if (ret) krb5_err (context, 1, ret, "krb5_cc_initialize"); ret = krb5_cc_initialize (context, id, cred.client); if (ret) krb5_err (context, 1, ret, "krb5_cc_initialize"); ret = krb5_cc_store_cred (context, id, &cred); if (ret) krb5_err (context, 1, ret, "krb5_cc_store_cred"); krb5_free_cred_contents (context, &cred); maj_stat = gss_krb5_import_cred(&min_stat, id, NULL, NULL, &gcred); krb5_cc_close(context, id); if (maj_stat) { logmessage(c, __FILE__, __LINE__, 0, "krb5 import creds failed with: %d", maj_stat); return convert_gss_to_gsm(maj_stat); } *handle = add_handle(c, handle_cred, gcred); return 0; } /* * */ #define HandleOP(h) \ handle##h(enum gssMaggotOp op, struct client *c) /* * */ static int HandleOP(GetVersionInfo) { put32(c, GSSMAGGOTPROTOCOL); errx(1, "GetVersionInfo"); } static int HandleOP(GoodBye) { struct handle *h = c->handles; unsigned int i = 0; while (h) { h = h->next; i++; } if (i) logmessage(c, __FILE__, __LINE__, 0, "Did not toast all resources: %d", i); return 1; } static int HandleOP(InitContext) { OM_uint32 maj_stat, min_stat, ret_flags; int32_t hContext, hCred, flags; krb5_data target_name, in_token; int32_t new_context_id = 0, gsm_error = 0; krb5_data out_token = { 0 , NULL }; gss_ctx_id_t ctx; gss_cred_id_t creds; gss_name_t gss_target_name; gss_buffer_desc input_token, output_token; gss_OID oid = GSS_C_NO_OID; gss_buffer_t input_token_ptr = GSS_C_NO_BUFFER; ret32(c, hContext); ret32(c, hCred); ret32(c, flags); retdata(c, target_name); retdata(c, in_token); logmessage(c, __FILE__, __LINE__, 0, "targetname: <%.*s>", (int)target_name.length, (char *)target_name.data); ctx = find_handle(c->handles, hContext, handle_context); if (ctx == NULL) hContext = 0; creds = find_handle(c->handles, hCred, handle_cred); if (creds == NULL) abort(); input_token.length = target_name.length; input_token.value = target_name.data; maj_stat = gss_import_name(&min_stat, &input_token, GSS_KRB5_NT_PRINCIPAL_NAME, &gss_target_name); if (GSS_ERROR(maj_stat)) { logmessage(c, __FILE__, __LINE__, 0, "import name creds failed with: %d", maj_stat); gsm_error = convert_gss_to_gsm(maj_stat); goto out; } /* oid from flags */ if (in_token.length) { input_token.length = in_token.length; input_token.value = in_token.data; input_token_ptr = &input_token; if (ctx == NULL) krb5_errx(context, 1, "initcreds, context NULL, but not first req"); } else { input_token.length = 0; input_token.value = NULL; if (ctx) krb5_errx(context, 1, "initcreds, context not NULL, but first req"); } if ((flags & GSS_C_DELEG_FLAG) != 0) logmessage(c, __FILE__, __LINE__, 0, "init_sec_context delegating"); if ((flags & GSS_C_DCE_STYLE) != 0) logmessage(c, __FILE__, __LINE__, 0, "init_sec_context dce-style"); maj_stat = gss_init_sec_context(&min_stat, creds, &ctx, gss_target_name, oid, flags & 0x7f, 0, NULL, input_token_ptr, NULL, &output_token, &ret_flags, NULL); if (GSS_ERROR(maj_stat)) { if (hContext != 0) del_handle(&c->handles, hContext); new_context_id = 0; logmessage(c, __FILE__, __LINE__, 0, "gss_init_sec_context returns code: %d/%d", maj_stat, min_stat); } else { if (input_token.length == 0) new_context_id = add_handle(c, handle_context, ctx); else new_context_id = hContext; } gsm_error = convert_gss_to_gsm(maj_stat); if (output_token.length) { out_token.data = output_token.value; out_token.length = output_token.length; } out: logmessage(c, __FILE__, __LINE__, 0, "InitContext return code: %d", gsm_error); put32(c, new_context_id); put32(c, gsm_error); putdata(c, out_token); gss_release_name(&min_stat, &gss_target_name); if (output_token.length) gss_release_buffer(&min_stat, &output_token); krb5_data_free(&in_token); krb5_data_free(&target_name); return 0; } static int HandleOP(AcceptContext) { OM_uint32 maj_stat, min_stat, ret_flags; int32_t hContext, deleg_hcred, flags; krb5_data in_token; int32_t new_context_id = 0, gsm_error = 0; krb5_data out_token = { 0 , NULL }; gss_ctx_id_t ctx; gss_cred_id_t deleg_cred = GSS_C_NO_CREDENTIAL; gss_buffer_desc input_token, output_token; gss_buffer_t input_token_ptr = GSS_C_NO_BUFFER; ret32(c, hContext); ret32(c, flags); retdata(c, in_token); ctx = find_handle(c->handles, hContext, handle_context); if (ctx == NULL) hContext = 0; if (in_token.length) { input_token.length = in_token.length; input_token.value = in_token.data; input_token_ptr = &input_token; } else { input_token.length = 0; input_token.value = NULL; } maj_stat = gss_accept_sec_context(&min_stat, &ctx, GSS_C_NO_CREDENTIAL, &input_token, GSS_C_NO_CHANNEL_BINDINGS, NULL, NULL, &output_token, &ret_flags, NULL, &deleg_cred); if (GSS_ERROR(maj_stat)) { if (hContext != 0) del_handle(&c->handles, hContext); logmessage(c, __FILE__, __LINE__, 0, "gss_accept_sec_context returns code: %d/%d", maj_stat, min_stat); new_context_id = 0; } else { if (hContext == 0) new_context_id = add_handle(c, handle_context, ctx); else new_context_id = hContext; } if (output_token.length) { out_token.data = output_token.value; out_token.length = output_token.length; } if ((ret_flags & GSS_C_DCE_STYLE) != 0) logmessage(c, __FILE__, __LINE__, 0, "accept_sec_context dce-style"); if ((ret_flags & GSS_C_DELEG_FLAG) != 0) { deleg_hcred = add_handle(c, handle_cred, deleg_cred); logmessage(c, __FILE__, __LINE__, 0, "accept_context delegated handle: %d", deleg_hcred); } else { gss_release_cred(&min_stat, &deleg_cred); deleg_hcred = 0; } gsm_error = convert_gss_to_gsm(maj_stat); put32(c, new_context_id); put32(c, gsm_error); putdata(c, out_token); put32(c, deleg_hcred); if (output_token.length) gss_release_buffer(&min_stat, &output_token); krb5_data_free(&in_token); return 0; } static int HandleOP(ToastResource) { int32_t handle; ret32(c, handle); logmessage(c, __FILE__, __LINE__, 0, "toasting %d", handle); del_handle(&c->handles, handle); put32(c, GSMERR_OK); return 0; } static int HandleOP(AcquireCreds) { char *name, *password; int32_t gsm_error, flags, handle = 0; krb5_principal principal = NULL; krb5_get_init_creds_opt *opt = NULL; krb5_error_code ret; retstring(c, name); retstring(c, password); ret32(c, flags); logmessage(c, __FILE__, __LINE__, 0, "username: %s password: %s", name, password); ret = krb5_parse_name(context, name, &principal); if (ret) { gsm_error = convert_krb5_to_gsm(ret); goto out; } ret = krb5_get_init_creds_opt_alloc (context, &opt); if (ret) krb5_err(context, 1, ret, "krb5_get_init_creds_opt_alloc"); krb5_get_init_creds_opt_set_pa_password(context, opt, password, NULL); gsm_error = acquire_cred(c, principal, opt, &handle); out: logmessage(c, __FILE__, __LINE__, 0, "AcquireCreds handle: %d return code: %d", handle, gsm_error); if (opt) krb5_get_init_creds_opt_free (context, opt); if (principal) krb5_free_principal(context, principal); free(name); free(password); put32(c, gsm_error); put32(c, handle); return 0; } static int HandleOP(Sign) { OM_uint32 maj_stat, min_stat; int32_t hContext, flags, seqno; krb5_data token; gss_ctx_id_t ctx; gss_buffer_desc input_token, output_token; ret32(c, hContext); ret32(c, flags); ret32(c, seqno); retdata(c, token); ctx = find_handle(c->handles, hContext, handle_context); if (ctx == NULL) errx(1, "sign: reference to unknown context"); input_token.length = token.length; input_token.value = token.data; maj_stat = gss_get_mic(&min_stat, ctx, 0, &input_token, &output_token); if (maj_stat != GSS_S_COMPLETE) errx(1, "gss_get_mic failed"); krb5_data_free(&token); token.data = output_token.value; token.length = output_token.length; put32(c, 0); /* XXX fix gsm_error */ putdata(c, token); gss_release_buffer(&min_stat, &output_token); return 0; } static int HandleOP(Verify) { OM_uint32 maj_stat, min_stat; int32_t hContext, flags, seqno; krb5_data msg, mic; gss_ctx_id_t ctx; gss_buffer_desc msg_token, mic_token; gss_qop_t qop; ret32(c, hContext); ctx = find_handle(c->handles, hContext, handle_context); if (ctx == NULL) errx(1, "verify: reference to unknown context"); ret32(c, flags); ret32(c, seqno); retdata(c, msg); msg_token.length = msg.length; msg_token.value = msg.data; retdata(c, mic); mic_token.length = mic.length; mic_token.value = mic.data; maj_stat = gss_verify_mic(&min_stat, ctx, &msg_token, &mic_token, &qop); if (maj_stat != GSS_S_COMPLETE) errx(1, "gss_verify_mic failed"); krb5_data_free(&mic); krb5_data_free(&msg); put32(c, 0); /* XXX fix gsm_error */ return 0; } static int HandleOP(GetVersionAndCapabilities) { int32_t cap = HAS_MONIKER; char name[256] = "unknown", *str; if (targetname) cap |= ISSERVER; /* is server */ #ifdef HAVE_UNAME { struct utsname ut; if (uname(&ut) == 0) { snprintf(name, sizeof(name), "%s-%s-%s", ut.sysname, ut.version, ut.machine); } } #endif asprintf(&str, "gssmask %s %s", PACKAGE_STRING, name); put32(c, GSSMAGGOTPROTOCOL); put32(c, cap); putstring(c, str); free(str); return 0; } static int HandleOP(GetTargetName) { if (targetname) putstring(c, targetname); else putstring(c, ""); return 0; } static int HandleOP(SetLoggingSocket) { int32_t portnum; int fd, ret; ret32(c, portnum); logmessage(c, __FILE__, __LINE__, 0, "logging port on peer is: %d", (int)portnum); socket_set_port((struct sockaddr *)(&c->sa), htons(portnum)); fd = socket(((struct sockaddr *)&c->sa)->sa_family, SOCK_STREAM, 0); if (fd < 0) return 0; ret = connect(fd, (struct sockaddr *)&c->sa, c->salen); if (ret < 0) { logmessage(c, __FILE__, __LINE__, 0, "failed connect to log port: %s", strerror(errno)); close(fd); return 0; } if (c->logging) krb5_storage_free(c->logging); c->logging = krb5_storage_from_fd(fd); close(fd); krb5_store_int32(c->logging, eLogSetMoniker); store_string(c->logging, c->moniker); logmessage(c, __FILE__, __LINE__, 0, "logging turned on"); return 0; } static int HandleOP(ChangePassword) { errx(1, "ChangePassword"); } static int HandleOP(SetPasswordSelf) { errx(1, "SetPasswordSelf"); } static int HandleOP(Wrap) { OM_uint32 maj_stat, min_stat; int32_t hContext, flags, seqno; krb5_data token; gss_ctx_id_t ctx; gss_buffer_desc input_token, output_token; int conf_state; ret32(c, hContext); ret32(c, flags); ret32(c, seqno); retdata(c, token); ctx = find_handle(c->handles, hContext, handle_context); if (ctx == NULL) errx(1, "wrap: reference to unknown context"); input_token.length = token.length; input_token.value = token.data; maj_stat = gss_wrap(&min_stat, ctx, flags, 0, &input_token, &conf_state, &output_token); if (maj_stat != GSS_S_COMPLETE) errx(1, "gss_wrap failed"); krb5_data_free(&token); token.data = output_token.value; token.length = output_token.length; put32(c, 0); /* XXX fix gsm_error */ putdata(c, token); gss_release_buffer(&min_stat, &output_token); return 0; } static int HandleOP(Unwrap) { OM_uint32 maj_stat, min_stat; int32_t hContext, flags, seqno; krb5_data token; gss_ctx_id_t ctx; gss_buffer_desc input_token, output_token; int conf_state; gss_qop_t qop_state; ret32(c, hContext); ret32(c, flags); ret32(c, seqno); retdata(c, token); ctx = find_handle(c->handles, hContext, handle_context); if (ctx == NULL) errx(1, "unwrap: reference to unknown context"); input_token.length = token.length; input_token.value = token.data; maj_stat = gss_unwrap(&min_stat, ctx, &input_token, &output_token, &conf_state, &qop_state); if (maj_stat != GSS_S_COMPLETE) errx(1, "gss_unwrap failed: %d/%d", maj_stat, min_stat); krb5_data_free(&token); if (maj_stat == GSS_S_COMPLETE) { token.data = output_token.value; token.length = output_token.length; } else { token.data = NULL; token.length = 0; } put32(c, 0); /* XXX fix gsm_error */ putdata(c, token); if (maj_stat == GSS_S_COMPLETE) gss_release_buffer(&min_stat, &output_token); return 0; } static int HandleOP(Encrypt) { return handleWrap(op, c); } static int HandleOP(Decrypt) { return handleUnwrap(op, c); } static int HandleOP(ConnectLoggingService2) { errx(1, "ConnectLoggingService2"); } static int HandleOP(GetMoniker) { putstring(c, c->moniker); return 0; } static int HandleOP(CallExtension) { errx(1, "CallExtension"); } static int HandleOP(AcquirePKInitCreds) { int32_t flags; krb5_data pfxdata; char fn[] = "FILE:/tmp/pkcs12-creds-XXXXXXX"; krb5_principal principal = NULL; int fd; ret32(c, flags); retdata(c, pfxdata); fd = mkstemp(fn + 5); if (fd < 0) errx(1, "mkstemp"); net_write(fd, pfxdata.data, pfxdata.length); krb5_data_free(&pfxdata); close(fd); if (principal) krb5_free_principal(context, principal); put32(c, -1); /* hResource */ put32(c, GSMERR_NOT_SUPPORTED); return 0; } static int HandleOP(WrapExt) { OM_uint32 maj_stat, min_stat; int32_t hContext, flags, bflags; krb5_data token, header, trailer; gss_ctx_id_t ctx; unsigned char *p; int conf_state, iov_len; gss_iov_buffer_desc iov[6]; ret32(c, hContext); ret32(c, flags); ret32(c, bflags); retdata(c, header); retdata(c, token); retdata(c, trailer); ctx = find_handle(c->handles, hContext, handle_context); if (ctx == NULL) errx(1, "wrap: reference to unknown context"); memset(&iov, 0, sizeof(iov)); iov_len = sizeof(iov)/sizeof(iov[0]); if (bflags & WRAP_EXP_ONLY_HEADER) iov_len -= 2; /* skip trailer and padding, aka dce-style */ iov[0].type = GSS_IOV_BUFFER_TYPE_HEADER | GSS_IOV_BUFFER_TYPE_FLAG_ALLOCATE; if (header.length != 0) { iov[1].type = GSS_IOV_BUFFER_TYPE_SIGN_ONLY; iov[1].buffer.length = header.length; iov[1].buffer.value = header.data; } else { iov[1].type = GSS_IOV_BUFFER_TYPE_EMPTY; } iov[2].type = GSS_IOV_BUFFER_TYPE_DATA; iov[2].buffer.length = token.length; iov[2].buffer.value = token.data; if (trailer.length != 0) { iov[3].type = GSS_IOV_BUFFER_TYPE_SIGN_ONLY; iov[3].buffer.length = trailer.length; iov[3].buffer.value = trailer.data; } else { iov[3].type = GSS_IOV_BUFFER_TYPE_EMPTY; } iov[4].type = GSS_IOV_BUFFER_TYPE_PADDING | GSS_IOV_BUFFER_TYPE_FLAG_ALLOCATE; iov[5].type = GSS_IOV_BUFFER_TYPE_TRAILER | GSS_IOV_BUFFER_TYPE_FLAG_ALLOCATE; maj_stat = gss_wrap_iov_length(&min_stat, ctx, flags, 0, &conf_state, iov, iov_len); if (maj_stat != GSS_S_COMPLETE) errx(1, "gss_wrap_iov_length failed"); maj_stat = gss_wrap_iov(&min_stat, ctx, flags, 0, &conf_state, iov, iov_len); if (maj_stat != GSS_S_COMPLETE) errx(1, "gss_wrap_iov failed"); krb5_data_free(&token); token.length = iov[0].buffer.length + iov[2].buffer.length + iov[4].buffer.length + iov[5].buffer.length; token.data = malloc(token.length); p = token.data; memcpy(p, iov[0].buffer.value, iov[0].buffer.length); p += iov[0].buffer.length; memcpy(p, iov[2].buffer.value, iov[2].buffer.length); p += iov[2].buffer.length; memcpy(p, iov[4].buffer.value, iov[4].buffer.length); p += iov[4].buffer.length; memcpy(p, iov[5].buffer.value, iov[5].buffer.length); +#ifndef __clang_analyzer__ p += iov[5].buffer.length; +#endif gss_release_iov_buffer(NULL, iov, iov_len); put32(c, 0); /* XXX fix gsm_error */ putdata(c, token); free(token.data); return 0; } static int HandleOP(UnwrapExt) { OM_uint32 maj_stat, min_stat; int32_t hContext, flags, bflags; krb5_data token, header, trailer; gss_ctx_id_t ctx; gss_iov_buffer_desc iov[3]; int conf_state, iov_len; gss_qop_t qop_state; ret32(c, hContext); ret32(c, flags); ret32(c, bflags); retdata(c, header); retdata(c, token); retdata(c, trailer); iov_len = sizeof(iov)/sizeof(iov[0]); if (bflags & WRAP_EXP_ONLY_HEADER) iov_len -= 1; /* skip trailer and padding, aka dce-style */ ctx = find_handle(c->handles, hContext, handle_context); if (ctx == NULL) errx(1, "unwrap: reference to unknown context"); if (header.length != 0) { iov[0].type = GSS_IOV_BUFFER_TYPE_SIGN_ONLY; iov[0].buffer.length = header.length; iov[0].buffer.value = header.data; } else { iov[0].type = GSS_IOV_BUFFER_TYPE_EMPTY; } iov[1].type = GSS_IOV_BUFFER_TYPE_DATA; iov[1].buffer.length = token.length; iov[1].buffer.value = token.data; if (trailer.length != 0) { iov[2].type = GSS_IOV_BUFFER_TYPE_SIGN_ONLY; iov[2].buffer.length = trailer.length; iov[2].buffer.value = trailer.data; } else { iov[2].type = GSS_IOV_BUFFER_TYPE_EMPTY; } maj_stat = gss_unwrap_iov(&min_stat, ctx, &conf_state, &qop_state, iov, iov_len); if (maj_stat != GSS_S_COMPLETE) errx(1, "gss_unwrap failed: %d/%d", maj_stat, min_stat); if (maj_stat == GSS_S_COMPLETE) { token.data = iov[1].buffer.value; token.length = iov[1].buffer.length; } else { token.data = NULL; token.length = 0; } put32(c, 0); /* XXX fix gsm_error */ putdata(c, token); return 0; } /* * */ struct handler { enum gssMaggotOp op; const char *name; int (*func)(enum gssMaggotOp, struct client *); }; #define S(a) { e##a, #a, handle##a } struct handler handlers[] = { S(GetVersionInfo), S(GoodBye), S(InitContext), S(AcceptContext), S(ToastResource), S(AcquireCreds), S(Encrypt), S(Decrypt), S(Sign), S(Verify), S(GetVersionAndCapabilities), S(GetTargetName), S(SetLoggingSocket), S(ChangePassword), S(SetPasswordSelf), S(Wrap), S(Unwrap), S(ConnectLoggingService2), S(GetMoniker), S(CallExtension), S(AcquirePKInitCreds), S(WrapExt), S(UnwrapExt), }; #undef S /* * */ static struct handler * find_op(int32_t op) { int i; for (i = 0; i < sizeof(handlers)/sizeof(handlers[0]); i++) if (handlers[i].op == op) return &handlers[i]; return NULL; } static struct client * create_client(int fd, int port, const char *moniker) { struct client *c; c = ecalloc(1, sizeof(*c)); if (moniker) { c->moniker = estrdup(moniker); } else { char hostname[MAXHOSTNAMELEN]; gethostname(hostname, sizeof(hostname)); asprintf(&c->moniker, "gssmask: %s:%d", hostname, port); } { c->salen = sizeof(c->sa); getpeername(fd, (struct sockaddr *)&c->sa, &c->salen); getnameinfo((struct sockaddr *)&c->sa, c->salen, c->servername, sizeof(c->servername), NULL, 0, NI_NUMERICHOST); } c->sock = krb5_storage_from_fd(fd); if (c->sock == NULL) errx(1, "krb5_storage_from_fd"); close(fd); return c; } static void free_client(struct client *c) { while(c->handles) del_handle(&c->handles, c->handles->idx); free(c->moniker); krb5_storage_free(c->sock); if (c->logging) krb5_storage_free(c->logging); free(c); } static void * handleServer(void *ptr) { struct handler *handler; struct client *c; int32_t op; c = (struct client *)ptr; while(1) { ret32(c, op); handler = find_op(op); if (handler == NULL) { logmessage(c, __FILE__, __LINE__, 0, "op %d not supported", (int)op); exit(1); } logmessage(c, __FILE__, __LINE__, 0, "---> Got op %s from server %s", handler->name, c->servername); if ((handler->func)(handler->op, c)) break; } return NULL; } static char *port_str; static int version_flag; static int help_flag; static char *logfile_str; static char *moniker_str; static int port = 4711; struct getargs args[] = { { "spn", 0, arg_string, &targetname, "This host's SPN", "service/host@REALM" }, { "port", 'p', arg_string, &port_str, "Use this port", "number-of-service" }, { "logfile", 0, arg_string, &logfile_str, "logfile", "number-of-service" }, { "moniker", 0, arg_string, &moniker_str, "nickname", "name" }, { "version", 0, arg_flag, &version_flag, "Print version", NULL }, { "help", 0, arg_flag, &help_flag, NULL, NULL } }; static void usage(int ret) { arg_printusage (args, sizeof(args) / sizeof(args[0]), NULL, ""); exit (ret); } int main(int argc, char **argv) { int optidx = 0; setprogname (argv[0]); if (getarg (args, sizeof(args) / sizeof(args[0]), argc, argv, &optidx)) usage (1); if (help_flag) usage (0); if (version_flag) { print_version (NULL); return 0; } if (optidx != argc) usage (1); if (port_str) { char *ptr; port = strtol (port_str, &ptr, 10); if (port == 0 && ptr == port_str) errx (1, "Bad port `%s'", port_str); } krb5_init_context(&context); { const char *lf = logfile_str; if (lf == NULL) lf = "/dev/tty"; logfile = fopen(lf, "w"); if (logfile == NULL) err(1, "error opening %s", lf); } mini_inetd(htons(port), NULL); fprintf(logfile, "connected\n"); { struct client *c; c = create_client(0, port, moniker_str); /* close(0); */ handleServer(c); free_client(c); } krb5_free_context(context); return 0; } diff --git a/crypto/heimdal/kadmin/kadmind.c b/crypto/heimdal/kadmin/kadmind.c index f99f9572334a..e52a836b9ad6 100644 --- a/crypto/heimdal/kadmin/kadmind.c +++ b/crypto/heimdal/kadmin/kadmind.c @@ -1,201 +1,205 @@ /* * 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 "kadmin_locl.h" static char *check_library = NULL; static char *check_function = NULL; static getarg_strings policy_libraries = { 0, NULL }; static char *config_file; static char sHDB[] = "HDB:"; static char *keytab_str = sHDB; static int help_flag; static int version_flag; static int debug_flag; static char *port_str; char *realm; static struct getargs args[] = { { "config-file", 'c', arg_string, &config_file, "location of config file", "file" }, { "keytab", 0, arg_string, &keytab_str, "what keytab to use", "keytab" }, { "realm", 'r', arg_string, &realm, "realm to use", "realm" }, #ifdef HAVE_DLOPEN { "check-library", 0, arg_string, &check_library, "library to load password check function from", "library" }, { "check-function", 0, arg_string, &check_function, "password check function to load", "function" }, { "policy-libraries", 0, arg_strings, &policy_libraries, "password check function to load", "function" }, #endif { "debug", 'd', arg_flag, &debug_flag, "enable debugging", NULL }, { "ports", 'p', arg_string, &port_str, "ports to listen to", "port" }, { "help", 'h', arg_flag, &help_flag, NULL, NULL }, { "version", 'v', arg_flag, &version_flag, NULL, NULL } }; static int num_args = sizeof(args) / sizeof(args[0]); krb5_context context; static void usage(int ret) { arg_printusage (args, num_args, NULL, ""); exit (ret); } int main(int argc, char **argv) { krb5_error_code ret; char **files; int optidx = 0; int i; krb5_log_facility *logfacility; krb5_keytab keytab; krb5_socket_t sfd = rk_INVALID_SOCKET; setprogname(argv[0]); ret = krb5_init_context(&context); if (ret) errx (1, "krb5_init_context failed: %d", ret); if (getarg(args, num_args, argc, argv, &optidx)) { warnx("error at argument `%s'", argv[optidx]); usage(1); } if (help_flag) usage (0); if (version_flag) { print_version(NULL); exit(0); } argc -= optidx; +#ifndef __clang_analyzer__ argv += optidx; +#endif + if (argc != 0) + usage(1); if (config_file == NULL) { asprintf(&config_file, "%s/kdc.conf", hdb_db_dir(context)); if (config_file == NULL) errx(1, "out of memory"); } ret = krb5_prepend_config_files_default(config_file, &files); if (ret) krb5_err(context, 1, ret, "getting configuration files"); ret = krb5_set_config_files(context, files); krb5_free_config_files(files); if(ret) krb5_err(context, 1, ret, "reading configuration files"); ret = krb5_openlog(context, "kadmind", &logfacility); if (ret) krb5_err(context, 1, ret, "krb5_openlog"); ret = krb5_set_warn_dest(context, logfacility); if (ret) krb5_err(context, 1, ret, "krb5_set_warn_dest"); ret = krb5_kt_register(context, &hdb_kt_ops); if(ret) krb5_err(context, 1, ret, "krb5_kt_register"); ret = krb5_kt_resolve(context, keytab_str, &keytab); if(ret) krb5_err(context, 1, ret, "krb5_kt_resolve"); kadm5_setup_passwd_quality_check (context, check_library, check_function); for (i = 0; i < policy_libraries.num_strings; i++) { ret = kadm5_add_passwd_quality_verifier(context, policy_libraries.strings[i]); if (ret) krb5_err(context, 1, ret, "kadm5_add_passwd_quality_verifier"); } ret = kadm5_add_passwd_quality_verifier(context, NULL); if (ret) krb5_err(context, 1, ret, "kadm5_add_passwd_quality_verifier"); if(debug_flag) { int debug_port; if(port_str == NULL) debug_port = krb5_getportbyname (context, "kerberos-adm", "tcp", 749); else debug_port = htons(atoi(port_str)); mini_inetd(debug_port, &sfd); } else { #ifdef _WIN32 pidfile(NULL); start_server(context, port_str); #else struct sockaddr_storage __ss; struct sockaddr *sa = (struct sockaddr *)&__ss; socklen_t sa_size = sizeof(__ss); /* * Check if we are running inside inetd or not, if not, start * our own server. */ if(roken_getsockname(STDIN_FILENO, sa, &sa_size) < 0 && rk_SOCK_ERRNO == ENOTSOCK) { pidfile(NULL); start_server(context, port_str); } #endif /* _WIN32 */ sfd = STDIN_FILENO; } if(realm) krb5_set_default_realm(context, realm); /* XXX */ kadmind_loop(context, keytab, sfd); return 0; } diff --git a/crypto/heimdal/kadmin/mod.c b/crypto/heimdal/kadmin/mod.c index 940425f2a54b..39b48c2e09a6 100644 --- a/crypto/heimdal/kadmin/mod.c +++ b/crypto/heimdal/kadmin/mod.c @@ -1,263 +1,270 @@ /* * Copyright (c) 1997 - 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 "kadmin_locl.h" #include "kadmin-commands.h" static void add_tl(kadm5_principal_ent_rec *princ, int type, krb5_data *data) { krb5_tl_data *tl, **ptl; tl = ecalloc(1, sizeof(*tl)); tl->tl_data_next = NULL; tl->tl_data_type = KRB5_TL_EXTENSION; tl->tl_data_length = data->length; tl->tl_data_contents = data->data; princ->n_tl_data++; ptl = &princ->tl_data; while (*ptl != NULL) ptl = &(*ptl)->tl_data_next; *ptl = tl; return; } static void add_constrained_delegation(krb5_context contextp, kadm5_principal_ent_rec *princ, struct getarg_strings *strings) { krb5_error_code ret; HDB_extension ext; krb5_data buf; size_t size = 0; memset(&ext, 0, sizeof(ext)); ext.mandatory = FALSE; ext.data.element = choice_HDB_extension_data_allowed_to_delegate_to; if (strings->num_strings == 1 && strings->strings[0][0] == '\0') { ext.data.u.allowed_to_delegate_to.val = NULL; ext.data.u.allowed_to_delegate_to.len = 0; } else { krb5_principal p; int i; ext.data.u.allowed_to_delegate_to.val = calloc(strings->num_strings, sizeof(ext.data.u.allowed_to_delegate_to.val[0])); ext.data.u.allowed_to_delegate_to.len = strings->num_strings; for (i = 0; i < strings->num_strings; i++) { ret = krb5_parse_name(contextp, strings->strings[i], &p); if (ret) abort(); ret = copy_Principal(p, &ext.data.u.allowed_to_delegate_to.val[i]); if (ret) abort(); krb5_free_principal(contextp, p); } } ASN1_MALLOC_ENCODE(HDB_extension, buf.data, buf.length, &ext, &size, ret); free_HDB_extension(&ext); if (ret) abort(); if (buf.length != size) abort(); add_tl(princ, KRB5_TL_EXTENSION, &buf); } static void add_aliases(krb5_context contextp, kadm5_principal_ent_rec *princ, struct getarg_strings *strings) { - krb5_error_code ret; + krb5_error_code ret = 0; HDB_extension ext; krb5_data buf; krb5_principal p; size_t size = 0; int i; memset(&ext, 0, sizeof(ext)); ext.mandatory = FALSE; ext.data.element = choice_HDB_extension_data_aliases; ext.data.u.aliases.case_insensitive = 0; if (strings->num_strings == 1 && strings->strings[0][0] == '\0') { ext.data.u.aliases.aliases.val = NULL; ext.data.u.aliases.aliases.len = 0; } else { ext.data.u.aliases.aliases.val = calloc(strings->num_strings, sizeof(ext.data.u.aliases.aliases.val[0])); ext.data.u.aliases.aliases.len = strings->num_strings; - for (i = 0; i < strings->num_strings; i++) { + for (i = 0; ret == 0 && i < strings->num_strings; i++) { ret = krb5_parse_name(contextp, strings->strings[i], &p); - ret = copy_Principal(p, &ext.data.u.aliases.aliases.val[i]); + if (ret) + krb5_err(contextp, 1, ret, "Could not parse alias %s", + strings->strings[i]); + if (ret == 0) + ret = copy_Principal(p, &ext.data.u.aliases.aliases.val[i]); + if (ret) + krb5_err(contextp, 1, ret, "Could not copy parsed alias %s", + strings->strings[i]); krb5_free_principal(contextp, p); } } ASN1_MALLOC_ENCODE(HDB_extension, buf.data, buf.length, &ext, &size, ret); free_HDB_extension(&ext); if (ret) abort(); if (buf.length != size) abort(); add_tl(princ, KRB5_TL_EXTENSION, &buf); } static void add_pkinit_acl(krb5_context contextp, kadm5_principal_ent_rec *princ, struct getarg_strings *strings) { krb5_error_code ret; HDB_extension ext; krb5_data buf; size_t size = 0; int i; memset(&ext, 0, sizeof(ext)); ext.mandatory = FALSE; ext.data.element = choice_HDB_extension_data_pkinit_acl; ext.data.u.aliases.case_insensitive = 0; if (strings->num_strings == 1 && strings->strings[0][0] == '\0') { ext.data.u.pkinit_acl.val = NULL; ext.data.u.pkinit_acl.len = 0; } else { ext.data.u.pkinit_acl.val = calloc(strings->num_strings, sizeof(ext.data.u.pkinit_acl.val[0])); ext.data.u.pkinit_acl.len = strings->num_strings; for (i = 0; i < strings->num_strings; i++) { ext.data.u.pkinit_acl.val[i].subject = estrdup(strings->strings[i]); } } ASN1_MALLOC_ENCODE(HDB_extension, buf.data, buf.length, &ext, &size, ret); free_HDB_extension(&ext); if (ret) abort(); if (buf.length != size) abort(); add_tl(princ, KRB5_TL_EXTENSION, &buf); } static int do_mod_entry(krb5_principal principal, void *data) { krb5_error_code ret; kadm5_principal_ent_rec princ; int mask = 0; struct modify_options *e = data; memset (&princ, 0, sizeof(princ)); ret = kadm5_get_principal(kadm_handle, principal, &princ, KADM5_PRINCIPAL | KADM5_ATTRIBUTES | KADM5_MAX_LIFE | KADM5_MAX_RLIFE | KADM5_PRINC_EXPIRE_TIME | KADM5_PW_EXPIRATION); if(ret) return ret; if(e->max_ticket_life_string || e->max_renewable_life_string || e->expiration_time_string || e->pw_expiration_time_string || e->attributes_string || e->kvno_integer != -1 || e->constrained_delegation_strings.num_strings || e->alias_strings.num_strings || e->pkinit_acl_strings.num_strings) { ret = set_entry(context, &princ, &mask, e->max_ticket_life_string, e->max_renewable_life_string, e->expiration_time_string, e->pw_expiration_time_string, e->attributes_string); if(e->kvno_integer != -1) { princ.kvno = e->kvno_integer; mask |= KADM5_KVNO; } if (e->constrained_delegation_strings.num_strings) { add_constrained_delegation(context, &princ, &e->constrained_delegation_strings); mask |= KADM5_TL_DATA; } if (e->alias_strings.num_strings) { add_aliases(context, &princ, &e->alias_strings); mask |= KADM5_TL_DATA; } if (e->pkinit_acl_strings.num_strings) { add_pkinit_acl(context, &princ, &e->pkinit_acl_strings); mask |= KADM5_TL_DATA; } } else ret = edit_entry(&princ, &mask, NULL, 0); if(ret == 0) { ret = kadm5_modify_principal(kadm_handle, &princ, mask); if(ret) krb5_warn(context, ret, "kadm5_modify_principal"); } kadm5_free_principal_ent(kadm_handle, &princ); return ret; } int mod_entry(struct modify_options *opt, int argc, char **argv) { krb5_error_code ret = 0; int i; for(i = 0; i < argc; i++) { ret = foreach_principal(argv[i], do_mod_entry, "mod", opt); if (ret) break; } return ret != 0; } diff --git a/crypto/heimdal/kadmin/stash.c b/crypto/heimdal/kadmin/stash.c index f9b940ac5b7d..0ca5b487a699 100644 --- a/crypto/heimdal/kadmin/stash.c +++ b/crypto/heimdal/kadmin/stash.c @@ -1,151 +1,154 @@ /* * Copyright (c) 2004 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 "kadmin_locl.h" #include "kadmin-commands.h" extern int local_flag; int stash(struct stash_options *opt, int argc, char **argv) { char buf[1024]; krb5_error_code ret; krb5_enctype enctype; hdb_master_key mkey; if(!local_flag) { krb5_warnx(context, "stash is only available in local (-l) mode"); return 0; } ret = krb5_string_to_enctype(context, opt->enctype_string, &enctype); if(ret) { krb5_warn(context, ret, "%s", opt->enctype_string); return 0; } if(opt->key_file_string == NULL) { asprintf(&opt->key_file_string, "%s/m-key", hdb_db_dir(context)); if (opt->key_file_string == NULL) errx(1, "out of memory"); } ret = hdb_read_master_key(context, opt->key_file_string, &mkey); if(ret && ret != ENOENT) { krb5_warn(context, ret, "reading master key from %s", opt->key_file_string); return 0; } if (opt->convert_file_flag) { if (ret) krb5_warn(context, ret, "reading master key from %s", opt->key_file_string); return 0; } else { krb5_keyblock key; krb5_salt salt; salt.salttype = KRB5_PW_SALT; /* XXX better value? */ salt.saltvalue.data = NULL; salt.saltvalue.length = 0; if(opt->master_key_fd_integer != -1) { ssize_t n; n = read(opt->master_key_fd_integer, buf, sizeof(buf)); if(n == 0) krb5_warnx(context, "end of file reading passphrase"); else if(n < 0) { krb5_warn(context, errno, "reading passphrase"); n = 0; } buf[n] = '\0'; buf[strcspn(buf, "\r\n")] = '\0'; } else if (opt->random_password_flag) { random_password (buf, sizeof(buf)); printf("Using random master stash password: %s\n", buf); } else { if(UI_UTIL_read_pw_string(buf, sizeof(buf), "Master key: ", 1)) { hdb_free_master_key(context, mkey); return 0; } } ret = krb5_string_to_key_salt(context, enctype, buf, salt, &key); - ret = hdb_add_master_key(context, &key, &mkey); + if (ret == 0) + ret = hdb_add_master_key(context, &key, &mkey); + if (ret) + krb5_warn(context, errno, "setting master key"); krb5_free_keyblock_contents(context, &key); } { char *new, *old; asprintf(&old, "%s.old", opt->key_file_string); asprintf(&new, "%s.new", opt->key_file_string); if(old == NULL || new == NULL) { ret = ENOMEM; goto out; } if(unlink(new) < 0 && errno != ENOENT) { ret = errno; goto out; } krb5_warnx(context, "writing key to \"%s\"", opt->key_file_string); ret = hdb_write_master_key(context, new, mkey); if(ret) unlink(new); else { unlink(old); #ifndef NO_POSIX_LINKS if(link(opt->key_file_string, old) < 0 && errno != ENOENT) { ret = errno; unlink(new); } else { #endif if(rename(new, opt->key_file_string) < 0) { ret = errno; } #ifndef NO_POSIX_LINKS } #endif } out: free(old); free(new); if(ret) krb5_warn(context, errno, "writing master key file"); } hdb_free_master_key(context, mkey); return 0; } diff --git a/crypto/heimdal/kcm/protocol.c b/crypto/heimdal/kcm/protocol.c index 0cf7157b7a71..c57a4c0b5f49 100644 --- a/crypto/heimdal/kcm/protocol.c +++ b/crypto/heimdal/kcm/protocol.c @@ -1,1810 +1,1810 @@ /* * Copyright (c) 2005, PADL Software Pty Ltd. * 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 PADL Software 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 PADL SOFTWARE 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 PADL SOFTWARE 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 "kcm_locl.h" #include static void kcm_drop_default_cache(krb5_context context, kcm_client *client, char *name); int kcm_is_same_session(kcm_client *client, uid_t uid, pid_t session) { #if 0 /* XXX pppd is running in diffrent session the user */ if (session != -1) return (client->session == session); else #endif return (client->uid == uid); } static krb5_error_code kcm_op_noop(krb5_context context, kcm_client *client, kcm_operation opcode, krb5_storage *request, krb5_storage *response) { KCM_LOG_REQUEST(context, client, opcode); return 0; } /* * Request: * NameZ * Response: * NameZ * */ static krb5_error_code kcm_op_get_name(krb5_context context, kcm_client *client, kcm_operation opcode, krb5_storage *request, krb5_storage *response) { krb5_error_code ret; char *name = NULL; kcm_ccache ccache; ret = krb5_ret_stringz(request, &name); if (ret) return ret; KCM_LOG_REQUEST_NAME(context, client, opcode, name); ret = kcm_ccache_resolve_client(context, client, opcode, name, &ccache); if (ret) { free(name); return ret; } ret = krb5_store_stringz(response, ccache->name); if (ret) { kcm_release_ccache(context, ccache); free(name); return ret; } free(name); kcm_release_ccache(context, ccache); return 0; } /* * Request: * * Response: * NameZ */ static krb5_error_code kcm_op_gen_new(krb5_context context, kcm_client *client, kcm_operation opcode, krb5_storage *request, krb5_storage *response) { krb5_error_code ret; char *name; KCM_LOG_REQUEST(context, client, opcode); name = kcm_ccache_nextid(client->pid, client->uid, client->gid); if (name == NULL) { return KRB5_CC_NOMEM; } ret = krb5_store_stringz(response, name); free(name); return ret; } /* * Request: * NameZ * Principal * * Response: * */ static krb5_error_code kcm_op_initialize(krb5_context context, kcm_client *client, kcm_operation opcode, krb5_storage *request, krb5_storage *response) { kcm_ccache ccache; krb5_principal principal; krb5_error_code ret; char *name; #if 0 kcm_event event; #endif KCM_LOG_REQUEST(context, client, opcode); ret = krb5_ret_stringz(request, &name); if (ret) return ret; ret = krb5_ret_principal(request, &principal); if (ret) { free(name); return ret; } ret = kcm_ccache_new_client(context, client, name, &ccache); if (ret) { free(name); krb5_free_principal(context, principal); return ret; } ccache->client = principal; free(name); #if 0 /* * Create a new credentials cache. To mitigate DoS attacks we will * expire it in 30 minutes unless it has some credentials added * to it */ event.fire_time = 30 * 60; event.expire_time = 0; event.backoff_time = 0; event.action = KCM_EVENT_DESTROY_EMPTY_CACHE; event.ccache = ccache; ret = kcm_enqueue_event_relative(context, &event); #endif kcm_release_ccache(context, ccache); return ret; } /* * Request: * NameZ * * Response: * */ static krb5_error_code kcm_op_destroy(krb5_context context, kcm_client *client, kcm_operation opcode, krb5_storage *request, krb5_storage *response) { krb5_error_code ret; char *name; ret = krb5_ret_stringz(request, &name); if (ret) return ret; KCM_LOG_REQUEST_NAME(context, client, opcode, name); ret = kcm_ccache_destroy_client(context, client, name); if (ret == 0) kcm_drop_default_cache(context, client, name); free(name); return ret; } /* * Request: * NameZ * Creds * * Response: * */ static krb5_error_code kcm_op_store(krb5_context context, kcm_client *client, kcm_operation opcode, krb5_storage *request, krb5_storage *response) { krb5_creds creds; krb5_error_code ret; kcm_ccache ccache; char *name; ret = krb5_ret_stringz(request, &name); if (ret) return ret; KCM_LOG_REQUEST_NAME(context, client, opcode, name); ret = krb5_ret_creds(request, &creds); if (ret) { free(name); return ret; } ret = kcm_ccache_resolve_client(context, client, opcode, name, &ccache); if (ret) { free(name); krb5_free_cred_contents(context, &creds); return ret; } ret = kcm_ccache_store_cred(context, ccache, &creds, 0); if (ret) { free(name); krb5_free_cred_contents(context, &creds); kcm_release_ccache(context, ccache); return ret; } kcm_ccache_enqueue_default(context, ccache, &creds); free(name); kcm_release_ccache(context, ccache); return 0; } /* * Request: * NameZ * WhichFields * MatchCreds * * Response: * Creds * */ static krb5_error_code kcm_op_retrieve(krb5_context context, kcm_client *client, kcm_operation opcode, krb5_storage *request, krb5_storage *response) { uint32_t flags; krb5_creds mcreds; krb5_error_code ret; kcm_ccache ccache; char *name; krb5_creds *credp; int free_creds = 0; ret = krb5_ret_stringz(request, &name); if (ret) return ret; KCM_LOG_REQUEST_NAME(context, client, opcode, name); ret = krb5_ret_uint32(request, &flags); if (ret) { free(name); return ret; } ret = krb5_ret_creds_tag(request, &mcreds); if (ret) { free(name); return ret; } if (disallow_getting_krbtgt && mcreds.server->name.name_string.len == 2 && strcmp(mcreds.server->name.name_string.val[0], KRB5_TGS_NAME) == 0) { free(name); krb5_free_cred_contents(context, &mcreds); return KRB5_FCC_PERM; } ret = kcm_ccache_resolve_client(context, client, opcode, name, &ccache); if (ret) { free(name); krb5_free_cred_contents(context, &mcreds); return ret; } ret = kcm_ccache_retrieve_cred(context, ccache, flags, &mcreds, &credp); if (ret && ((flags & KRB5_GC_CACHED) == 0) && !krb5_is_config_principal(context, mcreds.server)) { krb5_ccache_data ccdata; /* try and acquire */ HEIMDAL_MUTEX_lock(&ccache->mutex); /* Fake up an internal ccache */ kcm_internal_ccache(context, ccache, &ccdata); /* glue cc layer will store creds */ ret = krb5_get_credentials(context, 0, &ccdata, &mcreds, &credp); if (ret == 0) free_creds = 1; HEIMDAL_MUTEX_unlock(&ccache->mutex); } if (ret == 0) { ret = krb5_store_creds(response, credp); } free(name); krb5_free_cred_contents(context, &mcreds); kcm_release_ccache(context, ccache); if (free_creds) krb5_free_cred_contents(context, credp); return ret; } /* * Request: * NameZ * * Response: * Principal */ static krb5_error_code kcm_op_get_principal(krb5_context context, kcm_client *client, kcm_operation opcode, krb5_storage *request, krb5_storage *response) { krb5_error_code ret; kcm_ccache ccache; char *name; ret = krb5_ret_stringz(request, &name); if (ret) return ret; KCM_LOG_REQUEST_NAME(context, client, opcode, name); ret = kcm_ccache_resolve_client(context, client, opcode, name, &ccache); if (ret) { free(name); return ret; } if (ccache->client == NULL) ret = KRB5_CC_NOTFOUND; else ret = krb5_store_principal(response, ccache->client); free(name); kcm_release_ccache(context, ccache); - return 0; + return ret; } /* * Request: * NameZ * * Response: * UUIDs * */ static krb5_error_code kcm_op_get_cred_uuid_list(krb5_context context, kcm_client *client, kcm_operation opcode, krb5_storage *request, krb5_storage *response) { struct kcm_creds *creds; krb5_error_code ret; kcm_ccache ccache; char *name; ret = krb5_ret_stringz(request, &name); if (ret) return ret; KCM_LOG_REQUEST_NAME(context, client, opcode, name); ret = kcm_ccache_resolve_client(context, client, opcode, name, &ccache); free(name); if (ret) return ret; for (creds = ccache->creds ; creds ; creds = creds->next) { ssize_t sret; sret = krb5_storage_write(response, &creds->uuid, sizeof(creds->uuid)); if (sret != sizeof(creds->uuid)) { ret = ENOMEM; break; } } kcm_release_ccache(context, ccache); return ret; } /* * Request: * NameZ * Cursor * * Response: * Creds */ static krb5_error_code kcm_op_get_cred_by_uuid(krb5_context context, kcm_client *client, kcm_operation opcode, krb5_storage *request, krb5_storage *response) { krb5_error_code ret; kcm_ccache ccache; char *name; struct kcm_creds *c; kcmuuid_t uuid; ssize_t sret; ret = krb5_ret_stringz(request, &name); if (ret) return ret; KCM_LOG_REQUEST_NAME(context, client, opcode, name); ret = kcm_ccache_resolve_client(context, client, opcode, name, &ccache); free(name); if (ret) return ret; sret = krb5_storage_read(request, &uuid, sizeof(uuid)); if (sret != sizeof(uuid)) { kcm_release_ccache(context, ccache); krb5_clear_error_message(context); return KRB5_CC_IO; } c = kcm_ccache_find_cred_uuid(context, ccache, uuid); if (c == NULL) { kcm_release_ccache(context, ccache); return KRB5_CC_END; } HEIMDAL_MUTEX_lock(&ccache->mutex); ret = krb5_store_creds(response, &c->cred); HEIMDAL_MUTEX_unlock(&ccache->mutex); kcm_release_ccache(context, ccache); return ret; } /* * Request: * NameZ * WhichFields * MatchCreds * * Response: * */ static krb5_error_code kcm_op_remove_cred(krb5_context context, kcm_client *client, kcm_operation opcode, krb5_storage *request, krb5_storage *response) { uint32_t whichfields; krb5_creds mcreds; krb5_error_code ret; kcm_ccache ccache; char *name; ret = krb5_ret_stringz(request, &name); if (ret) return ret; KCM_LOG_REQUEST_NAME(context, client, opcode, name); ret = krb5_ret_uint32(request, &whichfields); if (ret) { free(name); return ret; } ret = krb5_ret_creds_tag(request, &mcreds); if (ret) { free(name); return ret; } ret = kcm_ccache_resolve_client(context, client, opcode, name, &ccache); if (ret) { free(name); krb5_free_cred_contents(context, &mcreds); return ret; } ret = kcm_ccache_remove_cred(context, ccache, whichfields, &mcreds); /* XXX need to remove any events that match */ free(name); krb5_free_cred_contents(context, &mcreds); kcm_release_ccache(context, ccache); return ret; } /* * Request: * NameZ * Flags * * Response: * */ static krb5_error_code kcm_op_set_flags(krb5_context context, kcm_client *client, kcm_operation opcode, krb5_storage *request, krb5_storage *response) { uint32_t flags; krb5_error_code ret; kcm_ccache ccache; char *name; ret = krb5_ret_stringz(request, &name); if (ret) return ret; KCM_LOG_REQUEST_NAME(context, client, opcode, name); ret = krb5_ret_uint32(request, &flags); if (ret) { free(name); return ret; } ret = kcm_ccache_resolve_client(context, client, opcode, name, &ccache); if (ret) { free(name); return ret; } /* we don't really support any flags yet */ free(name); kcm_release_ccache(context, ccache); return 0; } /* * Request: * NameZ * UID * GID * * Response: * */ static krb5_error_code kcm_op_chown(krb5_context context, kcm_client *client, kcm_operation opcode, krb5_storage *request, krb5_storage *response) { uint32_t uid; uint32_t gid; krb5_error_code ret; kcm_ccache ccache; char *name; ret = krb5_ret_stringz(request, &name); if (ret) return ret; KCM_LOG_REQUEST_NAME(context, client, opcode, name); ret = krb5_ret_uint32(request, &uid); if (ret) { free(name); return ret; } ret = krb5_ret_uint32(request, &gid); if (ret) { free(name); return ret; } ret = kcm_ccache_resolve_client(context, client, opcode, name, &ccache); if (ret) { free(name); return ret; } ret = kcm_chown(context, client, ccache, uid, gid); free(name); kcm_release_ccache(context, ccache); return ret; } /* * Request: * NameZ * Mode * * Response: * */ static krb5_error_code kcm_op_chmod(krb5_context context, kcm_client *client, kcm_operation opcode, krb5_storage *request, krb5_storage *response) { uint16_t mode; krb5_error_code ret; kcm_ccache ccache; char *name; ret = krb5_ret_stringz(request, &name); if (ret) return ret; KCM_LOG_REQUEST_NAME(context, client, opcode, name); ret = krb5_ret_uint16(request, &mode); if (ret) { free(name); return ret; } ret = kcm_ccache_resolve_client(context, client, opcode, name, &ccache); if (ret) { free(name); return ret; } ret = kcm_chmod(context, client, ccache, mode); free(name); kcm_release_ccache(context, ccache); return ret; } /* * Protocol extensions for moving ticket acquisition responsibility * from client to KCM follow. */ /* * Request: * NameZ * ServerPrincipalPresent * ServerPrincipal OPTIONAL * Key * * Repsonse: * */ static krb5_error_code kcm_op_get_initial_ticket(krb5_context context, kcm_client *client, kcm_operation opcode, krb5_storage *request, krb5_storage *response) { krb5_error_code ret; kcm_ccache ccache; char *name; int8_t not_tgt = 0; krb5_principal server = NULL; krb5_keyblock key; krb5_keyblock_zero(&key); ret = krb5_ret_stringz(request, &name); if (ret) return ret; KCM_LOG_REQUEST_NAME(context, client, opcode, name); ret = krb5_ret_int8(request, ¬_tgt); if (ret) { free(name); return ret; } if (not_tgt) { ret = krb5_ret_principal(request, &server); if (ret) { free(name); return ret; } } ret = krb5_ret_keyblock(request, &key); if (ret) { free(name); if (server != NULL) krb5_free_principal(context, server); return ret; } ret = kcm_ccache_resolve_client(context, client, opcode, name, &ccache); if (ret == 0) { HEIMDAL_MUTEX_lock(&ccache->mutex); if (ccache->server != NULL) { krb5_free_principal(context, ccache->server); ccache->server = NULL; } krb5_free_keyblock(context, &ccache->key.keyblock); ccache->server = server; ccache->key.keyblock = key; ccache->flags |= KCM_FLAGS_USE_CACHED_KEY; ret = kcm_ccache_enqueue_default(context, ccache, NULL); if (ret) { ccache->server = NULL; krb5_keyblock_zero(&ccache->key.keyblock); ccache->flags &= ~(KCM_FLAGS_USE_CACHED_KEY); } HEIMDAL_MUTEX_unlock(&ccache->mutex); } free(name); if (ret != 0) { krb5_free_principal(context, server); krb5_free_keyblock(context, &key); } kcm_release_ccache(context, ccache); return ret; } /* * Request: * NameZ * ServerPrincipal * KDCFlags * EncryptionType * * Repsonse: * */ static krb5_error_code kcm_op_get_ticket(krb5_context context, kcm_client *client, kcm_operation opcode, krb5_storage *request, krb5_storage *response) { krb5_error_code ret; kcm_ccache ccache; char *name; krb5_principal server = NULL; krb5_ccache_data ccdata; krb5_creds in, *out; krb5_kdc_flags flags; memset(&in, 0, sizeof(in)); ret = krb5_ret_stringz(request, &name); if (ret) return ret; KCM_LOG_REQUEST_NAME(context, client, opcode, name); ret = krb5_ret_uint32(request, &flags.i); if (ret) { free(name); return ret; } ret = krb5_ret_int32(request, &in.session.keytype); if (ret) { free(name); return ret; } ret = krb5_ret_principal(request, &server); if (ret) { free(name); return ret; } ret = kcm_ccache_resolve_client(context, client, opcode, name, &ccache); if (ret) { krb5_free_principal(context, server); free(name); return ret; } HEIMDAL_MUTEX_lock(&ccache->mutex); /* Fake up an internal ccache */ kcm_internal_ccache(context, ccache, &ccdata); in.client = ccache->client; in.server = server; in.times.endtime = 0; /* glue cc layer will store creds */ ret = krb5_get_credentials_with_flags(context, 0, flags, &ccdata, &in, &out); HEIMDAL_MUTEX_unlock(&ccache->mutex); krb5_free_principal(context, server); if (ret == 0) krb5_free_cred_contents(context, out); kcm_release_ccache(context, ccache); free(name); return ret; } /* * Request: * OldNameZ * NewNameZ * * Repsonse: * */ static krb5_error_code kcm_op_move_cache(krb5_context context, kcm_client *client, kcm_operation opcode, krb5_storage *request, krb5_storage *response) { krb5_error_code ret; kcm_ccache oldid, newid; char *oldname, *newname; ret = krb5_ret_stringz(request, &oldname); if (ret) return ret; KCM_LOG_REQUEST_NAME(context, client, opcode, oldname); ret = krb5_ret_stringz(request, &newname); if (ret) { free(oldname); return ret; } /* move to ourself is simple, done! */ if (strcmp(oldname, newname) == 0) { free(oldname); free(newname); return 0; } ret = kcm_ccache_resolve_client(context, client, opcode, oldname, &oldid); if (ret) { free(oldname); free(newname); return ret; } /* Check if new credential cache exists, if not create one. */ ret = kcm_ccache_resolve_client(context, client, opcode, newname, &newid); if (ret == KRB5_FCC_NOFILE) ret = kcm_ccache_new_client(context, client, newname, &newid); free(newname); if (ret) { free(oldname); kcm_release_ccache(context, oldid); return ret; } HEIMDAL_MUTEX_lock(&oldid->mutex); HEIMDAL_MUTEX_lock(&newid->mutex); /* move content */ { kcm_ccache_data tmp; #define MOVE(n,o,f) { tmp.f = n->f ; n->f = o->f; o->f = tmp.f; } MOVE(newid, oldid, flags); MOVE(newid, oldid, client); MOVE(newid, oldid, server); MOVE(newid, oldid, creds); MOVE(newid, oldid, tkt_life); MOVE(newid, oldid, renew_life); MOVE(newid, oldid, key); MOVE(newid, oldid, kdc_offset); #undef MOVE } HEIMDAL_MUTEX_unlock(&oldid->mutex); HEIMDAL_MUTEX_unlock(&newid->mutex); kcm_release_ccache(context, oldid); kcm_release_ccache(context, newid); ret = kcm_ccache_destroy_client(context, client, oldname); if (ret == 0) kcm_drop_default_cache(context, client, oldname); free(oldname); return ret; } static krb5_error_code kcm_op_get_cache_uuid_list(krb5_context context, kcm_client *client, kcm_operation opcode, krb5_storage *request, krb5_storage *response) { KCM_LOG_REQUEST(context, client, opcode); return kcm_ccache_get_uuids(context, client, opcode, response); } static krb5_error_code kcm_op_get_cache_by_uuid(krb5_context context, kcm_client *client, kcm_operation opcode, krb5_storage *request, krb5_storage *response) { krb5_error_code ret; kcmuuid_t uuid; ssize_t sret; kcm_ccache cache; KCM_LOG_REQUEST(context, client, opcode); sret = krb5_storage_read(request, &uuid, sizeof(uuid)); if (sret != sizeof(uuid)) { krb5_clear_error_message(context); return KRB5_CC_IO; } ret = kcm_ccache_resolve_by_uuid(context, uuid, &cache); if (ret) return ret; ret = kcm_access(context, client, opcode, cache); if (ret) ret = KRB5_FCC_NOFILE; if (ret == 0) ret = krb5_store_stringz(response, cache->name); kcm_release_ccache(context, cache); return ret; } struct kcm_default_cache *default_caches; static krb5_error_code kcm_op_get_default_cache(krb5_context context, kcm_client *client, kcm_operation opcode, krb5_storage *request, krb5_storage *response) { struct kcm_default_cache *c; krb5_error_code ret; const char *name = NULL; char *n = NULL; KCM_LOG_REQUEST(context, client, opcode); for (c = default_caches; c != NULL; c = c->next) { if (kcm_is_same_session(client, c->uid, c->session)) { name = c->name; break; } } if (name == NULL) name = n = kcm_ccache_first_name(client); if (name == NULL) { asprintf(&n, "%d", (int)client->uid); name = n; } if (name == NULL) return ENOMEM; ret = krb5_store_stringz(response, name); if (n) free(n); return ret; } static void kcm_drop_default_cache(krb5_context context, kcm_client *client, char *name) { struct kcm_default_cache **c; for (c = &default_caches; *c != NULL; c = &(*c)->next) { if (!kcm_is_same_session(client, (*c)->uid, (*c)->session)) continue; if (strcmp((*c)->name, name) == 0) { struct kcm_default_cache *h = *c; *c = (*c)->next; free(h->name); free(h); break; } } } static krb5_error_code kcm_op_set_default_cache(krb5_context context, kcm_client *client, kcm_operation opcode, krb5_storage *request, krb5_storage *response) { struct kcm_default_cache *c; krb5_error_code ret; char *name; ret = krb5_ret_stringz(request, &name); if (ret) return ret; KCM_LOG_REQUEST_NAME(context, client, opcode, name); for (c = default_caches; c != NULL; c = c->next) { if (kcm_is_same_session(client, c->uid, c->session)) break; } if (c == NULL) { c = malloc(sizeof(*c)); if (c == NULL) return ENOMEM; c->session = client->session; c->uid = client->uid; c->name = strdup(name); c->next = default_caches; default_caches = c; } else { free(c->name); c->name = strdup(name); } return 0; } static krb5_error_code kcm_op_get_kdc_offset(krb5_context context, kcm_client *client, kcm_operation opcode, krb5_storage *request, krb5_storage *response) { krb5_error_code ret; kcm_ccache ccache; char *name; ret = krb5_ret_stringz(request, &name); if (ret) return ret; KCM_LOG_REQUEST_NAME(context, client, opcode, name); ret = kcm_ccache_resolve_client(context, client, opcode, name, &ccache); free(name); if (ret) return ret; HEIMDAL_MUTEX_lock(&ccache->mutex); ret = krb5_store_int32(response, ccache->kdc_offset); HEIMDAL_MUTEX_unlock(&ccache->mutex); kcm_release_ccache(context, ccache); return ret; } static krb5_error_code kcm_op_set_kdc_offset(krb5_context context, kcm_client *client, kcm_operation opcode, krb5_storage *request, krb5_storage *response) { krb5_error_code ret; kcm_ccache ccache; int32_t offset; char *name; ret = krb5_ret_stringz(request, &name); if (ret) return ret; KCM_LOG_REQUEST_NAME(context, client, opcode, name); ret = krb5_ret_int32(request, &offset); if (ret) { free(name); return ret; } ret = kcm_ccache_resolve_client(context, client, opcode, name, &ccache); free(name); if (ret) return ret; HEIMDAL_MUTEX_lock(&ccache->mutex); ccache->kdc_offset = offset; HEIMDAL_MUTEX_unlock(&ccache->mutex); kcm_release_ccache(context, ccache); return ret; } struct kcm_ntlm_cred { kcmuuid_t uuid; char *user; char *domain; krb5_data nthash; uid_t uid; pid_t session; struct kcm_ntlm_cred *next; }; static struct kcm_ntlm_cred *ntlm_head; static void free_cred(struct kcm_ntlm_cred *cred) { free(cred->user); free(cred->domain); krb5_data_free(&cred->nthash); free(cred); } /* * name * domain * ntlm hash * * Reply: * uuid */ static struct kcm_ntlm_cred * find_ntlm_cred(const char *user, const char *domain, kcm_client *client) { struct kcm_ntlm_cred *c; for (c = ntlm_head; c != NULL; c = c->next) if ((user[0] == '\0' || strcmp(user, c->user) == 0) && (domain == NULL || strcmp(domain, c->domain) == 0) && kcm_is_same_session(client, c->uid, c->session)) return c; return NULL; } static krb5_error_code kcm_op_add_ntlm_cred(krb5_context context, kcm_client *client, kcm_operation opcode, krb5_storage *request, krb5_storage *response) { struct kcm_ntlm_cred *cred, *c; krb5_error_code ret; cred = calloc(1, sizeof(*cred)); if (cred == NULL) return ENOMEM; RAND_bytes(cred->uuid, sizeof(cred->uuid)); ret = krb5_ret_stringz(request, &cred->user); if (ret) goto error; ret = krb5_ret_stringz(request, &cred->domain); if (ret) goto error; ret = krb5_ret_data(request, &cred->nthash); if (ret) goto error; /* search for dups */ c = find_ntlm_cred(cred->user, cred->domain, client); if (c) { krb5_data hash = c->nthash; c->nthash = cred->nthash; cred->nthash = hash; free_cred(cred); cred = c; } else { cred->next = ntlm_head; ntlm_head = cred; } cred->uid = client->uid; cred->session = client->session; /* write response */ (void)krb5_storage_write(response, &cred->uuid, sizeof(cred->uuid)); return 0; error: free_cred(cred); return ret; } /* * { "HAVE_NTLM_CRED", NULL }, * * input: * name * domain */ static krb5_error_code kcm_op_have_ntlm_cred(krb5_context context, kcm_client *client, kcm_operation opcode, krb5_storage *request, krb5_storage *response) { struct kcm_ntlm_cred *c; char *user = NULL, *domain = NULL; krb5_error_code ret; ret = krb5_ret_stringz(request, &user); if (ret) goto error; ret = krb5_ret_stringz(request, &domain); if (ret) goto error; if (domain[0] == '\0') { free(domain); domain = NULL; } c = find_ntlm_cred(user, domain, client); if (c == NULL) ret = ENOENT; error: free(user); if (domain) free(domain); return ret; } /* * { "DEL_NTLM_CRED", NULL }, * * input: * name * domain */ static krb5_error_code kcm_op_del_ntlm_cred(krb5_context context, kcm_client *client, kcm_operation opcode, krb5_storage *request, krb5_storage *response) { struct kcm_ntlm_cred **cp, *c; char *user = NULL, *domain = NULL; krb5_error_code ret; ret = krb5_ret_stringz(request, &user); if (ret) goto error; ret = krb5_ret_stringz(request, &domain); if (ret) goto error; for (cp = &ntlm_head; *cp != NULL; cp = &(*cp)->next) { if (strcmp(user, (*cp)->user) == 0 && strcmp(domain, (*cp)->domain) == 0 && kcm_is_same_session(client, (*cp)->uid, (*cp)->session)) { c = *cp; *cp = c->next; free_cred(c); break; } } error: free(user); free(domain); return ret; } /* * { "DO_NTLM_AUTH", NULL }, * * input: * name:string * domain:string * type2:data * * reply: * type3:data * flags:int32 * session-key:data */ #define NTLM_FLAG_SESSIONKEY 1 #define NTLM_FLAG_NTLM2_SESSION 2 #define NTLM_FLAG_KEYEX 4 static krb5_error_code kcm_op_do_ntlm(krb5_context context, kcm_client *client, kcm_operation opcode, krb5_storage *request, krb5_storage *response) { struct kcm_ntlm_cred *c; struct ntlm_type2 type2; struct ntlm_type3 type3; char *user = NULL, *domain = NULL; struct ntlm_buf ndata, sessionkey; krb5_data data; krb5_error_code ret; uint32_t flags = 0; memset(&type2, 0, sizeof(type2)); memset(&type3, 0, sizeof(type3)); sessionkey.data = NULL; sessionkey.length = 0; ret = krb5_ret_stringz(request, &user); if (ret) goto error; ret = krb5_ret_stringz(request, &domain); if (ret) goto error; if (domain[0] == '\0') { free(domain); domain = NULL; } c = find_ntlm_cred(user, domain, client); if (c == NULL) { ret = EINVAL; goto error; } ret = krb5_ret_data(request, &data); if (ret) goto error; ndata.data = data.data; ndata.length = data.length; ret = heim_ntlm_decode_type2(&ndata, &type2); krb5_data_free(&data); if (ret) goto error; if (domain && strcmp(domain, type2.targetname) == 0) { ret = EINVAL; goto error; } type3.username = c->user; type3.flags = type2.flags; type3.targetname = type2.targetname; type3.ws = rk_UNCONST("workstation"); /* * NTLM Version 1 if no targetinfo buffer. */ if (1 || type2.targetinfo.length == 0) { struct ntlm_buf sessionkey; if (type2.flags & NTLM_NEG_NTLM2_SESSION) { unsigned char nonce[8]; if (RAND_bytes(nonce, sizeof(nonce)) != 1) { ret = EINVAL; goto error; } ret = heim_ntlm_calculate_ntlm2_sess(nonce, type2.challenge, c->nthash.data, &type3.lm, &type3.ntlm); } else { ret = heim_ntlm_calculate_ntlm1(c->nthash.data, c->nthash.length, type2.challenge, &type3.ntlm); } if (ret) goto error; ret = heim_ntlm_build_ntlm1_master(c->nthash.data, c->nthash.length, &sessionkey, &type3.sessionkey); if (ret) { if (type3.lm.data) free(type3.lm.data); if (type3.ntlm.data) free(type3.ntlm.data); goto error; } free(sessionkey.data); if (ret) { if (type3.lm.data) free(type3.lm.data); if (type3.ntlm.data) free(type3.ntlm.data); goto error; } flags |= NTLM_FLAG_SESSIONKEY; #if 0 } else { struct ntlm_buf sessionkey; unsigned char ntlmv2[16]; struct ntlm_targetinfo ti; /* verify infotarget */ ret = heim_ntlm_decode_targetinfo(&type2.targetinfo, 1, &ti); if(ret) { _gss_ntlm_delete_sec_context(minor_status, context_handle, NULL); *minor_status = ret; return GSS_S_FAILURE; } if (ti.domainname && strcmp(ti.domainname, name->domain) != 0) { _gss_ntlm_delete_sec_context(minor_status, context_handle, NULL); *minor_status = EINVAL; return GSS_S_FAILURE; } ret = heim_ntlm_calculate_ntlm2(ctx->client->key.data, ctx->client->key.length, type3.username, name->domain, type2.challenge, &type2.targetinfo, ntlmv2, &type3.ntlm); if (ret) { _gss_ntlm_delete_sec_context(minor_status, context_handle, NULL); *minor_status = ret; return GSS_S_FAILURE; } ret = heim_ntlm_build_ntlm1_master(ntlmv2, sizeof(ntlmv2), &sessionkey, &type3.sessionkey); memset(ntlmv2, 0, sizeof(ntlmv2)); if (ret) { _gss_ntlm_delete_sec_context(minor_status, context_handle, NULL); *minor_status = ret; return GSS_S_FAILURE; } flags |= NTLM_FLAG_NTLM2_SESSION | NTLM_FLAG_SESSION; if (type3.flags & NTLM_NEG_KEYEX) flags |= NTLM_FLAG_KEYEX; ret = krb5_data_copy(&ctx->sessionkey, sessionkey.data, sessionkey.length); free(sessionkey.data); if (ret) { _gss_ntlm_delete_sec_context(minor_status, context_handle, NULL); *minor_status = ret; return GSS_S_FAILURE; } #endif } #if 0 if (flags & NTLM_FLAG_NTLM2_SESSION) { _gss_ntlm_set_key(&ctx->u.v2.send, 0, (ctx->flags & NTLM_NEG_KEYEX), ctx->sessionkey.data, ctx->sessionkey.length); _gss_ntlm_set_key(&ctx->u.v2.recv, 1, (ctx->flags & NTLM_NEG_KEYEX), ctx->sessionkey.data, ctx->sessionkey.length); } else { flags |= NTLM_FLAG_SESSION; RC4_set_key(&ctx->u.v1.crypto_recv.key, ctx->sessionkey.length, ctx->sessionkey.data); RC4_set_key(&ctx->u.v1.crypto_send.key, ctx->sessionkey.length, ctx->sessionkey.data); } #endif ret = heim_ntlm_encode_type3(&type3, &ndata); if (ret) goto error; data.data = ndata.data; data.length = ndata.length; ret = krb5_store_data(response, data); heim_ntlm_free_buf(&ndata); if (ret) goto error; ret = krb5_store_int32(response, flags); if (ret) goto error; data.data = sessionkey.data; data.length = sessionkey.length; ret = krb5_store_data(response, data); if (ret) goto error; error: free(type3.username); heim_ntlm_free_type2(&type2); free(user); if (domain) free(domain); return ret; } /* * { "GET_NTLM_UUID_LIST", NULL } * * reply: * 1 user domain * 0 [ end of list ] */ static krb5_error_code kcm_op_get_ntlm_user_list(krb5_context context, kcm_client *client, kcm_operation opcode, krb5_storage *request, krb5_storage *response) { struct kcm_ntlm_cred *c; krb5_error_code ret; for (c = ntlm_head; c != NULL; c = c->next) { if (!kcm_is_same_session(client, c->uid, c->session)) continue; ret = krb5_store_uint32(response, 1); if (ret) return ret; ret = krb5_store_stringz(response, c->user); if (ret) return ret; ret = krb5_store_stringz(response, c->domain); if (ret) return ret; } return krb5_store_uint32(response, 0); } /* * */ static struct kcm_op kcm_ops[] = { { "NOOP", kcm_op_noop }, { "GET_NAME", kcm_op_get_name }, { "RESOLVE", kcm_op_noop }, { "GEN_NEW", kcm_op_gen_new }, { "INITIALIZE", kcm_op_initialize }, { "DESTROY", kcm_op_destroy }, { "STORE", kcm_op_store }, { "RETRIEVE", kcm_op_retrieve }, { "GET_PRINCIPAL", kcm_op_get_principal }, { "GET_CRED_UUID_LIST", kcm_op_get_cred_uuid_list }, { "GET_CRED_BY_UUID", kcm_op_get_cred_by_uuid }, { "REMOVE_CRED", kcm_op_remove_cred }, { "SET_FLAGS", kcm_op_set_flags }, { "CHOWN", kcm_op_chown }, { "CHMOD", kcm_op_chmod }, { "GET_INITIAL_TICKET", kcm_op_get_initial_ticket }, { "GET_TICKET", kcm_op_get_ticket }, { "MOVE_CACHE", kcm_op_move_cache }, { "GET_CACHE_UUID_LIST", kcm_op_get_cache_uuid_list }, { "GET_CACHE_BY_UUID", kcm_op_get_cache_by_uuid }, { "GET_DEFAULT_CACHE", kcm_op_get_default_cache }, { "SET_DEFAULT_CACHE", kcm_op_set_default_cache }, { "GET_KDC_OFFSET", kcm_op_get_kdc_offset }, { "SET_KDC_OFFSET", kcm_op_set_kdc_offset }, { "ADD_NTLM_CRED", kcm_op_add_ntlm_cred }, { "HAVE_USER_CRED", kcm_op_have_ntlm_cred }, { "DEL_NTLM_CRED", kcm_op_del_ntlm_cred }, { "DO_NTLM_AUTH", kcm_op_do_ntlm }, { "GET_NTLM_USER_LIST", kcm_op_get_ntlm_user_list } }; const char * kcm_op2string(kcm_operation opcode) { if (opcode >= sizeof(kcm_ops)/sizeof(kcm_ops[0])) return "Unknown operation"; return kcm_ops[opcode].name; } krb5_error_code kcm_dispatch(krb5_context context, kcm_client *client, krb5_data *req_data, krb5_data *resp_data) { krb5_error_code ret; kcm_method method; krb5_storage *req_sp = NULL; krb5_storage *resp_sp = NULL; uint16_t opcode; resp_sp = krb5_storage_emem(); if (resp_sp == NULL) { return ENOMEM; } if (client->pid == -1) { kcm_log(0, "Client had invalid process number"); ret = KRB5_FCC_INTERNAL; goto out; } req_sp = krb5_storage_from_data(req_data); if (req_sp == NULL) { kcm_log(0, "Process %d: failed to initialize storage from data", client->pid); ret = KRB5_CC_IO; goto out; } ret = krb5_ret_uint16(req_sp, &opcode); if (ret) { kcm_log(0, "Process %d: didn't send a message", client->pid); goto out; } if (opcode >= sizeof(kcm_ops)/sizeof(kcm_ops[0])) { kcm_log(0, "Process %d: invalid operation code %d", client->pid, opcode); ret = KRB5_FCC_INTERNAL; goto out; } method = kcm_ops[opcode].method; if (method == NULL) { kcm_log(0, "Process %d: operation code %s not implemented", client->pid, kcm_op2string(opcode)); ret = KRB5_FCC_INTERNAL; goto out; } /* seek past place for status code */ krb5_storage_seek(resp_sp, 4, SEEK_SET); ret = (*method)(context, client, opcode, req_sp, resp_sp); out: if (req_sp != NULL) { krb5_storage_free(req_sp); } krb5_storage_seek(resp_sp, 0, SEEK_SET); krb5_store_int32(resp_sp, ret); ret = krb5_storage_to_data(resp_sp, resp_data); krb5_storage_free(resp_sp); return ret; } diff --git a/crypto/heimdal/kdc/digest.c b/crypto/heimdal/kdc/digest.c index 9398803f04d2..f6b2342f0318 100644 --- a/crypto/heimdal/kdc/digest.c +++ b/crypto/heimdal/kdc/digest.c @@ -1,1515 +1,1519 @@ /* * 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_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; 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; } 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_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); + if (ret) { + krb5_prepend_error_message(context, ret, "Failed to encrypt digest: "); + goto out; + } 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 */ diff --git a/crypto/heimdal/kdc/hpropd.c b/crypto/heimdal/kdc/hpropd.c index 75b26a15f501..1cfc688b2a6c 100644 --- a/crypto/heimdal/kdc/hpropd.c +++ b/crypto/heimdal/kdc/hpropd.c @@ -1,281 +1,284 @@ /* * Copyright (c) 1997-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 "hprop.h" static int inetd_flag = -1; static int help_flag; static int version_flag; static int print_dump; static const char *database; static int from_stdin; static char *local_realm; static char *ktname = NULL; struct getargs args[] = { { "database", 'd', arg_string, rk_UNCONST(&database), "database", "file" }, { "stdin", 'n', arg_flag, &from_stdin, "read from stdin", NULL }, { "print", 0, arg_flag, &print_dump, "print dump to stdout", NULL }, #ifdef SUPPORT_INETD { "inetd", 'i', arg_negative_flag, &inetd_flag, "Not started from inetd", NULL }, #endif { "keytab", 'k', arg_string, &ktname, "keytab to use for authentication", "keytab" }, { "realm", 'r', arg_string, &local_realm, "realm to use", NULL }, { "version", 0, arg_flag, &version_flag, NULL, NULL }, { "help", 'h', arg_flag, &help_flag, NULL, NULL} }; static int num_args = sizeof(args) / sizeof(args[0]); static char unparseable_name[] = "unparseable name"; static void usage(int ret) { arg_printusage (args, num_args, NULL, ""); exit (ret); } int main(int argc, char **argv) { krb5_error_code ret; krb5_context context; krb5_auth_context ac = NULL; krb5_principal c1, c2; krb5_authenticator authent; krb5_keytab keytab; krb5_socket_t sock = rk_INVALID_SOCKET; HDB *db = NULL; int optidx = 0; char *tmp_db; krb5_log_facility *fac; int nprincs; setprogname(argv[0]); ret = krb5_init_context(&context); if(ret) exit(1); ret = krb5_openlog(context, "hpropd", &fac); if(ret) errx(1, "krb5_openlog"); krb5_set_warn_dest(context, fac); if(getarg(args, num_args, argc, argv, &optidx)) usage(1); if(local_realm != NULL) krb5_set_default_realm(context, local_realm); if(help_flag) usage(0); if(version_flag) { print_version(NULL); exit(0); } argc -= optidx; +#ifndef __clang_analyzer__ argv += optidx; +#endif if (argc != 0) usage(1); if (database == NULL) database = hdb_default_db(context); if(from_stdin) { sock = STDIN_FILENO; } else { struct sockaddr_storage ss; struct sockaddr *sa = (struct sockaddr *)&ss; socklen_t sin_len = sizeof(ss); char addr_name[256]; krb5_ticket *ticket; char *server; + memset(&ss, 0, sizeof(ss)); sock = STDIN_FILENO; #ifdef SUPPORT_INETD if (inetd_flag == -1) { if (getpeername (sock, sa, &sin_len) < 0) { inetd_flag = 0; } else { inetd_flag = 1; } } #else inetd_flag = 0; #endif if (!inetd_flag) { mini_inetd (krb5_getportbyname (context, "hprop", "tcp", HPROP_PORT), &sock); } sin_len = sizeof(ss); if(getpeername(sock, sa, &sin_len) < 0) krb5_err(context, 1, errno, "getpeername"); if (inet_ntop(sa->sa_family, socket_get_address (sa), addr_name, sizeof(addr_name)) == NULL) strlcpy (addr_name, "unknown address", sizeof(addr_name)); krb5_log(context, fac, 0, "Connection from %s", addr_name); ret = krb5_kt_register(context, &hdb_kt_ops); if(ret) krb5_err(context, 1, ret, "krb5_kt_register"); if (ktname != NULL) { ret = krb5_kt_resolve(context, ktname, &keytab); if (ret) krb5_err (context, 1, ret, "krb5_kt_resolve %s", ktname); } else { ret = krb5_kt_default (context, &keytab); if (ret) krb5_err (context, 1, ret, "krb5_kt_default"); } ret = krb5_recvauth(context, &ac, &sock, HPROP_VERSION, NULL, 0, keytab, &ticket); if(ret) krb5_err(context, 1, ret, "krb5_recvauth"); ret = krb5_unparse_name(context, ticket->server, &server); if (ret) krb5_err(context, 1, ret, "krb5_unparse_name"); if (strncmp(server, "hprop/", 5) != 0) krb5_errx(context, 1, "ticket not for hprop (%s)", server); free(server); krb5_free_ticket (context, ticket); ret = krb5_auth_con_getauthenticator(context, ac, &authent); if(ret) krb5_err(context, 1, ret, "krb5_auth_con_getauthenticator"); ret = krb5_make_principal(context, &c1, NULL, "kadmin", "hprop", NULL); if(ret) krb5_err(context, 1, ret, "krb5_make_principal"); _krb5_principalname2krb5_principal(context, &c2, authent->cname, authent->crealm); if(!krb5_principal_compare(context, c1, c2)) { char *s; ret = krb5_unparse_name(context, c2, &s); if (ret) s = unparseable_name; krb5_errx(context, 1, "Unauthorized connection from %s", s); } krb5_free_principal(context, c1); krb5_free_principal(context, c2); ret = krb5_kt_close(context, keytab); if(ret) krb5_err(context, 1, ret, "krb5_kt_close"); } if(!print_dump) { asprintf(&tmp_db, "%s~", database); ret = hdb_create(context, &db, tmp_db); if(ret) krb5_err(context, 1, ret, "hdb_create(%s)", tmp_db); ret = db->hdb_open(context, db, O_RDWR | O_CREAT | O_TRUNC, 0600); if(ret) krb5_err(context, 1, ret, "hdb_open(%s)", tmp_db); } nprincs = 0; while(1){ krb5_data data; hdb_entry_ex entry; if(from_stdin) { ret = krb5_read_message(context, &sock, &data); if(ret != 0 && ret != HEIM_ERR_EOF) krb5_err(context, 1, ret, "krb5_read_message"); } else { ret = krb5_read_priv_message(context, ac, &sock, &data); if(ret) krb5_err(context, 1, ret, "krb5_read_priv_message"); } if(ret == HEIM_ERR_EOF || data.length == 0) { if(!from_stdin) { data.data = NULL; data.length = 0; krb5_write_priv_message(context, ac, &sock, &data); } if(!print_dump) { ret = db->hdb_close(context, db); if(ret) krb5_err(context, 1, ret, "db_close"); ret = db->hdb_rename(context, db, database); if(ret) krb5_err(context, 1, ret, "db_rename"); } break; } memset(&entry, 0, sizeof(entry)); ret = hdb_value2entry(context, &data, &entry.entry); krb5_data_free(&data); if(ret) krb5_err(context, 1, ret, "hdb_value2entry"); if(print_dump) hdb_print_entry(context, db, &entry, stdout); else { ret = db->hdb_store(context, db, 0, &entry); if(ret == HDB_ERR_EXISTS) { char *s; ret = krb5_unparse_name(context, entry.entry.principal, &s); if (ret) s = strdup(unparseable_name); krb5_warnx(context, "Entry exists: %s", s); free(s); } else if(ret) krb5_err(context, 1, ret, "db_store"); else nprincs++; } hdb_free_entry(context, &entry); } if (!print_dump) krb5_log(context, fac, 0, "Received %d principals", nprincs); if (inetd_flag == 0) rk_closesocket(sock); exit(0); } diff --git a/crypto/heimdal/kdc/kdc-replay.c b/crypto/heimdal/kdc/kdc-replay.c index b0510f408924..90dcf33048a7 100644 --- a/crypto/heimdal/kdc/kdc-replay.c +++ b/crypto/heimdal/kdc/kdc-replay.c @@ -1,212 +1,214 @@ /* * Copyright (c) 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" static int version_flag; static int help_flag; struct getargs args[] = { { "version", 0, arg_flag, &version_flag }, { "help", 'h', arg_flag, &help_flag } }; const static int num_args = sizeof(args) / sizeof(args[0]); static void usage(int ret) { arg_printusage (args, num_args, NULL, "kdc-request-log-file"); exit (ret); } int main(int argc, char **argv) { krb5_error_code ret; krb5_context context; krb5_kdc_configuration *config; krb5_storage *sp; int fd, 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); } ret = krb5_init_context(&context); if (ret) errx (1, "krb5_init_context failed to parse configuration file"); ret = krb5_kdc_get_config(context, &config); if (ret) krb5_err(context, 1, ret, "krb5_kdc_default_config"); kdc_openlog(context, "kdc-replay", config); ret = krb5_kdc_set_dbinfo(context, config); if (ret) krb5_err(context, 1, ret, "krb5_kdc_set_dbinfo"); #ifdef PKINIT if (config->enable_pkinit) { if (config->pkinit_kdc_identity == NULL) krb5_errx(context, 1, "pkinit enabled but no identity"); if (config->pkinit_kdc_anchors == NULL) krb5_errx(context, 1, "pkinit enabled but no X509 anchors"); krb5_kdc_pk_initialize(context, config, config->pkinit_kdc_identity, config->pkinit_kdc_anchors, config->pkinit_kdc_cert_pool, config->pkinit_kdc_revoke); } #endif /* PKINIT */ if (argc != 2) errx(1, "argc != 2"); printf("kdc replay\n"); fd = open(argv[1], O_RDONLY); if (fd < 0) err(1, "open: %s", argv[1]); sp = krb5_storage_from_fd(fd); if (sp == NULL) krb5_errx(context, 1, "krb5_storage_from_fd"); while(1) { struct sockaddr_storage sa; krb5_socklen_t salen = sizeof(sa); struct timeval tv; krb5_address a; krb5_data d, r; uint32_t t, clty, tag; char astr[80]; ret = krb5_ret_uint32(sp, &t); if (ret == HEIM_ERR_EOF) break; else if (ret) krb5_errx(context, 1, "krb5_ret_uint32(version)"); if (t != 1) krb5_errx(context, 1, "version not 1"); ret = krb5_ret_uint32(sp, &t); if (ret) krb5_errx(context, 1, "krb5_ret_uint32(time)"); ret = krb5_ret_address(sp, &a); if (ret) krb5_errx(context, 1, "krb5_ret_address"); ret = krb5_ret_data(sp, &d); if (ret) krb5_errx(context, 1, "krb5_ret_data"); ret = krb5_ret_uint32(sp, &clty); if (ret) krb5_errx(context, 1, "krb5_ret_uint32(class|type)"); ret = krb5_ret_uint32(sp, &tag); if (ret) krb5_errx(context, 1, "krb5_ret_uint32(tag)"); ret = krb5_addr2sockaddr (context, &a, (struct sockaddr *)&sa, &salen, 88); if (ret == KRB5_PROG_ATYPE_NOSUPP) goto out; else if (ret) krb5_err(context, 1, ret, "krb5_addr2sockaddr"); ret = krb5_print_address(&a, astr, sizeof(astr), NULL); if (ret) krb5_err(context, 1, ret, "krb5_print_address"); printf("processing request from %s, %lu bytes\n", astr, (unsigned long)d.length); r.length = 0; r.data = NULL; tv.tv_sec = t; tv.tv_usec = 0; krb5_kdc_update_time(&tv); krb5_set_real_time(context, tv.tv_sec, 0); ret = krb5_kdc_process_request(context, config, d.data, d.length, &r, NULL, astr, (struct sockaddr *)&sa, 0); if (ret) krb5_err(context, 1, ret, "krb5_kdc_process_request"); if (r.length) { Der_class cl; Der_type ty; unsigned int tag2; ret = der_get_tag (r.data, r.length, &cl, &ty, &tag2, NULL); + if (ret) + krb5_err(context, 1, ret, "Could not decode replay data"); if (MAKE_TAG(cl, ty, 0) != clty) krb5_errx(context, 1, "class|type mismatch: %d != %d", (int)MAKE_TAG(cl, ty, 0), (int)clty); if (tag != tag2) krb5_errx(context, 1, "tag mismatch"); krb5_data_free(&r); } else { if (clty != 0xffffffff) krb5_errx(context, 1, "clty not invalid"); if (tag != 0xffffffff) krb5_errx(context, 1, "tag not invalid"); } out: krb5_data_free(&d); krb5_free_address(context, &a); } krb5_storage_free(sp); krb5_free_context(context); printf("done\n"); return 0; } diff --git a/crypto/heimdal/kdc/krb5tgs.c b/crypto/heimdal/kdc/krb5tgs.c index 87e33930be15..19d669798830 100644 --- a/crypto/heimdal/kdc/krb5tgs.c +++ b/crypto/heimdal/kdc/krb5tgs.c @@ -1,2395 +1,2407 @@ /* * 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 "kdc_locl.h" /* * return the realm of a krbtgt-ticket or NULL */ static Realm get_krbtgt_realm(const PrincipalName *p) { if(p->name_string.len == 2 && strcmp(p->name_string.val[0], KRB5_TGS_NAME) == 0) return p->name_string.val[1]; else return NULL; } /* * The KDC might add a signed path to the ticket authorization data * field. This is to avoid server impersonating clients and the * request constrained delegation. * * This is done by storing a KRB5_AUTHDATA_IF_RELEVANT with a single * entry of type KRB5SignedPath. */ static krb5_error_code find_KRB5SignedPath(krb5_context context, const AuthorizationData *ad, krb5_data *data) { AuthorizationData child; krb5_error_code ret; int pos; if (ad == NULL || ad->len == 0) return KRB5KDC_ERR_PADATA_TYPE_NOSUPP; pos = ad->len - 1; if (ad->val[pos].ad_type != KRB5_AUTHDATA_IF_RELEVANT) return KRB5KDC_ERR_PADATA_TYPE_NOSUPP; ret = decode_AuthorizationData(ad->val[pos].ad_data.data, ad->val[pos].ad_data.length, &child, NULL); if (ret) { krb5_set_error_message(context, ret, "Failed to decode " "IF_RELEVANT with %d", ret); return ret; } if (child.len != 1) { free_AuthorizationData(&child); return KRB5KDC_ERR_PADATA_TYPE_NOSUPP; } if (child.val[0].ad_type != KRB5_AUTHDATA_SIGNTICKET) { free_AuthorizationData(&child); return KRB5KDC_ERR_PADATA_TYPE_NOSUPP; } if (data) ret = der_copy_octet_string(&child.val[0].ad_data, data); free_AuthorizationData(&child); return ret; } krb5_error_code _kdc_add_KRB5SignedPath(krb5_context context, krb5_kdc_configuration *config, hdb_entry_ex *krbtgt, krb5_enctype enctype, krb5_principal client, krb5_const_principal server, krb5_principals principals, EncTicketPart *tkt) { krb5_error_code ret; KRB5SignedPath sp; krb5_data data; krb5_crypto crypto = NULL; size_t size = 0; if (server && principals) { ret = add_Principals(principals, server); if (ret) return ret; } { KRB5SignedPathData spd; spd.client = client; spd.authtime = tkt->authtime; spd.delegated = principals; spd.method_data = NULL; ASN1_MALLOC_ENCODE(KRB5SignedPathData, data.data, data.length, &spd, &size, ret); if (ret) return ret; if (data.length != size) krb5_abortx(context, "internal asn.1 encoder error"); } { Key *key; ret = hdb_enctype2key(context, &krbtgt->entry, enctype, &key); if (ret == 0) ret = krb5_crypto_init(context, &key->key, 0, &crypto); if (ret) { free(data.data); return ret; } } /* * Fill in KRB5SignedPath */ sp.etype = enctype; sp.delegated = principals; sp.method_data = NULL; ret = krb5_create_checksum(context, crypto, KRB5_KU_KRB5SIGNEDPATH, 0, data.data, data.length, &sp.cksum); krb5_crypto_destroy(context, crypto); free(data.data); if (ret) return ret; ASN1_MALLOC_ENCODE(KRB5SignedPath, data.data, data.length, &sp, &size, ret); free_Checksum(&sp.cksum); if (ret) return ret; if (data.length != size) krb5_abortx(context, "internal asn.1 encoder error"); /* * Add IF-RELEVANT(KRB5SignedPath) to the last slot in * authorization data field. */ ret = _kdc_tkt_add_if_relevant_ad(context, tkt, KRB5_AUTHDATA_SIGNTICKET, &data); krb5_data_free(&data); return ret; } static krb5_error_code check_KRB5SignedPath(krb5_context context, krb5_kdc_configuration *config, hdb_entry_ex *krbtgt, krb5_principal cp, EncTicketPart *tkt, krb5_principals *delegated, int *signedpath) { krb5_error_code ret; krb5_data data; krb5_crypto crypto = NULL; if (delegated) *delegated = NULL; ret = find_KRB5SignedPath(context, tkt->authorization_data, &data); if (ret == 0) { KRB5SignedPathData spd; KRB5SignedPath sp; size_t size = 0; ret = decode_KRB5SignedPath(data.data, data.length, &sp, NULL); krb5_data_free(&data); if (ret) return ret; spd.client = cp; spd.authtime = tkt->authtime; spd.delegated = sp.delegated; spd.method_data = sp.method_data; ASN1_MALLOC_ENCODE(KRB5SignedPathData, data.data, data.length, &spd, &size, ret); if (ret) { free_KRB5SignedPath(&sp); return ret; } if (data.length != size) krb5_abortx(context, "internal asn.1 encoder error"); { Key *key; ret = hdb_enctype2key(context, &krbtgt->entry, sp.etype, &key); if (ret == 0) ret = krb5_crypto_init(context, &key->key, 0, &crypto); if (ret) { free(data.data); free_KRB5SignedPath(&sp); return ret; } } ret = krb5_verify_checksum(context, crypto, KRB5_KU_KRB5SIGNEDPATH, data.data, data.length, &sp.cksum); krb5_crypto_destroy(context, crypto); free(data.data); if (ret) { free_KRB5SignedPath(&sp); kdc_log(context, config, 5, "KRB5SignedPath not signed correctly, not marking as signed"); return 0; } if (delegated && sp.delegated) { *delegated = malloc(sizeof(*sp.delegated)); if (*delegated == NULL) { free_KRB5SignedPath(&sp); return ENOMEM; } ret = copy_Principals(*delegated, sp.delegated); if (ret) { free_KRB5SignedPath(&sp); free(*delegated); *delegated = NULL; return ret; } } free_KRB5SignedPath(&sp); *signedpath = 1; } return 0; } /* * */ static krb5_error_code check_PAC(krb5_context context, krb5_kdc_configuration *config, const krb5_principal client_principal, const krb5_principal delegated_proxy_principal, hdb_entry_ex *client, hdb_entry_ex *server, hdb_entry_ex *krbtgt, const EncryptionKey *server_check_key, const EncryptionKey *krbtgt_check_key, const EncryptionKey *server_sign_key, const EncryptionKey *krbtgt_sign_key, EncTicketPart *tkt, krb5_data *rspac, int *signedpath) { AuthorizationData *ad = tkt->authorization_data; unsigned i, j; krb5_error_code ret; if (ad == NULL || ad->len == 0) return 0; for (i = 0; i < ad->len; i++) { AuthorizationData child; if (ad->val[i].ad_type != KRB5_AUTHDATA_IF_RELEVANT) continue; ret = decode_AuthorizationData(ad->val[i].ad_data.data, ad->val[i].ad_data.length, &child, NULL); if (ret) { krb5_set_error_message(context, ret, "Failed to decode " "IF_RELEVANT with %d", ret); return ret; } for (j = 0; j < child.len; j++) { if (child.val[j].ad_type == KRB5_AUTHDATA_WIN2K_PAC) { int signed_pac = 0; krb5_pac pac; /* Found PAC */ ret = krb5_pac_parse(context, child.val[j].ad_data.data, child.val[j].ad_data.length, &pac); free_AuthorizationData(&child); if (ret) return ret; ret = krb5_pac_verify(context, pac, tkt->authtime, client_principal, server_check_key, krbtgt_check_key); if (ret) { krb5_pac_free(context, pac); return ret; } ret = _kdc_pac_verify(context, client_principal, delegated_proxy_principal, client, server, krbtgt, &pac, &signed_pac); if (ret) { krb5_pac_free(context, pac); return ret; } /* * Only re-sign PAC if we could verify it with the PAC * function. The no-verify case happens when we get in * a PAC from cross realm from a Windows domain and * that there is no PAC verification function. */ if (signed_pac) { *signedpath = 1; ret = _krb5_pac_sign(context, pac, tkt->authtime, client_principal, server_sign_key, krbtgt_sign_key, rspac); } krb5_pac_free(context, pac); return ret; } } free_AuthorizationData(&child); } return 0; } /* * */ static krb5_error_code check_tgs_flags(krb5_context context, krb5_kdc_configuration *config, KDC_REQ_BODY *b, const EncTicketPart *tgt, EncTicketPart *et) { KDCOptions f = b->kdc_options; if(f.validate){ if(!tgt->flags.invalid || tgt->starttime == NULL){ kdc_log(context, config, 0, "Bad request to validate ticket"); return KRB5KDC_ERR_BADOPTION; } if(*tgt->starttime > kdc_time){ kdc_log(context, config, 0, "Early request to validate ticket"); return KRB5KRB_AP_ERR_TKT_NYV; } /* XXX tkt = tgt */ et->flags.invalid = 0; }else if(tgt->flags.invalid){ kdc_log(context, config, 0, "Ticket-granting ticket has INVALID flag set"); return KRB5KRB_AP_ERR_TKT_INVALID; } if(f.forwardable){ if(!tgt->flags.forwardable){ kdc_log(context, config, 0, "Bad request for forwardable ticket"); return KRB5KDC_ERR_BADOPTION; } et->flags.forwardable = 1; } if(f.forwarded){ if(!tgt->flags.forwardable){ kdc_log(context, config, 0, "Request to forward non-forwardable ticket"); return KRB5KDC_ERR_BADOPTION; } et->flags.forwarded = 1; et->caddr = b->addresses; } if(tgt->flags.forwarded) et->flags.forwarded = 1; if(f.proxiable){ if(!tgt->flags.proxiable){ kdc_log(context, config, 0, "Bad request for proxiable ticket"); return KRB5KDC_ERR_BADOPTION; } et->flags.proxiable = 1; } if(f.proxy){ if(!tgt->flags.proxiable){ kdc_log(context, config, 0, "Request to proxy non-proxiable ticket"); return KRB5KDC_ERR_BADOPTION; } et->flags.proxy = 1; et->caddr = b->addresses; } if(tgt->flags.proxy) et->flags.proxy = 1; if(f.allow_postdate){ if(!tgt->flags.may_postdate){ kdc_log(context, config, 0, "Bad request for post-datable ticket"); return KRB5KDC_ERR_BADOPTION; } et->flags.may_postdate = 1; } if(f.postdated){ if(!tgt->flags.may_postdate){ kdc_log(context, config, 0, "Bad request for postdated ticket"); return KRB5KDC_ERR_BADOPTION; } if(b->from) *et->starttime = *b->from; et->flags.postdated = 1; et->flags.invalid = 1; }else if(b->from && *b->from > kdc_time + context->max_skew){ kdc_log(context, config, 0, "Ticket cannot be postdated"); return KRB5KDC_ERR_CANNOT_POSTDATE; } if(f.renewable){ if(!tgt->flags.renewable || tgt->renew_till == NULL){ kdc_log(context, config, 0, "Bad request for renewable ticket"); return KRB5KDC_ERR_BADOPTION; } et->flags.renewable = 1; ALLOC(et->renew_till); _kdc_fix_time(&b->rtime); *et->renew_till = *b->rtime; } if(f.renew){ time_t old_life; if(!tgt->flags.renewable || tgt->renew_till == NULL){ kdc_log(context, config, 0, "Request to renew non-renewable ticket"); return KRB5KDC_ERR_BADOPTION; } old_life = tgt->endtime; if(tgt->starttime) old_life -= *tgt->starttime; else old_life -= tgt->authtime; et->endtime = *et->starttime + old_life; if (et->renew_till != NULL) et->endtime = min(*et->renew_till, et->endtime); } #if 0 /* checks for excess flags */ if(f.request_anonymous && !config->allow_anonymous){ kdc_log(context, config, 0, "Request for anonymous ticket"); return KRB5KDC_ERR_BADOPTION; } #endif return 0; } /* * Determine if constrained delegation is allowed from this client to this server */ static krb5_error_code check_constrained_delegation(krb5_context context, krb5_kdc_configuration *config, HDB *clientdb, hdb_entry_ex *client, hdb_entry_ex *server, krb5_const_principal target) { const HDB_Ext_Constrained_delegation_acl *acl; krb5_error_code ret; size_t i; /* * constrained_delegation (S4U2Proxy) only works within * the same realm. We use the already canonicalized version * of the principals here, while "target" is the principal * provided by the client. */ if(!krb5_realm_compare(context, client->entry.principal, server->entry.principal)) { ret = KRB5KDC_ERR_BADOPTION; kdc_log(context, config, 0, "Bad request for constrained delegation"); return ret; } if (clientdb->hdb_check_constrained_delegation) { ret = clientdb->hdb_check_constrained_delegation(context, clientdb, client, target); if (ret == 0) return 0; } else { /* if client delegates to itself, that ok */ if (krb5_principal_compare(context, client->entry.principal, server->entry.principal) == TRUE) return 0; ret = hdb_entry_get_ConstrainedDelegACL(&client->entry, &acl); if (ret) { krb5_clear_error_message(context); return ret; } if (acl) { for (i = 0; i < acl->len; i++) { if (krb5_principal_compare(context, target, &acl->val[i]) == TRUE) return 0; } } ret = KRB5KDC_ERR_BADOPTION; } kdc_log(context, config, 0, "Bad request for constrained delegation"); return ret; } /* * Determine if s4u2self is allowed from this client to this server * * For example, regardless of the principal being impersonated, if the * 'client' and 'server' are the same, then it's safe. */ static krb5_error_code check_s4u2self(krb5_context context, krb5_kdc_configuration *config, HDB *clientdb, hdb_entry_ex *client, krb5_const_principal server) { krb5_error_code ret; /* if client does a s4u2self to itself, that ok */ if (krb5_principal_compare(context, client->entry.principal, server) == TRUE) return 0; if (clientdb->hdb_check_s4u2self) { ret = clientdb->hdb_check_s4u2self(context, clientdb, client, server); if (ret == 0) return 0; } else { ret = KRB5KDC_ERR_BADOPTION; } return ret; } /* * */ static krb5_error_code verify_flags (krb5_context context, krb5_kdc_configuration *config, const EncTicketPart *et, const char *pstr) { if(et->endtime < kdc_time){ kdc_log(context, config, 0, "Ticket expired (%s)", pstr); return KRB5KRB_AP_ERR_TKT_EXPIRED; } if(et->flags.invalid){ kdc_log(context, config, 0, "Ticket not valid (%s)", pstr); return KRB5KRB_AP_ERR_TKT_NYV; } return 0; } /* * */ static krb5_error_code fix_transited_encoding(krb5_context context, krb5_kdc_configuration *config, krb5_boolean check_policy, const TransitedEncoding *tr, EncTicketPart *et, const char *client_realm, const char *server_realm, const char *tgt_realm) { krb5_error_code ret = 0; char **realms, **tmp; unsigned int num_realms; size_t i; switch (tr->tr_type) { case DOMAIN_X500_COMPRESS: break; case 0: /* * Allow empty content of type 0 because that is was Microsoft * generates in their TGT. */ if (tr->contents.length == 0) break; kdc_log(context, config, 0, "Transited type 0 with non empty content"); return KRB5KDC_ERR_TRTYPE_NOSUPP; default: kdc_log(context, config, 0, "Unknown transited type: %u", tr->tr_type); return KRB5KDC_ERR_TRTYPE_NOSUPP; } ret = krb5_domain_x500_decode(context, tr->contents, &realms, &num_realms, client_realm, server_realm); if(ret){ krb5_warn(context, ret, "Decoding transited encoding"); return ret; } if(strcmp(client_realm, tgt_realm) && strcmp(server_realm, tgt_realm)) { /* not us, so add the previous realm to transited set */ if (num_realms + 1 > UINT_MAX/sizeof(*realms)) { ret = ERANGE; goto free_realms; } tmp = realloc(realms, (num_realms + 1) * sizeof(*realms)); if(tmp == NULL){ ret = ENOMEM; goto free_realms; } realms = tmp; realms[num_realms] = strdup(tgt_realm); if(realms[num_realms] == NULL){ ret = ENOMEM; goto free_realms; } num_realms++; } if(num_realms == 0) { if(strcmp(client_realm, server_realm)) kdc_log(context, config, 0, "cross-realm %s -> %s", client_realm, server_realm); } else { size_t l = 0; char *rs; for(i = 0; i < num_realms; i++) l += strlen(realms[i]) + 2; rs = malloc(l); if(rs != NULL) { *rs = '\0'; for(i = 0; i < num_realms; i++) { if(i > 0) strlcat(rs, ", ", l); strlcat(rs, realms[i], l); } kdc_log(context, config, 0, "cross-realm %s -> %s via [%s]", client_realm, server_realm, rs); free(rs); } } if(check_policy) { ret = krb5_check_transited(context, client_realm, server_realm, realms, num_realms, NULL); if(ret) { krb5_warn(context, ret, "cross-realm %s -> %s", client_realm, server_realm); goto free_realms; } et->flags.transited_policy_checked = 1; } et->transited.tr_type = DOMAIN_X500_COMPRESS; ret = krb5_domain_x500_encode(realms, num_realms, &et->transited.contents); if(ret) krb5_warn(context, ret, "Encoding transited encoding"); free_realms: for(i = 0; i < num_realms; i++) free(realms[i]); free(realms); return ret; } static krb5_error_code tgs_make_reply(krb5_context context, krb5_kdc_configuration *config, KDC_REQ_BODY *b, krb5_const_principal tgt_name, const EncTicketPart *tgt, const krb5_keyblock *replykey, int rk_is_subkey, const EncryptionKey *serverkey, const krb5_keyblock *sessionkey, krb5_kvno kvno, AuthorizationData *auth_data, hdb_entry_ex *server, krb5_principal server_principal, const char *server_name, hdb_entry_ex *client, krb5_principal client_principal, hdb_entry_ex *krbtgt, krb5_enctype krbtgt_etype, krb5_principals spp, const krb5_data *rspac, const METHOD_DATA *enc_pa_data, const char **e_text, krb5_data *reply) { KDC_REP rep; EncKDCRepPart ek; EncTicketPart et; KDCOptions f = b->kdc_options; krb5_error_code ret; int is_weak = 0; memset(&rep, 0, sizeof(rep)); memset(&et, 0, sizeof(et)); memset(&ek, 0, sizeof(ek)); rep.pvno = 5; rep.msg_type = krb_tgs_rep; et.authtime = tgt->authtime; _kdc_fix_time(&b->till); et.endtime = min(tgt->endtime, *b->till); ALLOC(et.starttime); *et.starttime = kdc_time; ret = check_tgs_flags(context, config, b, tgt, &et); if(ret) goto out; /* We should check the transited encoding if: 1) the request doesn't ask not to be checked 2) globally enforcing a check 3) principal requires checking 4) we allow non-check per-principal, but principal isn't marked as allowing this 5) we don't globally allow this */ #define GLOBAL_FORCE_TRANSITED_CHECK \ (config->trpolicy == TRPOLICY_ALWAYS_CHECK) #define GLOBAL_ALLOW_PER_PRINCIPAL \ (config->trpolicy == TRPOLICY_ALLOW_PER_PRINCIPAL) #define GLOBAL_ALLOW_DISABLE_TRANSITED_CHECK \ (config->trpolicy == TRPOLICY_ALWAYS_HONOUR_REQUEST) /* these will consult the database in future release */ #define PRINCIPAL_FORCE_TRANSITED_CHECK(P) 0 #define PRINCIPAL_ALLOW_DISABLE_TRANSITED_CHECK(P) 0 ret = fix_transited_encoding(context, config, !f.disable_transited_check || GLOBAL_FORCE_TRANSITED_CHECK || PRINCIPAL_FORCE_TRANSITED_CHECK(server) || !((GLOBAL_ALLOW_PER_PRINCIPAL && PRINCIPAL_ALLOW_DISABLE_TRANSITED_CHECK(server)) || GLOBAL_ALLOW_DISABLE_TRANSITED_CHECK), &tgt->transited, &et, krb5_principal_get_realm(context, client_principal), krb5_principal_get_realm(context, server->entry.principal), krb5_principal_get_realm(context, krbtgt->entry.principal)); if(ret) goto out; copy_Realm(&server_principal->realm, &rep.ticket.realm); _krb5_principal2principalname(&rep.ticket.sname, server_principal); copy_Realm(&tgt_name->realm, &rep.crealm); /* if (f.request_anonymous) _kdc_make_anonymous_principalname (&rep.cname); else */ copy_PrincipalName(&tgt_name->name, &rep.cname); rep.ticket.tkt_vno = 5; ek.caddr = et.caddr; if(et.caddr == NULL) et.caddr = tgt->caddr; { time_t life; life = et.endtime - *et.starttime; if(client && client->entry.max_life) life = min(life, *client->entry.max_life); if(server->entry.max_life) life = min(life, *server->entry.max_life); et.endtime = *et.starttime + life; } if(f.renewable_ok && tgt->flags.renewable && et.renew_till == NULL && et.endtime < *b->till && tgt->renew_till != NULL) { et.flags.renewable = 1; ALLOC(et.renew_till); *et.renew_till = *b->till; } if(et.renew_till){ time_t renew; renew = *et.renew_till - et.authtime; if(client && client->entry.max_renew) renew = min(renew, *client->entry.max_renew); if(server->entry.max_renew) renew = min(renew, *server->entry.max_renew); *et.renew_till = et.authtime + renew; } if(et.renew_till){ *et.renew_till = min(*et.renew_till, *tgt->renew_till); *et.starttime = min(*et.starttime, *et.renew_till); et.endtime = min(et.endtime, *et.renew_till); } *et.starttime = min(*et.starttime, et.endtime); if(*et.starttime == et.endtime){ ret = KRB5KDC_ERR_NEVER_VALID; goto out; } if(et.renew_till && et.endtime == *et.renew_till){ free(et.renew_till); et.renew_till = NULL; et.flags.renewable = 0; } et.flags.pre_authent = tgt->flags.pre_authent; et.flags.hw_authent = tgt->flags.hw_authent; et.flags.anonymous = tgt->flags.anonymous; et.flags.ok_as_delegate = server->entry.flags.ok_as_delegate; if(rspac->length) { /* * No not need to filter out the any PAC from the * auth_data since it's signed by the KDC. */ ret = _kdc_tkt_add_if_relevant_ad(context, &et, KRB5_AUTHDATA_WIN2K_PAC, rspac); if (ret) goto out; } if (auth_data) { unsigned int i = 0; /* XXX check authdata */ if (et.authorization_data == NULL) { et.authorization_data = calloc(1, sizeof(*et.authorization_data)); if (et.authorization_data == NULL) { ret = ENOMEM; krb5_set_error_message(context, ret, "malloc: out of memory"); goto out; } } for(i = 0; i < auth_data->len ; i++) { ret = add_AuthorizationData(et.authorization_data, &auth_data->val[i]); if (ret) { krb5_set_error_message(context, ret, "malloc: out of memory"); goto out; } } /* Filter out type KRB5SignedPath */ ret = find_KRB5SignedPath(context, et.authorization_data, NULL); if (ret == 0) { if (et.authorization_data->len == 1) { free_AuthorizationData(et.authorization_data); free(et.authorization_data); et.authorization_data = NULL; } else { AuthorizationData *ad = et.authorization_data; free_AuthorizationDataElement(&ad->val[ad->len - 1]); ad->len--; } } } ret = krb5_copy_keyblock_contents(context, sessionkey, &et.key); if (ret) goto out; et.crealm = tgt_name->realm; et.cname = tgt_name->name; ek.key = et.key; /* MIT must have at least one last_req */ ek.last_req.len = 1; ek.last_req.val = calloc(1, sizeof(*ek.last_req.val)); if (ek.last_req.val == NULL) { ret = ENOMEM; goto out; } ek.nonce = b->nonce; ek.flags = et.flags; ek.authtime = et.authtime; ek.starttime = et.starttime; ek.endtime = et.endtime; ek.renew_till = et.renew_till; ek.srealm = rep.ticket.realm; ek.sname = rep.ticket.sname; _kdc_log_timestamp(context, config, "TGS-REQ", et.authtime, et.starttime, et.endtime, et.renew_till); /* Don't sign cross realm tickets, they can't be checked anyway */ { char *r = get_krbtgt_realm(&ek.sname); if (r == NULL || strcmp(r, ek.srealm) == 0) { ret = _kdc_add_KRB5SignedPath(context, config, krbtgt, krbtgt_etype, client_principal, NULL, spp, &et); if (ret) goto out; } } if (enc_pa_data->len) { rep.padata = calloc(1, sizeof(*rep.padata)); if (rep.padata == NULL) { ret = ENOMEM; goto out; } ret = copy_METHOD_DATA(enc_pa_data, rep.padata); if (ret) goto out; } if (krb5_enctype_valid(context, et.key.keytype) != 0 && _kdc_is_weak_exception(server->entry.principal, et.key.keytype)) { krb5_enctype_enable(context, et.key.keytype); is_weak = 1; } /* It is somewhat unclear where the etype in the following encryption should come from. What we have is a session key in the passed tgt, and a list of preferred etypes *for the new ticket*. Should we pick the best possible etype, given the keytype in the tgt, or should we look at the etype list here as well? What if the tgt session key is DES3 and we want a ticket with a (say) CAST session key. Should the DES3 etype be added to the etype list, even if we don't want a session key with DES3? */ ret = _kdc_encode_reply(context, config, &rep, &et, &ek, et.key.keytype, kvno, serverkey, 0, replykey, rk_is_subkey, e_text, reply); if (is_weak) krb5_enctype_disable(context, et.key.keytype); out: free_TGS_REP(&rep); free_TransitedEncoding(&et.transited); if(et.starttime) free(et.starttime); if(et.renew_till) free(et.renew_till); if(et.authorization_data) { free_AuthorizationData(et.authorization_data); free(et.authorization_data); } free_LastReq(&ek.last_req); memset(et.key.keyvalue.data, 0, et.key.keyvalue.length); free_EncryptionKey(&et.key); return ret; } static krb5_error_code tgs_check_authenticator(krb5_context context, krb5_kdc_configuration *config, krb5_auth_context ac, KDC_REQ_BODY *b, const char **e_text, krb5_keyblock *key) { krb5_authenticator auth; size_t len = 0; unsigned char *buf; size_t buf_size; krb5_error_code ret; krb5_crypto crypto; krb5_auth_con_getauthenticator(context, ac, &auth); if(auth->cksum == NULL){ kdc_log(context, config, 0, "No authenticator in request"); ret = KRB5KRB_AP_ERR_INAPP_CKSUM; goto out; } /* * according to RFC1510 it doesn't need to be keyed, * but according to the latest draft it needs to. */ if ( #if 0 !krb5_checksum_is_keyed(context, auth->cksum->cksumtype) || #endif !krb5_checksum_is_collision_proof(context, auth->cksum->cksumtype)) { kdc_log(context, config, 0, "Bad checksum type in authenticator: %d", auth->cksum->cksumtype); ret = KRB5KRB_AP_ERR_INAPP_CKSUM; goto out; } /* XXX should not re-encode this */ ASN1_MALLOC_ENCODE(KDC_REQ_BODY, buf, buf_size, b, &len, ret); if(ret){ const char *msg = krb5_get_error_message(context, ret); kdc_log(context, config, 0, "Failed to encode KDC-REQ-BODY: %s", msg); krb5_free_error_message(context, msg); goto out; } if(buf_size != len) { free(buf); kdc_log(context, config, 0, "Internal error in ASN.1 encoder"); *e_text = "KDC internal error"; ret = KRB5KRB_ERR_GENERIC; goto out; } ret = krb5_crypto_init(context, key, 0, &crypto); if (ret) { const char *msg = krb5_get_error_message(context, ret); free(buf); kdc_log(context, config, 0, "krb5_crypto_init failed: %s", msg); krb5_free_error_message(context, msg); goto out; } ret = krb5_verify_checksum(context, crypto, KRB5_KU_TGS_REQ_AUTH_CKSUM, buf, len, auth->cksum); free(buf); krb5_crypto_destroy(context, crypto); if(ret){ const char *msg = krb5_get_error_message(context, ret); kdc_log(context, config, 0, "Failed to verify authenticator checksum: %s", msg); krb5_free_error_message(context, msg); } out: free_Authenticator(auth); free(auth); return ret; } /* * */ static const char * find_rpath(krb5_context context, Realm crealm, Realm srealm) { const char *new_realm = krb5_config_get_string(context, NULL, "capaths", crealm, srealm, NULL); return new_realm; } static krb5_boolean need_referral(krb5_context context, krb5_kdc_configuration *config, const KDCOptions * const options, krb5_principal server, krb5_realm **realms) { const char *name; if(!options->canonicalize && server->name.name_type != KRB5_NT_SRV_INST) return FALSE; if (server->name.name_string.len == 1) name = server->name.name_string.val[0]; else if (server->name.name_string.len > 1) name = server->name.name_string.val[1]; else return FALSE; kdc_log(context, config, 0, "Searching referral for %s", name); return _krb5_get_host_realm_int(context, name, FALSE, realms) == 0; } static krb5_error_code tgs_parse_request(krb5_context context, krb5_kdc_configuration *config, KDC_REQ_BODY *b, const PA_DATA *tgs_req, hdb_entry_ex **krbtgt, krb5_enctype *krbtgt_etype, krb5_ticket **ticket, const char **e_text, const char *from, const struct sockaddr *from_addr, time_t **csec, int **cusec, AuthorizationData **auth_data, krb5_keyblock **replykey, int *rk_is_subkey) { static char failed[] = ""; krb5_ap_req ap_req; krb5_error_code ret; krb5_principal princ; krb5_auth_context ac = NULL; krb5_flags ap_req_options; krb5_flags verify_ap_req_flags; krb5_crypto crypto; Key *tkey; krb5_keyblock *subkey = NULL; unsigned usage; *auth_data = NULL; *csec = NULL; *cusec = NULL; *replykey = NULL; memset(&ap_req, 0, sizeof(ap_req)); ret = krb5_decode_ap_req(context, &tgs_req->padata_value, &ap_req); if(ret){ const char *msg = krb5_get_error_message(context, ret); kdc_log(context, config, 0, "Failed to decode AP-REQ: %s", msg); krb5_free_error_message(context, msg); goto out; } if(!get_krbtgt_realm(&ap_req.ticket.sname)){ /* XXX check for ticket.sname == req.sname */ kdc_log(context, config, 0, "PA-DATA is not a ticket-granting ticket"); ret = KRB5KDC_ERR_POLICY; /* ? */ goto out; } _krb5_principalname2krb5_principal(context, &princ, ap_req.ticket.sname, ap_req.ticket.realm); ret = _kdc_db_fetch(context, config, princ, HDB_F_GET_KRBTGT, ap_req.ticket.enc_part.kvno, NULL, krbtgt); if(ret == HDB_ERR_NOT_FOUND_HERE) { char *p; ret = krb5_unparse_name(context, princ, &p); if (ret != 0) p = failed; krb5_free_principal(context, princ); kdc_log(context, config, 5, "Ticket-granting ticket account %s does not have secrets at this KDC, need to proxy", p); if (ret == 0) free(p); ret = HDB_ERR_NOT_FOUND_HERE; goto out; } else if(ret){ const char *msg = krb5_get_error_message(context, ret); char *p; ret = krb5_unparse_name(context, princ, &p); if (ret != 0) p = failed; krb5_free_principal(context, princ); kdc_log(context, config, 0, "Ticket-granting ticket not found in database: %s", msg); krb5_free_error_message(context, msg); if (ret == 0) free(p); ret = KRB5KRB_AP_ERR_NOT_US; goto out; } if(ap_req.ticket.enc_part.kvno && *ap_req.ticket.enc_part.kvno != (*krbtgt)->entry.kvno){ char *p; ret = krb5_unparse_name (context, princ, &p); krb5_free_principal(context, princ); if (ret != 0) p = failed; kdc_log(context, config, 0, "Ticket kvno = %d, DB kvno = %d (%s)", *ap_req.ticket.enc_part.kvno, (*krbtgt)->entry.kvno, p); if (ret == 0) free (p); ret = KRB5KRB_AP_ERR_BADKEYVER; goto out; } *krbtgt_etype = ap_req.ticket.enc_part.etype; ret = hdb_enctype2key(context, &(*krbtgt)->entry, ap_req.ticket.enc_part.etype, &tkey); if(ret){ char *str = NULL, *p = NULL; krb5_enctype_to_string(context, ap_req.ticket.enc_part.etype, &str); krb5_unparse_name(context, princ, &p); kdc_log(context, config, 0, "No server key with enctype %s found for %s", str ? str : "", p ? p : ""); free(str); free(p); ret = KRB5KRB_AP_ERR_BADKEYVER; goto out; } if (b->kdc_options.validate) verify_ap_req_flags = KRB5_VERIFY_AP_REQ_IGNORE_INVALID; else verify_ap_req_flags = 0; ret = krb5_verify_ap_req2(context, &ac, &ap_req, princ, &tkey->key, verify_ap_req_flags, &ap_req_options, ticket, KRB5_KU_TGS_REQ_AUTH); krb5_free_principal(context, princ); if(ret) { const char *msg = krb5_get_error_message(context, ret); kdc_log(context, config, 0, "Failed to verify AP-REQ: %s", msg); krb5_free_error_message(context, msg); goto out; } { krb5_authenticator auth; ret = krb5_auth_con_getauthenticator(context, ac, &auth); if (ret == 0) { *csec = malloc(sizeof(**csec)); if (*csec == NULL) { krb5_free_authenticator(context, &auth); kdc_log(context, config, 0, "malloc failed"); goto out; } **csec = auth->ctime; *cusec = malloc(sizeof(**cusec)); if (*cusec == NULL) { krb5_free_authenticator(context, &auth); kdc_log(context, config, 0, "malloc failed"); goto out; } **cusec = auth->cusec; krb5_free_authenticator(context, &auth); } } ret = tgs_check_authenticator(context, config, ac, b, e_text, &(*ticket)->ticket.key); if (ret) { krb5_auth_con_free(context, ac); goto out; } usage = KRB5_KU_TGS_REQ_AUTH_DAT_SUBKEY; *rk_is_subkey = 1; ret = krb5_auth_con_getremotesubkey(context, ac, &subkey); if(ret){ const char *msg = krb5_get_error_message(context, ret); krb5_auth_con_free(context, ac); kdc_log(context, config, 0, "Failed to get remote subkey: %s", msg); krb5_free_error_message(context, msg); goto out; } if(subkey == NULL){ usage = KRB5_KU_TGS_REQ_AUTH_DAT_SESSION; *rk_is_subkey = 0; ret = krb5_auth_con_getkey(context, ac, &subkey); if(ret) { const char *msg = krb5_get_error_message(context, ret); krb5_auth_con_free(context, ac); kdc_log(context, config, 0, "Failed to get session key: %s", msg); krb5_free_error_message(context, msg); goto out; } } if(subkey == NULL){ krb5_auth_con_free(context, ac); kdc_log(context, config, 0, "Failed to get key for enc-authorization-data"); ret = KRB5KRB_AP_ERR_BAD_INTEGRITY; /* ? */ goto out; } *replykey = subkey; if (b->enc_authorization_data) { krb5_data ad; ret = krb5_crypto_init(context, subkey, 0, &crypto); if (ret) { const char *msg = krb5_get_error_message(context, ret); krb5_auth_con_free(context, ac); kdc_log(context, config, 0, "krb5_crypto_init failed: %s", msg); krb5_free_error_message(context, msg); goto out; } ret = krb5_decrypt_EncryptedData (context, crypto, usage, b->enc_authorization_data, &ad); krb5_crypto_destroy(context, crypto); if(ret){ krb5_auth_con_free(context, ac); kdc_log(context, config, 0, "Failed to decrypt enc-authorization-data"); ret = KRB5KRB_AP_ERR_BAD_INTEGRITY; /* ? */ goto out; } ALLOC(*auth_data); if (*auth_data == NULL) { krb5_auth_con_free(context, ac); ret = KRB5KRB_AP_ERR_BAD_INTEGRITY; /* ? */ goto out; } ret = decode_AuthorizationData(ad.data, ad.length, *auth_data, NULL); if(ret){ krb5_auth_con_free(context, ac); free(*auth_data); *auth_data = NULL; kdc_log(context, config, 0, "Failed to decode authorization data"); ret = KRB5KRB_AP_ERR_BAD_INTEGRITY; /* ? */ goto out; } } krb5_auth_con_free(context, ac); out: free_AP_REQ(&ap_req); return ret; } static krb5_error_code build_server_referral(krb5_context context, krb5_kdc_configuration *config, krb5_crypto session, krb5_const_realm referred_realm, const PrincipalName *true_principal_name, const PrincipalName *requested_principal, krb5_data *outdata) { PA_ServerReferralData ref; krb5_error_code ret; EncryptedData ed; krb5_data data; size_t size = 0; memset(&ref, 0, sizeof(ref)); if (referred_realm) { ALLOC(ref.referred_realm); if (ref.referred_realm == NULL) goto eout; *ref.referred_realm = strdup(referred_realm); if (*ref.referred_realm == NULL) goto eout; } if (true_principal_name) { ALLOC(ref.true_principal_name); if (ref.true_principal_name == NULL) goto eout; ret = copy_PrincipalName(true_principal_name, ref.true_principal_name); if (ret) goto eout; } if (requested_principal) { ALLOC(ref.requested_principal_name); if (ref.requested_principal_name == NULL) goto eout; ret = copy_PrincipalName(requested_principal, ref.requested_principal_name); if (ret) goto eout; } ASN1_MALLOC_ENCODE(PA_ServerReferralData, data.data, data.length, &ref, &size, ret); free_PA_ServerReferralData(&ref); if (ret) return ret; if (data.length != size) krb5_abortx(context, "internal asn.1 encoder error"); ret = krb5_encrypt_EncryptedData(context, session, KRB5_KU_PA_SERVER_REFERRAL, data.data, data.length, 0 /* kvno */, &ed); free(data.data); if (ret) return ret; ASN1_MALLOC_ENCODE(EncryptedData, outdata->data, outdata->length, &ed, &size, ret); free_EncryptedData(&ed); if (ret) return ret; if (outdata->length != size) krb5_abortx(context, "internal asn.1 encoder error"); return 0; eout: free_PA_ServerReferralData(&ref); krb5_set_error_message(context, ENOMEM, "malloc: out of memory"); return ENOMEM; } static krb5_error_code tgs_build_reply(krb5_context context, krb5_kdc_configuration *config, KDC_REQ *req, KDC_REQ_BODY *b, hdb_entry_ex *krbtgt, krb5_enctype krbtgt_etype, const krb5_keyblock *replykey, int rk_is_subkey, krb5_ticket *ticket, krb5_data *reply, const char *from, const char **e_text, AuthorizationData **auth_data, const struct sockaddr *from_addr) { krb5_error_code ret; krb5_principal cp = NULL, sp = NULL, rsp = NULL, tp = NULL, dp = NULL; krb5_principal krbtgt_principal = NULL; char *spn = NULL, *cpn = NULL, *tpn = NULL, *dpn = NULL; hdb_entry_ex *server = NULL, *client = NULL, *s4u2self_impersonated_client = NULL; HDB *clientdb, *s4u2self_impersonated_clientdb; krb5_realm ref_realm = NULL; EncTicketPart *tgt = &ticket->ticket; krb5_principals spp = NULL; const EncryptionKey *ekey; krb5_keyblock sessionkey; krb5_kvno kvno; krb5_data rspac; hdb_entry_ex *krbtgt_out = NULL; METHOD_DATA enc_pa_data; PrincipalName *s; Realm r; int nloop = 0; EncTicketPart adtkt; char opt_str[128]; int signedpath = 0; Key *tkey_check; Key *tkey_sign; int flags = HDB_F_FOR_TGS_REQ; memset(&sessionkey, 0, sizeof(sessionkey)); memset(&adtkt, 0, sizeof(adtkt)); krb5_data_zero(&rspac); memset(&enc_pa_data, 0, sizeof(enc_pa_data)); s = b->sname; r = b->realm; /* * Always to do CANON, see comment below about returned server principal (rsp). */ flags |= HDB_F_CANON; if(b->kdc_options.enc_tkt_in_skey){ Ticket *t; hdb_entry_ex *uu; krb5_principal p; Key *uukey; if(b->additional_tickets == NULL || b->additional_tickets->len == 0){ ret = KRB5KDC_ERR_BADOPTION; /* ? */ kdc_log(context, config, 0, "No second ticket present in request"); goto out; } t = &b->additional_tickets->val[0]; if(!get_krbtgt_realm(&t->sname)){ kdc_log(context, config, 0, "Additional ticket is not a ticket-granting ticket"); ret = KRB5KDC_ERR_POLICY; goto out; } _krb5_principalname2krb5_principal(context, &p, t->sname, t->realm); ret = _kdc_db_fetch(context, config, p, HDB_F_GET_KRBTGT, t->enc_part.kvno, NULL, &uu); krb5_free_principal(context, p); if(ret){ if (ret == HDB_ERR_NOENTRY) ret = KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN; goto out; } ret = hdb_enctype2key(context, &uu->entry, t->enc_part.etype, &uukey); if(ret){ _kdc_free_ent(context, uu); ret = KRB5KDC_ERR_ETYPE_NOSUPP; /* XXX */ goto out; } ret = krb5_decrypt_ticket(context, t, &uukey->key, &adtkt, 0); _kdc_free_ent(context, uu); if(ret) goto out; ret = verify_flags(context, config, &adtkt, spn); if (ret) goto out; s = &adtkt.cname; r = adtkt.crealm; } _krb5_principalname2krb5_principal(context, &sp, *s, r); ret = krb5_unparse_name(context, sp, &spn); if (ret) goto out; _krb5_principalname2krb5_principal(context, &cp, tgt->cname, tgt->crealm); ret = krb5_unparse_name(context, cp, &cpn); if (ret) goto out; unparse_flags (KDCOptions2int(b->kdc_options), asn1_KDCOptions_units(), opt_str, sizeof(opt_str)); if(*opt_str) kdc_log(context, config, 0, "TGS-REQ %s from %s for %s [%s]", cpn, from, spn, opt_str); else kdc_log(context, config, 0, "TGS-REQ %s from %s for %s", cpn, from, spn); /* * Fetch server */ server_lookup: ret = _kdc_db_fetch(context, config, sp, HDB_F_GET_SERVER | flags, NULL, NULL, &server); if(ret == HDB_ERR_NOT_FOUND_HERE) { kdc_log(context, config, 5, "target %s does not have secrets at this KDC, need to proxy", sp); goto out; } else if(ret){ const char *new_rlm, *msg; Realm req_rlm; krb5_realm *realms; if ((req_rlm = get_krbtgt_realm(&sp->name)) != NULL) { if(nloop++ < 2) { new_rlm = find_rpath(context, tgt->crealm, req_rlm); if(new_rlm) { kdc_log(context, config, 5, "krbtgt for realm %s " "not found, trying %s", req_rlm, new_rlm); krb5_free_principal(context, sp); free(spn); krb5_make_principal(context, &sp, r, KRB5_TGS_NAME, new_rlm, NULL); ret = krb5_unparse_name(context, sp, &spn); if (ret) goto out; if (ref_realm) free(ref_realm); ref_realm = strdup(new_rlm); goto server_lookup; } } } else if(need_referral(context, config, &b->kdc_options, sp, &realms)) { if (strcmp(realms[0], sp->realm) != 0) { kdc_log(context, config, 5, "Returning a referral to realm %s for " "server %s that was not found", realms[0], spn); krb5_free_principal(context, sp); free(spn); krb5_make_principal(context, &sp, r, KRB5_TGS_NAME, realms[0], NULL); ret = krb5_unparse_name(context, sp, &spn); if (ret) goto out; if (ref_realm) free(ref_realm); ref_realm = strdup(realms[0]); krb5_free_host_realm(context, realms); goto server_lookup; } krb5_free_host_realm(context, realms); } msg = krb5_get_error_message(context, ret); kdc_log(context, config, 0, "Server not found in database: %s: %s", spn, msg); krb5_free_error_message(context, msg); if (ret == HDB_ERR_NOENTRY) ret = KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN; goto out; } /* the name returned to the client depend on what was asked for, * return canonical name if kdc_options.canonicalize was set, the * client wants the true name of the principal, if not it just * wants the name its asked for. */ if (b->kdc_options.canonicalize) rsp = server->entry.principal; else rsp = sp; /* * Select enctype, return key and kvno. */ { krb5_enctype etype; if(b->kdc_options.enc_tkt_in_skey) { size_t i; ekey = &adtkt.key; for(i = 0; i < b->etype.len; i++) if (b->etype.val[i] == adtkt.key.keytype) break; if(i == b->etype.len) { kdc_log(context, config, 0, "Addition ticket have not matching etypes"); krb5_clear_error_message(context); ret = KRB5KDC_ERR_ETYPE_NOSUPP; goto out; } etype = b->etype.val[i]; kvno = 0; } else { Key *skey; ret = _kdc_find_etype(context, krb5_principal_is_krbtgt(context, sp) ? config->tgt_use_strongest_session_key : config->svc_use_strongest_session_key, FALSE, server, b->etype.val, b->etype.len, NULL, &skey); if(ret) { kdc_log(context, config, 0, "Server (%s) has no support for etypes", spn); goto out; } ekey = &skey->key; etype = skey->key.keytype; kvno = server->entry.kvno; } ret = krb5_generate_random_keyblock(context, etype, &sessionkey); if (ret) goto out; } /* * Check that service is in the same realm as the krbtgt. If it's * not the same, it's someone that is using a uni-directional trust * backward. */ /* * Validate authoriation data */ ret = hdb_enctype2key(context, &krbtgt->entry, krbtgt_etype, &tkey_check); if(ret) { kdc_log(context, config, 0, "Failed to find key for krbtgt PAC check"); goto out; } /* Now refetch the primary krbtgt, and get the current kvno (the * sign check may have been on an old kvno, and the server may * have been an incoming trust) */ ret = krb5_make_principal(context, &krbtgt_principal, krb5_principal_get_comp_string(context, krbtgt->entry.principal, 1), KRB5_TGS_NAME, krb5_principal_get_comp_string(context, krbtgt->entry.principal, 1), NULL); if(ret) { kdc_log(context, config, 0, "Failed to generate krbtgt principal"); goto out; } ret = _kdc_db_fetch(context, config, krbtgt_principal, HDB_F_GET_KRBTGT, NULL, NULL, &krbtgt_out); krb5_free_principal(context, krbtgt_principal); if (ret) { krb5_error_code ret2; char *ktpn, *ktpn2; ret = krb5_unparse_name(context, krbtgt->entry.principal, &ktpn); ret2 = krb5_unparse_name(context, krbtgt_principal, &ktpn2); kdc_log(context, config, 0, "Request with wrong krbtgt: %s, %s not found in our database", (ret == 0) ? ktpn : "", (ret2 == 0) ? ktpn2 : ""); if(ret == 0) free(ktpn); if(ret2 == 0) free(ktpn2); ret = KRB5KRB_AP_ERR_NOT_US; goto out; } /* The first realm is the realm of the service, the second is * krbtgt//@REALM component of the krbtgt DN the request was * encrypted to. The redirection via the krbtgt_out entry allows * the DB to possibly correct the case of the realm (Samba4 does * this) before the strcmp() */ if (strcmp(krb5_principal_get_realm(context, server->entry.principal), krb5_principal_get_realm(context, krbtgt_out->entry.principal)) != 0) { char *ktpn; ret = krb5_unparse_name(context, krbtgt_out->entry.principal, &ktpn); kdc_log(context, config, 0, "Request with wrong krbtgt: %s", (ret == 0) ? ktpn : ""); if(ret == 0) free(ktpn); ret = KRB5KRB_AP_ERR_NOT_US; } ret = hdb_enctype2key(context, &krbtgt_out->entry, krbtgt_etype, &tkey_sign); if(ret) { kdc_log(context, config, 0, "Failed to find key for krbtgt PAC signature"); goto out; } ret = _kdc_db_fetch(context, config, cp, HDB_F_GET_CLIENT | flags, NULL, &clientdb, &client); if(ret == HDB_ERR_NOT_FOUND_HERE) { /* This is OK, we are just trying to find out if they have * been disabled or deleted in the meantime, missing secrets * is OK */ } else if(ret){ const char *krbtgt_realm, *msg; /* * If the client belongs to the same realm as our krbtgt, it * should exist in the local database. * */ krbtgt_realm = krb5_principal_get_realm(context, krbtgt_out->entry.principal); if(strcmp(krb5_principal_get_realm(context, cp), krbtgt_realm) == 0) { if (ret == HDB_ERR_NOENTRY) ret = KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN; kdc_log(context, config, 1, "Client no longer in database: %s", cpn); goto out; } msg = krb5_get_error_message(context, ret); kdc_log(context, config, 1, "Client not found in database: %s", msg); krb5_free_error_message(context, msg); } ret = check_PAC(context, config, cp, NULL, client, server, krbtgt, &tkey_check->key, &tkey_check->key, ekey, &tkey_sign->key, tgt, &rspac, &signedpath); if (ret) { const char *msg = krb5_get_error_message(context, ret); kdc_log(context, config, 0, "Verify PAC failed for %s (%s) from %s with %s", spn, cpn, from, msg); krb5_free_error_message(context, msg); goto out; } /* also check the krbtgt for signature */ ret = check_KRB5SignedPath(context, config, krbtgt, cp, tgt, &spp, &signedpath); if (ret) { const char *msg = krb5_get_error_message(context, ret); kdc_log(context, config, 0, "KRB5SignedPath check failed for %s (%s) from %s with %s", spn, cpn, from, msg); krb5_free_error_message(context, msg); goto out; } /* * Process request */ /* by default the tgt principal matches the client principal */ tp = cp; tpn = cpn; if (client) { const PA_DATA *sdata; int i = 0; sdata = _kdc_find_padata(req, &i, KRB5_PADATA_FOR_USER); if (sdata) { krb5_crypto crypto; krb5_data datack; PA_S4U2Self self; const char *str; ret = decode_PA_S4U2Self(sdata->padata_value.data, sdata->padata_value.length, &self, NULL); if (ret) { kdc_log(context, config, 0, "Failed to decode PA-S4U2Self"); goto out; } ret = _krb5_s4u2self_to_checksumdata(context, &self, &datack); if (ret) goto out; ret = krb5_crypto_init(context, &tgt->key, 0, &crypto); if (ret) { const char *msg = krb5_get_error_message(context, ret); free_PA_S4U2Self(&self); krb5_data_free(&datack); kdc_log(context, config, 0, "krb5_crypto_init failed: %s", msg); krb5_free_error_message(context, msg); goto out; } ret = krb5_verify_checksum(context, crypto, KRB5_KU_OTHER_CKSUM, datack.data, datack.length, &self.cksum); krb5_data_free(&datack); krb5_crypto_destroy(context, crypto); if (ret) { const char *msg = krb5_get_error_message(context, ret); free_PA_S4U2Self(&self); kdc_log(context, config, 0, "krb5_verify_checksum failed for S4U2Self: %s", msg); krb5_free_error_message(context, msg); goto out; } ret = _krb5_principalname2krb5_principal(context, &tp, self.name, self.realm); free_PA_S4U2Self(&self); if (ret) goto out; ret = krb5_unparse_name(context, tp, &tpn); if (ret) goto out; + ret = _kdc_db_fetch(context, config, tp, HDB_F_GET_CLIENT | flags, + NULL, &s4u2self_impersonated_clientdb, + &s4u2self_impersonated_client); + if (ret) { + const char *msg; + + /* + * If the client belongs to the same realm as our krbtgt, it + * should exist in the local database. + * + */ + + if (ret == HDB_ERR_NOENTRY) + ret = KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN; + msg = krb5_get_error_message(context, ret); + kdc_log(context, config, 2, + "S4U2Self principal to impersonate %s not found in database: %s", + tpn, msg); + krb5_free_error_message(context, msg); + goto out; + } + + free(s4u2self_impersonated_client->entry.pw_end); + s4u2self_impersonated_client->entry.pw_end = NULL; + + ret = kdc_check_flags(context, config, s4u2self_impersonated_client, tpn, + NULL, NULL, FALSE); + if (ret) + goto out; + /* If we were about to put a PAC into the ticket, we better fix it to be the right PAC */ if(rspac.data) { krb5_pac p = NULL; krb5_data_free(&rspac); - ret = _kdc_db_fetch(context, config, tp, HDB_F_GET_CLIENT | flags, - NULL, &s4u2self_impersonated_clientdb, &s4u2self_impersonated_client); - if (ret) { - const char *msg; - - /* - * If the client belongs to the same realm as our krbtgt, it - * should exist in the local database. - * - */ - - if (ret == HDB_ERR_NOENTRY) - ret = KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN; - msg = krb5_get_error_message(context, ret); - kdc_log(context, config, 1, - "S2U4Self principal to impersonate %s not found in database: %s", - tpn, msg); - krb5_free_error_message(context, msg); - goto out; - } ret = _kdc_pac_generate(context, s4u2self_impersonated_client, &p); if (ret) { kdc_log(context, config, 0, "PAC generation failed for -- %s", tpn); goto out; } if (p != NULL) { ret = _krb5_pac_sign(context, p, ticket->ticket.authtime, s4u2self_impersonated_client->entry.principal, ekey, &tkey_sign->key, &rspac); krb5_pac_free(context, p); if (ret) { kdc_log(context, config, 0, "PAC signing failed for -- %s", tpn); goto out; } } } /* * Check that service doing the impersonating is * requesting a ticket to it-self. */ ret = check_s4u2self(context, config, clientdb, client, sp); if (ret) { kdc_log(context, config, 0, "S4U2Self: %s is not allowed " "to impersonate to service " "(tried for user %s to service %s)", cpn, tpn, spn); goto out; } /* * If the service isn't trusted for authentication to - * delegation, remove the forward flag. + * delegation or if the impersonate client is disallowed + * forwardable, remove the forwardable flag. */ - if (client->entry.flags.trusted_for_delegation) { + if (client->entry.flags.trusted_for_delegation && + s4u2self_impersonated_client->entry.flags.forwardable) { str = "[forwardable]"; } else { b->kdc_options.forwardable = 0; str = ""; } kdc_log(context, config, 0, "s4u2self %s impersonating %s to " "service %s %s", cpn, tpn, spn, str); } } /* * Constrained delegation */ if (client != NULL && b->additional_tickets != NULL && b->additional_tickets->len != 0 && b->kdc_options.enc_tkt_in_skey == 0) { int ad_signedpath = 0; Key *clientkey; Ticket *t; /* * Require that the KDC have issued the service's krbtgt (not * self-issued ticket with kimpersonate(1). */ if (!signedpath) { ret = KRB5KDC_ERR_BADOPTION; kdc_log(context, config, 0, "Constrained delegation done on service ticket %s/%s", cpn, spn); goto out; } t = &b->additional_tickets->val[0]; ret = hdb_enctype2key(context, &client->entry, t->enc_part.etype, &clientkey); if(ret){ ret = KRB5KDC_ERR_ETYPE_NOSUPP; /* XXX */ goto out; } ret = krb5_decrypt_ticket(context, t, &clientkey->key, &adtkt, 0); if (ret) { kdc_log(context, config, 0, "failed to decrypt ticket for " "constrained delegation from %s to %s ", cpn, spn); goto out; } ret = _krb5_principalname2krb5_principal(context, &tp, adtkt.cname, adtkt.crealm); if (ret) goto out; ret = krb5_unparse_name(context, tp, &tpn); if (ret) goto out; ret = _krb5_principalname2krb5_principal(context, &dp, t->sname, t->realm); if (ret) goto out; ret = krb5_unparse_name(context, dp, &dpn); if (ret) goto out; /* check that ticket is valid */ if (adtkt.flags.forwardable == 0) { kdc_log(context, config, 0, "Missing forwardable flag on ticket for " "constrained delegation from %s (%s) as %s to %s ", cpn, dpn, tpn, spn); ret = KRB5KDC_ERR_BADOPTION; goto out; } ret = check_constrained_delegation(context, config, clientdb, client, server, sp); if (ret) { kdc_log(context, config, 0, "constrained delegation from %s (%s) as %s to %s not allowed", cpn, dpn, tpn, spn); goto out; } ret = verify_flags(context, config, &adtkt, tpn); if (ret) { goto out; } krb5_data_free(&rspac); /* * generate the PAC for the user. * * TODO: pass in t->sname and t->realm and build * a S4U_DELEGATION_INFO blob to the PAC. */ ret = check_PAC(context, config, tp, dp, client, server, krbtgt, &clientkey->key, &tkey_check->key, ekey, &tkey_sign->key, &adtkt, &rspac, &ad_signedpath); if (ret) { const char *msg = krb5_get_error_message(context, ret); kdc_log(context, config, 0, "Verify delegated PAC failed to %s for client" "%s (%s) as %s from %s with %s", spn, cpn, dpn, tpn, from, msg); krb5_free_error_message(context, msg); goto out; } /* * Check that the KDC issued the user's ticket. */ ret = check_KRB5SignedPath(context, config, krbtgt, cp, &adtkt, NULL, &ad_signedpath); if (ret) { const char *msg = krb5_get_error_message(context, ret); kdc_log(context, config, 0, "KRB5SignedPath check from service %s failed " "for delegation to %s for client %s (%s)" "from %s failed with %s", spn, tpn, dpn, cpn, from, msg); krb5_free_error_message(context, msg); goto out; } if (!ad_signedpath) { ret = KRB5KDC_ERR_BADOPTION; kdc_log(context, config, 0, "Ticket not signed with PAC nor SignedPath service %s failed " "for delegation to %s for client %s (%s)" "from %s", spn, tpn, dpn, cpn, from); goto out; } kdc_log(context, config, 0, "constrained delegation for %s " "from %s (%s) to %s", tpn, cpn, dpn, spn); } /* * Check flags */ ret = kdc_check_flags(context, config, client, cpn, server, spn, FALSE); if(ret) goto out; if((b->kdc_options.validate || b->kdc_options.renew) && !krb5_principal_compare(context, krbtgt->entry.principal, server->entry.principal)){ kdc_log(context, config, 0, "Inconsistent request."); ret = KRB5KDC_ERR_SERVER_NOMATCH; goto out; } /* check for valid set of addresses */ if(!_kdc_check_addresses(context, config, tgt->caddr, from_addr)) { ret = KRB5KRB_AP_ERR_BADADDR; kdc_log(context, config, 0, "Request from wrong address"); goto out; } /* * If this is an referral, add server referral data to the * auth_data reply . */ if (ref_realm) { PA_DATA pa; krb5_crypto crypto; kdc_log(context, config, 0, "Adding server referral to %s", ref_realm); ret = krb5_crypto_init(context, &sessionkey, 0, &crypto); if (ret) goto out; ret = build_server_referral(context, config, crypto, ref_realm, NULL, s, &pa.padata_value); krb5_crypto_destroy(context, crypto); if (ret) { kdc_log(context, config, 0, "Failed building server referral"); goto out; } pa.padata_type = KRB5_PADATA_SERVER_REFERRAL; ret = add_METHOD_DATA(&enc_pa_data, &pa); krb5_data_free(&pa.padata_value); if (ret) { kdc_log(context, config, 0, "Add server referral METHOD-DATA failed"); goto out; } } /* * */ ret = tgs_make_reply(context, config, b, tp, tgt, replykey, rk_is_subkey, ekey, &sessionkey, kvno, *auth_data, server, rsp, spn, client, cp, krbtgt_out, krbtgt_etype, spp, &rspac, &enc_pa_data, e_text, reply); out: if (tpn != cpn) free(tpn); free(spn); free(cpn); if (dpn) free(dpn); krb5_data_free(&rspac); krb5_free_keyblock_contents(context, &sessionkey); if(krbtgt_out) _kdc_free_ent(context, krbtgt_out); if(server) _kdc_free_ent(context, server); if(client) _kdc_free_ent(context, client); if(s4u2self_impersonated_client) _kdc_free_ent(context, s4u2self_impersonated_client); if (tp && tp != cp) krb5_free_principal(context, tp); if (cp) krb5_free_principal(context, cp); if (dp) krb5_free_principal(context, dp); if (sp) krb5_free_principal(context, sp); if (ref_realm) free(ref_realm); free_METHOD_DATA(&enc_pa_data); free_EncTicketPart(&adtkt); return ret; } /* * */ krb5_error_code _kdc_tgs_rep(krb5_context context, krb5_kdc_configuration *config, KDC_REQ *req, krb5_data *data, const char *from, struct sockaddr *from_addr, int datagram_reply) { AuthorizationData *auth_data = NULL; krb5_error_code ret; int i = 0; const PA_DATA *tgs_req; hdb_entry_ex *krbtgt = NULL; krb5_ticket *ticket = NULL; const char *e_text = NULL; krb5_enctype krbtgt_etype = ETYPE_NULL; krb5_keyblock *replykey = NULL; int rk_is_subkey = 0; time_t *csec = NULL; int *cusec = NULL; if(req->padata == NULL){ ret = KRB5KDC_ERR_PREAUTH_REQUIRED; /* XXX ??? */ kdc_log(context, config, 0, "TGS-REQ from %s without PA-DATA", from); goto out; } tgs_req = _kdc_find_padata(req, &i, KRB5_PADATA_TGS_REQ); if(tgs_req == NULL){ ret = KRB5KDC_ERR_PADATA_TYPE_NOSUPP; kdc_log(context, config, 0, "TGS-REQ from %s without PA-TGS-REQ", from); goto out; } ret = tgs_parse_request(context, config, &req->req_body, tgs_req, &krbtgt, &krbtgt_etype, &ticket, &e_text, from, from_addr, &csec, &cusec, &auth_data, &replykey, &rk_is_subkey); if (ret == HDB_ERR_NOT_FOUND_HERE) { /* kdc_log() is called in tgs_parse_request() */ goto out; } if (ret) { kdc_log(context, config, 0, "Failed parsing TGS-REQ from %s", from); goto out; } ret = tgs_build_reply(context, config, req, &req->req_body, krbtgt, krbtgt_etype, replykey, rk_is_subkey, ticket, data, from, &e_text, &auth_data, from_addr); if (ret) { kdc_log(context, config, 0, "Failed building TGS-REP to %s", from); goto out; } /* */ if (datagram_reply && data->length > config->max_datagram_reply_length) { krb5_data_free(data); ret = KRB5KRB_ERR_RESPONSE_TOO_BIG; e_text = "Reply packet too large"; } out: if (replykey) krb5_free_keyblock(context, replykey); if(ret && ret != HDB_ERR_NOT_FOUND_HERE && data->data == NULL){ krb5_mk_error(context, ret, NULL, NULL, NULL, NULL, csec, cusec, data); ret = 0; } free(csec); free(cusec); if (ticket) krb5_free_ticket(context, ticket); if(krbtgt) _kdc_free_ent(context, krbtgt); if (auth_data) { free_AuthorizationData(auth_data); free(auth_data); } return ret; } diff --git a/crypto/heimdal/kdc/kstash.c b/crypto/heimdal/kdc/kstash.c index 0b75fb8d84a1..9a5217b27ccf 100644 --- a/crypto/heimdal/kdc/kstash.c +++ b/crypto/heimdal/kdc/kstash.c @@ -1,171 +1,173 @@ /* * 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 "headers.h" krb5_context context; static char *keyfile; static int convert_flag; static int help_flag; static int version_flag; static int master_key_fd = -1; static int random_key_flag; static const char *enctype_str = "des3-cbc-sha1"; static struct getargs args[] = { { "enctype", 'e', arg_string, rk_UNCONST(&enctype_str), "encryption type", NULL }, { "key-file", 'k', arg_string, &keyfile, "master key file", "file" }, { "convert-file", 0, arg_flag, &convert_flag, "just convert keyfile to new format", NULL }, { "master-key-fd", 0, arg_integer, &master_key_fd, "filedescriptor to read passphrase from", "fd" }, { "random-key", 0, arg_flag, &random_key_flag, "generate a random master key", NULL }, { "help", 'h', arg_flag, &help_flag, NULL, NULL }, { "version", 0, arg_flag, &version_flag, NULL, NULL } }; int num_args = sizeof(args) / sizeof(args[0]); int main(int argc, char **argv) { char buf[1024]; krb5_error_code ret; krb5_enctype enctype; hdb_master_key mkey; krb5_program_setup(&context, argc, argv, args, num_args, NULL); if(help_flag) krb5_std_usage(0, args, num_args); if(version_flag){ print_version(NULL); exit(0); } if (master_key_fd != -1 && random_key_flag) krb5_errx(context, 1, "random-key and master-key-fd " "is mutual exclusive"); if (keyfile == NULL) asprintf(&keyfile, "%s/m-key", hdb_db_dir(context)); ret = krb5_string_to_enctype(context, enctype_str, &enctype); if(ret) krb5_err(context, 1, ret, "krb5_string_to_enctype"); ret = hdb_read_master_key(context, keyfile, &mkey); if(ret && ret != ENOENT) krb5_err(context, 1, ret, "reading master key from %s", keyfile); if (convert_flag) { if (ret) krb5_err(context, 1, ret, "reading master key from %s", keyfile); } else { krb5_keyblock key; krb5_salt salt; salt.salttype = KRB5_PW_SALT; /* XXX better value? */ salt.saltvalue.data = NULL; salt.saltvalue.length = 0; if (random_key_flag) { ret = krb5_generate_random_keyblock(context, enctype, &key); if (ret) krb5_err(context, 1, ret, "krb5_generate_random_keyblock"); } else { if(master_key_fd != -1) { ssize_t n; n = read(master_key_fd, buf, sizeof(buf)); if(n <= 0) krb5_err(context, 1, errno, "failed to read passphrase"); buf[n] = '\0'; buf[strcspn(buf, "\r\n")] = '\0'; } else { if(UI_UTIL_read_pw_string(buf, sizeof(buf), "Master key: ", 1)) exit(1); } krb5_string_to_key_salt(context, enctype, buf, salt, &key); } ret = hdb_add_master_key(context, &key, &mkey); + if (ret) + krb5_err(context, 1, ret, "hdb_add_master_key"); krb5_free_keyblock_contents(context, &key); } { char *new, *old; asprintf(&old, "%s.old", keyfile); asprintf(&new, "%s.new", keyfile); if(unlink(new) < 0 && errno != ENOENT) { ret = errno; goto out; } krb5_warnx(context, "writing key to `%s'", keyfile); ret = hdb_write_master_key(context, new, mkey); if(ret) unlink(new); else { #ifndef NO_POSIX_LINKS unlink(old); if(link(keyfile, old) < 0 && errno != ENOENT) { ret = errno; unlink(new); } else { #endif if(rename(new, keyfile) < 0) { ret = errno; } #ifndef NO_POSIX_LINKS } #endif } out: free(old); free(new); if(ret) krb5_warn(context, errno, "writing master key file"); } hdb_free_master_key(context, mkey); exit(ret != 0); } diff --git a/crypto/heimdal/kdc/pkinit.c b/crypto/heimdal/kdc/pkinit.c index 75edda464f4e..c3955aaf66a7 100644 --- a/crypto/heimdal/kdc/pkinit.c +++ b/crypto/heimdal/kdc/pkinit.c @@ -1,2057 +1,2056 @@ /* * 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; 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; } if (DH_set0_pqg(dh, p, g, q) != 1) { BN_free(p); BN_free(g); BN_free(q); 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, 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; 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 */ diff --git a/crypto/heimdal/kuser/kdestroy.c b/crypto/heimdal/kuser/kdestroy.c index 1823bf56ca48..feabe55fdcdb 100644 --- a/crypto/heimdal/kuser/kdestroy.c +++ b/crypto/heimdal/kuser/kdestroy.c @@ -1,172 +1,174 @@ /* * Copyright (c) 1997 - 2000, 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 "kuser_locl.h" static const char *cache; static const char *credential; static int help_flag; static int version_flag; #ifndef NO_AFS static int unlog_flag = 1; #endif static int dest_tkt_flag = 1; static int all_flag = 0; struct getargs args[] = { { "credential", 0, arg_string, rk_UNCONST(&credential), "remove one credential", "principal" }, { "cache", 'c', arg_string, rk_UNCONST(&cache), "cache to destroy", "cache" }, { "all", 'A', arg_flag, &all_flag, "destroy all caches", NULL }, #ifndef NO_AFS { "unlog", 0, arg_negative_flag, &unlog_flag, "do not destroy tokens", NULL }, #endif { "delete-v4", 0, arg_negative_flag, &dest_tkt_flag, "do not destroy v4 tickets", NULL }, { "version", 0, arg_flag, &version_flag, NULL, NULL }, { "help", 'h', arg_flag, &help_flag, NULL, NULL} }; int num_args = sizeof(args) / sizeof(args[0]); static void usage (int status) { arg_printusage (args, num_args, NULL, ""); exit (status); } int main (int argc, char **argv) { krb5_error_code ret; krb5_context context; krb5_ccache ccache; int optidx = 0; int exit_val = 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); } argc -= optidx; +#ifndef __clang_analyzer__ argv += optidx; +#endif if (argc != 0) usage (1); ret = krb5_init_context (&context); if (ret) errx (1, "krb5_init_context failed: %d", ret); if (all_flag) { krb5_cccol_cursor cursor; ret = krb5_cccol_cursor_new (context, &cursor); if (ret) krb5_err(context, 1, ret, "krb5_cccol_cursor_new"); while (krb5_cccol_cursor_next (context, cursor, &ccache) == 0 && ccache != NULL) { ret = krb5_cc_destroy (context, ccache); if (ret) { krb5_warn(context, ret, "krb5_cc_destroy"); exit_val = 1; } } krb5_cccol_cursor_free(context, &cursor); } else { if(cache == NULL) { ret = krb5_cc_default(context, &ccache); if (ret) krb5_err(context, 1, ret, "krb5_cc_default"); } else { ret = krb5_cc_resolve(context, cache, &ccache); if (ret) krb5_err(context, 1, ret, "krb5_cc_resolve"); } if (ret == 0) { if (credential) { krb5_creds mcred; krb5_cc_clear_mcred(&mcred); ret = krb5_parse_name(context, credential, &mcred.server); if (ret) krb5_err(context, 1, ret, "Can't parse principal %s", credential); ret = krb5_cc_remove_cred(context, ccache, 0, &mcred); if (ret) krb5_err(context, 1, ret, "Failed to remove principal %s", credential); krb5_cc_close(context, ccache); krb5_free_principal(context, mcred.server); krb5_free_context(context); return 0; } ret = krb5_cc_destroy (context, ccache); if (ret) { krb5_warn(context, ret, "krb5_cc_destroy"); exit_val = 1; } } } krb5_free_context (context); #ifndef NO_AFS if (unlog_flag && k_hasafs ()) { if (k_unlog ()) exit_val = 1; } #endif return exit_val; } diff --git a/crypto/heimdal/kuser/kswitch.c b/crypto/heimdal/kuser/kswitch.c index cdb6ee11cae8..4887d43be15c 100644 --- a/crypto/heimdal/kuser/kswitch.c +++ b/crypto/heimdal/kuser/kswitch.c @@ -1,172 +1,173 @@ /* * Copyright (c) 2008 - 2010 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 "kuser_locl.h" #include "kcc-commands.h" #ifdef HAVE_READLINE char *readline(const char *prompt); #else static char * readline(const char *prompt) { char buf[BUFSIZ]; printf ("%s", prompt); fflush (stdout); if(fgets(buf, sizeof(buf), stdin) == NULL) return NULL; buf[strcspn(buf, "\r\n")] = '\0'; return strdup(buf); } #endif /* * */ int kswitch(struct kswitch_options *opt, int argc, char **argv) { krb5_error_code ret; krb5_ccache id = NULL; if (opt->cache_string && opt->principal_string) krb5_errx(kcc_context, 1, N_("Both --cache and --principal given, choose one", "")); if (opt->interactive_flag) { krb5_cc_cache_cursor cursor; krb5_ccache *ids = NULL; size_t i, len = 0; char *name; rtbl_t ct; ct = rtbl_create(); rtbl_add_column_by_id(ct, 0, "#", 0); rtbl_add_column_by_id(ct, 1, "Principal", 0); rtbl_set_column_affix_by_id(ct, 1, " ", ""); rtbl_add_column_by_id(ct, 2, "Type", 0); rtbl_set_column_affix_by_id(ct, 2, " ", ""); ret = krb5_cc_cache_get_first(kcc_context, NULL, &cursor); if (ret) krb5_err(kcc_context, 1, ret, "krb5_cc_cache_get_first"); while (krb5_cc_cache_next(kcc_context, cursor, &id) == 0) { - krb5_principal p; + krb5_principal p = NULL; char num[10]; ret = krb5_cc_get_principal(kcc_context, id, &p); + if (ret == 0) + ret = krb5_unparse_name(kcc_context, p, &name); if (ret) continue; - ret = krb5_unparse_name(kcc_context, p, &name); krb5_free_principal(kcc_context, p); snprintf(num, sizeof(num), "%d", (int)(len + 1)); rtbl_add_column_entry_by_id(ct, 0, num); rtbl_add_column_entry_by_id(ct, 1, name); rtbl_add_column_entry_by_id(ct, 2, krb5_cc_get_type(kcc_context, id)); free(name); ids = erealloc(ids, (len + 1) * sizeof(ids[0])); ids[len] = id; len++; } krb5_cc_cache_end_seq_get(kcc_context, cursor); rtbl_format(ct, stdout); rtbl_destroy(ct); name = readline("Select number: "); if (name) { i = atoi(name); if (i == 0) krb5_errx(kcc_context, 1, "Cache number '%s' is invalid", name); if (i > len) krb5_errx(kcc_context, 1, "Cache number '%s' is too large", name); id = ids[i - 1]; ids[i - 1] = NULL; } else krb5_errx(kcc_context, 1, "No cache selected"); for (i = 0; i < len; i++) if (ids[i]) krb5_cc_close(kcc_context, ids[i]); } else if (opt->principal_string) { krb5_principal p; ret = krb5_parse_name(kcc_context, opt->principal_string, &p); if (ret) krb5_err(kcc_context, 1, ret, "krb5_parse_name: %s", opt->principal_string); ret = krb5_cc_cache_match(kcc_context, p, &id); if (ret) krb5_err(kcc_context, 1, ret, N_("Did not find principal: %s", ""), opt->principal_string); krb5_free_principal(kcc_context, p); } else if (opt->cache_string) { const krb5_cc_ops *ops; char *str; ops = krb5_cc_get_prefix_ops(kcc_context, opt->type_string); if (ops == NULL) krb5_err(kcc_context, 1, 0, "krb5_cc_get_prefix_ops"); asprintf(&str, "%s:%s", ops->prefix, opt->cache_string); if (str == NULL) krb5_errx(kcc_context, 1, N_("out of memory", "")); ret = krb5_cc_resolve(kcc_context, str, &id); if (ret) krb5_err(kcc_context, 1, ret, "krb5_cc_resolve: %s", str); free(str); } else { krb5_errx(kcc_context, 1, "missing option for kswitch"); } ret = krb5_cc_switch(kcc_context, id); if (ret) krb5_err(kcc_context, 1, ret, "krb5_cc_switch"); return 0; } diff --git a/crypto/heimdal/lib/asn1/der_copy.c b/crypto/heimdal/lib/asn1/der_copy.c index 3a0a8c5ffa6a..abaaf8e5d740 100644 --- a/crypto/heimdal/lib/asn1/der_copy.c +++ b/crypto/heimdal/lib/asn1/der_copy.c @@ -1,181 +1,185 @@ /* * Copyright (c) 1997 - 2006 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 "der_locl.h" RCSID("$Id$"); int der_copy_general_string (const heim_general_string *from, heim_general_string *to) { *to = strdup(*from); if(*to == NULL) return ENOMEM; return 0; } int der_copy_integer (const int *from, int *to) { *to = *from; return 0; } int der_copy_unsigned (const unsigned *from, unsigned *to) { *to = *from; return 0; } int der_copy_generalized_time (const time_t *from, time_t *to) { *to = *from; return 0; } int der_copy_utctime (const time_t *from, time_t *to) { *to = *from; return 0; } int der_copy_utf8string (const heim_utf8_string *from, heim_utf8_string *to) { return der_copy_general_string(from, to); } int der_copy_printable_string (const heim_printable_string *from, heim_printable_string *to) { to->length = from->length; to->data = malloc(to->length + 1); if(to->data == NULL) return ENOMEM; memcpy(to->data, from->data, to->length); ((char *)to->data)[to->length] = '\0'; return 0; } int der_copy_ia5_string (const heim_ia5_string *from, heim_ia5_string *to) { return der_copy_printable_string(from, to); } int der_copy_bmp_string (const heim_bmp_string *from, heim_bmp_string *to) { to->length = from->length; to->data = malloc(to->length * sizeof(to->data[0])); if(to->length != 0 && to->data == NULL) return ENOMEM; memcpy(to->data, from->data, to->length * sizeof(to->data[0])); return 0; } int der_copy_universal_string (const heim_universal_string *from, heim_universal_string *to) { to->length = from->length; to->data = malloc(to->length * sizeof(to->data[0])); if(to->length != 0 && to->data == NULL) return ENOMEM; memcpy(to->data, from->data, to->length * sizeof(to->data[0])); return 0; } int der_copy_visible_string (const heim_visible_string *from, heim_visible_string *to) { return der_copy_general_string(from, to); } int der_copy_octet_string (const heim_octet_string *from, heim_octet_string *to) { to->length = from->length; - to->data = malloc(to->length); - if(to->length != 0 && to->data == NULL) + if (from->data == NULL) { + to->data = NULL; + return 0; + } + to->data = malloc(to->length); + if (to->length != 0 && to->data == NULL) return ENOMEM; memcpy(to->data, from->data, to->length); return 0; } int der_copy_heim_integer (const heim_integer *from, heim_integer *to) { to->length = from->length; to->data = malloc(to->length); if(to->length != 0 && to->data == NULL) return ENOMEM; memcpy(to->data, from->data, to->length); to->negative = from->negative; return 0; } int der_copy_oid (const heim_oid *from, heim_oid *to) { to->length = from->length; to->components = malloc(to->length * sizeof(*to->components)); if (to->length != 0 && to->components == NULL) return ENOMEM; memcpy(to->components, from->components, to->length * sizeof(*to->components)); return 0; } int der_copy_bit_string (const heim_bit_string *from, heim_bit_string *to) { size_t len; len = (from->length + 7) / 8; to->length = from->length; to->data = malloc(len); if(len != 0 && to->data == NULL) return ENOMEM; memcpy(to->data, from->data, len); return 0; } diff --git a/crypto/heimdal/lib/asn1/gen_decode.c b/crypto/heimdal/lib/asn1/gen_decode.c index 9d816d5400d7..bf2d93b806df 100644 --- a/crypto/heimdal/lib/asn1/gen_decode.c +++ b/crypto/heimdal/lib/asn1/gen_decode.c @@ -1,731 +1,731 @@ /* * Copyright (c) 1997 - 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 "gen_locl.h" #include "lex.h" RCSID("$Id$"); static void decode_primitive (const char *typename, const char *name, const char *forwstr) { #if 0 fprintf (codefile, "e = decode_%s(p, len, %s, &l);\n" "%s;\n", typename, name, forwstr); #else fprintf (codefile, "e = der_get_%s(p, len, %s, &l);\n" "if(e) %s;\np += l; len -= l; ret += l;\n", typename, name, forwstr); #endif } static void find_tag (const Type *t, Der_class *cl, Der_type *ty, unsigned *tag) { switch (t->type) { case TBitString: *cl = ASN1_C_UNIV; *ty = PRIM; *tag = UT_BitString; break; case TBoolean: *cl = ASN1_C_UNIV; *ty = PRIM; *tag = UT_Boolean; break; case TChoice: errx(1, "Cannot have recursive CHOICE"); case TEnumerated: *cl = ASN1_C_UNIV; *ty = PRIM; *tag = UT_Enumerated; break; case TGeneralString: *cl = ASN1_C_UNIV; *ty = PRIM; *tag = UT_GeneralString; break; case TTeletexString: *cl = ASN1_C_UNIV; *ty = PRIM; *tag = UT_TeletexString; break; case TGeneralizedTime: *cl = ASN1_C_UNIV; *ty = PRIM; *tag = UT_GeneralizedTime; break; case TIA5String: *cl = ASN1_C_UNIV; *ty = PRIM; *tag = UT_IA5String; break; case TInteger: *cl = ASN1_C_UNIV; *ty = PRIM; *tag = UT_Integer; break; case TNull: *cl = ASN1_C_UNIV; *ty = PRIM; *tag = UT_Null; break; case TOID: *cl = ASN1_C_UNIV; *ty = PRIM; *tag = UT_OID; break; case TOctetString: *cl = ASN1_C_UNIV; *ty = PRIM; *tag = UT_OctetString; break; case TPrintableString: *cl = ASN1_C_UNIV; *ty = PRIM; *tag = UT_PrintableString; break; case TSequence: case TSequenceOf: *cl = ASN1_C_UNIV; *ty = CONS; *tag = UT_Sequence; break; case TSet: case TSetOf: *cl = ASN1_C_UNIV; *ty = CONS; *tag = UT_Set; break; case TTag: *cl = t->tag.tagclass; *ty = is_primitive_type(t->subtype->type) ? PRIM : CONS; *tag = t->tag.tagvalue; break; case TType: if ((t->symbol->stype == Stype && t->symbol->type == NULL) || t->symbol->stype == SUndefined) { lex_error_message("%s is imported or still undefined, " " can't generate tag checking data in CHOICE " "without this information", t->symbol->name); exit(1); } find_tag(t->symbol->type, cl, ty, tag); return; case TUTCTime: *cl = ASN1_C_UNIV; *ty = PRIM; *tag = UT_UTCTime; break; case TUTF8String: *cl = ASN1_C_UNIV; *ty = PRIM; *tag = UT_UTF8String; break; case TBMPString: *cl = ASN1_C_UNIV; *ty = PRIM; *tag = UT_BMPString; break; case TUniversalString: *cl = ASN1_C_UNIV; *ty = PRIM; *tag = UT_UniversalString; break; case TVisibleString: *cl = ASN1_C_UNIV; *ty = PRIM; *tag = UT_VisibleString; break; default: abort(); } } static void range_check(const char *name, const char *length, const char *forwstr, struct range *r) { if (r->min == r->max + 2 || r->min < r->max) fprintf (codefile, "if ((%s)->%s > %d) {\n" "e = ASN1_MAX_CONSTRAINT; %s;\n" "}\n", name, length, r->max, forwstr); if (r->min - 1 == r->max || r->min < r->max) fprintf (codefile, "if ((%s)->%s < %d) {\n" "e = ASN1_MIN_CONSTRAINT; %s;\n" "}\n", name, length, r->min, forwstr); if (r->max == r->min) fprintf (codefile, "if ((%s)->%s != %d) {\n" "e = ASN1_EXACT_CONSTRAINT; %s;\n" "}\n", name, length, r->min, forwstr); } static int decode_type (const char *name, const Type *t, int optional, const char *forwstr, const char *tmpstr, const char *dertype, unsigned int depth) { switch (t->type) { case TType: { if (optional) fprintf(codefile, "%s = calloc(1, sizeof(*%s));\n" "if (%s == NULL) %s;\n", name, name, name, forwstr); fprintf (codefile, "e = decode_%s(p, len, %s, &l);\n", t->symbol->gen_name, name); if (optional) { fprintf (codefile, "if(e) {\n" "free(%s);\n" "%s = NULL;\n" "} else {\n" "p += l; len -= l; ret += l;\n" "}\n", name, name); } else { fprintf (codefile, "if(e) %s;\n", forwstr); fprintf (codefile, "p += l; len -= l; ret += l;\n"); } break; } case TInteger: if(t->members) { fprintf(codefile, "{\n" "int enumint;\n"); decode_primitive ("integer", "&enumint", forwstr); fprintf(codefile, "*%s = enumint;\n" "}\n", name); } else if (t->range == NULL) { decode_primitive ("heim_integer", name, forwstr); } else if (t->range->min == INT_MIN && t->range->max == INT_MAX) { decode_primitive ("integer", name, forwstr); } else if (t->range->min == 0 && t->range->max == UINT_MAX) { decode_primitive ("unsigned", name, forwstr); } else if (t->range->min == 0 && t->range->max == INT_MAX) { decode_primitive ("unsigned", name, forwstr); } else errx(1, "%s: unsupported range %d -> %d", name, t->range->min, t->range->max); break; case TBoolean: decode_primitive ("boolean", name, forwstr); break; case TEnumerated: decode_primitive ("enumerated", name, forwstr); break; case TOctetString: if (dertype) { fprintf(codefile, "if (%s == CONS) {\n", dertype); decode_primitive("octet_string_ber", name, forwstr); fprintf(codefile, "} else {\n"); } decode_primitive ("octet_string", name, forwstr); if (dertype) fprintf(codefile, "}\n"); if (t->range) range_check(name, "length", forwstr, t->range); break; case TBitString: { Member *m; int pos = 0; if (ASN1_TAILQ_EMPTY(t->members)) { decode_primitive ("bit_string", name, forwstr); break; } fprintf(codefile, "if (len < 1) return ASN1_OVERRUN;\n" "p++; len--; ret++;\n"); fprintf(codefile, "do {\n" "if (len < 1) break;\n"); ASN1_TAILQ_FOREACH(m, t->members, members) { while (m->val / 8 > pos / 8) { fprintf (codefile, "p++; len--; ret++;\n" "if (len < 1) break;\n"); pos += 8; } fprintf (codefile, "(%s)->%s = (*p >> %d) & 1;\n", name, m->gen_name, 7 - m->val % 8); } fprintf(codefile, "} while(0);\n"); fprintf (codefile, "p += len; ret += len;\n"); break; } case TSequence: { Member *m; if (t->members == NULL) break; ASN1_TAILQ_FOREACH(m, t->members, members) { char *s = NULL; if (m->ellipsis) continue; if (asprintf (&s, "%s(%s)->%s", m->optional ? "" : "&", name, m->gen_name) < 0 || s == NULL) errx(1, "malloc"); decode_type (s, m->type, m->optional, forwstr, m->gen_name, NULL, depth + 1); free (s); } break; } case TSet: { Member *m; unsigned int memno; if(t->members == NULL) break; fprintf(codefile, "{\n"); fprintf(codefile, "unsigned int members = 0;\n"); fprintf(codefile, "while(len > 0) {\n"); fprintf(codefile, "Der_class class;\n" "Der_type type;\n" "int tag;\n" "e = der_get_tag (p, len, &class, &type, &tag, NULL);\n" "if(e) %s;\n", forwstr); fprintf(codefile, "switch (MAKE_TAG(class, type, tag)) {\n"); memno = 0; ASN1_TAILQ_FOREACH(m, t->members, members) { char *s; assert(m->type->type == TTag); fprintf(codefile, "case MAKE_TAG(%s, %s, %s):\n", classname(m->type->tag.tagclass), is_primitive_type(m->type->subtype->type) ? "PRIM" : "CONS", valuename(m->type->tag.tagclass, m->type->tag.tagvalue)); if (asprintf (&s, "%s(%s)->%s", m->optional ? "" : "&", name, m->gen_name) < 0 || s == NULL) errx(1, "malloc"); if(m->optional) fprintf(codefile, "%s = calloc(1, sizeof(*%s));\n" "if (%s == NULL) { e = ENOMEM; %s; }\n", s, s, s, forwstr); decode_type (s, m->type, 0, forwstr, m->gen_name, NULL, depth + 1); free (s); fprintf(codefile, "members |= (1 << %d);\n", memno); memno++; fprintf(codefile, "break;\n"); } fprintf(codefile, "default:\n" "return ASN1_MISPLACED_FIELD;\n" "break;\n"); fprintf(codefile, "}\n"); fprintf(codefile, "}\n"); memno = 0; ASN1_TAILQ_FOREACH(m, t->members, members) { char *s; if (asprintf (&s, "%s->%s", name, m->gen_name) < 0 || s == NULL) errx(1, "malloc"); fprintf(codefile, "if((members & (1 << %d)) == 0)\n", memno); if(m->optional) fprintf(codefile, "%s = NULL;\n", s); else if(m->defval) gen_assign_defval(s, m->defval); else fprintf(codefile, "return ASN1_MISSING_FIELD;\n"); free(s); memno++; } fprintf(codefile, "}\n"); break; } case TSetOf: case TSequenceOf: { char *n = NULL; char *sname = NULL; fprintf (codefile, "{\n" "size_t %s_origlen = len;\n" "size_t %s_oldret = ret;\n" "size_t %s_olen = 0;\n" "void *%s_tmp;\n" "ret = 0;\n" "(%s)->len = 0;\n" "(%s)->val = NULL;\n", tmpstr, tmpstr, tmpstr, tmpstr, name, name); fprintf (codefile, "while(ret < %s_origlen) {\n" "size_t %s_nlen = %s_olen + sizeof(*((%s)->val));\n" "if (%s_olen > %s_nlen) { e = ASN1_OVERFLOW; %s; }\n" "%s_olen = %s_nlen;\n" "%s_tmp = realloc((%s)->val, %s_olen);\n" "if (%s_tmp == NULL) { e = ENOMEM; %s; }\n" "(%s)->val = %s_tmp;\n", tmpstr, tmpstr, tmpstr, name, tmpstr, tmpstr, forwstr, tmpstr, tmpstr, tmpstr, name, tmpstr, tmpstr, forwstr, name, tmpstr); if (asprintf (&n, "&(%s)->val[(%s)->len]", name, name) < 0 || n == NULL) errx(1, "malloc"); if (asprintf (&sname, "%s_s_of", tmpstr) < 0 || sname == NULL) errx(1, "malloc"); decode_type (n, t->subtype, 0, forwstr, sname, NULL, depth + 1); fprintf (codefile, "(%s)->len++;\n" "len = %s_origlen - ret;\n" "}\n" "ret += %s_oldret;\n" "}\n", name, tmpstr, tmpstr); if (t->range) range_check(name, "len", forwstr, t->range); free (n); free (sname); break; } case TGeneralizedTime: decode_primitive ("generalized_time", name, forwstr); break; case TGeneralString: decode_primitive ("general_string", name, forwstr); break; case TTeletexString: decode_primitive ("general_string", name, forwstr); break; case TTag:{ char *tname = NULL, *typestring = NULL; char *ide = NULL; if (asprintf(&typestring, "%s_type", tmpstr) < 0 || typestring == NULL) errx(1, "malloc"); fprintf(codefile, "{\n" "size_t %s_datalen, %s_oldlen;\n" "Der_type %s;\n", tmpstr, tmpstr, typestring); if(support_ber) fprintf(codefile, "int is_indefinite%u;\n", depth); fprintf(codefile, "e = der_match_tag_and_length(p, len, %s, &%s, %s, " "&%s_datalen, &l);\n", classname(t->tag.tagclass), typestring, valuename(t->tag.tagclass, t->tag.tagvalue), tmpstr); /* XXX hardcode for now */ if (support_ber && t->subtype->type == TOctetString) { ide = typestring; } else { fprintf(codefile, "if (e == 0 && %s != %s) { e = ASN1_BAD_ID; }\n", typestring, is_primitive_type(t->subtype->type) ? "PRIM" : "CONS"); } if(optional) { fprintf(codefile, "if(e) {\n" "%s = NULL;\n" "} else {\n" "%s = calloc(1, sizeof(*%s));\n" "if (%s == NULL) { e = ENOMEM; %s; }\n", name, name, name, name, forwstr); } else { fprintf(codefile, "if(e) %s;\n", forwstr); } fprintf (codefile, "p += l; len -= l; ret += l;\n" "%s_oldlen = len;\n", tmpstr); if(support_ber) fprintf (codefile, "if((is_indefinite%u = _heim_fix_dce(%s_datalen, &len)) < 0)\n" "{ e = ASN1_BAD_FORMAT; %s; }\n" "if (is_indefinite%u) { if (len < 2) { e = ASN1_OVERRUN; %s; } len -= 2; }", depth, tmpstr, forwstr, depth, forwstr); else fprintf(codefile, "if (%s_datalen > len) { e = ASN1_OVERRUN; %s; }\n" "len = %s_datalen;\n", tmpstr, forwstr, tmpstr); if (asprintf (&tname, "%s_Tag", tmpstr) < 0 || tname == NULL) errx(1, "malloc"); decode_type (name, t->subtype, 0, forwstr, tname, ide, depth + 1); if(support_ber) fprintf(codefile, "if(is_indefinite%u){\n" "len += 2;\n" "e = der_match_tag_and_length(p, len, " "(Der_class)0, &%s, UT_EndOfContent, " "&%s_datalen, &l);\n" "if(e) %s;\n" "p += l; len -= l; ret += l;\n" "if (%s != (Der_type)0) { e = ASN1_BAD_ID; %s; }\n" "} else \n", depth, typestring, tmpstr, forwstr, typestring, forwstr); fprintf(codefile, "len = %s_oldlen - %s_datalen;\n", tmpstr, tmpstr); if(optional) fprintf(codefile, "}\n"); fprintf(codefile, "}\n"); free(tname); free(typestring); break; } case TChoice: { Member *m, *have_ellipsis = NULL; const char *els = ""; if (t->members == NULL) break; ASN1_TAILQ_FOREACH(m, t->members, members) { const Type *tt = m->type; char *s = NULL; Der_class cl; Der_type ty; unsigned tag; if (m->ellipsis) { have_ellipsis = m; continue; } find_tag(tt, &cl, &ty, &tag); fprintf(codefile, "%sif (der_match_tag(p, len, %s, %s, %s, NULL) == 0) {\n", els, classname(cl), ty ? "CONS" : "PRIM", valuename(cl, tag)); + fprintf(codefile, + "(%s)->element = %s;\n", + name, m->label); if (asprintf (&s, "%s(%s)->u.%s", m->optional ? "" : "&", name, m->gen_name) < 0 || s == NULL) errx(1, "malloc"); decode_type (s, m->type, m->optional, forwstr, m->gen_name, NULL, depth + 1); - fprintf(codefile, - "(%s)->element = %s;\n", - name, m->label); free(s); fprintf(codefile, "}\n"); els = "else "; } if (have_ellipsis) { fprintf(codefile, "else {\n" + "(%s)->element = %s;\n" "(%s)->u.%s.data = calloc(1, len);\n" "if ((%s)->u.%s.data == NULL) {\n" "e = ENOMEM; %s;\n" "}\n" "(%s)->u.%s.length = len;\n" "memcpy((%s)->u.%s.data, p, len);\n" - "(%s)->element = %s;\n" "p += len;\n" "ret += len;\n" "len = 0;\n" "}\n", + name, have_ellipsis->label, name, have_ellipsis->gen_name, name, have_ellipsis->gen_name, forwstr, name, have_ellipsis->gen_name, - name, have_ellipsis->gen_name, - name, have_ellipsis->label); + name, have_ellipsis->gen_name); } else { fprintf(codefile, "else {\n" "e = ASN1_PARSE_ERROR;\n" "%s;\n" "}\n", forwstr); } break; } case TUTCTime: decode_primitive ("utctime", name, forwstr); break; case TUTF8String: decode_primitive ("utf8string", name, forwstr); break; case TPrintableString: decode_primitive ("printable_string", name, forwstr); break; case TIA5String: decode_primitive ("ia5_string", name, forwstr); break; case TBMPString: decode_primitive ("bmp_string", name, forwstr); break; case TUniversalString: decode_primitive ("universal_string", name, forwstr); break; case TVisibleString: decode_primitive ("visible_string", name, forwstr); break; case TNull: fprintf (codefile, "/* NULL */\n"); break; case TOID: decode_primitive ("oid", name, forwstr); break; default : abort (); } return 0; } void generate_type_decode (const Symbol *s) { int preserve = preserve_type(s->name) ? TRUE : FALSE; fprintf (codefile, "int ASN1CALL\n" "decode_%s(const unsigned char *p HEIMDAL_UNUSED_ATTRIBUTE," " size_t len HEIMDAL_UNUSED_ATTRIBUTE, %s *data, size_t *size)\n" "{\n", s->gen_name, s->gen_name); switch (s->type->type) { case TInteger: case TBoolean: case TOctetString: case TOID: case TGeneralizedTime: case TGeneralString: case TTeletexString: case TUTF8String: case TPrintableString: case TIA5String: case TBMPString: case TUniversalString: case TVisibleString: case TUTCTime: case TNull: case TEnumerated: case TBitString: case TSequence: case TSequenceOf: case TSet: case TSetOf: case TTag: case TType: case TChoice: fprintf (codefile, "size_t ret = 0;\n" "size_t l HEIMDAL_UNUSED_ATTRIBUTE;\n" "int e HEIMDAL_UNUSED_ATTRIBUTE;\n"); if (preserve) fprintf (codefile, "const unsigned char *begin = p;\n"); fprintf (codefile, "\n"); fprintf (codefile, "memset(data, 0, sizeof(*data));\n"); /* hack to avoid `unused variable' */ decode_type ("data", s->type, 0, "goto fail", "Top", NULL, 1); if (preserve) fprintf (codefile, "data->_save.data = calloc(1, ret);\n" "if (data->_save.data == NULL) { \n" "e = ENOMEM; goto fail; \n" "}\n" "data->_save.length = ret;\n" "memcpy(data->_save.data, begin, ret);\n"); fprintf (codefile, "if(size) *size = ret;\n" "return 0;\n"); fprintf (codefile, "fail:\n" "free_%s(data);\n" "return e;\n", s->gen_name); break; default: abort (); } fprintf (codefile, "}\n\n"); } diff --git a/crypto/heimdal/lib/asn1/gen_free.c b/crypto/heimdal/lib/asn1/gen_free.c index b9cae7533b17..74449fe6ca82 100644 --- a/crypto/heimdal/lib/asn1/gen_free.c +++ b/crypto/heimdal/lib/asn1/gen_free.c @@ -1,191 +1,198 @@ /* * Copyright (c) 1997 - 2005 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 "gen_locl.h" RCSID("$Id$"); static void free_primitive (const char *typename, const char *name) { fprintf (codefile, "der_free_%s(%s);\n", typename, name); } static void free_type (const char *name, const Type *t, int preserve) { switch (t->type) { case TType: #if 0 free_type (name, t->symbol->type, preserve); #endif fprintf (codefile, "free_%s(%s);\n", t->symbol->gen_name, name); break; case TInteger: if (t->range == NULL && t->members == NULL) { free_primitive ("heim_integer", name); break; } case TBoolean: case TEnumerated : case TNull: case TGeneralizedTime: case TUTCTime: + /* + * This doesn't do much, but it leaves zeros where garbage might + * otherwise have been found. Gets us closer to having the equivalent + * of a memset()-to-zero data structure after calling the free + * functions. + */ + fprintf(codefile, "*%s = 0;\n", name); break; case TBitString: if (ASN1_TAILQ_EMPTY(t->members)) free_primitive("bit_string", name); break; case TOctetString: free_primitive ("octet_string", name); break; case TChoice: case TSet: case TSequence: { Member *m, *have_ellipsis = NULL; if (t->members == NULL) break; if ((t->type == TSequence || t->type == TChoice) && preserve) fprintf(codefile, "der_free_octet_string(&data->_save);\n"); if(t->type == TChoice) fprintf(codefile, "switch((%s)->element) {\n", name); ASN1_TAILQ_FOREACH(m, t->members, members) { char *s; if (m->ellipsis){ have_ellipsis = m; continue; } if(t->type == TChoice) fprintf(codefile, "case %s:\n", m->label); if (asprintf (&s, "%s(%s)->%s%s", m->optional ? "" : "&", name, t->type == TChoice ? "u." : "", m->gen_name) < 0 || s == NULL) errx(1, "malloc"); if(m->optional) fprintf(codefile, "if(%s) {\n", s); free_type (s, m->type, FALSE); if(m->optional) fprintf(codefile, "free(%s);\n" "%s = NULL;\n" "}\n",s, s); free (s); if(t->type == TChoice) fprintf(codefile, "break;\n"); } if(t->type == TChoice) { if (have_ellipsis) fprintf(codefile, "case %s:\n" "der_free_octet_string(&(%s)->u.%s);\n" "break;", have_ellipsis->label, name, have_ellipsis->gen_name); fprintf(codefile, "}\n"); } break; } case TSetOf: case TSequenceOf: { char *n; fprintf (codefile, "while((%s)->len){\n", name); if (asprintf (&n, "&(%s)->val[(%s)->len-1]", name, name) < 0 || n == NULL) errx(1, "malloc"); free_type(n, t->subtype, FALSE); fprintf(codefile, "(%s)->len--;\n" "}\n", name); fprintf(codefile, "free((%s)->val);\n" "(%s)->val = NULL;\n", name, name); free(n); break; } case TGeneralString: free_primitive ("general_string", name); break; case TTeletexString: free_primitive ("general_string", name); break; case TUTF8String: free_primitive ("utf8string", name); break; case TPrintableString: free_primitive ("printable_string", name); break; case TIA5String: free_primitive ("ia5_string", name); break; case TBMPString: free_primitive ("bmp_string", name); break; case TUniversalString: free_primitive ("universal_string", name); break; case TVisibleString: free_primitive ("visible_string", name); break; case TTag: free_type (name, t->subtype, preserve); break; case TOID : free_primitive ("oid", name); break; default : abort (); } } void generate_type_free (const Symbol *s) { int preserve = preserve_type(s->name) ? TRUE : FALSE; fprintf (codefile, "void ASN1CALL\n" "free_%s(%s *data)\n" "{\n", s->gen_name, s->gen_name); free_type ("data", s->type, preserve); fprintf (codefile, "}\n\n"); } diff --git a/crypto/heimdal/lib/gssapi/krb5/accept_sec_context.c b/crypto/heimdal/lib/gssapi/krb5/accept_sec_context.c index 5a00e124c2cf..baeafb95efaf 100644 --- a/crypto/heimdal/lib/gssapi/krb5/accept_sec_context.c +++ b/crypto/heimdal/lib/gssapi/krb5/accept_sec_context.c @@ -1,935 +1,936 @@ /* * Copyright (c) 1997 - 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" HEIMDAL_MUTEX gssapi_keytab_mutex = HEIMDAL_MUTEX_INITIALIZER; krb5_keytab _gsskrb5_keytab; static krb5_error_code validate_keytab(krb5_context context, const char *name, krb5_keytab *id) { krb5_error_code ret; ret = krb5_kt_resolve(context, name, id); if (ret) return ret; ret = krb5_kt_have_content(context, *id); if (ret) { krb5_kt_close(context, *id); *id = NULL; } return ret; } OM_uint32 _gsskrb5_register_acceptor_identity(OM_uint32 *min_stat, const char *identity) { krb5_context context; krb5_error_code ret; *min_stat = 0; ret = _gsskrb5_init(&context); if(ret) return GSS_S_FAILURE; HEIMDAL_MUTEX_lock(&gssapi_keytab_mutex); if(_gsskrb5_keytab != NULL) { krb5_kt_close(context, _gsskrb5_keytab); _gsskrb5_keytab = NULL; } if (identity == NULL) { ret = krb5_kt_default(context, &_gsskrb5_keytab); } else { /* * First check if we can the keytab as is and if it has content... */ ret = validate_keytab(context, identity, &_gsskrb5_keytab); /* * if it doesn't, lets prepend FILE: and try again */ if (ret) { char *p = NULL; ret = asprintf(&p, "FILE:%s", identity); if(ret < 0 || p == NULL) { HEIMDAL_MUTEX_unlock(&gssapi_keytab_mutex); return GSS_S_FAILURE; } ret = validate_keytab(context, p, &_gsskrb5_keytab); free(p); } } HEIMDAL_MUTEX_unlock(&gssapi_keytab_mutex); if(ret) { *min_stat = ret; return GSS_S_FAILURE; } return GSS_S_COMPLETE; } void _gsskrb5i_is_cfx(krb5_context context, gsskrb5_ctx ctx, int acceptor) { krb5_error_code ret; krb5_keyblock *key; if (acceptor) { if (ctx->auth_context->local_subkey) key = ctx->auth_context->local_subkey; else key = ctx->auth_context->remote_subkey; } else { if (ctx->auth_context->remote_subkey) key = ctx->auth_context->remote_subkey; else key = ctx->auth_context->local_subkey; } if (key == NULL) key = ctx->auth_context->keyblock; if (key == NULL) return; switch (key->keytype) { case ETYPE_DES_CBC_CRC: case ETYPE_DES_CBC_MD4: case ETYPE_DES_CBC_MD5: case ETYPE_DES3_CBC_MD5: case ETYPE_OLD_DES3_CBC_SHA1: case ETYPE_DES3_CBC_SHA1: case ETYPE_ARCFOUR_HMAC_MD5: case ETYPE_ARCFOUR_HMAC_MD5_56: break; default : ctx->more_flags |= IS_CFX; if ((acceptor && ctx->auth_context->local_subkey) || (!acceptor && ctx->auth_context->remote_subkey)) ctx->more_flags |= ACCEPTOR_SUBKEY; break; } if (ctx->crypto) krb5_crypto_destroy(context, ctx->crypto); ret = krb5_crypto_init(context, key, 0, &ctx->crypto); } static OM_uint32 gsskrb5_accept_delegated_token (OM_uint32 * minor_status, gsskrb5_ctx ctx, krb5_context context, gss_cred_id_t * delegated_cred_handle ) { krb5_ccache ccache = NULL; krb5_error_code kret; int32_t ac_flags, ret = GSS_S_COMPLETE; *minor_status = 0; /* XXX Create a new delegated_cred_handle? */ if (delegated_cred_handle == NULL) { kret = krb5_cc_default (context, &ccache); } else { *delegated_cred_handle = NULL; kret = krb5_cc_new_unique (context, krb5_cc_type_memory, NULL, &ccache); } if (kret) { ctx->flags &= ~GSS_C_DELEG_FLAG; goto out; } kret = krb5_cc_initialize(context, ccache, ctx->source); if (kret) { ctx->flags &= ~GSS_C_DELEG_FLAG; goto out; } krb5_auth_con_removeflags(context, ctx->auth_context, KRB5_AUTH_CONTEXT_DO_TIME, &ac_flags); kret = krb5_rd_cred2(context, ctx->auth_context, ccache, &ctx->fwd_data); krb5_auth_con_setflags(context, ctx->auth_context, ac_flags); if (kret) { ctx->flags &= ~GSS_C_DELEG_FLAG; ret = GSS_S_FAILURE; *minor_status = kret; goto out; } if (delegated_cred_handle) { gsskrb5_cred handle; ret = _gsskrb5_krb5_import_cred(minor_status, ccache, NULL, NULL, delegated_cred_handle); if (ret != GSS_S_COMPLETE) goto out; handle = (gsskrb5_cred) *delegated_cred_handle; handle->cred_flags |= GSS_CF_DESTROY_CRED_ON_RELEASE; krb5_cc_close(context, ccache); ccache = NULL; } out: if (ccache) { /* Don't destroy the default cred cache */ if (delegated_cred_handle == NULL) krb5_cc_close(context, ccache); else krb5_cc_destroy(context, ccache); } return ret; } static OM_uint32 gsskrb5_acceptor_ready(OM_uint32 * minor_status, gsskrb5_ctx ctx, krb5_context context, gss_cred_id_t *delegated_cred_handle) { OM_uint32 ret; int32_t seq_number; int is_cfx = 0; krb5_auth_con_getremoteseqnumber (context, ctx->auth_context, &seq_number); _gsskrb5i_is_cfx(context, ctx, 1); is_cfx = (ctx->more_flags & IS_CFX); ret = _gssapi_msg_order_create(minor_status, &ctx->order, _gssapi_msg_order_f(ctx->flags), seq_number, 0, is_cfx); if (ret) return ret; /* * If requested, set local sequence num to remote sequence if this * isn't a mutual authentication context */ if (!(ctx->flags & GSS_C_MUTUAL_FLAG) && _gssapi_msg_order_f(ctx->flags)) { krb5_auth_con_setlocalseqnumber(context, ctx->auth_context, seq_number); } /* * We should handle the delegation ticket, in case it's there */ if (ctx->fwd_data.length > 0 && (ctx->flags & GSS_C_DELEG_FLAG)) { ret = gsskrb5_accept_delegated_token(minor_status, ctx, context, delegated_cred_handle); if (ret) return ret; } else { /* Well, looks like it wasn't there after all */ ctx->flags &= ~GSS_C_DELEG_FLAG; } ctx->state = ACCEPTOR_READY; ctx->more_flags |= OPEN; return GSS_S_COMPLETE; } static OM_uint32 send_error_token(OM_uint32 *minor_status, krb5_context context, krb5_error_code kret, krb5_principal server, krb5_data *indata, gss_buffer_t output_token) { krb5_principal ap_req_server = NULL; krb5_error_code ret; krb5_data outbuf; /* this e_data value encodes KERB_AP_ERR_TYPE_SKEW_RECOVERY which tells windows to try again with the corrected timestamp. See [MS-KILE] 2.2.1 KERB-ERROR-DATA */ krb5_data e_data = { 7, rk_UNCONST("\x30\x05\xa1\x03\x02\x01\x02") }; /* build server from request if the acceptor had not selected one */ if (server == NULL) { AP_REQ ap_req; ret = krb5_decode_ap_req(context, indata, &ap_req); if (ret) { *minor_status = ret; return GSS_S_FAILURE; } ret = _krb5_principalname2krb5_principal(context, &ap_req_server, ap_req.ticket.sname, ap_req.ticket.realm); free_AP_REQ(&ap_req); if (ret) { *minor_status = ret; return GSS_S_FAILURE; } server = ap_req_server; } ret = krb5_mk_error(context, kret, NULL, &e_data, NULL, server, NULL, NULL, &outbuf); if (ap_req_server) krb5_free_principal(context, ap_req_server); if (ret) { *minor_status = ret; return GSS_S_FAILURE; } ret = _gsskrb5_encapsulate(minor_status, &outbuf, output_token, "\x03\x00", GSS_KRB5_MECHANISM); krb5_data_free (&outbuf); if (ret) return ret; *minor_status = 0; return GSS_S_CONTINUE_NEEDED; } static OM_uint32 gsskrb5_acceptor_start(OM_uint32 * minor_status, gsskrb5_ctx ctx, krb5_context context, const gss_cred_id_t acceptor_cred_handle, const gss_buffer_t input_token_buffer, const gss_channel_bindings_t input_chan_bindings, gss_name_t * src_name, gss_OID * mech_type, gss_buffer_t output_token, OM_uint32 * ret_flags, OM_uint32 * time_rec, gss_cred_id_t * delegated_cred_handle) { krb5_error_code kret; OM_uint32 ret = GSS_S_COMPLETE; krb5_data indata; krb5_flags ap_options; krb5_keytab keytab = NULL; int is_cfx = 0; const gsskrb5_cred acceptor_cred = (gsskrb5_cred)acceptor_cred_handle; /* * We may, or may not, have an escapsulation. */ ret = _gsskrb5_decapsulate (minor_status, input_token_buffer, &indata, "\x01\x00", GSS_KRB5_MECHANISM); if (ret) { /* Assume that there is no OID wrapping. */ indata.length = input_token_buffer->length; indata.data = input_token_buffer->value; } /* * We need to get our keytab */ if (acceptor_cred == NULL) { if (_gsskrb5_keytab != NULL) keytab = _gsskrb5_keytab; } else if (acceptor_cred->keytab != NULL) { keytab = acceptor_cred->keytab; } /* * We need to check the ticket and create the AP-REP packet */ { krb5_rd_req_in_ctx in = NULL; krb5_rd_req_out_ctx out = NULL; krb5_principal server = NULL; if (acceptor_cred) server = acceptor_cred->principal; kret = krb5_rd_req_in_ctx_alloc(context, &in); if (kret == 0) kret = krb5_rd_req_in_set_keytab(context, in, keytab); if (kret) { if (in) krb5_rd_req_in_ctx_free(context, in); *minor_status = kret; return GSS_S_FAILURE; } kret = krb5_rd_req_ctx(context, &ctx->auth_context, &indata, server, in, &out); krb5_rd_req_in_ctx_free(context, in); if (kret == KRB5KRB_AP_ERR_SKEW || kret == KRB5KRB_AP_ERR_TKT_NYV) { /* * No reply in non-MUTUAL mode, but we don't know that its * non-MUTUAL mode yet, thats inside the 8003 checksum, so * lets only send the error token on clock skew, that * limit when send error token for non-MUTUAL. */ + free_Authenticator(ctx->auth_context->authenticator); return send_error_token(minor_status, context, kret, server, &indata, output_token); } else if (kret) { *minor_status = kret; return GSS_S_FAILURE; } /* * we need to remember some data on the context_handle. */ kret = krb5_rd_req_out_get_ap_req_options(context, out, &ap_options); if (kret == 0) kret = krb5_rd_req_out_get_ticket(context, out, &ctx->ticket); if (kret == 0) kret = krb5_rd_req_out_get_keyblock(context, out, &ctx->service_keyblock); ctx->lifetime = ctx->ticket->ticket.endtime; krb5_rd_req_out_ctx_free(context, out); if (kret) { ret = GSS_S_FAILURE; *minor_status = kret; return ret; } } /* * We need to copy the principal names to the context and the * calling layer. */ kret = krb5_copy_principal(context, ctx->ticket->client, &ctx->source); if (kret) { ret = GSS_S_FAILURE; *minor_status = kret; } kret = krb5_copy_principal(context, ctx->ticket->server, &ctx->target); if (kret) { ret = GSS_S_FAILURE; *minor_status = kret; return ret; } /* * We need to setup some compat stuff, this assumes that * context_handle->target is already set. */ ret = _gss_DES3_get_mic_compat(minor_status, ctx, context); if (ret) return ret; if (src_name != NULL) { kret = krb5_copy_principal (context, ctx->ticket->client, (gsskrb5_name*)src_name); if (kret) { ret = GSS_S_FAILURE; *minor_status = kret; return ret; } } /* * We need to get the flags out of the 8003 checksum. */ { krb5_authenticator authenticator; kret = krb5_auth_con_getauthenticator(context, ctx->auth_context, &authenticator); if(kret) { ret = GSS_S_FAILURE; *minor_status = kret; return ret; } if (authenticator->cksum == NULL) { krb5_free_authenticator(context, &authenticator); *minor_status = 0; return GSS_S_BAD_BINDINGS; } if (authenticator->cksum->cksumtype == CKSUMTYPE_GSSAPI) { ret = _gsskrb5_verify_8003_checksum(minor_status, input_chan_bindings, authenticator->cksum, &ctx->flags, &ctx->fwd_data); krb5_free_authenticator(context, &authenticator); if (ret) { return ret; } } else { krb5_crypto crypto; kret = krb5_crypto_init(context, ctx->auth_context->keyblock, 0, &crypto); if(kret) { krb5_free_authenticator(context, &authenticator); ret = GSS_S_FAILURE; *minor_status = kret; return ret; } /* * Windows accepts Samba3's use of a kerberos, rather than * GSSAPI checksum here */ kret = krb5_verify_checksum(context, crypto, KRB5_KU_AP_REQ_AUTH_CKSUM, NULL, 0, authenticator->cksum); krb5_free_authenticator(context, &authenticator); krb5_crypto_destroy(context, crypto); if(kret) { ret = GSS_S_BAD_SIG; *minor_status = kret; return ret; } /* * Samba style get some flags (but not DCE-STYLE), use * ap_options to guess the mutual flag. */ ctx->flags = GSS_C_REPLAY_FLAG | GSS_C_SEQUENCE_FLAG; if (ap_options & AP_OPTS_MUTUAL_REQUIRED) ctx->flags |= GSS_C_MUTUAL_FLAG; } } if(ctx->flags & GSS_C_MUTUAL_FLAG) { krb5_data outbuf; int use_subkey = 0; _gsskrb5i_is_cfx(context, ctx, 1); is_cfx = (ctx->more_flags & IS_CFX); if (is_cfx || (ap_options & AP_OPTS_USE_SUBKEY)) { use_subkey = 1; } else { krb5_keyblock *rkey; /* * If there is a initiator subkey, copy that to acceptor * subkey to match Windows behavior */ kret = krb5_auth_con_getremotesubkey(context, ctx->auth_context, &rkey); if (kret == 0) { kret = krb5_auth_con_setlocalsubkey(context, ctx->auth_context, rkey); if (kret == 0) use_subkey = 1; krb5_free_keyblock(context, rkey); } } if (use_subkey) { ctx->more_flags |= ACCEPTOR_SUBKEY; krb5_auth_con_addflags(context, ctx->auth_context, KRB5_AUTH_CONTEXT_USE_SUBKEY, NULL); } kret = krb5_mk_rep(context, ctx->auth_context, &outbuf); if (kret) { *minor_status = kret; return GSS_S_FAILURE; } if (IS_DCE_STYLE(ctx)) { output_token->length = outbuf.length; output_token->value = outbuf.data; } else { ret = _gsskrb5_encapsulate(minor_status, &outbuf, output_token, "\x02\x00", GSS_KRB5_MECHANISM); krb5_data_free (&outbuf); if (ret) return ret; } } ctx->flags |= GSS_C_TRANS_FLAG; /* Remember the flags */ ctx->lifetime = ctx->ticket->ticket.endtime; ctx->more_flags |= OPEN; if (mech_type) *mech_type = GSS_KRB5_MECHANISM; if (time_rec) { ret = _gsskrb5_lifetime_left(minor_status, context, ctx->lifetime, time_rec); if (ret) { return ret; } } /* * When GSS_C_DCE_STYLE is in use, we need ask for a AP-REP from * the client. */ if (IS_DCE_STYLE(ctx)) { /* * Return flags to caller, but we haven't processed * delgations yet */ if (ret_flags) *ret_flags = (ctx->flags & ~GSS_C_DELEG_FLAG); ctx->state = ACCEPTOR_WAIT_FOR_DCESTYLE; return GSS_S_CONTINUE_NEEDED; } ret = gsskrb5_acceptor_ready(minor_status, ctx, context, delegated_cred_handle); if (ret_flags) *ret_flags = ctx->flags; return ret; } static OM_uint32 acceptor_wait_for_dcestyle(OM_uint32 * minor_status, gsskrb5_ctx ctx, krb5_context context, const gss_cred_id_t acceptor_cred_handle, const gss_buffer_t input_token_buffer, const gss_channel_bindings_t input_chan_bindings, gss_name_t * src_name, gss_OID * mech_type, gss_buffer_t output_token, OM_uint32 * ret_flags, OM_uint32 * time_rec, gss_cred_id_t * delegated_cred_handle) { OM_uint32 ret; krb5_error_code kret; krb5_data inbuf; int32_t r_seq_number, l_seq_number; /* * We know it's GSS_C_DCE_STYLE so we don't need to decapsulate the AP_REP */ inbuf.length = input_token_buffer->length; inbuf.data = input_token_buffer->value; /* * We need to remeber the old remote seq_number, then check if the * client has replied with our local seq_number, and then reset * the remote seq_number to the old value */ { kret = krb5_auth_con_getlocalseqnumber(context, ctx->auth_context, &l_seq_number); if (kret) { *minor_status = kret; return GSS_S_FAILURE; } kret = krb5_auth_con_getremoteseqnumber(context, ctx->auth_context, &r_seq_number); if (kret) { *minor_status = kret; return GSS_S_FAILURE; } kret = krb5_auth_con_setremoteseqnumber(context, ctx->auth_context, l_seq_number); if (kret) { *minor_status = kret; return GSS_S_FAILURE; } } /* * We need to verify the AP_REP, but we need to flag that this is * DCE_STYLE, so don't check the timestamps this time, but put the * flag DO_TIME back afterward. */ { krb5_ap_rep_enc_part *repl; int32_t auth_flags; krb5_auth_con_removeflags(context, ctx->auth_context, KRB5_AUTH_CONTEXT_DO_TIME, &auth_flags); kret = krb5_rd_rep(context, ctx->auth_context, &inbuf, &repl); if (kret) { *minor_status = kret; return GSS_S_FAILURE; } krb5_free_ap_rep_enc_part(context, repl); krb5_auth_con_setflags(context, ctx->auth_context, auth_flags); } /* We need to check the liftime */ { OM_uint32 lifetime_rec; ret = _gsskrb5_lifetime_left(minor_status, context, ctx->lifetime, &lifetime_rec); if (ret) { return ret; } if (lifetime_rec == 0) { return GSS_S_CONTEXT_EXPIRED; } if (time_rec) *time_rec = lifetime_rec; } /* We need to give the caller the flags which are in use */ if (ret_flags) *ret_flags = ctx->flags; if (src_name) { kret = krb5_copy_principal(context, ctx->source, (gsskrb5_name*)src_name); if (kret) { *minor_status = kret; return GSS_S_FAILURE; } } /* * After the krb5_rd_rep() the remote and local seq_number should * be the same, because the client just replies the seq_number * from our AP-REP in its AP-REP, but then the client uses the * seq_number from its AP-REQ for GSS_wrap() */ { int32_t tmp_r_seq_number, tmp_l_seq_number; kret = krb5_auth_con_getremoteseqnumber(context, ctx->auth_context, &tmp_r_seq_number); if (kret) { *minor_status = kret; return GSS_S_FAILURE; } kret = krb5_auth_con_getlocalseqnumber(context, ctx->auth_context, &tmp_l_seq_number); if (kret) { *minor_status = kret; return GSS_S_FAILURE; } /* * Here we check if the client has responsed with our local seq_number, */ if (tmp_r_seq_number != tmp_l_seq_number) { return GSS_S_UNSEQ_TOKEN; } } /* * We need to reset the remote seq_number, because the client will use, * the old one for the GSS_wrap() calls */ { kret = krb5_auth_con_setremoteseqnumber(context, ctx->auth_context, r_seq_number); if (kret) { *minor_status = kret; return GSS_S_FAILURE; } } return gsskrb5_acceptor_ready(minor_status, ctx, context, delegated_cred_handle); } OM_uint32 GSSAPI_CALLCONV _gsskrb5_accept_sec_context(OM_uint32 * minor_status, gss_ctx_id_t * context_handle, const gss_cred_id_t acceptor_cred_handle, const gss_buffer_t input_token_buffer, const gss_channel_bindings_t input_chan_bindings, gss_name_t * src_name, gss_OID * mech_type, gss_buffer_t output_token, OM_uint32 * ret_flags, OM_uint32 * time_rec, gss_cred_id_t * delegated_cred_handle) { krb5_context context; OM_uint32 ret; gsskrb5_ctx ctx; GSSAPI_KRB5_INIT(&context); output_token->length = 0; output_token->value = NULL; if (src_name != NULL) *src_name = NULL; if (mech_type) *mech_type = GSS_KRB5_MECHANISM; if (*context_handle == GSS_C_NO_CONTEXT) { ret = _gsskrb5_create_ctx(minor_status, context_handle, context, input_chan_bindings, ACCEPTOR_START); if (ret) return ret; } ctx = (gsskrb5_ctx)*context_handle; /* * TODO: check the channel_bindings * (above just sets them to krb5 layer) */ HEIMDAL_MUTEX_lock(&ctx->ctx_id_mutex); switch (ctx->state) { case ACCEPTOR_START: ret = gsskrb5_acceptor_start(minor_status, ctx, context, acceptor_cred_handle, input_token_buffer, input_chan_bindings, src_name, mech_type, output_token, ret_flags, time_rec, delegated_cred_handle); break; case ACCEPTOR_WAIT_FOR_DCESTYLE: ret = acceptor_wait_for_dcestyle(minor_status, ctx, context, acceptor_cred_handle, input_token_buffer, input_chan_bindings, src_name, mech_type, output_token, ret_flags, time_rec, delegated_cred_handle); break; case ACCEPTOR_READY: /* * If we get there, the caller have called * gss_accept_sec_context() one time too many. */ ret = GSS_S_BAD_STATUS; break; default: /* TODO: is this correct here? --metze */ ret = GSS_S_BAD_STATUS; break; } HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex); if (GSS_ERROR(ret)) { OM_uint32 min2; _gsskrb5_delete_sec_context(&min2, context_handle, GSS_C_NO_BUFFER); } return ret; } diff --git a/crypto/heimdal/lib/gssapi/krb5/arcfour.c b/crypto/heimdal/lib/gssapi/krb5/arcfour.c index b4ef8d39ffd7..3b8d452877dd 100644 --- a/crypto/heimdal/lib/gssapi/krb5/arcfour.c +++ b/crypto/heimdal/lib/gssapi/krb5/arcfour.c @@ -1,791 +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; _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); 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); + cmp = (ct_memcmp(cksum_data, p + 8, 8) == 0); if (cmp) { *minor_status = 0; return GSS_S_BAD_MIC; } { EVP_CIPHER_CTX *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); + cmp = (ct_memcmp(&SND_SEQ[4], "\xff\xff\xff\xff", 4) != 0); else - cmp = memcmp(&SND_SEQ[4], "\x00\x00\x00\x00", 4); + cmp = (ct_memcmp(&SND_SEQ[4], "\x00\x00\x00\x00", 4) != 0); 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; 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; 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; 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); + cmp = (ct_memcmp(&SND_SEQ[4], "\xff\xff\xff\xff", 4) != 0); else - cmp = memcmp(&SND_SEQ[4], "\x00\x00\x00\x00", 4); + cmp = (ct_memcmp(&SND_SEQ[4], "\x00\x00\x00\x00", 4) != 0); 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; 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 */ + cmp = (ct_memcmp(cksum_data, p0 + 16, 8) == 0); /* 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; } diff --git a/crypto/heimdal/lib/gssapi/krb5/decapsulate.c b/crypto/heimdal/lib/gssapi/krb5/decapsulate.c index 640c064d0bf1..343a3d7acb97 100644 --- a/crypto/heimdal/lib/gssapi/krb5/decapsulate.c +++ b/crypto/heimdal/lib/gssapi/krb5/decapsulate.c @@ -1,207 +1,213 @@ /* * Copyright (c) 1997 - 2001 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 the length of the mechanism in token or -1 * (which implies that the token was bad - GSS_S_DEFECTIVE_TOKEN */ ssize_t _gsskrb5_get_mech (const u_char *ptr, size_t total_len, const u_char **mech_ret) { size_t len, len_len, mech_len, foo; const u_char *p = ptr; int e; if (total_len < 1) return -1; if (*p++ != 0x60) return -1; e = der_get_length (p, total_len - 1, &len, &len_len); if (e || 1 + len_len + len != total_len) return -1; + if (total_len < 1 + len_len + 1) + return -1; p += len_len; if (*p++ != 0x06) return -1; e = der_get_length (p, total_len - 1 - len_len - 1, &mech_len, &foo); if (e) return -1; p += foo; *mech_ret = p; return mech_len; } OM_uint32 _gssapi_verify_mech_header(u_char **str, size_t total_len, gss_OID mech) { const u_char *p; ssize_t mech_len; mech_len = _gsskrb5_get_mech (*str, total_len, &p); if (mech_len < 0) return GSS_S_DEFECTIVE_TOKEN; if (mech_len != mech->length) return GSS_S_BAD_MECH; + if (mech_len > total_len) + return GSS_S_BAD_MECH; + if (p - *str > total_len - mech_len) + return GSS_S_BAD_MECH; if (ct_memcmp(p, mech->elements, mech->length) != 0) return GSS_S_BAD_MECH; p += mech_len; *str = rk_UNCONST(p); return GSS_S_COMPLETE; } OM_uint32 _gsskrb5_verify_header(u_char **str, size_t total_len, const void *type, gss_OID oid) { OM_uint32 ret; size_t len; u_char *p = *str; ret = _gssapi_verify_mech_header(str, total_len, oid); if (ret) return ret; len = total_len - (*str - p); if (len < 2) return GSS_S_DEFECTIVE_TOKEN; if (ct_memcmp (*str, type, 2) != 0) return GSS_S_DEFECTIVE_TOKEN; *str += 2; return 0; } /* * Remove the GSS-API wrapping from `in_token' giving `out_data. * Does not copy data, so just free `in_token'. */ OM_uint32 _gssapi_decapsulate( OM_uint32 *minor_status, gss_buffer_t input_token_buffer, krb5_data *out_data, const gss_OID mech ) { u_char *p; OM_uint32 ret; p = input_token_buffer->value; ret = _gssapi_verify_mech_header(&p, input_token_buffer->length, mech); if (ret) { *minor_status = 0; return ret; } out_data->length = input_token_buffer->length - (p - (u_char *)input_token_buffer->value); out_data->data = p; return GSS_S_COMPLETE; } /* * Remove the GSS-API wrapping from `in_token' giving `out_data. * Does not copy data, so just free `in_token'. */ OM_uint32 _gsskrb5_decapsulate(OM_uint32 *minor_status, gss_buffer_t input_token_buffer, krb5_data *out_data, const void *type, gss_OID oid) { u_char *p; OM_uint32 ret; p = input_token_buffer->value; ret = _gsskrb5_verify_header(&p, input_token_buffer->length, type, oid); if (ret) { *minor_status = 0; return ret; } out_data->length = input_token_buffer->length - (p - (u_char *)input_token_buffer->value); out_data->data = p; return GSS_S_COMPLETE; } /* * Verify padding of a gss wrapped message and return its length. */ OM_uint32 _gssapi_verify_pad(gss_buffer_t wrapped_token, size_t datalen, size_t *padlen) { u_char *pad; size_t padlength; int i; - pad = (u_char *)wrapped_token->value + wrapped_token->length - 1; - padlength = *pad; + pad = (u_char *)wrapped_token->value + wrapped_token->length; + padlength = pad[-1]; if (padlength > datalen) return GSS_S_BAD_MECH; - for (i = padlength; i > 0 && *pad == padlength; i--, pad--) + for (i = padlength; i > 0 && *--pad == padlength; i--) ; if (i != 0) return GSS_S_BAD_MIC; *padlen = padlength; return 0; } diff --git a/crypto/heimdal/lib/gssapi/krb5/unwrap.c b/crypto/heimdal/lib/gssapi/krb5/unwrap.c index 5a003815a0f3..640677e20b9a 100644 --- a/crypto/heimdal/lib/gssapi/krb5/unwrap.c +++ b/crypto/heimdal/lib/gssapi/krb5/unwrap.c @@ -1,463 +1,479 @@ /* * 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; 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 */ + if (input_message_buffer->length < token_len) + return GSS_S_BAD_MECH; } 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; + len = (p - (u_char *)input_message_buffer->value) + + 22 + 8; + if (input_message_buffer->length < len) + return GSS_S_BAD_MECH; + 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; 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 (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, + input_message_buffer->length - len - 8, &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) { 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_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); + if (output_message_buffer->value != NULL) + 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 */ + if (input_message_buffer->length < token_len) + return GSS_S_BAD_MECH; } 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 */ + len = (p - (u_char *)input_message_buffer->value) + + 34 + 8; + if (input_message_buffer->length < len) + return GSS_S_BAD_MECH; + + if (ct_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, + input_message_buffer->length - len - 8, &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); + if (output_message_buffer->value != NULL) + 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; } diff --git a/crypto/heimdal/lib/gssapi/mech/gss_display_status.c b/crypto/heimdal/lib/gssapi/mech/gss_display_status.c index 1e508caa9baf..9529fabf319d 100644 --- a/crypto/heimdal/lib/gssapi/mech/gss_display_status.c +++ b/crypto/heimdal/lib/gssapi/mech/gss_display_status.c @@ -1,211 +1,210 @@ /*- * Copyright (c) 2005 Doug Rabson * 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. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. * * $FreeBSD: src/lib/libgssapi/gss_display_status.c,v 1.1 2005/12/29 14:40:20 dfr Exp $ */ /* * Copyright (c) 1998 - 2005 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 "mech_locl.h" static const char * calling_error(OM_uint32 v) { static const char *msgs[] = { NULL, /* 0 */ "A required input parameter could not be read.", /* */ "A required output parameter could not be written.", /* */ "A parameter was malformed" }; v >>= GSS_C_CALLING_ERROR_OFFSET; if (v == 0) return ""; else if (v >= sizeof(msgs)/sizeof(*msgs)) return "unknown calling error"; else return msgs[v]; } static const char * routine_error(OM_uint32 v) { static const char *msgs[] = { "Function completed successfully", /* 0 */ "An unsupported mechanism was requested", "An invalid name was supplied", "A supplied name was of an unsupported type", "Incorrect channel bindings were supplied", "An invalid status code was supplied", "A token had an invalid MIC", - "No credentials were supplied, " - "or the credentials were unavailable or inaccessible.", + "No credentials were supplied, or the credentials were unavailable or inaccessible.", "No context has been established", "A token was invalid", "A credential was invalid", "The referenced credentials have expired", "The context has expired", "Miscellaneous failure (see text)", "The quality-of-protection requested could not be provide", "The operation is forbidden by local security policy", "The operation or option is not available", "The requested credential element already exists", "The provided name was not a mechanism name.", }; v >>= GSS_C_ROUTINE_ERROR_OFFSET; if (v >= sizeof(msgs)/sizeof(*msgs)) return "unknown routine error"; else return msgs[v]; } static const char * supplementary_error(OM_uint32 v) { static const char *msgs[] = { "normal completion", "continuation call to routine required", "duplicate per-message token detected", "timed-out per-message token detected", "reordered (early) per-message token detected", "skipped predecessor token(s) detected" }; v >>= GSS_C_SUPPLEMENTARY_OFFSET; if (v >= sizeof(msgs)/sizeof(*msgs)) return "unknown routine error"; else return msgs[v]; } GSSAPI_LIB_FUNCTION OM_uint32 GSSAPI_LIB_CALL gss_display_status(OM_uint32 *minor_status, OM_uint32 status_value, int status_type, const gss_OID mech_type, OM_uint32 *message_content, gss_buffer_t status_string) { OM_uint32 major_status; _mg_buffer_zero(status_string); *message_content = 0; major_status = _gss_mg_get_error(mech_type, status_type, status_value, status_string); if (major_status == GSS_S_COMPLETE) { *message_content = 0; *minor_status = 0; return GSS_S_COMPLETE; } *minor_status = 0; switch (status_type) { case GSS_C_GSS_CODE: { char *buf = NULL; int e; if (GSS_SUPPLEMENTARY_INFO(status_value)) e = asprintf(&buf, "%s", supplementary_error( GSS_SUPPLEMENTARY_INFO(status_value))); else e = asprintf (&buf, "%s %s", calling_error(GSS_CALLING_ERROR(status_value)), routine_error(GSS_ROUTINE_ERROR(status_value))); if (e < 0 || buf == NULL) break; status_string->length = strlen(buf); status_string->value = buf; return GSS_S_COMPLETE; } case GSS_C_MECH_CODE: { OM_uint32 maj_junk, min_junk; gss_buffer_desc oid; char *buf = NULL; int e; maj_junk = gss_oid_to_str(&min_junk, mech_type, &oid); if (maj_junk != GSS_S_COMPLETE) { oid.value = rk_UNCONST("unknown"); oid.length = 7; } e = asprintf (&buf, "unknown mech-code %lu for mech %.*s", (unsigned long)status_value, (int)oid.length, (char *)oid.value); if (maj_junk == GSS_S_COMPLETE) gss_release_buffer(&min_junk, &oid); if (e < 0 || buf == NULL) break; status_string->length = strlen(buf); status_string->value = buf; return GSS_S_COMPLETE; } } _mg_buffer_zero(status_string); return (GSS_S_BAD_STATUS); } diff --git a/crypto/heimdal/lib/gssapi/mech/gss_import_name.c b/crypto/heimdal/lib/gssapi/mech/gss_import_name.c index d1b3dc95b4a4..830770223583 100644 --- a/crypto/heimdal/lib/gssapi/mech/gss_import_name.c +++ b/crypto/heimdal/lib/gssapi/mech/gss_import_name.c @@ -1,291 +1,291 @@ /*- * Copyright (c) 2005 Doug Rabson * 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. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. * * $FreeBSD: src/lib/libgssapi/gss_import_name.c,v 1.1 2005/12/29 14:40:20 dfr Exp $ */ #include "mech_locl.h" static OM_uint32 _gss_import_export_name(OM_uint32 *minor_status, const gss_buffer_t input_name_buffer, gss_name_t *output_name) { OM_uint32 major_status; unsigned char *p = input_name_buffer->value; size_t len = input_name_buffer->length; size_t t; gss_OID_desc mech_oid; gssapi_mech_interface m; struct _gss_name *name; gss_name_t new_canonical_name; int composite = 0; *minor_status = 0; *output_name = 0; /* * Make sure that TOK_ID is {4, 1}. */ if (len < 2) return (GSS_S_BAD_NAME); if (p[0] != 4) return (GSS_S_BAD_NAME); switch (p[1]) { case 1: /* non-composite name */ break; case 2: /* composite name */ composite = 1; break; default: return (GSS_S_BAD_NAME); } p += 2; len -= 2; /* * Get the mech length and the name length and sanity * check the size of of the buffer. */ if (len < 2) return (GSS_S_BAD_NAME); t = (p[0] << 8) + p[1]; p += 2; len -= 2; /* * Check the DER encoded OID to make sure it agrees with the * length we just decoded. */ if (p[0] != 6) /* 6=OID */ return (GSS_S_BAD_NAME); p++; len--; t--; if (p[0] & 0x80) { int digits = p[0]; p++; len--; t--; mech_oid.length = 0; while (digits--) { mech_oid.length = (mech_oid.length << 8) | p[0]; p++; len--; t--; } } else { mech_oid.length = p[0]; p++; len--; t--; } if (mech_oid.length != t) return (GSS_S_BAD_NAME); mech_oid.elements = p; if (len < t + 4) return (GSS_S_BAD_NAME); p += t; len -= t; t = (p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]; - p += 4; + /* p += 4; */ len -= 4; if (!composite && len != t) return (GSS_S_BAD_NAME); m = __gss_get_mechanism(&mech_oid); if (!m) return (GSS_S_BAD_MECH); /* * Ask the mechanism to import the name. */ major_status = m->gm_import_name(minor_status, input_name_buffer, GSS_C_NT_EXPORT_NAME, &new_canonical_name); if (major_status != GSS_S_COMPLETE) { _gss_mg_error(m, major_status, *minor_status); return major_status; } /* * Now we make a new name and mark it as an MN. */ name = _gss_make_name(m, new_canonical_name); if (!name) { m->gm_release_name(minor_status, &new_canonical_name); return (GSS_S_FAILURE); } *output_name = (gss_name_t) name; *minor_status = 0; return (GSS_S_COMPLETE); } /** * Import a name internal or mechanism name * * Type of name and their format: * - GSS_C_NO_OID * - GSS_C_NT_USER_NAME * - GSS_C_NT_HOSTBASED_SERVICE * - GSS_C_NT_EXPORT_NAME * - GSS_C_NT_ANONYMOUS * - GSS_KRB5_NT_PRINCIPAL_NAME * * For more information about @ref internalVSmechname. * * @param minor_status minor status code * @param input_name_buffer import name buffer * @param input_name_type type of the import name buffer * @param output_name the resulting type, release with * gss_release_name(), independent of input_name * * @returns a gss_error code, see gss_display_status() about printing * the error code. * * @ingroup gssapi */ GSSAPI_LIB_FUNCTION OM_uint32 GSSAPI_LIB_CALL gss_import_name(OM_uint32 *minor_status, const gss_buffer_t input_name_buffer, const gss_OID input_name_type, gss_name_t *output_name) { struct _gss_mechanism_name *mn; gss_OID name_type = input_name_type; OM_uint32 major_status, ms; struct _gss_name *name; struct _gss_mech_switch *m; gss_name_t rname; *output_name = GSS_C_NO_NAME; if (input_name_buffer->length == 0) { *minor_status = 0; return (GSS_S_BAD_NAME); } _gss_load_mech(); /* * Use GSS_NT_USER_NAME as default name type. */ if (name_type == GSS_C_NO_OID) name_type = GSS_C_NT_USER_NAME; /* * If this is an exported name, we need to parse it to find * the mechanism and then import it as an MN. See RFC 2743 * section 3.2 for a description of the format. */ if (gss_oid_equal(name_type, GSS_C_NT_EXPORT_NAME)) { return _gss_import_export_name(minor_status, input_name_buffer, output_name); } *minor_status = 0; name = calloc(1, sizeof(struct _gss_name)); if (!name) { *minor_status = ENOMEM; return (GSS_S_FAILURE); } HEIM_SLIST_INIT(&name->gn_mn); major_status = _gss_copy_oid(minor_status, name_type, &name->gn_type); if (major_status) { free(name); return (GSS_S_FAILURE); } major_status = _gss_copy_buffer(minor_status, input_name_buffer, &name->gn_value); if (major_status) goto out; /* * Walk over the mechs and import the name into a mech name * for those supported this nametype. */ HEIM_SLIST_FOREACH(m, &_gss_mechs, gm_link) { int present = 0; major_status = gss_test_oid_set_member(minor_status, name_type, m->gm_name_types, &present); if (major_status || present == 0) continue; mn = malloc(sizeof(struct _gss_mechanism_name)); if (!mn) { *minor_status = ENOMEM; major_status = GSS_S_FAILURE; goto out; } major_status = (*m->gm_mech.gm_import_name)(minor_status, &name->gn_value, (name->gn_type.elements ? &name->gn_type : GSS_C_NO_OID), &mn->gmn_name); if (major_status != GSS_S_COMPLETE) { _gss_mg_error(&m->gm_mech, major_status, *minor_status); free(mn); goto out; } mn->gmn_mech = &m->gm_mech; mn->gmn_mech_oid = &m->gm_mech_oid; HEIM_SLIST_INSERT_HEAD(&name->gn_mn, mn, gmn_link); } /* * If we can't find a mn for the name, bail out already here. */ mn = HEIM_SLIST_FIRST(&name->gn_mn); if (!mn) { *minor_status = 0; major_status = GSS_S_NAME_NOT_MN; goto out; } *output_name = (gss_name_t) name; return (GSS_S_COMPLETE); out: rname = (gss_name_t)name; gss_release_name(&ms, &rname); return major_status; } diff --git a/crypto/heimdal/lib/gssapi/mech/gss_mech_switch.c b/crypto/heimdal/lib/gssapi/mech/gss_mech_switch.c index 55e01094ff91..6a100d391c5a 100644 --- a/crypto/heimdal/lib/gssapi/mech/gss_mech_switch.c +++ b/crypto/heimdal/lib/gssapi/mech/gss_mech_switch.c @@ -1,438 +1,440 @@ /*- * Copyright (c) 2005 Doug Rabson * 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. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. * * $FreeBSD: src/lib/libgssapi/gss_mech_switch.c,v 1.2 2006/02/04 09:40:21 dfr Exp $ */ #include "mech_locl.h" #include #ifndef _PATH_GSS_MECH #define _PATH_GSS_MECH "/etc/gss/mech" #endif struct _gss_mech_switch_list _gss_mechs = { NULL } ; gss_OID_set _gss_mech_oids; static HEIMDAL_MUTEX _gss_mech_mutex = HEIMDAL_MUTEX_INITIALIZER; /* * Convert a string containing an OID in 'dot' form * (e.g. 1.2.840.113554.1.2.2) to a gss_OID. */ static int _gss_string_to_oid(const char* s, gss_OID oid) { int number_count, i, j; size_t byte_count; const char *p, *q; char *res; oid->length = 0; oid->elements = NULL; /* * First figure out how many numbers in the oid, then * calculate the compiled oid size. */ number_count = 0; for (p = s; p; p = q) { q = strchr(p, '.'); if (q) q = q + 1; number_count++; } /* * The first two numbers are in the first byte and each * subsequent number is encoded in a variable byte sequence. */ if (number_count < 2) return (EINVAL); /* * We do this in two passes. The first pass, we just figure * out the size. Second time around, we actually encode the * number. */ res = 0; for (i = 0; i < 2; i++) { byte_count = 0; for (p = s, j = 0; p; p = q, j++) { unsigned int number = 0; /* * Find the end of this number. */ q = strchr(p, '.'); if (q) q = q + 1; /* * Read the number of of the string. Don't * bother with anything except base ten. */ while (*p && *p != '.') { number = 10 * number + (*p - '0'); p++; } /* * Encode the number. The first two numbers * are packed into the first byte. Subsequent * numbers are encoded in bytes seven bits at * a time with the last byte having the high * bit set. */ if (j == 0) { if (res) *res = number * 40; } else if (j == 1) { if (res) { *res += number; res++; } byte_count++; } else if (j >= 2) { /* * The number is encoded in seven bit chunks. */ unsigned int t; unsigned int bytes; bytes = 0; for (t = number; t; t >>= 7) bytes++; if (bytes == 0) bytes = 1; while (bytes) { if (res) { int bit = 7*(bytes-1); *res = (number >> bit) & 0x7f; if (bytes != 1) *res |= 0x80; res++; } byte_count++; bytes--; } } } + if (byte_count == 0) + return EINVAL; if (!res) { res = malloc(byte_count); if (!res) return (ENOMEM); oid->length = byte_count; oid->elements = res; } } return (0); } #define SYM(name) \ do { \ m->gm_mech.gm_ ## name = dlsym(so, "gss_" #name); \ if (!m->gm_mech.gm_ ## name || \ m->gm_mech.gm_ ##name == gss_ ## name) { \ fprintf(stderr, "can't find symbol gss_" #name "\n"); \ goto bad; \ } \ } while (0) #define OPTSYM(name) \ do { \ m->gm_mech.gm_ ## name = dlsym(so, "gss_" #name); \ if (m->gm_mech.gm_ ## name == gss_ ## name) \ m->gm_mech.gm_ ## name = NULL; \ } while (0) #define OPTSPISYM(name) \ do { \ m->gm_mech.gm_ ## name = dlsym(so, "gssspi_" #name); \ } while (0) #define COMPATSYM(name) \ do { \ m->gm_mech.gm_compat->gmc_ ## name = dlsym(so, "gss_" #name); \ if (m->gm_mech.gm_compat->gmc_ ## name == gss_ ## name) \ m->gm_mech.gm_compat->gmc_ ## name = NULL; \ } while (0) #define COMPATSPISYM(name) \ do { \ m->gm_mech.gm_compat->gmc_ ## name = dlsym(so, "gssspi_" #name);\ if (m->gm_mech.gm_compat->gmc_ ## name == gss_ ## name) \ m->gm_mech.gm_compat->gmc_ ## name = NULL; \ } while (0) /* * */ static int add_builtin(gssapi_mech_interface mech) { struct _gss_mech_switch *m; OM_uint32 minor_status; /* not registering any mech is ok */ if (mech == NULL) return 0; m = calloc(1, sizeof(*m)); if (m == NULL) return ENOMEM; m->gm_so = NULL; m->gm_mech = *mech; m->gm_mech_oid = mech->gm_mech_oid; /* XXX */ gss_add_oid_set_member(&minor_status, &m->gm_mech.gm_mech_oid, &_gss_mech_oids); /* pick up the oid sets of names */ if (m->gm_mech.gm_inquire_names_for_mech) (*m->gm_mech.gm_inquire_names_for_mech)(&minor_status, &m->gm_mech.gm_mech_oid, &m->gm_name_types); if (m->gm_name_types == NULL) gss_create_empty_oid_set(&minor_status, &m->gm_name_types); HEIM_SLIST_INSERT_HEAD(&_gss_mechs, m, gm_link); return 0; } /* * Load the mechanisms file (/etc/gss/mech). */ void _gss_load_mech(void) { OM_uint32 major_status, minor_status; FILE *fp; char buf[256]; char *p; char *name, *oid, *lib, *kobj; struct _gss_mech_switch *m; void *so; gss_OID_desc mech_oid; int found; HEIMDAL_MUTEX_lock(&_gss_mech_mutex); if (HEIM_SLIST_FIRST(&_gss_mechs)) { HEIMDAL_MUTEX_unlock(&_gss_mech_mutex); return; } major_status = gss_create_empty_oid_set(&minor_status, &_gss_mech_oids); if (major_status) { HEIMDAL_MUTEX_unlock(&_gss_mech_mutex); return; } add_builtin(__gss_krb5_initialize()); add_builtin(__gss_spnego_initialize()); add_builtin(__gss_ntlm_initialize()); #ifdef HAVE_DLOPEN fp = fopen(_PATH_GSS_MECH, "r"); if (!fp) { HEIMDAL_MUTEX_unlock(&_gss_mech_mutex); return; } rk_cloexec_file(fp); while (fgets(buf, sizeof(buf), fp)) { _gss_mo_init *mi; if (*buf == '#') continue; p = buf; name = strsep(&p, "\t\n "); if (p) while (isspace((unsigned char)*p)) p++; oid = strsep(&p, "\t\n "); if (p) while (isspace((unsigned char)*p)) p++; lib = strsep(&p, "\t\n "); if (p) while (isspace((unsigned char)*p)) p++; kobj = strsep(&p, "\t\n "); if (!name || !oid || !lib || !kobj) continue; if (_gss_string_to_oid(oid, &mech_oid)) continue; /* * Check for duplicates, already loaded mechs. */ found = 0; HEIM_SLIST_FOREACH(m, &_gss_mechs, gm_link) { if (gss_oid_equal(&m->gm_mech.gm_mech_oid, &mech_oid)) { found = 1; free(mech_oid.elements); break; } } if (found) continue; #ifndef RTLD_LOCAL #define RTLD_LOCAL 0 #endif #ifndef RTLD_GROUP #define RTLD_GROUP 0 #endif so = dlopen(lib, RTLD_LAZY | RTLD_LOCAL | RTLD_GROUP); if (so == NULL) { /* fprintf(stderr, "dlopen: %s\n", dlerror()); */ goto bad; } m = calloc(1, sizeof(*m)); if (m == NULL) goto bad; m->gm_so = so; m->gm_mech.gm_mech_oid = mech_oid; m->gm_mech.gm_flags = 0; m->gm_mech.gm_compat = calloc(1, sizeof(struct gss_mech_compat_desc_struct)); if (m->gm_mech.gm_compat == NULL) goto bad; major_status = gss_add_oid_set_member(&minor_status, &m->gm_mech.gm_mech_oid, &_gss_mech_oids); if (GSS_ERROR(major_status)) goto bad; SYM(acquire_cred); SYM(release_cred); SYM(init_sec_context); SYM(accept_sec_context); SYM(process_context_token); SYM(delete_sec_context); SYM(context_time); SYM(get_mic); SYM(verify_mic); SYM(wrap); SYM(unwrap); SYM(display_status); SYM(indicate_mechs); SYM(compare_name); SYM(display_name); SYM(import_name); SYM(export_name); SYM(release_name); SYM(inquire_cred); SYM(inquire_context); SYM(wrap_size_limit); SYM(add_cred); SYM(inquire_cred_by_mech); SYM(export_sec_context); SYM(import_sec_context); SYM(inquire_names_for_mech); SYM(inquire_mechs_for_name); SYM(canonicalize_name); SYM(duplicate_name); OPTSYM(inquire_cred_by_oid); OPTSYM(inquire_sec_context_by_oid); OPTSYM(set_sec_context_option); OPTSPISYM(set_cred_option); OPTSYM(pseudo_random); OPTSYM(wrap_iov); OPTSYM(unwrap_iov); OPTSYM(wrap_iov_length); OPTSYM(store_cred); OPTSYM(export_cred); OPTSYM(import_cred); #if 0 OPTSYM(acquire_cred_ext); OPTSYM(iter_creds); OPTSYM(destroy_cred); OPTSYM(cred_hold); OPTSYM(cred_unhold); OPTSYM(cred_label_get); OPTSYM(cred_label_set); #endif OPTSYM(display_name_ext); OPTSYM(inquire_name); OPTSYM(get_name_attribute); OPTSYM(set_name_attribute); OPTSYM(delete_name_attribute); OPTSYM(export_name_composite); OPTSYM(pname_to_uid); OPTSPISYM(authorize_localname); mi = dlsym(so, "gss_mo_init"); if (mi != NULL) { major_status = mi(&minor_status, &mech_oid, &m->gm_mech.gm_mo, &m->gm_mech.gm_mo_num); if (GSS_ERROR(major_status)) goto bad; } else { /* API-as-SPI compatibility */ COMPATSYM(inquire_saslname_for_mech); COMPATSYM(inquire_mech_for_saslname); COMPATSYM(inquire_attrs_for_mech); COMPATSPISYM(acquire_cred_with_password); } /* pick up the oid sets of names */ if (m->gm_mech.gm_inquire_names_for_mech) (*m->gm_mech.gm_inquire_names_for_mech)(&minor_status, &m->gm_mech.gm_mech_oid, &m->gm_name_types); if (m->gm_name_types == NULL) gss_create_empty_oid_set(&minor_status, &m->gm_name_types); HEIM_SLIST_INSERT_HEAD(&_gss_mechs, m, gm_link); continue; bad: if (m != NULL) { free(m->gm_mech.gm_compat); free(m->gm_mech.gm_mech_oid.elements); free(m); } dlclose(so); continue; } fclose(fp); #endif HEIMDAL_MUTEX_unlock(&_gss_mech_mutex); } gssapi_mech_interface __gss_get_mechanism(gss_const_OID mech) { struct _gss_mech_switch *m; _gss_load_mech(); HEIM_SLIST_FOREACH(m, &_gss_mechs, gm_link) { if (gss_oid_equal(&m->gm_mech.gm_mech_oid, mech)) return &m->gm_mech; } return NULL; } diff --git a/crypto/heimdal/lib/gssapi/mech/mech_locl.h b/crypto/heimdal/lib/gssapi/mech/mech_locl.h index 6c23ac5256b1..0f4d8e51b2c3 100644 --- a/crypto/heimdal/lib/gssapi/mech/mech_locl.h +++ b/crypto/heimdal/lib/gssapi/mech/mech_locl.h @@ -1,81 +1,82 @@ /* * 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. */ /* $Id$ */ #include #include #include #include #include #include #include #include #include #include #include #include +#include #include #include #include #include "mechqueue.h" #include "context.h" #include "cred.h" #include "mech_switch.h" #include "name.h" #include "utils.h" #include "compat.h" #define _mg_buffer_zero(buffer) \ do { \ if (buffer) { \ (buffer)->value = NULL; \ (buffer)->length = 0; \ } \ } while(0) #define _mg_oid_set_zero(oid_set) \ do { \ if (oid_set) { \ (oid_set)->elements = NULL; \ (oid_set)->count = 0; \ } \ } while(0) diff --git a/crypto/heimdal/lib/gssapi/ntlm/init_sec_context.c b/crypto/heimdal/lib/gssapi/ntlm/init_sec_context.c index bae04e174060..48fc03b5ff5e 100644 --- a/crypto/heimdal/lib/gssapi/ntlm/init_sec_context.c +++ b/crypto/heimdal/lib/gssapi/ntlm/init_sec_context.c @@ -1,501 +1,503 @@ /* * Copyright (c) 2006 - 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 "ntlm.h" static int from_file(const char *fn, const char *target_domain, char **username, struct ntlm_buf *key) { char *str, buf[1024]; FILE *f; f = fopen(fn, "r"); if (f == NULL) return ENOENT; rk_cloexec_file(f); while (fgets(buf, sizeof(buf), f) != NULL) { char *d, *u, *p; buf[strcspn(buf, "\r\n")] = '\0'; if (buf[0] == '#') continue; str = NULL; d = strtok_r(buf, ":", &str); + if (!d) + continue; if (d && strcasecmp(target_domain, d) != 0) continue; u = strtok_r(NULL, ":", &str); p = strtok_r(NULL, ":", &str); if (u == NULL || p == NULL) continue; *username = strdup(u); heim_ntlm_nt_key(p, key); memset(buf, 0, sizeof(buf)); fclose(f); return 0; } memset(buf, 0, sizeof(buf)); fclose(f); return ENOENT; } static int get_user_file(const ntlm_name target_name, char **username, struct ntlm_buf *key) { const char *fn; if (issuid()) return ENOENT; fn = getenv("NTLM_USER_FILE"); if (fn == NULL) return ENOENT; if (from_file(fn, target_name->domain, username, key) == 0) return 0; return ENOENT; } /* * Pick up the ntlm cred from the default krb5 credential cache. */ static int get_user_ccache(const ntlm_name name, char **username, struct ntlm_buf *key) { krb5_context context = NULL; krb5_principal client; krb5_ccache id = NULL; krb5_error_code ret; char *confname; krb5_data data; *username = NULL; krb5_data_zero(&data); key->length = 0; key->data = NULL; ret = krb5_init_context(&context); if (ret) return ret; ret = krb5_cc_default(context, &id); if (ret) goto out; ret = krb5_cc_get_principal(context, id, &client); if (ret) goto out; ret = krb5_unparse_name_flags(context, client, KRB5_PRINCIPAL_UNPARSE_NO_REALM, username); krb5_free_principal(context, client); if (ret) goto out; asprintf(&confname, "ntlm-key-%s", name->domain); if (confname == NULL) { krb5_clear_error_message(context); ret = ENOMEM; goto out; } ret = krb5_cc_get_config(context, id, NULL, confname, &data); if (ret) goto out; key->data = malloc(data.length); if (key->data == NULL) { ret = ENOMEM; goto out; } key->length = data.length; memcpy(key->data, data.data, data.length); out: krb5_data_free(&data); if (id) krb5_cc_close(context, id); krb5_free_context(context); return ret; } int _gss_ntlm_get_user_cred(const ntlm_name target_name, ntlm_cred *rcred) { ntlm_cred cred; int ret; cred = calloc(1, sizeof(*cred)); if (cred == NULL) return ENOMEM; ret = get_user_file(target_name, &cred->username, &cred->key); if (ret) ret = get_user_ccache(target_name, &cred->username, &cred->key); if (ret) { free(cred); return ret; } cred->domain = strdup(target_name->domain); *rcred = cred; return ret; } static int _gss_copy_cred(ntlm_cred from, ntlm_cred *to) { *to = calloc(1, sizeof(**to)); if (*to == NULL) return ENOMEM; (*to)->username = strdup(from->username); if ((*to)->username == NULL) { free(*to); return ENOMEM; } (*to)->domain = strdup(from->domain); if ((*to)->domain == NULL) { free((*to)->username); free(*to); return ENOMEM; } (*to)->key.data = malloc(from->key.length); if ((*to)->key.data == NULL) { free((*to)->domain); free((*to)->username); free(*to); return ENOMEM; } memcpy((*to)->key.data, from->key.data, from->key.length); (*to)->key.length = from->key.length; return 0; } OM_uint32 GSSAPI_CALLCONV _gss_ntlm_init_sec_context (OM_uint32 * minor_status, const gss_cred_id_t initiator_cred_handle, gss_ctx_id_t * context_handle, const gss_name_t target_name, const gss_OID mech_type, OM_uint32 req_flags, OM_uint32 time_req, const gss_channel_bindings_t input_chan_bindings, const gss_buffer_t input_token, gss_OID * actual_mech_type, gss_buffer_t output_token, OM_uint32 * ret_flags, OM_uint32 * time_rec ) { ntlm_ctx ctx; ntlm_name name = (ntlm_name)target_name; *minor_status = 0; if (ret_flags) *ret_flags = 0; if (time_rec) *time_rec = 0; if (actual_mech_type) *actual_mech_type = GSS_C_NO_OID; if (*context_handle == GSS_C_NO_CONTEXT) { struct ntlm_type1 type1; struct ntlm_buf data; uint32_t flags = 0; int ret; ctx = calloc(1, sizeof(*ctx)); if (ctx == NULL) { *minor_status = EINVAL; return GSS_S_FAILURE; } *context_handle = (gss_ctx_id_t)ctx; if (initiator_cred_handle != GSS_C_NO_CREDENTIAL) { ntlm_cred cred = (ntlm_cred)initiator_cred_handle; ret = _gss_copy_cred(cred, &ctx->client); } else ret = _gss_ntlm_get_user_cred(name, &ctx->client); if (ret) { _gss_ntlm_delete_sec_context(minor_status, context_handle, NULL); *minor_status = ret; return GSS_S_FAILURE; } if (req_flags & GSS_C_CONF_FLAG) flags |= NTLM_NEG_SEAL; if (req_flags & GSS_C_INTEG_FLAG) flags |= NTLM_NEG_SIGN; else flags |= NTLM_NEG_ALWAYS_SIGN; flags |= NTLM_NEG_UNICODE; flags |= NTLM_NEG_NTLM; flags |= NTLM_NEG_NTLM2_SESSION; flags |= NTLM_NEG_KEYEX; memset(&type1, 0, sizeof(type1)); type1.flags = flags; type1.domain = name->domain; type1.hostname = NULL; type1.os[0] = 0; type1.os[1] = 0; ret = heim_ntlm_encode_type1(&type1, &data); if (ret) { _gss_ntlm_delete_sec_context(minor_status, context_handle, NULL); *minor_status = ret; return GSS_S_FAILURE; } output_token->value = data.data; output_token->length = data.length; return GSS_S_CONTINUE_NEEDED; } else { krb5_error_code ret; struct ntlm_type2 type2; struct ntlm_type3 type3; struct ntlm_buf data; ctx = (ntlm_ctx)*context_handle; data.data = input_token->value; data.length = input_token->length; ret = heim_ntlm_decode_type2(&data, &type2); if (ret) { _gss_ntlm_delete_sec_context(minor_status, context_handle, NULL); *minor_status = ret; return GSS_S_FAILURE; } ctx->flags = type2.flags; /* XXX check that type2.targetinfo matches `target_name´ */ /* XXX check verify targetinfo buffer */ memset(&type3, 0, sizeof(type3)); type3.username = ctx->client->username; type3.flags = type2.flags; type3.targetname = type2.targetname; type3.ws = rk_UNCONST("workstation"); /* * NTLM Version 1 if no targetinfo buffer. */ if (1 || type2.targetinfo.length == 0) { struct ntlm_buf sessionkey; if (type2.flags & NTLM_NEG_NTLM2_SESSION) { unsigned char nonce[8]; if (RAND_bytes(nonce, sizeof(nonce)) != 1) { _gss_ntlm_delete_sec_context(minor_status, context_handle, NULL); *minor_status = EINVAL; return GSS_S_FAILURE; } ret = heim_ntlm_calculate_ntlm2_sess(nonce, type2.challenge, ctx->client->key.data, &type3.lm, &type3.ntlm); } else { ret = heim_ntlm_calculate_ntlm1(ctx->client->key.data, ctx->client->key.length, type2.challenge, &type3.ntlm); } if (ret) { _gss_ntlm_delete_sec_context(minor_status,context_handle,NULL); *minor_status = ret; return GSS_S_FAILURE; } ret = heim_ntlm_build_ntlm1_master(ctx->client->key.data, ctx->client->key.length, &sessionkey, &type3.sessionkey); if (ret) { if (type3.lm.data) free(type3.lm.data); if (type3.ntlm.data) free(type3.ntlm.data); _gss_ntlm_delete_sec_context(minor_status,context_handle,NULL); *minor_status = ret; return GSS_S_FAILURE; } ret = krb5_data_copy(&ctx->sessionkey, sessionkey.data, sessionkey.length); free(sessionkey.data); if (ret) { if (type3.lm.data) free(type3.lm.data); if (type3.ntlm.data) free(type3.ntlm.data); _gss_ntlm_delete_sec_context(minor_status,context_handle,NULL); *minor_status = ret; return GSS_S_FAILURE; } ctx->status |= STATUS_SESSIONKEY; } else { struct ntlm_buf sessionkey; unsigned char ntlmv2[16]; struct ntlm_targetinfo ti; /* verify infotarget */ ret = heim_ntlm_decode_targetinfo(&type2.targetinfo, 1, &ti); if(ret) { _gss_ntlm_delete_sec_context(minor_status, context_handle, NULL); *minor_status = ret; return GSS_S_FAILURE; } if (ti.domainname && strcmp(ti.domainname, name->domain) != 0) { _gss_ntlm_delete_sec_context(minor_status, context_handle, NULL); *minor_status = EINVAL; return GSS_S_FAILURE; } ret = heim_ntlm_calculate_ntlm2(ctx->client->key.data, ctx->client->key.length, ctx->client->username, name->domain, type2.challenge, &type2.targetinfo, ntlmv2, &type3.ntlm); if (ret) { _gss_ntlm_delete_sec_context(minor_status, context_handle, NULL); *minor_status = ret; return GSS_S_FAILURE; } ret = heim_ntlm_build_ntlm1_master(ntlmv2, sizeof(ntlmv2), &sessionkey, &type3.sessionkey); memset(ntlmv2, 0, sizeof(ntlmv2)); if (ret) { _gss_ntlm_delete_sec_context(minor_status, context_handle, NULL); *minor_status = ret; return GSS_S_FAILURE; } ctx->flags |= NTLM_NEG_NTLM2_SESSION; ret = krb5_data_copy(&ctx->sessionkey, sessionkey.data, sessionkey.length); free(sessionkey.data); if (ret) { _gss_ntlm_delete_sec_context(minor_status, context_handle, NULL); *minor_status = ret; return GSS_S_FAILURE; } } if (ctx->flags & NTLM_NEG_NTLM2_SESSION) { ctx->status |= STATUS_SESSIONKEY; _gss_ntlm_set_key(&ctx->u.v2.send, 0, (ctx->flags & NTLM_NEG_KEYEX), ctx->sessionkey.data, ctx->sessionkey.length); _gss_ntlm_set_key(&ctx->u.v2.recv, 1, (ctx->flags & NTLM_NEG_KEYEX), ctx->sessionkey.data, ctx->sessionkey.length); } else { ctx->status |= STATUS_SESSIONKEY; RC4_set_key(&ctx->u.v1.crypto_recv.key, ctx->sessionkey.length, ctx->sessionkey.data); RC4_set_key(&ctx->u.v1.crypto_send.key, ctx->sessionkey.length, ctx->sessionkey.data); } ret = heim_ntlm_encode_type3(&type3, &data); free(type3.sessionkey.data); if (type3.lm.data) free(type3.lm.data); if (type3.ntlm.data) free(type3.ntlm.data); if (ret) { _gss_ntlm_delete_sec_context(minor_status, context_handle, NULL); *minor_status = ret; return GSS_S_FAILURE; } output_token->length = data.length; output_token->value = data.data; if (actual_mech_type) *actual_mech_type = GSS_NTLM_MECHANISM; if (ret_flags) *ret_flags = 0; if (time_rec) *time_rec = GSS_C_INDEFINITE; ctx->status |= STATUS_OPEN; return GSS_S_COMPLETE; } } diff --git a/crypto/heimdal/lib/gssapi/spnego/accept_sec_context.c b/crypto/heimdal/lib/gssapi/spnego/accept_sec_context.c index 3a51dd3a0a61..b60dc19ad8e3 100644 --- a/crypto/heimdal/lib/gssapi/spnego/accept_sec_context.c +++ b/crypto/heimdal/lib/gssapi/spnego/accept_sec_context.c @@ -1,917 +1,919 @@ /* * Copyright (c) 1997 - 2006 Kungliga Tekniska Högskolan * (Royal Institute of Technology, Stockholm, Sweden). * Portions Copyright (c) 2004 PADL Software Pty Ltd. * * 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 "spnego_locl.h" static OM_uint32 send_reject (OM_uint32 *minor_status, gss_buffer_t output_token) { NegotiationToken nt; size_t size; nt.element = choice_NegotiationToken_negTokenResp; ALLOC(nt.u.negTokenResp.negResult, 1); if (nt.u.negTokenResp.negResult == NULL) { *minor_status = ENOMEM; return GSS_S_FAILURE; } *(nt.u.negTokenResp.negResult) = reject; nt.u.negTokenResp.supportedMech = NULL; nt.u.negTokenResp.responseToken = NULL; nt.u.negTokenResp.mechListMIC = NULL; ASN1_MALLOC_ENCODE(NegotiationToken, output_token->value, output_token->length, &nt, &size, *minor_status); free_NegotiationToken(&nt); if (*minor_status != 0) return GSS_S_FAILURE; return GSS_S_BAD_MECH; } static OM_uint32 acceptor_approved(gss_name_t target_name, gss_OID mech) { gss_cred_id_t cred = GSS_C_NO_CREDENTIAL; gss_OID_set oidset; OM_uint32 junk, ret; if (target_name == GSS_C_NO_NAME) return GSS_S_COMPLETE; gss_create_empty_oid_set(&junk, &oidset); gss_add_oid_set_member(&junk, mech, &oidset); ret = gss_acquire_cred(&junk, target_name, GSS_C_INDEFINITE, oidset, GSS_C_ACCEPT, &cred, NULL, NULL); gss_release_oid_set(&junk, &oidset); if (ret != GSS_S_COMPLETE) return ret; gss_release_cred(&junk, &cred); return GSS_S_COMPLETE; } static OM_uint32 send_supported_mechs (OM_uint32 *minor_status, gss_buffer_t output_token) { NegotiationTokenWin nt; size_t buf_len = 0; gss_buffer_desc data; OM_uint32 ret; memset(&nt, 0, sizeof(nt)); nt.element = choice_NegotiationTokenWin_negTokenInit; nt.u.negTokenInit.reqFlags = NULL; nt.u.negTokenInit.mechToken = NULL; nt.u.negTokenInit.negHints = NULL; ret = _gss_spnego_indicate_mechtypelist(minor_status, GSS_C_NO_NAME, acceptor_approved, 1, NULL, &nt.u.negTokenInit.mechTypes, NULL); if (ret != GSS_S_COMPLETE) { return ret; } ALLOC(nt.u.negTokenInit.negHints, 1); if (nt.u.negTokenInit.negHints == NULL) { *minor_status = ENOMEM; free_NegotiationTokenWin(&nt); return GSS_S_FAILURE; } ALLOC(nt.u.negTokenInit.negHints->hintName, 1); if (nt.u.negTokenInit.negHints->hintName == NULL) { *minor_status = ENOMEM; free_NegotiationTokenWin(&nt); return GSS_S_FAILURE; } *nt.u.negTokenInit.negHints->hintName = strdup("not_defined_in_RFC4178@please_ignore"); nt.u.negTokenInit.negHints->hintAddress = NULL; ASN1_MALLOC_ENCODE(NegotiationTokenWin, data.value, data.length, &nt, &buf_len, ret); free_NegotiationTokenWin(&nt); if (ret) { *minor_status = ret; return GSS_S_FAILURE; } if (data.length != buf_len) { abort(); UNREACHABLE(return GSS_S_FAILURE); } ret = gss_encapsulate_token(&data, GSS_SPNEGO_MECHANISM, output_token); free (data.value); if (ret != GSS_S_COMPLETE) return ret; *minor_status = 0; return GSS_S_CONTINUE_NEEDED; } static OM_uint32 send_accept (OM_uint32 *minor_status, gssspnego_ctx context_handle, gss_buffer_t mech_token, int initial_response, gss_buffer_t mech_buf, gss_buffer_t output_token) { NegotiationToken nt; OM_uint32 ret; gss_buffer_desc mech_mic_buf; size_t size; memset(&nt, 0, sizeof(nt)); nt.element = choice_NegotiationToken_negTokenResp; ALLOC(nt.u.negTokenResp.negResult, 1); if (nt.u.negTokenResp.negResult == NULL) { *minor_status = ENOMEM; return GSS_S_FAILURE; } if (context_handle->open) { if (mech_token != GSS_C_NO_BUFFER && mech_token->length != 0 && mech_buf != GSS_C_NO_BUFFER) *(nt.u.negTokenResp.negResult) = accept_incomplete; else *(nt.u.negTokenResp.negResult) = accept_completed; } else { if (initial_response && context_handle->require_mic) *(nt.u.negTokenResp.negResult) = request_mic; else *(nt.u.negTokenResp.negResult) = accept_incomplete; } if (initial_response) { ALLOC(nt.u.negTokenResp.supportedMech, 1); if (nt.u.negTokenResp.supportedMech == NULL) { free_NegotiationToken(&nt); *minor_status = ENOMEM; return GSS_S_FAILURE; } ret = der_get_oid(context_handle->preferred_mech_type->elements, context_handle->preferred_mech_type->length, nt.u.negTokenResp.supportedMech, NULL); if (ret) { free_NegotiationToken(&nt); *minor_status = ENOMEM; return GSS_S_FAILURE; } } else { nt.u.negTokenResp.supportedMech = NULL; } if (mech_token != GSS_C_NO_BUFFER && mech_token->length != 0) { ALLOC(nt.u.negTokenResp.responseToken, 1); if (nt.u.negTokenResp.responseToken == NULL) { free_NegotiationToken(&nt); *minor_status = ENOMEM; return GSS_S_FAILURE; } nt.u.negTokenResp.responseToken->length = mech_token->length; nt.u.negTokenResp.responseToken->data = mech_token->value; mech_token->length = 0; mech_token->value = NULL; } else { nt.u.negTokenResp.responseToken = NULL; } if (mech_buf != GSS_C_NO_BUFFER) { ret = gss_get_mic(minor_status, context_handle->negotiated_ctx_id, 0, mech_buf, &mech_mic_buf); if (ret == GSS_S_COMPLETE) { ALLOC(nt.u.negTokenResp.mechListMIC, 1); if (nt.u.negTokenResp.mechListMIC == NULL) { gss_release_buffer(minor_status, &mech_mic_buf); free_NegotiationToken(&nt); *minor_status = ENOMEM; return GSS_S_FAILURE; } nt.u.negTokenResp.mechListMIC->length = mech_mic_buf.length; nt.u.negTokenResp.mechListMIC->data = mech_mic_buf.value; } else if (ret == GSS_S_UNAVAILABLE) { nt.u.negTokenResp.mechListMIC = NULL; } else { free_NegotiationToken(&nt); return ret; } } else nt.u.negTokenResp.mechListMIC = NULL; ASN1_MALLOC_ENCODE(NegotiationToken, output_token->value, output_token->length, &nt, &size, ret); if (ret) { free_NegotiationToken(&nt); *minor_status = ret; return GSS_S_FAILURE; } /* * The response should not be encapsulated, because * it is a SubsequentContextToken (note though RFC 1964 * specifies encapsulation for all _Kerberos_ tokens). */ if (*(nt.u.negTokenResp.negResult) == accept_completed) ret = GSS_S_COMPLETE; else ret = GSS_S_CONTINUE_NEEDED; free_NegotiationToken(&nt); return ret; } static OM_uint32 verify_mechlist_mic (OM_uint32 *minor_status, gssspnego_ctx context_handle, gss_buffer_t mech_buf, heim_octet_string *mechListMIC ) { OM_uint32 ret; gss_buffer_desc mic_buf; if (context_handle->verified_mic) { /* This doesn't make sense, we've already verified it? */ *minor_status = 0; return GSS_S_DUPLICATE_TOKEN; } if (mechListMIC == NULL) { *minor_status = 0; return GSS_S_DEFECTIVE_TOKEN; } mic_buf.length = mechListMIC->length; mic_buf.value = mechListMIC->data; ret = gss_verify_mic(minor_status, context_handle->negotiated_ctx_id, mech_buf, &mic_buf, NULL); if (ret != GSS_S_COMPLETE) ret = GSS_S_DEFECTIVE_TOKEN; return ret; } static OM_uint32 select_mech(OM_uint32 *minor_status, MechType *mechType, int verify_p, gss_OID *mech_p) { char mechbuf[64]; size_t mech_len; gss_OID_desc oid; gss_OID oidp; gss_OID_set mechs; size_t i; OM_uint32 ret, junk; ret = der_put_oid ((unsigned char *)mechbuf + sizeof(mechbuf) - 1, sizeof(mechbuf), mechType, &mech_len); if (ret) { return GSS_S_DEFECTIVE_TOKEN; } oid.length = mech_len; oid.elements = mechbuf + sizeof(mechbuf) - mech_len; if (gss_oid_equal(&oid, GSS_SPNEGO_MECHANISM)) { return GSS_S_BAD_MECH; } *minor_status = 0; /* Translate broken MS Kebreros OID */ if (gss_oid_equal(&oid, &_gss_spnego_mskrb_mechanism_oid_desc)) oidp = &_gss_spnego_krb5_mechanism_oid_desc; else oidp = &oid; ret = gss_indicate_mechs(&junk, &mechs); if (ret) return (ret); for (i = 0; i < mechs->count; i++) if (gss_oid_equal(&mechs->elements[i], oidp)) break; if (i == mechs->count) { gss_release_oid_set(&junk, &mechs); return GSS_S_BAD_MECH; } gss_release_oid_set(&junk, &mechs); ret = gss_duplicate_oid(minor_status, &oid, /* possibly this should be oidp */ mech_p); if (verify_p) { gss_name_t name = GSS_C_NO_NAME; gss_buffer_desc namebuf; char *str = NULL, *host, hostname[MAXHOSTNAMELEN]; host = getenv("GSSAPI_SPNEGO_NAME"); if (host == NULL || issuid()) { int rv; if (gethostname(hostname, sizeof(hostname)) != 0) { *minor_status = errno; return GSS_S_FAILURE; } rv = asprintf(&str, "host@%s", hostname); if (rv < 0 || str == NULL) { *minor_status = ENOMEM; return GSS_S_FAILURE; } host = str; } namebuf.length = strlen(host); namebuf.value = host; ret = gss_import_name(minor_status, &namebuf, GSS_C_NT_HOSTBASED_SERVICE, &name); if (str) free(str); if (ret != GSS_S_COMPLETE) return ret; ret = acceptor_approved(name, *mech_p); gss_release_name(&junk, &name); } return ret; } static OM_uint32 acceptor_complete(OM_uint32 * minor_status, gssspnego_ctx ctx, int *get_mic, gss_buffer_t mech_buf, gss_buffer_t mech_input_token, gss_buffer_t mech_output_token, heim_octet_string *mic, gss_buffer_t output_token) { OM_uint32 ret; int require_mic, verify_mic; ret = _gss_spnego_require_mechlist_mic(minor_status, ctx, &require_mic); if (ret) return ret; ctx->require_mic = require_mic; if (mic != NULL) require_mic = 1; if (ctx->open && require_mic) { if (mech_input_token == GSS_C_NO_BUFFER) { /* Even/One */ verify_mic = 1; *get_mic = 0; } else if (mech_output_token != GSS_C_NO_BUFFER && mech_output_token->length == 0) { /* Odd */ *get_mic = verify_mic = 1; } else { /* Even/One */ verify_mic = 0; *get_mic = 1; } if (verify_mic || *get_mic) { int eret; size_t buf_len = 0; ASN1_MALLOC_ENCODE(MechTypeList, mech_buf->value, mech_buf->length, &ctx->initiator_mech_types, &buf_len, eret); if (eret) { *minor_status = eret; return GSS_S_FAILURE; } heim_assert(mech_buf->length == buf_len, "Internal ASN.1 error"); UNREACHABLE(return GSS_S_FAILURE); } if (verify_mic) { ret = verify_mechlist_mic(minor_status, ctx, mech_buf, mic); if (ret) { if (*get_mic) send_reject (minor_status, output_token); return ret; } ctx->verified_mic = 1; } } else *get_mic = 0; return GSS_S_COMPLETE; } static OM_uint32 GSSAPI_CALLCONV acceptor_start (OM_uint32 * minor_status, gss_ctx_id_t * context_handle, const gss_cred_id_t acceptor_cred_handle, const gss_buffer_t input_token_buffer, const gss_channel_bindings_t input_chan_bindings, gss_name_t * src_name, gss_OID * mech_type, gss_buffer_t output_token, OM_uint32 * ret_flags, OM_uint32 * time_rec, gss_cred_id_t *delegated_cred_handle ) { OM_uint32 ret, junk; NegotiationToken nt; size_t nt_len; NegTokenInit *ni; gss_buffer_desc data; gss_buffer_t mech_input_token = GSS_C_NO_BUFFER; gss_buffer_desc mech_output_token; gss_buffer_desc mech_buf; gss_OID preferred_mech_type = GSS_C_NO_OID; gssspnego_ctx ctx; int get_mic = 0; int first_ok = 0; mech_output_token.value = NULL; mech_output_token.length = 0; mech_buf.value = NULL; if (input_token_buffer->length == 0) return send_supported_mechs (minor_status, output_token); ret = _gss_spnego_alloc_sec_context(minor_status, context_handle); if (ret != GSS_S_COMPLETE) return ret; ctx = (gssspnego_ctx)*context_handle; /* * The GSS-API encapsulation is only present on the initial * context token (negTokenInit). */ ret = gss_decapsulate_token (input_token_buffer, GSS_SPNEGO_MECHANISM, &data); if (ret) return ret; ret = decode_NegotiationToken(data.value, data.length, &nt, &nt_len); gss_release_buffer(minor_status, &data); if (ret) { *minor_status = ret; return GSS_S_DEFECTIVE_TOKEN; } if (nt.element != choice_NegotiationToken_negTokenInit) { *minor_status = 0; return GSS_S_DEFECTIVE_TOKEN; } ni = &nt.u.negTokenInit; if (ni->mechTypes.len < 1) { free_NegotiationToken(&nt); *minor_status = 0; return GSS_S_DEFECTIVE_TOKEN; } HEIMDAL_MUTEX_lock(&ctx->ctx_id_mutex); ret = copy_MechTypeList(&ni->mechTypes, &ctx->initiator_mech_types); if (ret) { HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex); free_NegotiationToken(&nt); *minor_status = ret; return GSS_S_FAILURE; } /* * First we try the opportunistic token if we have support for it, * don't try to verify we have credential for the token, * gss_accept_sec_context() will (hopefully) tell us that. * If that failes, */ ret = select_mech(minor_status, &ni->mechTypes.val[0], 0, &preferred_mech_type); if (ret == 0 && ni->mechToken != NULL) { gss_buffer_desc ibuf; ibuf.length = ni->mechToken->length; ibuf.value = ni->mechToken->data; mech_input_token = &ibuf; if (ctx->mech_src_name != GSS_C_NO_NAME) gss_release_name(&junk, &ctx->mech_src_name); ret = gss_accept_sec_context(minor_status, &ctx->negotiated_ctx_id, acceptor_cred_handle, mech_input_token, input_chan_bindings, &ctx->mech_src_name, &ctx->negotiated_mech_type, &mech_output_token, &ctx->mech_flags, &ctx->mech_time_rec, delegated_cred_handle); if (ret == GSS_S_COMPLETE || ret == GSS_S_CONTINUE_NEEDED) { ctx->preferred_mech_type = preferred_mech_type; if (ret == GSS_S_COMPLETE) ctx->open = 1; ret = acceptor_complete(minor_status, ctx, &get_mic, &mech_buf, mech_input_token, &mech_output_token, ni->mechListMIC, output_token); if (ret != GSS_S_COMPLETE) goto out; first_ok = 1; } else { gss_mg_collect_error(preferred_mech_type, ret, *minor_status); } } /* * If opportunistic token failed, lets try the other mechs. */ if (!first_ok && ni->mechToken != NULL) { size_t j; preferred_mech_type = GSS_C_NO_OID; /* Call glue layer to find first mech we support */ for (j = 1; j < ni->mechTypes.len; ++j) { ret = select_mech(minor_status, &ni->mechTypes.val[j], 1, &preferred_mech_type); if (ret == 0) break; } - if (preferred_mech_type == GSS_C_NO_OID) { - HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex); - free_NegotiationToken(&nt); - return ret; - } + } + + ctx->preferred_mech_type = preferred_mech_type; - ctx->preferred_mech_type = preferred_mech_type; + if (preferred_mech_type == GSS_C_NO_OID) { + send_reject(minor_status, output_token); + HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex); + free_NegotiationToken(&nt); + return ret; } /* * The initial token always have a response */ ret = send_accept (minor_status, ctx, &mech_output_token, 1, get_mic ? &mech_buf : NULL, output_token); if (ret) goto out; out: if (mech_output_token.value != NULL) gss_release_buffer(&junk, &mech_output_token); if (mech_buf.value != NULL) { free(mech_buf.value); mech_buf.value = NULL; } free_NegotiationToken(&nt); if (ret == GSS_S_COMPLETE) { if (src_name != NULL && ctx->mech_src_name != NULL) { spnego_name name; name = calloc(1, sizeof(*name)); if (name) { name->mech = ctx->mech_src_name; ctx->mech_src_name = NULL; *src_name = (gss_name_t)name; } } } if (mech_type != NULL) *mech_type = ctx->negotiated_mech_type; if (ret_flags != NULL) *ret_flags = ctx->mech_flags; if (time_rec != NULL) *time_rec = ctx->mech_time_rec; if (ret == GSS_S_COMPLETE || ret == GSS_S_CONTINUE_NEEDED) { HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex); return ret; } _gss_spnego_internal_delete_sec_context(&junk, context_handle, GSS_C_NO_BUFFER); return ret; } static OM_uint32 GSSAPI_CALLCONV acceptor_continue (OM_uint32 * minor_status, gss_ctx_id_t * context_handle, const gss_cred_id_t acceptor_cred_handle, const gss_buffer_t input_token_buffer, const gss_channel_bindings_t input_chan_bindings, gss_name_t * src_name, gss_OID * mech_type, gss_buffer_t output_token, OM_uint32 * ret_flags, OM_uint32 * time_rec, gss_cred_id_t *delegated_cred_handle ) { OM_uint32 ret, ret2, minor; NegotiationToken nt; size_t nt_len; NegTokenResp *na; unsigned int negResult = accept_incomplete; gss_buffer_t mech_input_token = GSS_C_NO_BUFFER; gss_buffer_t mech_output_token = GSS_C_NO_BUFFER; gss_buffer_desc mech_buf; gssspnego_ctx ctx; mech_buf.value = NULL; ctx = (gssspnego_ctx)*context_handle; /* * The GSS-API encapsulation is only present on the initial * context token (negTokenInit). */ ret = decode_NegotiationToken(input_token_buffer->value, input_token_buffer->length, &nt, &nt_len); if (ret) { *minor_status = ret; return GSS_S_DEFECTIVE_TOKEN; } if (nt.element != choice_NegotiationToken_negTokenResp) { *minor_status = 0; return GSS_S_DEFECTIVE_TOKEN; } na = &nt.u.negTokenResp; if (na->negResult != NULL) { negResult = *(na->negResult); } HEIMDAL_MUTEX_lock(&ctx->ctx_id_mutex); { gss_buffer_desc ibuf, obuf; int require_mic, get_mic = 0; int require_response; heim_octet_string *mic; if (na->responseToken != NULL) { ibuf.length = na->responseToken->length; ibuf.value = na->responseToken->data; mech_input_token = &ibuf; } else { ibuf.value = NULL; ibuf.length = 0; } if (mech_input_token != GSS_C_NO_BUFFER) { if (ctx->mech_src_name != GSS_C_NO_NAME) gss_release_name(&minor, &ctx->mech_src_name); ret = gss_accept_sec_context(&minor, &ctx->negotiated_ctx_id, acceptor_cred_handle, mech_input_token, input_chan_bindings, &ctx->mech_src_name, &ctx->negotiated_mech_type, &obuf, &ctx->mech_flags, &ctx->mech_time_rec, delegated_cred_handle); if (ret == GSS_S_COMPLETE || ret == GSS_S_CONTINUE_NEEDED) { mech_output_token = &obuf; } if (ret != GSS_S_COMPLETE && ret != GSS_S_CONTINUE_NEEDED) { free_NegotiationToken(&nt); gss_mg_collect_error(ctx->negotiated_mech_type, ret, minor); send_reject (minor_status, output_token); HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex); return ret; } if (ret == GSS_S_COMPLETE) ctx->open = 1; } else ret = GSS_S_COMPLETE; ret2 = _gss_spnego_require_mechlist_mic(minor_status, ctx, &require_mic); if (ret2) goto out; ctx->require_mic = require_mic; mic = na->mechListMIC; if (mic != NULL) require_mic = 1; if (ret == GSS_S_COMPLETE) ret = acceptor_complete(minor_status, ctx, &get_mic, &mech_buf, mech_input_token, mech_output_token, na->mechListMIC, output_token); if (ctx->mech_flags & GSS_C_DCE_STYLE) require_response = (negResult != accept_completed); else require_response = 0; /* * Check whether we need to send a result: there should be only * one accept_completed response sent in the entire negotiation */ if ((mech_output_token != GSS_C_NO_BUFFER && mech_output_token->length != 0) || (ctx->open && negResult == accept_incomplete) || require_response || get_mic) { ret2 = send_accept (minor_status, ctx, mech_output_token, 0, get_mic ? &mech_buf : NULL, output_token); if (ret2) goto out; } out: if (ret2 != GSS_S_COMPLETE) ret = ret2; if (mech_output_token != NULL) gss_release_buffer(&minor, mech_output_token); if (mech_buf.value != NULL) free(mech_buf.value); free_NegotiationToken(&nt); } if (ret == GSS_S_COMPLETE) { if (src_name != NULL && ctx->mech_src_name != NULL) { spnego_name name; name = calloc(1, sizeof(*name)); if (name) { name->mech = ctx->mech_src_name; ctx->mech_src_name = NULL; *src_name = (gss_name_t)name; } } } if (mech_type != NULL) *mech_type = ctx->negotiated_mech_type; if (ret_flags != NULL) *ret_flags = ctx->mech_flags; if (time_rec != NULL) *time_rec = ctx->mech_time_rec; if (ret == GSS_S_COMPLETE || ret == GSS_S_CONTINUE_NEEDED) { HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex); return ret; } _gss_spnego_internal_delete_sec_context(&minor, context_handle, GSS_C_NO_BUFFER); return ret; } OM_uint32 GSSAPI_CALLCONV _gss_spnego_accept_sec_context (OM_uint32 * minor_status, gss_ctx_id_t * context_handle, const gss_cred_id_t acceptor_cred_handle, const gss_buffer_t input_token_buffer, const gss_channel_bindings_t input_chan_bindings, gss_name_t * src_name, gss_OID * mech_type, gss_buffer_t output_token, OM_uint32 * ret_flags, OM_uint32 * time_rec, gss_cred_id_t *delegated_cred_handle ) { _gss_accept_sec_context_t *func; *minor_status = 0; output_token->length = 0; output_token->value = NULL; if (src_name != NULL) *src_name = GSS_C_NO_NAME; if (mech_type != NULL) *mech_type = GSS_C_NO_OID; if (ret_flags != NULL) *ret_flags = 0; if (time_rec != NULL) *time_rec = 0; if (delegated_cred_handle != NULL) *delegated_cred_handle = GSS_C_NO_CREDENTIAL; if (*context_handle == GSS_C_NO_CONTEXT) func = acceptor_start; else func = acceptor_continue; return (*func)(minor_status, context_handle, acceptor_cred_handle, input_token_buffer, input_chan_bindings, src_name, mech_type, output_token, ret_flags, time_rec, delegated_cred_handle); } diff --git a/crypto/heimdal/lib/hdb/hdb-mitdb.c b/crypto/heimdal/lib/hdb/hdb-mitdb.c index cd619b3b8eb4..02c575050fe2 100644 --- a/crypto/heimdal/lib/hdb/hdb-mitdb.c +++ b/crypto/heimdal/lib/hdb/hdb-mitdb.c @@ -1,818 +1,817 @@ /* * Copyright (c) 1997 - 2001 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. */ #define KRB5_KDB_DISALLOW_POSTDATED 0x00000001 #define KRB5_KDB_DISALLOW_FORWARDABLE 0x00000002 #define KRB5_KDB_DISALLOW_TGT_BASED 0x00000004 #define KRB5_KDB_DISALLOW_RENEWABLE 0x00000008 #define KRB5_KDB_DISALLOW_PROXIABLE 0x00000010 #define KRB5_KDB_DISALLOW_DUP_SKEY 0x00000020 #define KRB5_KDB_DISALLOW_ALL_TIX 0x00000040 #define KRB5_KDB_REQUIRES_PRE_AUTH 0x00000080 #define KRB5_KDB_REQUIRES_HW_AUTH 0x00000100 #define KRB5_KDB_REQUIRES_PWCHANGE 0x00000200 #define KRB5_KDB_DISALLOW_SVR 0x00001000 #define KRB5_KDB_PWCHANGE_SERVICE 0x00002000 #define KRB5_KDB_SUPPORT_DESMD5 0x00004000 #define KRB5_KDB_NEW_PRINC 0x00008000 /* key: krb5_unparse_name + NUL 16: baselength 32: attributes 32: max time 32: max renewable time 32: client expire 32: passwd expire 32: last successful passwd 32: last failed attempt 32: num of failed attempts 16: num tl data 16: num data data 16: principal length length: principal for num tl data times 16: tl data type 16: tl data length length: length for num key data times 16: version (num keyblocks) 16: kvno for version times: 16: type 16: length length: keydata key_data_contents[0] int16: length read-of-data: key-encrypted, key-usage 0, master-key salt: version2 = salt in key_data->key_data_contents[1] else default salt. */ #include "hdb_locl.h" #define KDB_V1_BASE_LENGTH 38 #if HAVE_DB1 #if defined(HAVE_DB_185_H) #include #elif defined(HAVE_DB_H) #include #endif #define CHECK(x) do { if ((x)) goto out; } while(0) static krb5_error_code mdb_principal2key(krb5_context context, krb5_const_principal principal, krb5_data *key) { krb5_error_code ret; char *str; ret = krb5_unparse_name(context, principal, &str); if (ret) return ret; key->data = str; key->length = strlen(str) + 1; return 0; } #define KRB5_KDB_SALTTYPE_NORMAL 0 #define KRB5_KDB_SALTTYPE_V4 1 #define KRB5_KDB_SALTTYPE_NOREALM 2 #define KRB5_KDB_SALTTYPE_ONLYREALM 3 #define KRB5_KDB_SALTTYPE_SPECIAL 4 #define KRB5_KDB_SALTTYPE_AFS3 5 #define KRB5_KDB_SALTTYPE_CERTHASH 6 static krb5_error_code fix_salt(krb5_context context, hdb_entry *ent, int key_num) { krb5_error_code ret; Salt *salt = ent->keys.val[key_num].salt; /* fix salt type */ switch((int)salt->type) { case KRB5_KDB_SALTTYPE_NORMAL: salt->type = KRB5_PADATA_PW_SALT; break; case KRB5_KDB_SALTTYPE_V4: krb5_data_free(&salt->salt); salt->type = KRB5_PADATA_PW_SALT; break; case KRB5_KDB_SALTTYPE_NOREALM: { size_t len; size_t i; char *p; len = 0; for (i = 0; i < ent->principal->name.name_string.len; ++i) len += strlen(ent->principal->name.name_string.val[i]); ret = krb5_data_alloc (&salt->salt, len); if (ret) return ret; p = salt->salt.data; for (i = 0; i < ent->principal->name.name_string.len; ++i) { memcpy (p, ent->principal->name.name_string.val[i], strlen(ent->principal->name.name_string.val[i])); p += strlen(ent->principal->name.name_string.val[i]); } salt->type = KRB5_PADATA_PW_SALT; break; } case KRB5_KDB_SALTTYPE_ONLYREALM: krb5_data_free(&salt->salt); ret = krb5_data_copy(&salt->salt, ent->principal->realm, strlen(ent->principal->realm)); if(ret) return ret; salt->type = KRB5_PADATA_PW_SALT; break; case KRB5_KDB_SALTTYPE_SPECIAL: salt->type = KRB5_PADATA_PW_SALT; break; case KRB5_KDB_SALTTYPE_AFS3: krb5_data_free(&salt->salt); ret = krb5_data_copy(&salt->salt, ent->principal->realm, strlen(ent->principal->realm)); if(ret) return ret; salt->type = KRB5_PADATA_AFS3_SALT; break; case KRB5_KDB_SALTTYPE_CERTHASH: krb5_data_free(&salt->salt); free(ent->keys.val[key_num].salt); ent->keys.val[key_num].salt = NULL; break; default: abort(); } return 0; } static krb5_error_code mdb_value2entry(krb5_context context, krb5_data *data, krb5_kvno kvno, hdb_entry *entry) { krb5_error_code ret; krb5_storage *sp; uint32_t u32; uint16_t u16, num_keys, num_tl; size_t i, j; char *p; sp = krb5_storage_from_data(data); if (sp == NULL) { krb5_set_error_message(context, ENOMEM, "out of memory"); return ENOMEM; } krb5_storage_set_byteorder(sp, KRB5_STORAGE_BYTEORDER_LE); /* * 16: baselength * * The story here is that these 16 bits have to be a constant: * KDB_V1_BASE_LENGTH. Once upon a time a different value here * would have been used to indicate the presence of "extra data" * between the "base" contents and the {principal name, TL data, * keys} that follow it. Nothing supports such "extra data" * nowadays, so neither do we here. * * XXX But... surely we ought to log about this extra data, or skip * it, or something, in case anyone has MIT KDBs with ancient * entries in them... Logging would allow the admin to know which * entries to dump with MIT krb5's kdb5_util. */ CHECK(ret = krb5_ret_uint16(sp, &u16)); if (u16 != KDB_V1_BASE_LENGTH) { ret = EINVAL; goto out; } /* 32: attributes */ CHECK(ret = krb5_ret_uint32(sp, &u32)); entry->flags.postdate = !(u32 & KRB5_KDB_DISALLOW_POSTDATED); entry->flags.forwardable = !(u32 & KRB5_KDB_DISALLOW_FORWARDABLE); entry->flags.initial = !!(u32 & KRB5_KDB_DISALLOW_TGT_BASED); entry->flags.renewable = !(u32 & KRB5_KDB_DISALLOW_RENEWABLE); entry->flags.proxiable = !(u32 & KRB5_KDB_DISALLOW_PROXIABLE); /* DUP_SKEY */ entry->flags.invalid = !!(u32 & KRB5_KDB_DISALLOW_ALL_TIX); entry->flags.require_preauth =!!(u32 & KRB5_KDB_REQUIRES_PRE_AUTH); entry->flags.require_hwauth =!!(u32 & KRB5_KDB_REQUIRES_HW_AUTH); entry->flags.server = !(u32 & KRB5_KDB_DISALLOW_SVR); entry->flags.change_pw = !!(u32 & KRB5_KDB_PWCHANGE_SERVICE); entry->flags.client = 1; /* XXX */ /* 32: max time */ CHECK(ret = krb5_ret_uint32(sp, &u32)); if (u32) { entry->max_life = malloc(sizeof(*entry->max_life)); *entry->max_life = u32; } /* 32: max renewable time */ CHECK(ret = krb5_ret_uint32(sp, &u32)); if (u32) { entry->max_renew = malloc(sizeof(*entry->max_renew)); *entry->max_renew = u32; } /* 32: client expire */ CHECK(ret = krb5_ret_uint32(sp, &u32)); if (u32) { entry->valid_end = malloc(sizeof(*entry->valid_end)); *entry->valid_end = u32; } /* 32: passwd expire */ CHECK(ret = krb5_ret_uint32(sp, &u32)); if (u32) { entry->pw_end = malloc(sizeof(*entry->pw_end)); *entry->pw_end = u32; } /* 32: last successful passwd */ CHECK(ret = krb5_ret_uint32(sp, &u32)); /* 32: last failed attempt */ CHECK(ret = krb5_ret_uint32(sp, &u32)); /* 32: num of failed attempts */ CHECK(ret = krb5_ret_uint32(sp, &u32)); /* 16: num tl data */ CHECK(ret = krb5_ret_uint16(sp, &u16)); num_tl = u16; /* 16: num key data */ CHECK(ret = krb5_ret_uint16(sp, &u16)); num_keys = u16; /* 16: principal length */ CHECK(ret = krb5_ret_uint16(sp, &u16)); /* length: principal */ { /* * Note that the principal name includes the NUL in the entry, * but we don't want to take chances, so we add an extra NUL. */ p = malloc(u16 + 1); if (p == NULL) { ret = ENOMEM; goto out; } krb5_storage_read(sp, p, u16); p[u16] = '\0'; CHECK(ret = krb5_parse_name(context, p, &entry->principal)); free(p); } /* for num tl data times 16: tl data type 16: tl data length length: length */ for (i = 0; i < num_tl; i++) { /* 16: TL data type */ CHECK(ret = krb5_ret_uint16(sp, &u16)); /* 16: TL data length */ CHECK(ret = krb5_ret_uint16(sp, &u16)); krb5_storage_seek(sp, u16, SEEK_CUR); } /* * for num key data times * 16: "version" * 16: kvno * for version times: * 16: type * 16: length * length: keydata * * "version" here is really 1 or 2, the first meaning there's only * keys for this kvno, the second meaning there's keys and salt[s?]. * That's right... hold that gag reflex, you can do it. */ for (i = 0; i < num_keys; i++) { int keep = 0; uint16_t version; void *ptr; CHECK(ret = krb5_ret_uint16(sp, &u16)); version = u16; CHECK(ret = krb5_ret_uint16(sp, &u16)); /* * First time through, and until we find one matching key, * entry->kvno == 0. */ if ((entry->kvno < u16) && (kvno == 0 || kvno == u16)) { keep = 1; entry->kvno = u16; /* * Found a higher kvno than earlier, so free the old highest * kvno keys. * * XXX Of course, we actually want to extract the old kvnos * as well, for some of the kadm5 APIs. We shouldn't free * these keys, but keep them elsewhere. */ for (j = 0; j < entry->keys.len; j++) free_Key(&entry->keys.val[j]); free(entry->keys.val); entry->keys.len = 0; entry->keys.val = NULL; } else if (entry->kvno == u16) /* Accumulate keys */ keep = 1; if (keep) { Key *k; ptr = realloc(entry->keys.val, sizeof(entry->keys.val[0]) * (entry->keys.len + 1)); if (ptr == NULL) { ret = ENOMEM; goto out; } entry->keys.val = ptr; /* k points to current Key */ k = &entry->keys.val[entry->keys.len]; memset(k, 0, sizeof(*k)); entry->keys.len += 1; k->mkvno = malloc(sizeof(*k->mkvno)); if (k->mkvno == NULL) { ret = ENOMEM; goto out; } *k->mkvno = 1; for (j = 0; j < version; j++) { uint16_t type; CHECK(ret = krb5_ret_uint16(sp, &type)); CHECK(ret = krb5_ret_uint16(sp, &u16)); if (j == 0) { /* This "version" means we have a key */ k->key.keytype = type; if (u16 < 2) { ret = EINVAL; goto out; } /* * MIT stores keys encrypted keys as {16-bit length * of plaintext key, {encrypted key}}. The reason * for this is that the Kerberos cryptosystem is not * length-preserving. Heimdal's approach is to * truncate the plaintext to the expected length of * the key given its enctype, so we ignore this * 16-bit length-of-plaintext-key field. */ krb5_storage_seek(sp, 2, SEEK_CUR); /* skip real length */ k->key.keyvalue.length = u16 - 2; /* adjust cipher len */ k->key.keyvalue.data = malloc(k->key.keyvalue.length); krb5_storage_read(sp, k->key.keyvalue.data, k->key.keyvalue.length); } else if (j == 1) { /* This "version" means we have a salt */ k->salt = calloc(1, sizeof(*k->salt)); if (k->salt == NULL) { ret = ENOMEM; goto out; } k->salt->type = type; if (u16 != 0) { k->salt->salt.data = malloc(u16); if (k->salt->salt.data == NULL) { ret = ENOMEM; goto out; } k->salt->salt.length = u16; krb5_storage_read(sp, k->salt->salt.data, k->salt->salt.length); } fix_salt(context, entry, entry->keys.len - 1); } else { /* * Whatever this "version" might be, we skip it * * XXX A krb5.conf parameter requesting that we log * about strangeness like this, or return an error * from here, might be nice. */ krb5_storage_seek(sp, u16, SEEK_CUR); } } } else { /* * XXX For now we skip older kvnos, but we should extract * them... */ for (j = 0; j < version; j++) { /* enctype */ CHECK(ret = krb5_ret_uint16(sp, &u16)); /* encrypted key (or plaintext salt) */ CHECK(ret = krb5_ret_uint16(sp, &u16)); krb5_storage_seek(sp, u16, SEEK_CUR); } } } if (entry->kvno == 0 && kvno != 0) { ret = HDB_ERR_NOT_FOUND_HERE; goto out; } return 0; out: if (ret == HEIM_ERR_EOF) /* Better error code than "end of file" */ ret = HEIM_ERR_BAD_HDBENT_ENCODING; return ret; } #if 0 static krb5_error_code mdb_entry2value(krb5_context context, hdb_entry *entry, krb5_data *data) { return EINVAL; } #endif static krb5_error_code mdb_close(krb5_context context, HDB *db) { DB *d = (DB*)db->hdb_db; (*d->close)(d); return 0; } static krb5_error_code mdb_destroy(krb5_context context, HDB *db) { krb5_error_code ret; ret = hdb_clear_master_key (context, db); free(db->hdb_name); free(db); return ret; } static krb5_error_code mdb_lock(krb5_context context, HDB *db, int operation) { DB *d = (DB*)db->hdb_db; int fd = (*d->fd)(d); if(fd < 0) { krb5_set_error_message(context, HDB_ERR_CANT_LOCK_DB, "Can't lock database: %s", db->hdb_name); return HDB_ERR_CANT_LOCK_DB; } return hdb_lock(fd, operation); } static krb5_error_code mdb_unlock(krb5_context context, HDB *db) { DB *d = (DB*)db->hdb_db; int fd = (*d->fd)(d); if(fd < 0) { krb5_set_error_message(context, HDB_ERR_CANT_LOCK_DB, "Can't unlock database: %s", db->hdb_name); return HDB_ERR_CANT_LOCK_DB; } return hdb_unlock(fd); } static krb5_error_code mdb_seq(krb5_context context, HDB *db, unsigned flags, hdb_entry_ex *entry, int flag) { DB *d = (DB*)db->hdb_db; DBT key, value; krb5_data key_data, data; int code; code = db->hdb_lock(context, db, HDB_RLOCK); if(code == -1) { krb5_set_error_message(context, HDB_ERR_DB_INUSE, "Database %s in use", db->hdb_name); return HDB_ERR_DB_INUSE; } code = (*d->seq)(d, &key, &value, flag); db->hdb_unlock(context, db); /* XXX check value */ if(code == -1) { code = errno; krb5_set_error_message(context, code, "Database %s seq error: %s", db->hdb_name, strerror(code)); return code; } if(code == 1) { krb5_clear_error_message(context); return HDB_ERR_NOENTRY; } key_data.data = key.data; key_data.length = key.size; data.data = value.data; data.length = value.size; memset(entry, 0, sizeof(*entry)); if (mdb_value2entry(context, &data, 0, &entry->entry)) return mdb_seq(context, db, flags, entry, R_NEXT); if (db->hdb_master_key_set && (flags & HDB_F_DECRYPT)) { code = hdb_unseal_keys (context, db, &entry->entry); if (code) hdb_free_entry (context, entry); } return code; } static krb5_error_code mdb_firstkey(krb5_context context, HDB *db, unsigned flags, hdb_entry_ex *entry) { return mdb_seq(context, db, flags, entry, R_FIRST); } static krb5_error_code mdb_nextkey(krb5_context context, HDB *db, unsigned flags, hdb_entry_ex *entry) { return mdb_seq(context, db, flags, entry, R_NEXT); } static krb5_error_code mdb_rename(krb5_context context, HDB *db, const char *new_name) { int ret; char *old, *new; asprintf(&old, "%s.db", db->hdb_name); asprintf(&new, "%s.db", new_name); ret = rename(old, new); free(old); free(new); if(ret) return errno; free(db->hdb_name); db->hdb_name = strdup(new_name); return 0; } static krb5_error_code mdb__get(krb5_context context, HDB *db, krb5_data key, krb5_data *reply) { DB *d = (DB*)db->hdb_db; DBT k, v; int code; k.data = key.data; k.size = key.length; code = db->hdb_lock(context, db, HDB_RLOCK); if(code) return code; code = (*d->get)(d, &k, &v, 0); db->hdb_unlock(context, db); if(code < 0) { code = errno; krb5_set_error_message(context, code, "Database %s get error: %s", db->hdb_name, strerror(code)); return code; } if(code == 1) { krb5_clear_error_message(context); return HDB_ERR_NOENTRY; } krb5_data_copy(reply, v.data, v.size); return 0; } static krb5_error_code mdb__put(krb5_context context, HDB *db, int replace, krb5_data key, krb5_data value) { DB *d = (DB*)db->hdb_db; DBT k, v; int code; k.data = key.data; k.size = key.length; v.data = value.data; v.size = value.length; code = db->hdb_lock(context, db, HDB_WLOCK); if(code) return code; code = (*d->put)(d, &k, &v, replace ? 0 : R_NOOVERWRITE); db->hdb_unlock(context, db); if(code < 0) { code = errno; krb5_set_error_message(context, code, "Database %s put error: %s", db->hdb_name, strerror(code)); return code; } if(code == 1) { krb5_clear_error_message(context); return HDB_ERR_EXISTS; } return 0; } static krb5_error_code mdb__del(krb5_context context, HDB *db, krb5_data key) { DB *d = (DB*)db->hdb_db; DBT k; krb5_error_code code; k.data = key.data; k.size = key.length; code = db->hdb_lock(context, db, HDB_WLOCK); if(code) return code; code = (*d->del)(d, &k, 0); db->hdb_unlock(context, db); if(code == 1) { code = errno; krb5_set_error_message(context, code, "Database %s put error: %s", db->hdb_name, strerror(code)); return code; } if(code < 0) return errno; return 0; } static krb5_error_code mdb_fetch_kvno(krb5_context context, HDB *db, krb5_const_principal principal, unsigned flags, krb5_kvno kvno, hdb_entry_ex *entry) { krb5_data key, value; krb5_error_code code; code = mdb_principal2key(context, principal, &key); if (code) return code; code = db->hdb__get(context, db, key, &value); krb5_data_free(&key); if(code) return code; code = mdb_value2entry(context, &value, kvno, &entry->entry); krb5_data_free(&value); if (code) return code; if (db->hdb_master_key_set && (flags & HDB_F_DECRYPT)) { code = hdb_unseal_keys (context, db, &entry->entry); if (code) hdb_free_entry(context, entry); } return 0; } static krb5_error_code mdb_store(krb5_context context, HDB *db, unsigned flags, hdb_entry_ex *entry) { krb5_set_error_message(context, EINVAL, "can't set principal in mdb"); return EINVAL; } static krb5_error_code mdb_remove(krb5_context context, HDB *db, krb5_const_principal principal) { krb5_error_code code; krb5_data key; - mdb_principal2key(context, principal, &key); code = db->hdb__del(context, db, key); krb5_data_free(&key); return code; } static krb5_error_code mdb_open(krb5_context context, HDB *db, int flags, mode_t mode) { char *fn; krb5_error_code ret; asprintf(&fn, "%s.db", db->hdb_name); if (fn == NULL) { krb5_set_error_message(context, ENOMEM, "malloc: out of memory"); return ENOMEM; } db->hdb_db = dbopen(fn, flags, mode, DB_BTREE, NULL); free(fn); if (db->hdb_db == NULL) { switch (errno) { #ifdef EFTYPE case EFTYPE: #endif case EINVAL: db->hdb_db = dbopen(fn, flags, mode, DB_BTREE, NULL); } } /* try to open without .db extension */ if(db->hdb_db == NULL && errno == ENOENT) db->hdb_db = dbopen(db->hdb_name, flags, mode, DB_BTREE, NULL); if(db->hdb_db == NULL) { ret = errno; krb5_set_error_message(context, ret, "dbopen (%s): %s", db->hdb_name, strerror(ret)); return ret; } if((flags & O_ACCMODE) == O_RDONLY) ret = hdb_check_db_format(context, db); else ret = hdb_init_db(context, db); if(ret == HDB_ERR_NOENTRY) { krb5_clear_error_message(context); return 0; } if (ret) { mdb_close(context, db); krb5_set_error_message(context, ret, "hdb_open: failed %s database %s", (flags & O_ACCMODE) == O_RDONLY ? "checking format of" : "initialize", db->hdb_name); } return ret; } krb5_error_code hdb_mdb_create(krb5_context context, HDB **db, const char *filename) { *db = calloc(1, sizeof(**db)); if (*db == NULL) { krb5_set_error_message(context, ENOMEM, "malloc: out of memory"); return ENOMEM; } (*db)->hdb_db = NULL; (*db)->hdb_name = strdup(filename); if ((*db)->hdb_name == NULL) { free(*db); *db = NULL; krb5_set_error_message(context, ENOMEM, "malloc: out of memory"); return ENOMEM; } (*db)->hdb_master_key_set = 0; (*db)->hdb_openp = 0; (*db)->hdb_capability_flags = 0; (*db)->hdb_open = mdb_open; (*db)->hdb_close = mdb_close; (*db)->hdb_fetch_kvno = mdb_fetch_kvno; (*db)->hdb_store = mdb_store; (*db)->hdb_remove = mdb_remove; (*db)->hdb_firstkey = mdb_firstkey; (*db)->hdb_nextkey= mdb_nextkey; (*db)->hdb_lock = mdb_lock; (*db)->hdb_unlock = mdb_unlock; (*db)->hdb_rename = mdb_rename; (*db)->hdb__get = mdb__get; (*db)->hdb__put = mdb__put; (*db)->hdb__del = mdb__del; (*db)->hdb_destroy = mdb_destroy; return 0; } #endif /* HAVE_DB1 */ diff --git a/crypto/heimdal/lib/hx509/hxtool.c b/crypto/heimdal/lib/hx509/hxtool.c index 06c7958592ff..690ee5da612b 100644 --- a/crypto/heimdal/lib/hx509/hxtool.c +++ b/crypto/heimdal/lib/hx509/hxtool.c @@ -1,2241 +1,2242 @@ /* * 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)); + memset(&signer, 0, sizeof(signer)); 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", RSA_meth_get0_name(m)); } { const DH_METHOD *m = DH_get_default_method(); if (m != NULL) 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; } diff --git a/crypto/heimdal/lib/hx509/ks_file.c b/crypto/heimdal/lib/hx509/ks_file.c index 6aa36f4e204e..b8404a60f66f 100644 --- a/crypto/heimdal/lib/hx509/ks_file.c +++ b/crypto/heimdal/lib/hx509/ks_file.c @@ -1,697 +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; 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); 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; + int ret = 0; 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); + ret = hx509_pem_write(context, _hx509_private_pem_name(key), NULL, + sc->f, data.data, data.length); free(data.data); } break; } - return 0; + return ret; } 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); } diff --git a/crypto/heimdal/lib/hx509/name.c b/crypto/heimdal/lib/hx509/name.c index efd7b703422f..ffb67c85e574 100644 --- a/crypto/heimdal/lib/hx509/name.c +++ b/crypto/heimdal/lib/hx509/name.c @@ -1,1026 +1,1025 @@ /* * Copyright (c) 2004 - 2009 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 "char_map.h" /** * @page page_name PKIX/X.509 Names * * There are several names in PKIX/X.509, GeneralName and Name. * * A Name consists of an ordered list of Relative Distinguished Names * (RDN). Each RDN consists of an unordered list of typed strings. The * types are defined by OID and have long and short description. For * example id-at-commonName (2.5.4.3) have the long name CommonName * and short name CN. The string itself can be of several encoding, * UTF8, UTF16, Teltex string, etc. The type limit what encoding * should be used. * * GeneralName is a broader nametype that can contains al kind of * stuff like Name, IP addresses, partial Name, etc. * * Name is mapped into a hx509_name object. * * Parse and string name into a hx509_name object with hx509_parse_name(), * make it back into string representation with hx509_name_to_string(). * * Name string are defined rfc2253, rfc1779 and X.501. * * See the library functions here: @ref hx509_name */ static const struct { const char *n; const heim_oid *o; wind_profile_flags flags; } no[] = { { "C", &asn1_oid_id_at_countryName, 0 }, { "CN", &asn1_oid_id_at_commonName, 0 }, { "DC", &asn1_oid_id_domainComponent, 0 }, { "L", &asn1_oid_id_at_localityName, 0 }, { "O", &asn1_oid_id_at_organizationName, 0 }, { "OU", &asn1_oid_id_at_organizationalUnitName, 0 }, { "S", &asn1_oid_id_at_stateOrProvinceName, 0 }, { "STREET", &asn1_oid_id_at_streetAddress, 0 }, { "UID", &asn1_oid_id_Userid, 0 }, { "emailAddress", &asn1_oid_id_pkcs9_emailAddress, 0 }, { "serialNumber", &asn1_oid_id_at_serialNumber, 0 } }; static char * quote_string(const char *f, size_t len, int flags, size_t *rlen) { size_t i, j, tolen; const unsigned char *from = (const unsigned char *)f; unsigned char *to; tolen = len * 3 + 1; to = malloc(tolen); if (to == NULL) return NULL; for (i = 0, j = 0; i < len; i++) { unsigned char map = char_map[from[i]] & flags; if (i == 0 && (map & Q_RFC2253_QUOTE_FIRST)) { to[j++] = '\\'; to[j++] = from[i]; } else if ((i + 1) == len && (map & Q_RFC2253_QUOTE_LAST)) { to[j++] = '\\'; to[j++] = from[i]; } else if (map & Q_RFC2253_QUOTE) { to[j++] = '\\'; to[j++] = from[i]; } else if (map & Q_RFC2253_HEX) { int l = snprintf((char *)&to[j], tolen - j - 1, "#%02x", (unsigned char)from[i]); j += l; } else { to[j++] = from[i]; } } to[j] = '\0'; assert(j < tolen); *rlen = j; return (char *)to; } static int append_string(char **str, size_t *total_len, const char *ss, size_t len, int quote) { char *s, *qs; if (quote) qs = quote_string(ss, len, Q_RFC2253, &len); else qs = rk_UNCONST(ss); s = realloc(*str, len + *total_len + 1); if (s == NULL) _hx509_abort("allocation failure"); /* XXX */ memcpy(s + *total_len, qs, len); if (qs != ss) free(qs); s[*total_len + len] = '\0'; *str = s; *total_len += len; return 0; } static char * oidtostring(const heim_oid *type) { char *s; size_t i; for (i = 0; i < sizeof(no)/sizeof(no[0]); i++) { if (der_heim_oid_cmp(no[i].o, type) == 0) return strdup(no[i].n); } if (der_print_heim_oid(type, '.', &s) != 0) return NULL; return s; } static int stringtooid(const char *name, size_t len, heim_oid *oid) { int ret; size_t i; char *s; memset(oid, 0, sizeof(*oid)); for (i = 0; i < sizeof(no)/sizeof(no[0]); i++) { if (strncasecmp(no[i].n, name, len) == 0) return der_copy_oid(no[i].o, oid); } s = malloc(len + 1); if (s == NULL) return ENOMEM; memcpy(s, name, len); s[len] = '\0'; ret = der_parse_heim_oid(s, ".", oid); free(s); return ret; } /** * Convert the hx509 name object into a printable string. * The resulting string should be freed with free(). * * @param name name to print * @param str the string to return * * @return An hx509 error code, see hx509_get_error_string(). * * @ingroup hx509_name */ int hx509_name_to_string(const hx509_name name, char **str) { return _hx509_Name_to_string(&name->der_name, str); } int _hx509_Name_to_string(const Name *n, char **str) { size_t total_len = 0; size_t i, j, m; int ret; *str = strdup(""); if (*str == NULL) return ENOMEM; for (m = n->u.rdnSequence.len; m > 0; m--) { size_t len; i = m - 1; for (j = 0; j < n->u.rdnSequence.val[i].len; j++) { DirectoryString *ds = &n->u.rdnSequence.val[i].val[j].value; char *oidname; char *ss; oidname = oidtostring(&n->u.rdnSequence.val[i].val[j].type); switch(ds->element) { case choice_DirectoryString_ia5String: ss = ds->u.ia5String.data; len = ds->u.ia5String.length; break; case choice_DirectoryString_printableString: ss = ds->u.printableString.data; len = ds->u.printableString.length; break; case choice_DirectoryString_utf8String: ss = ds->u.utf8String; len = strlen(ss); break; case choice_DirectoryString_bmpString: { const uint16_t *bmp = ds->u.bmpString.data; size_t bmplen = ds->u.bmpString.length; size_t k; ret = wind_ucs2utf8_length(bmp, bmplen, &k); if (ret) return ret; ss = malloc(k + 1); if (ss == NULL) _hx509_abort("allocation failure"); /* XXX */ ret = wind_ucs2utf8(bmp, bmplen, ss, NULL); if (ret) { free(ss); return ret; } ss[k] = '\0'; len = k; break; } case choice_DirectoryString_teletexString: ss = ds->u.teletexString; len = strlen(ss); break; case choice_DirectoryString_universalString: { const uint32_t *uni = ds->u.universalString.data; size_t unilen = ds->u.universalString.length; size_t k; ret = wind_ucs4utf8_length(uni, unilen, &k); if (ret) return ret; ss = malloc(k + 1); if (ss == NULL) _hx509_abort("allocation failure"); /* XXX */ ret = wind_ucs4utf8(uni, unilen, ss, NULL); if (ret) { free(ss); return ret; } ss[k] = '\0'; len = k; break; } default: _hx509_abort("unknown directory type: %d", ds->element); exit(1); } append_string(str, &total_len, oidname, strlen(oidname), 0); free(oidname); append_string(str, &total_len, "=", 1, 0); append_string(str, &total_len, ss, len, 1); if (ds->element == choice_DirectoryString_bmpString || ds->element == choice_DirectoryString_universalString) { free(ss); } if (j + 1 < n->u.rdnSequence.val[i].len) append_string(str, &total_len, "+", 1, 0); } if (i > 0) append_string(str, &total_len, ",", 1, 0); } return 0; } #define COPYCHARARRAY(_ds,_el,_l,_n) \ (_l) = strlen(_ds->u._el); \ (_n) = malloc((_l) * sizeof((_n)[0])); \ if ((_n) == NULL) \ return ENOMEM; \ for (i = 0; i < (_l); i++) \ (_n)[i] = _ds->u._el[i] #define COPYVALARRAY(_ds,_el,_l,_n) \ (_l) = _ds->u._el.length; \ (_n) = malloc((_l) * sizeof((_n)[0])); \ if ((_n) == NULL) \ return ENOMEM; \ for (i = 0; i < (_l); i++) \ (_n)[i] = _ds->u._el.data[i] #define COPYVOIDARRAY(_ds,_el,_l,_n) \ (_l) = _ds->u._el.length; \ (_n) = malloc((_l) * sizeof((_n)[0])); \ if ((_n) == NULL) \ return ENOMEM; \ for (i = 0; i < (_l); i++) \ (_n)[i] = ((unsigned char *)_ds->u._el.data)[i] static int dsstringprep(const DirectoryString *ds, uint32_t **rname, size_t *rlen) { wind_profile_flags flags; size_t i, len; int ret; uint32_t *name; *rname = NULL; *rlen = 0; switch(ds->element) { case choice_DirectoryString_ia5String: flags = WIND_PROFILE_LDAP; COPYVOIDARRAY(ds, ia5String, len, name); break; case choice_DirectoryString_printableString: flags = WIND_PROFILE_LDAP; flags |= WIND_PROFILE_LDAP_CASE_EXACT_ATTRIBUTE; COPYVOIDARRAY(ds, printableString, len, name); break; case choice_DirectoryString_teletexString: flags = WIND_PROFILE_LDAP_CASE; COPYCHARARRAY(ds, teletexString, len, name); break; case choice_DirectoryString_bmpString: flags = WIND_PROFILE_LDAP; COPYVALARRAY(ds, bmpString, len, name); break; case choice_DirectoryString_universalString: flags = WIND_PROFILE_LDAP; COPYVALARRAY(ds, universalString, len, name); break; case choice_DirectoryString_utf8String: flags = WIND_PROFILE_LDAP; ret = wind_utf8ucs4_length(ds->u.utf8String, &len); if (ret) return ret; name = malloc(len * sizeof(name[0])); if (name == NULL) return ENOMEM; ret = wind_utf8ucs4(ds->u.utf8String, name, &len); if (ret) { free(name); return ret; } break; default: _hx509_abort("unknown directory type: %d", ds->element); } *rlen = len; /* try a couple of times to get the length right, XXX gross */ for (i = 0; i < 4; i++) { *rlen = *rlen * 2; *rname = malloc(*rlen * sizeof((*rname)[0])); ret = wind_stringprep(name, len, *rname, rlen, flags); if (ret == WIND_ERR_OVERRUN) { free(*rname); *rname = NULL; continue; } else break; } free(name); if (ret) { if (*rname) free(*rname); *rname = NULL; *rlen = 0; return ret; } return 0; } int _hx509_name_ds_cmp(const DirectoryString *ds1, const DirectoryString *ds2, int *diff) { uint32_t *ds1lp, *ds2lp; size_t ds1len, ds2len, i; int ret; ret = dsstringprep(ds1, &ds1lp, &ds1len); if (ret) return ret; ret = dsstringprep(ds2, &ds2lp, &ds2len); if (ret) { free(ds1lp); return ret; } if (ds1len != ds2len) *diff = ds1len - ds2len; else { for (i = 0; i < ds1len; i++) { *diff = ds1lp[i] - ds2lp[i]; if (*diff) break; } } free(ds1lp); free(ds2lp); return 0; } int _hx509_name_cmp(const Name *n1, const Name *n2, int *c) { int ret; size_t i, j; *c = n1->u.rdnSequence.len - n2->u.rdnSequence.len; if (*c) return 0; for (i = 0 ; i < n1->u.rdnSequence.len; i++) { *c = n1->u.rdnSequence.val[i].len - n2->u.rdnSequence.val[i].len; if (*c) return 0; for (j = 0; j < n1->u.rdnSequence.val[i].len; j++) { *c = der_heim_oid_cmp(&n1->u.rdnSequence.val[i].val[j].type, &n1->u.rdnSequence.val[i].val[j].type); if (*c) return 0; ret = _hx509_name_ds_cmp(&n1->u.rdnSequence.val[i].val[j].value, &n2->u.rdnSequence.val[i].val[j].value, c); if (ret) return ret; if (*c) return 0; } } *c = 0; return 0; } /** * Compare to hx509 name object, useful for sorting. * * @param n1 a hx509 name object. * @param n2 a hx509 name object. * * @return 0 the objects are the same, returns > 0 is n2 is "larger" * then n2, < 0 if n1 is "smaller" then n2. * * @ingroup hx509_name */ int hx509_name_cmp(hx509_name n1, hx509_name n2) { int ret, diff; ret = _hx509_name_cmp(&n1->der_name, &n2->der_name, &diff); if (ret) return ret; return diff; } int _hx509_name_from_Name(const Name *n, hx509_name *name) { int ret; *name = calloc(1, sizeof(**name)); if (*name == NULL) return ENOMEM; ret = copy_Name(n, &(*name)->der_name); if (ret) { free(*name); *name = NULL; } return ret; } int _hx509_name_modify(hx509_context context, Name *name, int append, const heim_oid *oid, const char *str) { RelativeDistinguishedName *rdn; int ret; void *ptr; ptr = realloc(name->u.rdnSequence.val, sizeof(name->u.rdnSequence.val[0]) * (name->u.rdnSequence.len + 1)); if (ptr == NULL) { hx509_set_error_string(context, 0, ENOMEM, "Out of memory"); return ENOMEM; } name->u.rdnSequence.val = ptr; if (append) { rdn = &name->u.rdnSequence.val[name->u.rdnSequence.len]; } else { memmove(&name->u.rdnSequence.val[1], &name->u.rdnSequence.val[0], name->u.rdnSequence.len * sizeof(name->u.rdnSequence.val[0])); rdn = &name->u.rdnSequence.val[0]; } rdn->val = malloc(sizeof(rdn->val[0])); if (rdn->val == NULL) return ENOMEM; rdn->len = 1; ret = der_copy_oid(oid, &rdn->val[0].type); if (ret) return ret; rdn->val[0].value.element = choice_DirectoryString_utf8String; rdn->val[0].value.u.utf8String = strdup(str); if (rdn->val[0].value.u.utf8String == NULL) return ENOMEM; name->u.rdnSequence.len += 1; return 0; } /** * Parse a string into a hx509 name object. * * @param context A hx509 context. * @param str a string to parse. * @param name the resulting object, NULL in case of error. * * @return An hx509 error code, see hx509_get_error_string(). * * @ingroup hx509_name */ int hx509_parse_name(hx509_context context, const char *str, hx509_name *name) { const char *p, *q; size_t len; hx509_name n; int ret; *name = NULL; n = calloc(1, sizeof(*n)); if (n == NULL) { hx509_set_error_string(context, 0, ENOMEM, "out of memory"); return ENOMEM; } n->der_name.element = choice_Name_rdnSequence; p = str; while (p != NULL && *p != '\0') { heim_oid oid; int last; q = strchr(p, ','); if (q) { len = (q - p); last = 1; } else { len = strlen(p); last = 0; } q = strchr(p, '='); if (q == NULL) { ret = HX509_PARSING_NAME_FAILED; hx509_set_error_string(context, 0, ret, "missing = in %s", p); goto out; } if (q == p) { ret = HX509_PARSING_NAME_FAILED; hx509_set_error_string(context, 0, ret, "missing name before = in %s", p); goto out; } if ((size_t)(q - p) > len) { ret = HX509_PARSING_NAME_FAILED; hx509_set_error_string(context, 0, ret, " = after , in %s", p); goto out; } ret = stringtooid(p, q - p, &oid); if (ret) { ret = HX509_PARSING_NAME_FAILED; hx509_set_error_string(context, 0, ret, "unknown type: %.*s", (int)(q - p), p); goto out; } { size_t pstr_len = len - (q - p) - 1; const char *pstr = p + (q - p) + 1; char *r; r = malloc(pstr_len + 1); if (r == NULL) { der_free_oid(&oid); ret = ENOMEM; hx509_set_error_string(context, 0, ret, "out of memory"); goto out; } memcpy(r, pstr, pstr_len); r[pstr_len] = '\0'; ret = _hx509_name_modify(context, &n->der_name, 0, &oid, r); free(r); der_free_oid(&oid); if(ret) goto out; } p += len + last; } *name = n; return 0; out: hx509_name_free(&n); return HX509_NAME_MALFORMED; } /** * Copy a hx509 name object. * * @param context A hx509 cotext. * @param from the name to copy from * @param to the name to copy to * * @return An hx509 error code, see hx509_get_error_string(). * * @ingroup hx509_name */ int hx509_name_copy(hx509_context context, const hx509_name from, hx509_name *to) { int ret; *to = calloc(1, sizeof(**to)); if (*to == NULL) return ENOMEM; ret = copy_Name(&from->der_name, &(*to)->der_name); if (ret) { free(*to); *to = NULL; return ENOMEM; } return 0; } /** * Convert a hx509_name into a Name. * * @param from the name to copy from * @param to the name to copy to * * @return An hx509 error code, see hx509_get_error_string(). * * @ingroup hx509_name */ int hx509_name_to_Name(const hx509_name from, Name *to) { return copy_Name(&from->der_name, to); } int hx509_name_normalize(hx509_context context, hx509_name name) { return 0; } /** * Expands variables in the name using env. Variables are on the form * ${name}. Useful when dealing with certificate templates. * * @param context A hx509 cotext. * @param name the name to expand. * @param env environment variable to expand. * * @return An hx509 error code, see hx509_get_error_string(). * * @ingroup hx509_name */ int hx509_name_expand(hx509_context context, hx509_name name, hx509_env env) { Name *n = &name->der_name; size_t i, j; if (env == NULL) return 0; if (n->element != choice_Name_rdnSequence) { hx509_set_error_string(context, 0, EINVAL, "RDN not of supported type"); return EINVAL; } for (i = 0 ; i < n->u.rdnSequence.len; i++) { for (j = 0; j < n->u.rdnSequence.val[i].len; j++) { /** Only UTF8String rdnSequence names are allowed */ /* THIS SHOULD REALLY BE: COMP = n->u.rdnSequence.val[i].val[j]; normalize COMP to utf8 check if there are variables expand variables convert back to orignal format, store in COMP free normalized utf8 string */ DirectoryString *ds = &n->u.rdnSequence.val[i].val[j].value; char *p, *p2; struct rk_strpool *strpool = NULL; if (ds->element != choice_DirectoryString_utf8String) { hx509_set_error_string(context, 0, EINVAL, "unsupported type"); return EINVAL; } p = strstr(ds->u.utf8String, "${"); if (p) { strpool = rk_strpoolprintf(strpool, "%.*s", (int)(p - ds->u.utf8String), ds->u.utf8String); if (strpool == NULL) { hx509_set_error_string(context, 0, ENOMEM, "out of memory"); return ENOMEM; } } while (p != NULL) { /* expand variables */ const char *value; p2 = strchr(p, '}'); if (p2 == NULL) { hx509_set_error_string(context, 0, EINVAL, "missing }"); rk_strpoolfree(strpool); return EINVAL; } p += 2; value = hx509_env_lfind(context, env, p, p2 - p); if (value == NULL) { hx509_set_error_string(context, 0, EINVAL, "variable %.*s missing", (int)(p2 - p), p); rk_strpoolfree(strpool); return EINVAL; } strpool = rk_strpoolprintf(strpool, "%s", value); if (strpool == NULL) { hx509_set_error_string(context, 0, ENOMEM, "out of memory"); return ENOMEM; } p2++; p = strstr(p2, "${"); if (p) strpool = rk_strpoolprintf(strpool, "%.*s", (int)(p - p2), p2); else strpool = rk_strpoolprintf(strpool, "%s", p2); if (strpool == NULL) { hx509_set_error_string(context, 0, ENOMEM, "out of memory"); return ENOMEM; } } if (strpool) { free(ds->u.utf8String); ds->u.utf8String = rk_strpoolcollect(strpool); if (ds->u.utf8String == NULL) { hx509_set_error_string(context, 0, ENOMEM, "out of memory"); return ENOMEM; } } } } return 0; } /** * Free a hx509 name object, upond return *name will be NULL. * * @param name a hx509 name object to be freed. * * @ingroup hx509_name */ void hx509_name_free(hx509_name *name) { free_Name(&(*name)->der_name); memset(*name, 0, sizeof(**name)); free(*name); *name = NULL; } /** * Convert a DER encoded name info a string. * * @param data data to a DER/BER encoded name * @param length length of data * @param str the resulting string, is NULL on failure. * * @return An hx509 error code, see hx509_get_error_string(). * * @ingroup hx509_name */ int hx509_unparse_der_name(const void *data, size_t length, char **str) { Name name; int ret; *str = NULL; ret = decode_Name(data, length, &name, NULL); if (ret) return ret; ret = _hx509_Name_to_string(&name, str); free_Name(&name); return ret; } /** * Convert a hx509_name object to DER encoded name. * * @param name name to concert * @param os data to a DER encoded name, free the resulting octet * string with hx509_xfree(os->data). * * @return An hx509 error code, see hx509_get_error_string(). * * @ingroup hx509_name */ int hx509_name_binary(const hx509_name name, heim_octet_string *os) { size_t size; int ret; ASN1_MALLOC_ENCODE(Name, os->data, os->length, &name->der_name, &size, ret); if (ret) return ret; if (os->length != size) _hx509_abort("internal ASN.1 encoder error"); return 0; } int _hx509_unparse_Name(const Name *aname, char **str) { hx509_name name; int ret; ret = _hx509_name_from_Name(aname, &name); if (ret) return ret; ret = hx509_name_to_string(name, str); hx509_name_free(&name); return ret; } /** * Unparse the hx509 name in name into a string. * * @param name the name to check if its empty/null. * * @return non zero if the name is empty/null. * * @ingroup hx509_name */ int hx509_name_is_null_p(const hx509_name name) { return name->der_name.u.rdnSequence.len == 0; } /** * Unparse the hx509 name in name into a string. * * @param name the name to print * @param str an allocated string returns the name in string form * * @return An hx509 error code, see hx509_get_error_string(). * * @ingroup hx509_name */ int hx509_general_name_unparse(GeneralName *name, char **str) { struct rk_strpool *strpool = NULL; + int ret = 0; *str = NULL; switch (name->element) { case choice_GeneralName_otherName: { char *oid; hx509_oid_sprint(&name->u.otherName.type_id, &oid); if (oid == NULL) return ENOMEM; strpool = rk_strpoolprintf(strpool, "otherName: %s", oid); free(oid); break; } case choice_GeneralName_rfc822Name: strpool = rk_strpoolprintf(strpool, "rfc822Name: %.*s\n", (int)name->u.rfc822Name.length, (char *)name->u.rfc822Name.data); break; case choice_GeneralName_dNSName: strpool = rk_strpoolprintf(strpool, "dNSName: %.*s\n", (int)name->u.dNSName.length, (char *)name->u.dNSName.data); break; case choice_GeneralName_directoryName: { Name dir; char *s; - int ret; memset(&dir, 0, sizeof(dir)); dir.element = name->u.directoryName.element; dir.u.rdnSequence = name->u.directoryName.u.rdnSequence; ret = _hx509_unparse_Name(&dir, &s); if (ret) return ret; strpool = rk_strpoolprintf(strpool, "directoryName: %s", s); free(s); break; } case choice_GeneralName_uniformResourceIdentifier: strpool = rk_strpoolprintf(strpool, "URI: %.*s", (int)name->u.uniformResourceIdentifier.length, (char *)name->u.uniformResourceIdentifier.data); break; case choice_GeneralName_iPAddress: { unsigned char *a = name->u.iPAddress.data; strpool = rk_strpoolprintf(strpool, "IPAddress: "); if (strpool == NULL) break; if (name->u.iPAddress.length == 4) strpool = rk_strpoolprintf(strpool, "%d.%d.%d.%d", a[0], a[1], a[2], a[3]); else if (name->u.iPAddress.length == 16) strpool = rk_strpoolprintf(strpool, "%02X:%02X:%02X:%02X:" "%02X:%02X:%02X:%02X:" "%02X:%02X:%02X:%02X:" "%02X:%02X:%02X:%02X", a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7], a[8], a[9], a[10], a[11], a[12], a[13], a[14], a[15]); else strpool = rk_strpoolprintf(strpool, "unknown IP address of length %lu", (unsigned long)name->u.iPAddress.length); break; } case choice_GeneralName_registeredID: { char *oid; hx509_oid_sprint(&name->u.registeredID, &oid); if (oid == NULL) return ENOMEM; strpool = rk_strpoolprintf(strpool, "registeredID: %s", oid); free(oid); break; } default: return EINVAL; } - if (strpool == NULL) + if (ret) + rk_strpoolfree(strpool); + else if (strpool == NULL || (*str = rk_strpoolcollect(strpool)) == NULL) return ENOMEM; - - *str = rk_strpoolcollect(strpool); - - return 0; + return ret; } diff --git a/crypto/heimdal/lib/hx509/softp11.c b/crypto/heimdal/lib/hx509/softp11.c index 38f587e0fea2..6a516a50cb66 100644 --- a/crypto/heimdal/lib/hx509/softp11.c +++ b/crypto/heimdal/lib/hx509/softp11.c @@ -1,1780 +1,1784 @@ /* * 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. */ #define CRYPTOKI_EXPORTS 1 #include "hx_locl.h" #include "pkcs11.h" #define OBJECT_ID_MASK 0xfff #define HANDLE_OBJECT_ID(h) ((h) & OBJECT_ID_MASK) #define OBJECT_ID(obj) HANDLE_OBJECT_ID((obj)->object_handle) #ifndef HAVE_RANDOM #define random() rand() #define srandom(s) srand(s) #endif #ifdef _WIN32 #include #endif struct st_attr { CK_ATTRIBUTE attribute; int secret; }; struct st_object { CK_OBJECT_HANDLE object_handle; struct st_attr *attrs; int num_attributes; hx509_cert cert; }; static struct soft_token { CK_VOID_PTR application; CK_NOTIFY notify; char *config_file; hx509_certs certs; struct { struct st_object **objs; int num_objs; } object; struct { int hardware_slot; int app_error_fatal; int login_done; } flags; int open_sessions; struct session_state { CK_SESSION_HANDLE session_handle; struct { CK_ATTRIBUTE *attributes; CK_ULONG num_attributes; int next_object; } find; int sign_object; CK_MECHANISM_PTR sign_mechanism; int verify_object; CK_MECHANISM_PTR verify_mechanism; } state[10]; #define MAX_NUM_SESSION (sizeof(soft_token.state)/sizeof(soft_token.state[0])) FILE *logfile; } soft_token; static hx509_context context; static void application_error(const char *fmt, ...) { va_list ap; va_start(ap, fmt); vprintf(fmt, ap); va_end(ap); if (soft_token.flags.app_error_fatal) abort(); } static void st_logf(const char *fmt, ...) { va_list ap; if (soft_token.logfile == NULL) return; va_start(ap, fmt); vfprintf(soft_token.logfile, fmt, ap); va_end(ap); fflush(soft_token.logfile); } static CK_RV init_context(void) { if (context == NULL) { int ret = hx509_context_init(&context); if (ret) return CKR_GENERAL_ERROR; } return CKR_OK; } #define INIT_CONTEXT() { CK_RV icret = init_context(); if (icret) return icret; } static void snprintf_fill(char *str, size_t size, char fillchar, const char *fmt, ...) { int len; va_list ap; va_start(ap, fmt); len = vsnprintf(str, size, fmt, ap); va_end(ap); if (len < 0 || (size_t)len > size) return; while ((size_t)len < size) str[len++] = fillchar; } #ifndef TEST_APP #define printf error_use_st_logf #endif #define VERIFY_SESSION_HANDLE(s, state) \ { \ CK_RV xret; \ xret = verify_session_handle(s, state); \ if (xret != CKR_OK) { \ /* return CKR_OK */; \ } \ } static CK_RV verify_session_handle(CK_SESSION_HANDLE hSession, struct session_state **state) { size_t i; for (i = 0; i < MAX_NUM_SESSION; i++){ if (soft_token.state[i].session_handle == hSession) break; } if (i == MAX_NUM_SESSION) { application_error("use of invalid handle: 0x%08lx\n", (unsigned long)hSession); return CKR_SESSION_HANDLE_INVALID; } if (state) *state = &soft_token.state[i]; return CKR_OK; } static CK_RV object_handle_to_object(CK_OBJECT_HANDLE handle, struct st_object **object) { int i = HANDLE_OBJECT_ID(handle); *object = NULL; if (i >= soft_token.object.num_objs) return CKR_ARGUMENTS_BAD; if (soft_token.object.objs[i] == NULL) return CKR_ARGUMENTS_BAD; if (soft_token.object.objs[i]->object_handle != handle) return CKR_ARGUMENTS_BAD; *object = soft_token.object.objs[i]; return CKR_OK; } static int attributes_match(const struct st_object *obj, const CK_ATTRIBUTE *attributes, CK_ULONG num_attributes) { CK_ULONG i; int j; st_logf("attributes_match: %ld\n", (unsigned long)OBJECT_ID(obj)); for (i = 0; i < num_attributes; i++) { int match = 0; for (j = 0; j < obj->num_attributes; j++) { if (attributes[i].type == obj->attrs[j].attribute.type && attributes[i].ulValueLen == obj->attrs[j].attribute.ulValueLen && memcmp(attributes[i].pValue, obj->attrs[j].attribute.pValue, attributes[i].ulValueLen) == 0) { match = 1; break; } } if (match == 0) { st_logf("type %d attribute have no match\n", attributes[i].type); return 0; } } st_logf("attribute matches\n"); return 1; } static void print_attributes(const CK_ATTRIBUTE *attributes, CK_ULONG num_attributes) { CK_ULONG i; st_logf("find objects: attrs: %lu\n", (unsigned long)num_attributes); for (i = 0; i < num_attributes; i++) { st_logf(" type: "); switch (attributes[i].type) { case CKA_TOKEN: { CK_BBOOL *ck_true; if (attributes[i].ulValueLen != sizeof(CK_BBOOL)) { application_error("token attribute wrong length\n"); break; } ck_true = attributes[i].pValue; st_logf("token: %s", *ck_true ? "TRUE" : "FALSE"); break; } case CKA_CLASS: { CK_OBJECT_CLASS *class; if (attributes[i].ulValueLen != sizeof(CK_ULONG)) { application_error("class attribute wrong length\n"); break; } class = attributes[i].pValue; st_logf("class "); switch (*class) { case CKO_CERTIFICATE: st_logf("certificate"); break; case CKO_PUBLIC_KEY: st_logf("public key"); break; case CKO_PRIVATE_KEY: st_logf("private key"); break; case CKO_SECRET_KEY: st_logf("secret key"); break; case CKO_DOMAIN_PARAMETERS: st_logf("domain parameters"); break; default: st_logf("[class %lx]", (long unsigned)*class); break; } break; } case CKA_PRIVATE: st_logf("private"); break; case CKA_LABEL: st_logf("label"); break; case CKA_APPLICATION: st_logf("application"); break; case CKA_VALUE: st_logf("value"); break; case CKA_ID: st_logf("id"); break; default: st_logf("[unknown 0x%08lx]", (unsigned long)attributes[i].type); break; } st_logf("\n"); } } static struct st_object * add_st_object(void) { struct st_object *o, **objs; int i; o = calloc(1, sizeof(*o)); if (o == NULL) return NULL; for (i = 0; i < soft_token.object.num_objs; i++) { if (soft_token.object.objs == NULL) { soft_token.object.objs[i] = o; break; } } if (i == soft_token.object.num_objs) { objs = realloc(soft_token.object.objs, (soft_token.object.num_objs + 1) * sizeof(soft_token.object.objs[0])); if (objs == NULL) { free(o); return NULL; } soft_token.object.objs = objs; soft_token.object.objs[soft_token.object.num_objs++] = o; } soft_token.object.objs[i]->object_handle = (random() & (~OBJECT_ID_MASK)) | i; return o; } static CK_RV add_object_attribute(struct st_object *o, int secret, CK_ATTRIBUTE_TYPE type, CK_VOID_PTR pValue, CK_ULONG ulValueLen) { struct st_attr *a; int i; + if (pValue == NULL && ulValueLen) + return CKR_ARGUMENTS_BAD; + i = o->num_attributes; a = realloc(o->attrs, (i + 1) * sizeof(o->attrs[0])); if (a == NULL) return CKR_DEVICE_MEMORY; o->attrs = a; o->attrs[i].secret = secret; o->attrs[i].attribute.type = type; o->attrs[i].attribute.pValue = malloc(ulValueLen); if (o->attrs[i].attribute.pValue == NULL && ulValueLen != 0) return CKR_DEVICE_MEMORY; - memcpy(o->attrs[i].attribute.pValue, pValue, ulValueLen); + if (ulValueLen) + memcpy(o->attrs[i].attribute.pValue, pValue, ulValueLen); o->attrs[i].attribute.ulValueLen = ulValueLen; o->num_attributes++; return CKR_OK; } static CK_RV add_pubkey_info(hx509_context hxctx, struct st_object *o, CK_KEY_TYPE key_type, hx509_cert cert) { BIGNUM *num; CK_BYTE *modulus = NULL; size_t modulus_len = 0; CK_ULONG modulus_bits = 0; CK_BYTE *exponent = NULL; size_t exponent_len = 0; if (key_type != CKK_RSA) return CKR_OK; if (_hx509_cert_private_key(cert) == NULL) return CKR_OK; num = _hx509_private_key_get_internal(context, _hx509_cert_private_key(cert), "rsa-modulus"); if (num == NULL) return CKR_GENERAL_ERROR; modulus_bits = BN_num_bits(num); modulus_len = BN_num_bytes(num); modulus = malloc(modulus_len); BN_bn2bin(num, modulus); BN_free(num); add_object_attribute(o, 0, CKA_MODULUS, modulus, modulus_len); add_object_attribute(o, 0, CKA_MODULUS_BITS, &modulus_bits, sizeof(modulus_bits)); free(modulus); num = _hx509_private_key_get_internal(context, _hx509_cert_private_key(cert), "rsa-exponent"); if (num == NULL) return CKR_GENERAL_ERROR; exponent_len = BN_num_bytes(num); exponent = malloc(exponent_len); BN_bn2bin(num, exponent); BN_free(num); add_object_attribute(o, 0, CKA_PUBLIC_EXPONENT, exponent, exponent_len); free(exponent); return CKR_OK; } struct foo { char *label; char *id; }; static int add_cert(hx509_context hxctx, void *ctx, hx509_cert cert) { static char empty[] = ""; struct foo *foo = (struct foo *)ctx; struct st_object *o = NULL; CK_OBJECT_CLASS type; CK_BBOOL bool_true = CK_TRUE; CK_BBOOL bool_false = CK_FALSE; CK_CERTIFICATE_TYPE cert_type = CKC_X_509; CK_KEY_TYPE key_type; CK_MECHANISM_TYPE mech_type; CK_RV ret = CKR_GENERAL_ERROR; int hret; heim_octet_string cert_data, subject_data, issuer_data, serial_data; st_logf("adding certificate\n"); serial_data.data = NULL; serial_data.length = 0; cert_data = subject_data = issuer_data = serial_data; hret = hx509_cert_binary(hxctx, cert, &cert_data); if (hret) goto out; { hx509_name name; hret = hx509_cert_get_issuer(cert, &name); if (hret) goto out; hret = hx509_name_binary(name, &issuer_data); hx509_name_free(&name); if (hret) goto out; hret = hx509_cert_get_subject(cert, &name); if (hret) goto out; hret = hx509_name_binary(name, &subject_data); hx509_name_free(&name); if (hret) goto out; } { AlgorithmIdentifier alg; hret = hx509_cert_get_SPKI_AlgorithmIdentifier(context, cert, &alg); if (hret) { ret = CKR_DEVICE_MEMORY; goto out; } key_type = CKK_RSA; /* XXX */ free_AlgorithmIdentifier(&alg); } type = CKO_CERTIFICATE; o = add_st_object(); if (o == NULL) { ret = CKR_DEVICE_MEMORY; goto out; } o->cert = hx509_cert_ref(cert); add_object_attribute(o, 0, CKA_CLASS, &type, sizeof(type)); add_object_attribute(o, 0, CKA_TOKEN, &bool_true, sizeof(bool_true)); add_object_attribute(o, 0, CKA_PRIVATE, &bool_false, sizeof(bool_false)); add_object_attribute(o, 0, CKA_MODIFIABLE, &bool_false, sizeof(bool_false)); add_object_attribute(o, 0, CKA_LABEL, foo->label, strlen(foo->label)); add_object_attribute(o, 0, CKA_CERTIFICATE_TYPE, &cert_type, sizeof(cert_type)); add_object_attribute(o, 0, CKA_ID, foo->id, strlen(foo->id)); add_object_attribute(o, 0, CKA_SUBJECT, subject_data.data, subject_data.length); add_object_attribute(o, 0, CKA_ISSUER, issuer_data.data, issuer_data.length); add_object_attribute(o, 0, CKA_SERIAL_NUMBER, serial_data.data, serial_data.length); add_object_attribute(o, 0, CKA_VALUE, cert_data.data, cert_data.length); add_object_attribute(o, 0, CKA_TRUSTED, &bool_false, sizeof(bool_false)); st_logf("add cert ok: %lx\n", (unsigned long)OBJECT_ID(o)); type = CKO_PUBLIC_KEY; o = add_st_object(); if (o == NULL) { ret = CKR_DEVICE_MEMORY; goto out; } o->cert = hx509_cert_ref(cert); add_object_attribute(o, 0, CKA_CLASS, &type, sizeof(type)); add_object_attribute(o, 0, CKA_TOKEN, &bool_true, sizeof(bool_true)); add_object_attribute(o, 0, CKA_PRIVATE, &bool_false, sizeof(bool_false)); add_object_attribute(o, 0, CKA_MODIFIABLE, &bool_false, sizeof(bool_false)); add_object_attribute(o, 0, CKA_LABEL, foo->label, strlen(foo->label)); add_object_attribute(o, 0, CKA_KEY_TYPE, &key_type, sizeof(key_type)); add_object_attribute(o, 0, CKA_ID, foo->id, strlen(foo->id)); add_object_attribute(o, 0, CKA_START_DATE, empty, 1); /* XXX */ add_object_attribute(o, 0, CKA_END_DATE, empty, 1); /* XXX */ add_object_attribute(o, 0, CKA_DERIVE, &bool_false, sizeof(bool_false)); add_object_attribute(o, 0, CKA_LOCAL, &bool_false, sizeof(bool_false)); mech_type = CKM_RSA_X_509; add_object_attribute(o, 0, CKA_KEY_GEN_MECHANISM, &mech_type, sizeof(mech_type)); add_object_attribute(o, 0, CKA_SUBJECT, subject_data.data, subject_data.length); add_object_attribute(o, 0, CKA_ENCRYPT, &bool_true, sizeof(bool_true)); add_object_attribute(o, 0, CKA_VERIFY, &bool_true, sizeof(bool_true)); add_object_attribute(o, 0, CKA_VERIFY_RECOVER, &bool_false, sizeof(bool_false)); add_object_attribute(o, 0, CKA_WRAP, &bool_true, sizeof(bool_true)); add_object_attribute(o, 0, CKA_TRUSTED, &bool_true, sizeof(bool_true)); add_pubkey_info(hxctx, o, key_type, cert); st_logf("add key ok: %lx\n", (unsigned long)OBJECT_ID(o)); if (hx509_cert_have_private_key(cert)) { CK_FLAGS flags; type = CKO_PRIVATE_KEY; o = add_st_object(); if (o == NULL) { ret = CKR_DEVICE_MEMORY; goto out; } o->cert = hx509_cert_ref(cert); add_object_attribute(o, 0, CKA_CLASS, &type, sizeof(type)); add_object_attribute(o, 0, CKA_TOKEN, &bool_true, sizeof(bool_true)); add_object_attribute(o, 0, CKA_PRIVATE, &bool_true, sizeof(bool_false)); add_object_attribute(o, 0, CKA_MODIFIABLE, &bool_false, sizeof(bool_false)); add_object_attribute(o, 0, CKA_LABEL, foo->label, strlen(foo->label)); add_object_attribute(o, 0, CKA_KEY_TYPE, &key_type, sizeof(key_type)); add_object_attribute(o, 0, CKA_ID, foo->id, strlen(foo->id)); add_object_attribute(o, 0, CKA_START_DATE, empty, 1); /* XXX */ add_object_attribute(o, 0, CKA_END_DATE, empty, 1); /* XXX */ add_object_attribute(o, 0, CKA_DERIVE, &bool_false, sizeof(bool_false)); add_object_attribute(o, 0, CKA_LOCAL, &bool_false, sizeof(bool_false)); mech_type = CKM_RSA_X_509; add_object_attribute(o, 0, CKA_KEY_GEN_MECHANISM, &mech_type, sizeof(mech_type)); add_object_attribute(o, 0, CKA_SUBJECT, subject_data.data, subject_data.length); add_object_attribute(o, 0, CKA_SENSITIVE, &bool_true, sizeof(bool_true)); add_object_attribute(o, 0, CKA_SECONDARY_AUTH, &bool_false, sizeof(bool_true)); flags = 0; add_object_attribute(o, 0, CKA_AUTH_PIN_FLAGS, &flags, sizeof(flags)); add_object_attribute(o, 0, CKA_DECRYPT, &bool_true, sizeof(bool_true)); add_object_attribute(o, 0, CKA_SIGN, &bool_true, sizeof(bool_true)); add_object_attribute(o, 0, CKA_SIGN_RECOVER, &bool_false, sizeof(bool_false)); add_object_attribute(o, 0, CKA_UNWRAP, &bool_true, sizeof(bool_true)); add_object_attribute(o, 0, CKA_EXTRACTABLE, &bool_true, sizeof(bool_true)); add_object_attribute(o, 0, CKA_NEVER_EXTRACTABLE, &bool_false, sizeof(bool_false)); add_pubkey_info(hxctx, o, key_type, cert); } ret = CKR_OK; out: if (ret != CKR_OK) { st_logf("something went wrong when adding cert!\n"); /* XXX wack o */; } hx509_xfree(cert_data.data); hx509_xfree(serial_data.data); hx509_xfree(issuer_data.data); hx509_xfree(subject_data.data); return 0; } static CK_RV add_certificate(const char *cert_file, const char *pin, char *id, char *label) { hx509_certs certs; hx509_lock lock = NULL; int ret, flags = 0; struct foo foo; foo.id = id; foo.label = label; if (pin == NULL) flags |= HX509_CERTS_UNPROTECT_ALL; if (pin) { char *str; asprintf(&str, "PASS:%s", pin); hx509_lock_init(context, &lock); hx509_lock_command_string(lock, str); memset(str, 0, strlen(str)); free(str); } ret = hx509_certs_init(context, cert_file, flags, lock, &certs); if (ret) { st_logf("failed to open file %s\n", cert_file); return CKR_GENERAL_ERROR; } ret = hx509_certs_iter_f(context, certs, add_cert, &foo); hx509_certs_free(&certs); if (ret) { st_logf("failed adding certs from file %s\n", cert_file); return CKR_GENERAL_ERROR; } return CKR_OK; } static void find_object_final(struct session_state *state) { if (state->find.attributes) { CK_ULONG i; for (i = 0; i < state->find.num_attributes; i++) { if (state->find.attributes[i].pValue) free(state->find.attributes[i].pValue); } free(state->find.attributes); state->find.attributes = NULL; state->find.num_attributes = 0; state->find.next_object = -1; } } static void reset_crypto_state(struct session_state *state) { state->sign_object = -1; if (state->sign_mechanism) free(state->sign_mechanism); state->sign_mechanism = NULL_PTR; state->verify_object = -1; if (state->verify_mechanism) free(state->verify_mechanism); state->verify_mechanism = NULL_PTR; } static void close_session(struct session_state *state) { if (state->find.attributes) { application_error("application didn't do C_FindObjectsFinal\n"); find_object_final(state); } state->session_handle = CK_INVALID_HANDLE; soft_token.application = NULL_PTR; soft_token.notify = NULL_PTR; reset_crypto_state(state); } static const char * has_session(void) { return soft_token.open_sessions > 0 ? "yes" : "no"; } static CK_RV read_conf_file(const char *fn, CK_USER_TYPE userType, const char *pin) { char buf[1024], *type, *s, *p; FILE *f; CK_RV ret = CKR_OK; CK_RV failed = CKR_OK; if (fn == NULL) { st_logf("Can't open configuration file. No file specified\n"); return CKR_GENERAL_ERROR; } f = fopen(fn, "r"); if (f == NULL) { st_logf("can't open configuration file %s\n", fn); return CKR_GENERAL_ERROR; } rk_cloexec_file(f); while(fgets(buf, sizeof(buf), f) != NULL) { buf[strcspn(buf, "\n")] = '\0'; st_logf("line: %s\n", buf); p = buf; while (isspace((unsigned char)*p)) p++; if (*p == '#') continue; while (isspace((unsigned char)*p)) p++; s = NULL; type = strtok_r(p, "\t", &s); if (type == NULL) continue; if (strcasecmp("certificate", type) == 0) { char *cert, *id, *label; id = strtok_r(NULL, "\t", &s); if (id == NULL) { st_logf("no id\n"); continue; } st_logf("id: %s\n", id); label = strtok_r(NULL, "\t", &s); if (label == NULL) { st_logf("no label\n"); continue; } cert = strtok_r(NULL, "\t", &s); if (cert == NULL) { st_logf("no certfiicate store\n"); continue; } st_logf("adding: %s: %s in file %s\n", id, label, cert); ret = add_certificate(cert, pin, id, label); if (ret) failed = ret; } else if (strcasecmp("debug", type) == 0) { char *name; name = strtok_r(NULL, "\t", &s); if (name == NULL) { st_logf("no filename\n"); continue; } if (soft_token.logfile) fclose(soft_token.logfile); if (strcasecmp(name, "stdout") == 0) soft_token.logfile = stdout; else { soft_token.logfile = fopen(name, "a"); if (soft_token.logfile) rk_cloexec_file(soft_token.logfile); } if (soft_token.logfile == NULL) st_logf("failed to open file: %s\n", name); } else if (strcasecmp("app-fatal", type) == 0) { char *name; name = strtok_r(NULL, "\t", &s); if (name == NULL) { st_logf("argument to app-fatal\n"); continue; } if (strcmp(name, "true") == 0 || strcmp(name, "on") == 0) soft_token.flags.app_error_fatal = 1; else if (strcmp(name, "false") == 0 || strcmp(name, "off") == 0) soft_token.flags.app_error_fatal = 0; else st_logf("unknown app-fatal: %s\n", name); } else { st_logf("unknown type: %s\n", type); } } fclose(f); return failed; } static CK_RV func_not_supported(void) { st_logf("function not supported\n"); return CKR_FUNCTION_NOT_SUPPORTED; } static char * get_config_file_for_user(void) { char *fn = NULL; #ifndef _WIN32 char *home = NULL; if (!issuid()) { fn = getenv("SOFTPKCS11RC"); if (fn) fn = strdup(fn); home = getenv("HOME"); } if (fn == NULL && home == NULL) { struct passwd *pw = getpwuid(getuid()); if(pw != NULL) home = pw->pw_dir; } if (fn == NULL) { if (home) asprintf(&fn, "%s/.soft-token.rc", home); else fn = strdup("/etc/soft-token.rc"); } #else /* Windows */ char appdatafolder[MAX_PATH]; fn = getenv("SOFTPKCS11RC"); /* Retrieve the roaming AppData folder for the current user. The current user is the user account represented by the current thread token. */ if (fn == NULL && SUCCEEDED(SHGetFolderPath(NULL, CSIDL_APPDATA, NULL, SHGFP_TYPE_CURRENT, appdatafolder))) { asprintf(&fn, "%s\\.soft-token.rc", appdatafolder); } #endif /* _WIN32 */ return fn; } CK_RV CK_SPEC C_Initialize(CK_VOID_PTR a) { CK_C_INITIALIZE_ARGS_PTR args = a; CK_RV ret; size_t i; st_logf("Initialize\n"); INIT_CONTEXT(); OpenSSL_add_all_algorithms(); srandom(getpid() ^ (int) time(NULL)); for (i = 0; i < MAX_NUM_SESSION; i++) { soft_token.state[i].session_handle = CK_INVALID_HANDLE; soft_token.state[i].find.attributes = NULL; soft_token.state[i].find.num_attributes = 0; soft_token.state[i].find.next_object = -1; reset_crypto_state(&soft_token.state[i]); } soft_token.flags.hardware_slot = 1; soft_token.flags.app_error_fatal = 0; soft_token.flags.login_done = 0; soft_token.object.objs = NULL; soft_token.object.num_objs = 0; soft_token.logfile = NULL; #if 0 soft_token.logfile = stdout; #endif #if 0 soft_token.logfile = fopen("/tmp/log-pkcs11.txt", "a"); #endif if (a != NULL_PTR) { st_logf("\tCreateMutex:\t%p\n", args->CreateMutex); st_logf("\tDestroyMutext\t%p\n", args->DestroyMutex); st_logf("\tLockMutext\t%p\n", args->LockMutex); st_logf("\tUnlockMutext\t%p\n", args->UnlockMutex); st_logf("\tFlags\t%04x\n", (unsigned int)args->flags); } soft_token.config_file = get_config_file_for_user(); /* * This operations doesn't return CKR_OK if any of the * certificates failes to be unparsed (ie password protected). */ ret = read_conf_file(soft_token.config_file, CKU_USER, NULL); if (ret == CKR_OK) soft_token.flags.login_done = 1; return CKR_OK; } CK_RV C_Finalize(CK_VOID_PTR args) { size_t i; INIT_CONTEXT(); st_logf("Finalize\n"); for (i = 0; i < MAX_NUM_SESSION; i++) { if (soft_token.state[i].session_handle != CK_INVALID_HANDLE) { application_error("application finalized without " "closing session\n"); close_session(&soft_token.state[i]); } } return CKR_OK; } CK_RV C_GetInfo(CK_INFO_PTR args) { INIT_CONTEXT(); st_logf("GetInfo\n"); memset(args, 17, sizeof(*args)); args->cryptokiVersion.major = 2; args->cryptokiVersion.minor = 10; snprintf_fill((char *)args->manufacturerID, sizeof(args->manufacturerID), ' ', "Heimdal hx509 SoftToken"); snprintf_fill((char *)args->libraryDescription, sizeof(args->libraryDescription), ' ', "Heimdal hx509 SoftToken"); args->libraryVersion.major = 2; args->libraryVersion.minor = 0; return CKR_OK; } extern CK_FUNCTION_LIST funcs; CK_RV C_GetFunctionList(CK_FUNCTION_LIST_PTR_PTR ppFunctionList) { INIT_CONTEXT(); *ppFunctionList = &funcs; return CKR_OK; } CK_RV C_GetSlotList(CK_BBOOL tokenPresent, CK_SLOT_ID_PTR pSlotList, CK_ULONG_PTR pulCount) { INIT_CONTEXT(); st_logf("GetSlotList: %s\n", tokenPresent ? "tokenPresent" : "token not Present"); if (pSlotList) pSlotList[0] = 1; *pulCount = 1; return CKR_OK; } CK_RV C_GetSlotInfo(CK_SLOT_ID slotID, CK_SLOT_INFO_PTR pInfo) { INIT_CONTEXT(); st_logf("GetSlotInfo: slot: %d : %s\n", (int)slotID, has_session()); memset(pInfo, 18, sizeof(*pInfo)); if (slotID != 1) return CKR_ARGUMENTS_BAD; snprintf_fill((char *)pInfo->slotDescription, sizeof(pInfo->slotDescription), ' ', "Heimdal hx509 SoftToken (slot)"); snprintf_fill((char *)pInfo->manufacturerID, sizeof(pInfo->manufacturerID), ' ', "Heimdal hx509 SoftToken (slot)"); pInfo->flags = CKF_TOKEN_PRESENT; if (soft_token.flags.hardware_slot) pInfo->flags |= CKF_HW_SLOT; pInfo->hardwareVersion.major = 1; pInfo->hardwareVersion.minor = 0; pInfo->firmwareVersion.major = 1; pInfo->firmwareVersion.minor = 0; return CKR_OK; } CK_RV C_GetTokenInfo(CK_SLOT_ID slotID, CK_TOKEN_INFO_PTR pInfo) { INIT_CONTEXT(); st_logf("GetTokenInfo: %s\n", has_session()); memset(pInfo, 19, sizeof(*pInfo)); snprintf_fill((char *)pInfo->label, sizeof(pInfo->label), ' ', "Heimdal hx509 SoftToken (token)"); snprintf_fill((char *)pInfo->manufacturerID, sizeof(pInfo->manufacturerID), ' ', "Heimdal hx509 SoftToken (token)"); snprintf_fill((char *)pInfo->model, sizeof(pInfo->model), ' ', "Heimdal hx509 SoftToken (token)"); snprintf_fill((char *)pInfo->serialNumber, sizeof(pInfo->serialNumber), ' ', "4711"); pInfo->flags = CKF_TOKEN_INITIALIZED | CKF_USER_PIN_INITIALIZED; if (soft_token.flags.login_done == 0) pInfo->flags |= CKF_LOGIN_REQUIRED; /* CFK_RNG | CKF_RESTORE_KEY_NOT_NEEDED | */ pInfo->ulMaxSessionCount = MAX_NUM_SESSION; pInfo->ulSessionCount = soft_token.open_sessions; pInfo->ulMaxRwSessionCount = MAX_NUM_SESSION; pInfo->ulRwSessionCount = soft_token.open_sessions; pInfo->ulMaxPinLen = 1024; pInfo->ulMinPinLen = 0; pInfo->ulTotalPublicMemory = 4711; pInfo->ulFreePublicMemory = 4712; pInfo->ulTotalPrivateMemory = 4713; pInfo->ulFreePrivateMemory = 4714; pInfo->hardwareVersion.major = 2; pInfo->hardwareVersion.minor = 0; pInfo->firmwareVersion.major = 2; pInfo->firmwareVersion.minor = 0; return CKR_OK; } CK_RV C_GetMechanismList(CK_SLOT_ID slotID, CK_MECHANISM_TYPE_PTR pMechanismList, CK_ULONG_PTR pulCount) { INIT_CONTEXT(); st_logf("GetMechanismList\n"); *pulCount = 1; if (pMechanismList == NULL_PTR) return CKR_OK; pMechanismList[1] = CKM_RSA_PKCS; return CKR_OK; } CK_RV C_GetMechanismInfo(CK_SLOT_ID slotID, CK_MECHANISM_TYPE type, CK_MECHANISM_INFO_PTR pInfo) { INIT_CONTEXT(); st_logf("GetMechanismInfo: slot %d type: %d\n", (int)slotID, (int)type); memset(pInfo, 0, sizeof(*pInfo)); return CKR_OK; } CK_RV C_InitToken(CK_SLOT_ID slotID, CK_UTF8CHAR_PTR pPin, CK_ULONG ulPinLen, CK_UTF8CHAR_PTR pLabel) { INIT_CONTEXT(); st_logf("InitToken: slot %d\n", (int)slotID); return CKR_FUNCTION_NOT_SUPPORTED; } CK_RV C_OpenSession(CK_SLOT_ID slotID, CK_FLAGS flags, CK_VOID_PTR pApplication, CK_NOTIFY Notify, CK_SESSION_HANDLE_PTR phSession) { size_t i; INIT_CONTEXT(); st_logf("OpenSession: slot: %d\n", (int)slotID); if (soft_token.open_sessions == MAX_NUM_SESSION) return CKR_SESSION_COUNT; soft_token.application = pApplication; soft_token.notify = Notify; for (i = 0; i < MAX_NUM_SESSION; i++) if (soft_token.state[i].session_handle == CK_INVALID_HANDLE) break; if (i == MAX_NUM_SESSION) abort(); soft_token.open_sessions++; soft_token.state[i].session_handle = (CK_SESSION_HANDLE)(random() & 0xfffff); *phSession = soft_token.state[i].session_handle; return CKR_OK; } CK_RV C_CloseSession(CK_SESSION_HANDLE hSession) { struct session_state *state; INIT_CONTEXT(); st_logf("CloseSession\n"); if (verify_session_handle(hSession, &state) != CKR_OK) application_error("closed session not open"); else close_session(state); return CKR_OK; } CK_RV C_CloseAllSessions(CK_SLOT_ID slotID) { size_t i; INIT_CONTEXT(); st_logf("CloseAllSessions\n"); for (i = 0; i < MAX_NUM_SESSION; i++) if (soft_token.state[i].session_handle != CK_INVALID_HANDLE) close_session(&soft_token.state[i]); return CKR_OK; } CK_RV C_GetSessionInfo(CK_SESSION_HANDLE hSession, CK_SESSION_INFO_PTR pInfo) { st_logf("GetSessionInfo\n"); INIT_CONTEXT(); VERIFY_SESSION_HANDLE(hSession, NULL); memset(pInfo, 20, sizeof(*pInfo)); pInfo->slotID = 1; if (soft_token.flags.login_done) pInfo->state = CKS_RO_USER_FUNCTIONS; else pInfo->state = CKS_RO_PUBLIC_SESSION; pInfo->flags = CKF_SERIAL_SESSION; pInfo->ulDeviceError = 0; return CKR_OK; } CK_RV C_Login(CK_SESSION_HANDLE hSession, CK_USER_TYPE userType, CK_UTF8CHAR_PTR pPin, CK_ULONG ulPinLen) { char *pin = NULL; CK_RV ret; INIT_CONTEXT(); st_logf("Login\n"); VERIFY_SESSION_HANDLE(hSession, NULL); if (pPin != NULL_PTR) { asprintf(&pin, "%.*s", (int)ulPinLen, pPin); st_logf("type: %d password: %s\n", (int)userType, pin); } /* * Login */ ret = read_conf_file(soft_token.config_file, userType, pin); if (ret == CKR_OK) soft_token.flags.login_done = 1; free(pin); return soft_token.flags.login_done ? CKR_OK : CKR_PIN_INCORRECT; } CK_RV C_Logout(CK_SESSION_HANDLE hSession) { st_logf("Logout\n"); INIT_CONTEXT(); VERIFY_SESSION_HANDLE(hSession, NULL); return CKR_FUNCTION_NOT_SUPPORTED; } CK_RV C_GetObjectSize(CK_SESSION_HANDLE hSession, CK_OBJECT_HANDLE hObject, CK_ULONG_PTR pulSize) { st_logf("GetObjectSize\n"); INIT_CONTEXT(); VERIFY_SESSION_HANDLE(hSession, NULL); return CKR_FUNCTION_NOT_SUPPORTED; } CK_RV C_GetAttributeValue(CK_SESSION_HANDLE hSession, CK_OBJECT_HANDLE hObject, CK_ATTRIBUTE_PTR pTemplate, CK_ULONG ulCount) { struct session_state *state; struct st_object *obj; CK_ULONG i; CK_RV ret; int j; INIT_CONTEXT(); st_logf("GetAttributeValue: %lx\n", (unsigned long)HANDLE_OBJECT_ID(hObject)); VERIFY_SESSION_HANDLE(hSession, &state); if ((ret = object_handle_to_object(hObject, &obj)) != CKR_OK) { st_logf("object not found: %lx\n", (unsigned long)HANDLE_OBJECT_ID(hObject)); return ret; } for (i = 0; i < ulCount; i++) { st_logf(" getting 0x%08lx\n", (unsigned long)pTemplate[i].type); for (j = 0; j < obj->num_attributes; j++) { if (obj->attrs[j].secret) { pTemplate[i].ulValueLen = (CK_ULONG)-1; break; } if (pTemplate[i].type == obj->attrs[j].attribute.type) { if (pTemplate[i].pValue != NULL_PTR && obj->attrs[j].secret == 0) { if (pTemplate[i].ulValueLen >= obj->attrs[j].attribute.ulValueLen) memcpy(pTemplate[i].pValue, obj->attrs[j].attribute.pValue, obj->attrs[j].attribute.ulValueLen); } pTemplate[i].ulValueLen = obj->attrs[j].attribute.ulValueLen; break; } } if (j == obj->num_attributes) { st_logf("key type: 0x%08lx not found\n", (unsigned long)pTemplate[i].type); pTemplate[i].ulValueLen = (CK_ULONG)-1; } } return CKR_OK; } CK_RV C_FindObjectsInit(CK_SESSION_HANDLE hSession, CK_ATTRIBUTE_PTR pTemplate, CK_ULONG ulCount) { struct session_state *state; st_logf("FindObjectsInit\n"); INIT_CONTEXT(); VERIFY_SESSION_HANDLE(hSession, &state); if (state->find.next_object != -1) { application_error("application didn't do C_FindObjectsFinal\n"); find_object_final(state); } if (ulCount) { CK_ULONG i; print_attributes(pTemplate, ulCount); state->find.attributes = calloc(1, ulCount * sizeof(state->find.attributes[0])); if (state->find.attributes == NULL) return CKR_DEVICE_MEMORY; for (i = 0; i < ulCount; i++) { state->find.attributes[i].pValue = malloc(pTemplate[i].ulValueLen); if (state->find.attributes[i].pValue == NULL) { find_object_final(state); return CKR_DEVICE_MEMORY; } memcpy(state->find.attributes[i].pValue, pTemplate[i].pValue, pTemplate[i].ulValueLen); state->find.attributes[i].type = pTemplate[i].type; state->find.attributes[i].ulValueLen = pTemplate[i].ulValueLen; } state->find.num_attributes = ulCount; state->find.next_object = 0; } else { st_logf("find all objects\n"); state->find.attributes = NULL; state->find.num_attributes = 0; state->find.next_object = 0; } return CKR_OK; } CK_RV C_FindObjects(CK_SESSION_HANDLE hSession, CK_OBJECT_HANDLE_PTR phObject, CK_ULONG ulMaxObjectCount, CK_ULONG_PTR pulObjectCount) { struct session_state *state; int i; INIT_CONTEXT(); st_logf("FindObjects\n"); VERIFY_SESSION_HANDLE(hSession, &state); if (state->find.next_object == -1) { application_error("application didn't do C_FindObjectsInit\n"); return CKR_ARGUMENTS_BAD; } if (ulMaxObjectCount == 0) { application_error("application asked for 0 objects\n"); return CKR_ARGUMENTS_BAD; } *pulObjectCount = 0; for (i = state->find.next_object; i < soft_token.object.num_objs; i++) { st_logf("FindObjects: %d\n", i); state->find.next_object = i + 1; if (attributes_match(soft_token.object.objs[i], state->find.attributes, state->find.num_attributes)) { *phObject++ = soft_token.object.objs[i]->object_handle; ulMaxObjectCount--; (*pulObjectCount)++; if (ulMaxObjectCount == 0) break; } } return CKR_OK; } CK_RV C_FindObjectsFinal(CK_SESSION_HANDLE hSession) { struct session_state *state; INIT_CONTEXT(); st_logf("FindObjectsFinal\n"); VERIFY_SESSION_HANDLE(hSession, &state); find_object_final(state); return CKR_OK; } static CK_RV commonInit(CK_ATTRIBUTE *attr_match, int attr_match_len, const CK_MECHANISM_TYPE *mechs, int mechs_len, const CK_MECHANISM_PTR pMechanism, CK_OBJECT_HANDLE hKey, struct st_object **o) { CK_RV ret; int i; *o = NULL; if ((ret = object_handle_to_object(hKey, o)) != CKR_OK) return ret; ret = attributes_match(*o, attr_match, attr_match_len); if (!ret) { application_error("called commonInit on key that doesn't " "support required attr"); return CKR_ARGUMENTS_BAD; } for (i = 0; i < mechs_len; i++) if (mechs[i] == pMechanism->mechanism) break; if (i == mechs_len) { application_error("called mech (%08lx) not supported\n", pMechanism->mechanism); return CKR_ARGUMENTS_BAD; } return CKR_OK; } static CK_RV dup_mechanism(CK_MECHANISM_PTR *dp, const CK_MECHANISM_PTR pMechanism) { CK_MECHANISM_PTR p; p = malloc(sizeof(*p)); if (p == NULL) return CKR_DEVICE_MEMORY; if (*dp) free(*dp); *dp = p; memcpy(p, pMechanism, sizeof(*p)); return CKR_OK; } CK_RV C_DigestInit(CK_SESSION_HANDLE hSession, CK_MECHANISM_PTR pMechanism) { st_logf("DigestInit\n"); INIT_CONTEXT(); VERIFY_SESSION_HANDLE(hSession, NULL); return CKR_FUNCTION_NOT_SUPPORTED; } CK_RV C_SignInit(CK_SESSION_HANDLE hSession, CK_MECHANISM_PTR pMechanism, CK_OBJECT_HANDLE hKey) { struct session_state *state; CK_MECHANISM_TYPE mechs[] = { CKM_RSA_PKCS }; CK_BBOOL bool_true = CK_TRUE; CK_ATTRIBUTE attr[] = { { CKA_SIGN, &bool_true, sizeof(bool_true) } }; struct st_object *o; CK_RV ret; INIT_CONTEXT(); st_logf("SignInit\n"); VERIFY_SESSION_HANDLE(hSession, &state); ret = commonInit(attr, sizeof(attr)/sizeof(attr[0]), mechs, sizeof(mechs)/sizeof(mechs[0]), pMechanism, hKey, &o); if (ret) return ret; ret = dup_mechanism(&state->sign_mechanism, pMechanism); if (ret == CKR_OK) state->sign_object = OBJECT_ID(o); return CKR_OK; } CK_RV C_Sign(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pData, CK_ULONG ulDataLen, CK_BYTE_PTR pSignature, CK_ULONG_PTR pulSignatureLen) { struct session_state *state; struct st_object *o; CK_RV ret; int hret; const AlgorithmIdentifier *alg; heim_octet_string sig, data; INIT_CONTEXT(); st_logf("Sign\n"); VERIFY_SESSION_HANDLE(hSession, &state); sig.data = NULL; sig.length = 0; if (state->sign_object == -1) return CKR_ARGUMENTS_BAD; if (pulSignatureLen == NULL) { st_logf("signature len NULL\n"); ret = CKR_ARGUMENTS_BAD; goto out; } if (pData == NULL_PTR) { st_logf("data NULL\n"); ret = CKR_ARGUMENTS_BAD; goto out; } o = soft_token.object.objs[state->sign_object]; if (hx509_cert_have_private_key(o->cert) == 0) { st_logf("private key NULL\n"); return CKR_ARGUMENTS_BAD; } switch(state->sign_mechanism->mechanism) { case CKM_RSA_PKCS: alg = hx509_signature_rsa_pkcs1_x509(); break; default: ret = CKR_FUNCTION_NOT_SUPPORTED; goto out; } data.data = pData; data.length = ulDataLen; hret = _hx509_create_signature(context, _hx509_cert_private_key(o->cert), alg, &data, NULL, &sig); if (hret) { ret = CKR_DEVICE_ERROR; goto out; } *pulSignatureLen = sig.length; if (pSignature != NULL_PTR) memcpy(pSignature, sig.data, sig.length); ret = CKR_OK; out: if (sig.data) { memset(sig.data, 0, sig.length); der_free_octet_string(&sig); } return ret; } CK_RV C_SignUpdate(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pPart, CK_ULONG ulPartLen) { INIT_CONTEXT(); st_logf("SignUpdate\n"); VERIFY_SESSION_HANDLE(hSession, NULL); return CKR_FUNCTION_NOT_SUPPORTED; } CK_RV C_SignFinal(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pSignature, CK_ULONG_PTR pulSignatureLen) { INIT_CONTEXT(); st_logf("SignUpdate\n"); VERIFY_SESSION_HANDLE(hSession, NULL); return CKR_FUNCTION_NOT_SUPPORTED; } CK_RV C_VerifyInit(CK_SESSION_HANDLE hSession, CK_MECHANISM_PTR pMechanism, CK_OBJECT_HANDLE hKey) { struct session_state *state; CK_MECHANISM_TYPE mechs[] = { CKM_RSA_PKCS }; CK_BBOOL bool_true = CK_TRUE; CK_ATTRIBUTE attr[] = { { CKA_VERIFY, &bool_true, sizeof(bool_true) } }; struct st_object *o; CK_RV ret; INIT_CONTEXT(); st_logf("VerifyInit\n"); VERIFY_SESSION_HANDLE(hSession, &state); ret = commonInit(attr, sizeof(attr)/sizeof(attr[0]), mechs, sizeof(mechs)/sizeof(mechs[0]), pMechanism, hKey, &o); if (ret) return ret; ret = dup_mechanism(&state->verify_mechanism, pMechanism); if (ret == CKR_OK) state->verify_object = OBJECT_ID(o); return ret; } CK_RV C_Verify(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pData, CK_ULONG ulDataLen, CK_BYTE_PTR pSignature, CK_ULONG ulSignatureLen) { struct session_state *state; struct st_object *o; const AlgorithmIdentifier *alg; CK_RV ret; int hret; heim_octet_string data, sig; INIT_CONTEXT(); st_logf("Verify\n"); VERIFY_SESSION_HANDLE(hSession, &state); if (state->verify_object == -1) return CKR_ARGUMENTS_BAD; o = soft_token.object.objs[state->verify_object]; switch(state->verify_mechanism->mechanism) { case CKM_RSA_PKCS: alg = hx509_signature_rsa_pkcs1_x509(); break; default: ret = CKR_FUNCTION_NOT_SUPPORTED; goto out; } sig.data = pData; sig.length = ulDataLen; data.data = pSignature; data.length = ulSignatureLen; hret = _hx509_verify_signature(context, o->cert, alg, &data, &sig); if (hret) { ret = CKR_GENERAL_ERROR; goto out; } ret = CKR_OK; out: return ret; } CK_RV C_VerifyUpdate(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pPart, CK_ULONG ulPartLen) { INIT_CONTEXT(); st_logf("VerifyUpdate\n"); VERIFY_SESSION_HANDLE(hSession, NULL); return CKR_FUNCTION_NOT_SUPPORTED; } CK_RV C_VerifyFinal(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pSignature, CK_ULONG ulSignatureLen) { INIT_CONTEXT(); st_logf("VerifyFinal\n"); VERIFY_SESSION_HANDLE(hSession, NULL); return CKR_FUNCTION_NOT_SUPPORTED; } CK_RV C_GenerateRandom(CK_SESSION_HANDLE hSession, CK_BYTE_PTR RandomData, CK_ULONG ulRandomLen) { INIT_CONTEXT(); st_logf("GenerateRandom\n"); VERIFY_SESSION_HANDLE(hSession, NULL); return CKR_FUNCTION_NOT_SUPPORTED; } CK_FUNCTION_LIST funcs = { { 2, 11 }, C_Initialize, C_Finalize, C_GetInfo, C_GetFunctionList, C_GetSlotList, C_GetSlotInfo, C_GetTokenInfo, C_GetMechanismList, C_GetMechanismInfo, C_InitToken, (void *)func_not_supported, /* C_InitPIN */ (void *)func_not_supported, /* C_SetPIN */ C_OpenSession, C_CloseSession, C_CloseAllSessions, C_GetSessionInfo, (void *)func_not_supported, /* C_GetOperationState */ (void *)func_not_supported, /* C_SetOperationState */ C_Login, C_Logout, (void *)func_not_supported, /* C_CreateObject */ (void *)func_not_supported, /* C_CopyObject */ (void *)func_not_supported, /* C_DestroyObject */ (void *)func_not_supported, /* C_GetObjectSize */ C_GetAttributeValue, (void *)func_not_supported, /* C_SetAttributeValue */ C_FindObjectsInit, C_FindObjects, C_FindObjectsFinal, (void *)func_not_supported, /* C_EncryptInit, */ (void *)func_not_supported, /* C_Encrypt, */ (void *)func_not_supported, /* C_EncryptUpdate, */ (void *)func_not_supported, /* C_EncryptFinal, */ (void *)func_not_supported, /* C_DecryptInit, */ (void *)func_not_supported, /* C_Decrypt, */ (void *)func_not_supported, /* C_DecryptUpdate, */ (void *)func_not_supported, /* C_DecryptFinal, */ C_DigestInit, (void *)func_not_supported, /* C_Digest */ (void *)func_not_supported, /* C_DigestUpdate */ (void *)func_not_supported, /* C_DigestKey */ (void *)func_not_supported, /* C_DigestFinal */ C_SignInit, C_Sign, C_SignUpdate, C_SignFinal, (void *)func_not_supported, /* C_SignRecoverInit */ (void *)func_not_supported, /* C_SignRecover */ C_VerifyInit, C_Verify, C_VerifyUpdate, C_VerifyFinal, (void *)func_not_supported, /* C_VerifyRecoverInit */ (void *)func_not_supported, /* C_VerifyRecover */ (void *)func_not_supported, /* C_DigestEncryptUpdate */ (void *)func_not_supported, /* C_DecryptDigestUpdate */ (void *)func_not_supported, /* C_SignEncryptUpdate */ (void *)func_not_supported, /* C_DecryptVerifyUpdate */ (void *)func_not_supported, /* C_GenerateKey */ (void *)func_not_supported, /* C_GenerateKeyPair */ (void *)func_not_supported, /* C_WrapKey */ (void *)func_not_supported, /* C_UnwrapKey */ (void *)func_not_supported, /* C_DeriveKey */ (void *)func_not_supported, /* C_SeedRandom */ C_GenerateRandom, (void *)func_not_supported, /* C_GetFunctionStatus */ (void *)func_not_supported, /* C_CancelFunction */ (void *)func_not_supported /* C_WaitForSlotEvent */ }; diff --git a/crypto/heimdal/lib/ipc/client.c b/crypto/heimdal/lib/ipc/client.c index bb7d9750bffa..809fc6a01047 100644 --- a/crypto/heimdal/lib/ipc/client.c +++ b/crypto/heimdal/lib/ipc/client.c @@ -1,574 +1,572 @@ /* * Copyright (c) 2009 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 "hi_locl.h" #if defined(__APPLE__) && defined(HAVE_GCD) #include "heim_ipc.h" #include "heim_ipc_asyncServer.h" #include #include static dispatch_once_t jobqinited = 0; static dispatch_queue_t jobq = NULL; static dispatch_queue_t syncq; struct mach_ctx { mach_port_t server; char *name; }; static int mach_release(void *ctx); static int mach_init(const char *service, void **ctx) { struct mach_ctx *ipc; mach_port_t sport; int ret; dispatch_once(&jobqinited, ^{ jobq = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); syncq = dispatch_queue_create("heim-ipc-syncq", NULL); }); ret = bootstrap_look_up(bootstrap_port, service, &sport); if (ret) return ret; ipc = malloc(sizeof(*ipc)); if (ipc == NULL) { mach_port_destroy(mach_task_self(), sport); return ENOMEM; } ipc->server = sport; ipc->name = strdup(service); if (ipc->name == NULL) { mach_release(ipc); return ENOMEM; } *ctx = ipc; return 0; } static int mach_ipc(void *ctx, const heim_idata *request, heim_idata *response, heim_icred *cred) { struct mach_ctx *ipc = ctx; heim_ipc_message_inband_t requestin; mach_msg_type_number_t requestin_length = 0; heim_ipc_message_outband_t requestout = NULL; mach_msg_type_number_t requestout_length = 0; heim_ipc_message_inband_t replyin; mach_msg_type_number_t replyin_length; heim_ipc_message_outband_t replyout; mach_msg_type_number_t replyout_length; int ret, errorcode, retries = 0; memcpy(requestin, request->data, request->length); requestin_length = request->length; while (retries < 2) { __block mach_port_t sport; dispatch_sync(syncq, ^{ sport = ipc->server; }); ret = mheim_ipc_call(sport, requestin, requestin_length, requestout, requestout_length, &errorcode, replyin, &replyin_length, &replyout, &replyout_length); if (ret == MACH_SEND_INVALID_DEST) { mach_port_t nport; /* race other threads to get a new port */ ret = bootstrap_look_up(bootstrap_port, ipc->name, &nport); if (ret) return ret; dispatch_sync(syncq, ^{ /* check if we lost the race to lookup the port */ if (sport != ipc->server) { mach_port_deallocate(mach_task_self(), nport); } else { mach_port_deallocate(mach_task_self(), ipc->server); ipc->server = nport; } }); retries++; } else if (ret) { return ret; } else break; } if (retries >= 2) return EINVAL; if (errorcode) { if (replyout_length) vm_deallocate (mach_task_self (), (vm_address_t) replyout, replyout_length); return errorcode; } if (replyout_length) { response->data = malloc(replyout_length); if (response->data == NULL) { vm_deallocate (mach_task_self (), (vm_address_t) replyout, replyout_length); return ENOMEM; } memcpy(response->data, replyout, replyout_length); response->length = replyout_length; vm_deallocate (mach_task_self (), (vm_address_t) replyout, replyout_length); } else { response->data = malloc(replyin_length); if (response->data == NULL) return ENOMEM; memcpy(response->data, replyin, replyin_length); response->length = replyin_length; } return 0; } struct async_client { mach_port_t mp; dispatch_source_t source; dispatch_queue_t queue; void (*func)(void *, int, heim_idata *, heim_icred); void *userctx; }; kern_return_t mheim_ado_acall_reply(mach_port_t server_port, audit_token_t client_creds, int returnvalue, heim_ipc_message_inband_t replyin, mach_msg_type_number_t replyinCnt, heim_ipc_message_outband_t replyout, mach_msg_type_number_t replyoutCnt) { struct async_client *c = dispatch_get_context(dispatch_get_current_queue()); heim_idata response; if (returnvalue) { response.data = NULL; response.length = 0; } else if (replyoutCnt) { response.data = replyout; response.length = replyoutCnt; } else { response.data = replyin; response.length = replyinCnt; } (*c->func)(c->userctx, returnvalue, &response, NULL); if (replyoutCnt) vm_deallocate (mach_task_self (), (vm_address_t) replyout, replyoutCnt); dispatch_source_cancel(c->source); return 0; } static int mach_async(void *ctx, const heim_idata *request, void *userctx, void (*func)(void *, int, heim_idata *, heim_icred)) { struct mach_ctx *ipc = ctx; heim_ipc_message_inband_t requestin; mach_msg_type_number_t requestin_length = 0; heim_ipc_message_outband_t requestout = NULL; mach_msg_type_number_t requestout_length = 0; int ret, retries = 0; kern_return_t kr; struct async_client *c; /* first create the service that will catch the reply from the server */ /* XXX these object should be cached and reused */ c = malloc(sizeof(*c)); if (c == NULL) return ENOMEM; kr = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &c->mp); if (kr != KERN_SUCCESS) return EINVAL; c->queue = dispatch_queue_create("heim-ipc-async-client", NULL); c->source = dispatch_source_create(DISPATCH_SOURCE_TYPE_MACH_RECV, c->mp, 0, c->queue); dispatch_set_context(c->queue, c); dispatch_source_set_event_handler(c->source, ^{ dispatch_mig_server(c->source, sizeof(union __RequestUnion__mheim_ado_mheim_aipc_subsystem), mheim_aipc_server); }); dispatch_source_set_cancel_handler(c->source, ^{ mach_port_mod_refs(mach_task_self(), c->mp, MACH_PORT_RIGHT_RECEIVE, -1); dispatch_release(c->queue); dispatch_release(c->source); free(c); }); c->func = func; c->userctx = userctx; dispatch_resume(c->source); /* ok, send the message */ memcpy(requestin, request->data, request->length); requestin_length = request->length; while (retries < 2) { __block mach_port_t sport; dispatch_sync(syncq, ^{ sport = ipc->server; }); ret = mheim_ipc_call_request(sport, c->mp, requestin, requestin_length, requestout, requestout_length); if (ret == MACH_SEND_INVALID_DEST) { ret = bootstrap_look_up(bootstrap_port, ipc->name, &sport); if (ret) { dispatch_source_cancel(c->source); return ret; } mach_port_deallocate(mach_task_self(), ipc->server); ipc->server = sport; retries++; } else if (ret) { dispatch_source_cancel(c->source); return ret; } else break; } if (retries >= 2) { dispatch_source_cancel(c->source); return EINVAL; } return 0; } static int mach_release(void *ctx) { struct mach_ctx *ipc = ctx; if (ipc->server != MACH_PORT_NULL) mach_port_deallocate(mach_task_self(), ipc->server); free(ipc->name); free(ipc); return 0; } #endif struct path_ctx { char *path; int fd; }; static int common_release(void *); static int connect_unix(struct path_ctx *s) { struct sockaddr_un addr; addr.sun_family = AF_UNIX; strlcpy(addr.sun_path, s->path, sizeof(addr.sun_path)); s->fd = socket(AF_UNIX, SOCK_STREAM, 0); if (s->fd < 0) return errno; rk_cloexec(s->fd); - if (connect(s->fd, (struct sockaddr *)&addr, sizeof(addr)) != 0) { - close(s->fd); + if (connect(s->fd, (struct sockaddr *)&addr, sizeof(addr)) != 0) return errno; - } return 0; } static int common_path_init(const char *service, const char *file, void **ctx) { struct path_ctx *s; s = malloc(sizeof(*s)); if (s == NULL) return ENOMEM; s->fd = -1; asprintf(&s->path, "/var/run/.heim_%s-%s", service, file); *ctx = s; return 0; } static int unix_socket_init(const char *service, void **ctx) { int ret; ret = common_path_init(service, "socket", ctx); if (ret) return ret; ret = connect_unix(*ctx); if (ret) common_release(*ctx); return ret; } static int unix_socket_ipc(void *ctx, const heim_idata *req, heim_idata *rep, heim_icred *cred) { struct path_ctx *s = ctx; uint32_t len = htonl(req->length); uint32_t rv; int retval; if (cred) *cred = NULL; rep->data = NULL; rep->length = 0; if (net_write(s->fd, &len, sizeof(len)) != sizeof(len)) return -1; if (net_write(s->fd, req->data, req->length) != (ssize_t)req->length) return -1; if (net_read(s->fd, &len, sizeof(len)) != sizeof(len)) return -1; if (net_read(s->fd, &rv, sizeof(rv)) != sizeof(rv)) return -1; retval = ntohl(rv); rep->length = ntohl(len); if (rep->length > 0) { rep->data = malloc(rep->length); if (rep->data == NULL) return -1; if (net_read(s->fd, rep->data, rep->length) != (ssize_t)rep->length) return -1; } else rep->data = NULL; return retval; } int common_release(void *ctx) { struct path_ctx *s = ctx; if (s->fd >= 0) close(s->fd); free(s->path); free(s); return 0; } #ifdef HAVE_DOOR static int door_init(const char *service, void **ctx) { ret = common_path_init(context, service, "door", ctx); if (ret) return ret; ret = connect_door(*ctx); if (ret) common_release(*ctx); return ret; } static int door_ipc(void *ctx, const heim_idata *request, heim_idata *response, heim_icred *cred) { door_arg_t arg; int ret; arg.data_ptr = request->data; arg.data_size = request->length; arg.desc_ptr = NULL; arg.desc_num = 0; arg.rbuf = NULL; arg.rsize = 0; ret = door_call(fd, &arg); close(fd); if (ret != 0) return errno; response->data = malloc(arg.rsize); if (response->data == NULL) { munmap(arg.rbuf, arg.rsize); return ENOMEM; } memcpy(response->data, arg.rbuf, arg.rsize); response->length = arg.rsize; munmap(arg.rbuf, arg.rsize); return ret; } #endif struct hipc_ops { const char *prefix; int (*init)(const char *, void **); int (*release)(void *); int (*ipc)(void *,const heim_idata *, heim_idata *, heim_icred *); int (*async)(void *, const heim_idata *, void *, void (*)(void *, int, heim_idata *, heim_icred)); }; struct hipc_ops ipcs[] = { #if defined(__APPLE__) && defined(HAVE_GCD) { "MACH", mach_init, mach_release, mach_ipc, mach_async }, #endif #ifdef HAVE_DOOR { "DOOR", door_init, common_release, door_ipc, NULL } #endif { "UNIX", unix_socket_init, common_release, unix_socket_ipc, NULL } }; struct heim_ipc { struct hipc_ops *ops; void *ctx; }; int heim_ipc_init_context(const char *name, heim_ipc *ctx) { unsigned int i; int ret, any = 0; for(i = 0; i < sizeof(ipcs)/sizeof(ipcs[0]); i++) { size_t prefix_len = strlen(ipcs[i].prefix); heim_ipc c; if(strncmp(ipcs[i].prefix, name, prefix_len) == 0 && name[prefix_len] == ':') { } else if (strncmp("ANY:", name, 4) == 0) { prefix_len = 3; any = 1; } else continue; c = calloc(1, sizeof(*c)); if (c == NULL) return ENOMEM; c->ops = &ipcs[i]; ret = (c->ops->init)(name + prefix_len + 1, &c->ctx); if (ret) { free(c); if (any) continue; return ret; } *ctx = c; return 0; } return ENOENT; } void heim_ipc_free_context(heim_ipc ctx) { (ctx->ops->release)(ctx->ctx); free(ctx); } int heim_ipc_call(heim_ipc ctx, const heim_idata *snd, heim_idata *rcv, heim_icred *cred) { if (cred) *cred = NULL; return (ctx->ops->ipc)(ctx->ctx, snd, rcv, cred); } int heim_ipc_async(heim_ipc ctx, const heim_idata *snd, void *userctx, void (*func)(void *, int, heim_idata *, heim_icred)) { if (ctx->ops->async == NULL) { heim_idata rcv; heim_icred cred = NULL; int ret; ret = (ctx->ops->ipc)(ctx->ctx, snd, &rcv, &cred); (*func)(userctx, ret, &rcv, cred); heim_ipc_free_cred(cred); free(rcv.data); return ret; } else { return (ctx->ops->async)(ctx->ctx, snd, userctx, func); } } diff --git a/crypto/heimdal/lib/kadm5/get_s.c b/crypto/heimdal/lib/kadm5/get_s.c index e03585e222a8..6c456e118316 100644 --- a/crypto/heimdal/lib/kadm5/get_s.c +++ b/crypto/heimdal/lib/kadm5/get_s.c @@ -1,312 +1,312 @@ /* * Copyright (c) 1997 - 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 "kadm5_locl.h" RCSID("$Id$"); static kadm5_ret_t add_tl_data(kadm5_principal_ent_t ent, int16_t type, const void *data, size_t size) { krb5_tl_data *tl; tl = calloc(1, sizeof(*tl)); if (tl == NULL) return _kadm5_error_code(ENOMEM); tl->tl_data_type = type; tl->tl_data_length = size; tl->tl_data_contents = malloc(size); if (tl->tl_data_contents == NULL && size != 0) { free(tl); return _kadm5_error_code(ENOMEM); } memcpy(tl->tl_data_contents, data, size); tl->tl_data_next = ent->tl_data; ent->tl_data = tl; ent->n_tl_data++; return 0; } KRB5_LIB_FUNCTION krb5_ssize_t KRB5_LIB_CALL _krb5_put_int(void *buffer, unsigned long value, size_t size); /* XXX */ kadm5_ret_t kadm5_s_get_principal(void *server_handle, krb5_principal princ, kadm5_principal_ent_t out, uint32_t mask) { kadm5_server_context *context = server_handle; kadm5_ret_t ret; hdb_entry_ex ent; memset(&ent, 0, sizeof(ent)); ret = context->db->hdb_open(context->context, context->db, O_RDONLY, 0); if(ret) return ret; ret = context->db->hdb_fetch_kvno(context->context, context->db, princ, HDB_F_DECRYPT|HDB_F_GET_ANY|HDB_F_ADMIN_DATA, 0, &ent); context->db->hdb_close(context->context, context->db); if(ret) return _kadm5_error_code(ret); memset(out, 0, sizeof(*out)); if(mask & KADM5_PRINCIPAL) ret = krb5_copy_principal(context->context, ent.entry.principal, &out->principal); if(ret) goto out; if(mask & KADM5_PRINC_EXPIRE_TIME && ent.entry.valid_end) out->princ_expire_time = *ent.entry.valid_end; if(mask & KADM5_PW_EXPIRATION && ent.entry.pw_end) out->pw_expiration = *ent.entry.pw_end; if(mask & KADM5_LAST_PWD_CHANGE) hdb_entry_get_pw_change_time(&ent.entry, &out->last_pwd_change); if(mask & KADM5_ATTRIBUTES){ out->attributes |= ent.entry.flags.postdate ? 0 : KRB5_KDB_DISALLOW_POSTDATED; out->attributes |= ent.entry.flags.forwardable ? 0 : KRB5_KDB_DISALLOW_FORWARDABLE; out->attributes |= ent.entry.flags.initial ? KRB5_KDB_DISALLOW_TGT_BASED : 0; out->attributes |= ent.entry.flags.renewable ? 0 : KRB5_KDB_DISALLOW_RENEWABLE; out->attributes |= ent.entry.flags.proxiable ? 0 : KRB5_KDB_DISALLOW_PROXIABLE; out->attributes |= ent.entry.flags.invalid ? KRB5_KDB_DISALLOW_ALL_TIX : 0; out->attributes |= ent.entry.flags.require_preauth ? KRB5_KDB_REQUIRES_PRE_AUTH : 0; out->attributes |= ent.entry.flags.server ? 0 : KRB5_KDB_DISALLOW_SVR; out->attributes |= ent.entry.flags.change_pw ? KRB5_KDB_PWCHANGE_SERVICE : 0; out->attributes |= ent.entry.flags.ok_as_delegate ? KRB5_KDB_OK_AS_DELEGATE : 0; out->attributes |= ent.entry.flags.trusted_for_delegation ? KRB5_KDB_TRUSTED_FOR_DELEGATION : 0; out->attributes |= ent.entry.flags.allow_kerberos4 ? KRB5_KDB_ALLOW_KERBEROS4 : 0; out->attributes |= ent.entry.flags.allow_digest ? KRB5_KDB_ALLOW_DIGEST : 0; } if(mask & KADM5_MAX_LIFE) { if(ent.entry.max_life) out->max_life = *ent.entry.max_life; else out->max_life = INT_MAX; } if(mask & KADM5_MOD_TIME) { if(ent.entry.modified_by) out->mod_date = ent.entry.modified_by->time; else out->mod_date = ent.entry.created_by.time; } if(mask & KADM5_MOD_NAME) { if(ent.entry.modified_by) { if (ent.entry.modified_by->principal != NULL) ret = krb5_copy_principal(context->context, ent.entry.modified_by->principal, &out->mod_name); } else if(ent.entry.created_by.principal != NULL) ret = krb5_copy_principal(context->context, ent.entry.created_by.principal, &out->mod_name); else out->mod_name = NULL; } if(ret) goto out; if(mask & KADM5_KVNO) out->kvno = ent.entry.kvno; if(mask & KADM5_MKVNO) { size_t n; out->mkvno = 0; /* XXX */ for(n = 0; n < ent.entry.keys.len; n++) if(ent.entry.keys.val[n].mkvno) { out->mkvno = *ent.entry.keys.val[n].mkvno; /* XXX this isn't right */ break; } } #if 0 /* XXX implement */ if(mask & KADM5_AUX_ATTRIBUTES) ; if(mask & KADM5_LAST_SUCCESS) ; if(mask & KADM5_LAST_FAILED) ; if(mask & KADM5_FAIL_AUTH_COUNT) ; #endif if(mask & KADM5_POLICY) out->policy = NULL; if(mask & KADM5_MAX_RLIFE) { if(ent.entry.max_renew) out->max_renewable_life = *ent.entry.max_renew; else out->max_renewable_life = INT_MAX; } if(mask & KADM5_KEY_DATA){ size_t i; Key *key; krb5_key_data *kd; krb5_salt salt; krb5_data *sp; krb5_get_pw_salt(context->context, ent.entry.principal, &salt); out->key_data = malloc(ent.entry.keys.len * sizeof(*out->key_data)); if (out->key_data == NULL && ent.entry.keys.len != 0) { ret = ENOMEM; goto out; } for(i = 0; i < ent.entry.keys.len; i++){ key = &ent.entry.keys.val[i]; kd = &out->key_data[i]; kd->key_data_ver = 2; kd->key_data_kvno = ent.entry.kvno; kd->key_data_type[0] = key->key.keytype; if(key->salt) kd->key_data_type[1] = key->salt->type; else kd->key_data_type[1] = KRB5_PADATA_PW_SALT; /* setup key */ kd->key_data_length[0] = key->key.keyvalue.length; kd->key_data_contents[0] = malloc(kd->key_data_length[0]); if(kd->key_data_contents[0] == NULL && kd->key_data_length[0] != 0){ ret = ENOMEM; break; } memcpy(kd->key_data_contents[0], key->key.keyvalue.data, kd->key_data_length[0]); /* setup salt */ if(key->salt) sp = &key->salt->salt; else sp = &salt.saltvalue; kd->key_data_length[1] = sp->length; kd->key_data_contents[1] = malloc(kd->key_data_length[1]); if(kd->key_data_length[1] != 0 && kd->key_data_contents[1] == NULL) { memset(kd->key_data_contents[0], 0, kd->key_data_length[0]); ret = ENOMEM; break; } memcpy(kd->key_data_contents[1], sp->data, kd->key_data_length[1]); out->n_key_data = i + 1; } krb5_free_salt(context->context, salt); } if(ret){ kadm5_free_principal_ent(context, out); goto out; } if(mask & KADM5_TL_DATA) { time_t last_pw_expire; const HDB_Ext_PKINIT_acl *acl; const HDB_Ext_Aliases *aliases; ret = hdb_entry_get_pw_change_time(&ent.entry, &last_pw_expire); if (ret == 0 && last_pw_expire) { unsigned char buf[4]; _krb5_put_int(buf, last_pw_expire, sizeof(buf)); ret = add_tl_data(out, KRB5_TL_LAST_PWD_CHANGE, buf, sizeof(buf)); } if(ret){ kadm5_free_principal_ent(context, out); goto out; } /* * If the client was allowed to get key data, let it have the * password too. */ if(mask & KADM5_KEY_DATA) { heim_utf8_string pw; ret = hdb_entry_get_password(context->context, context->db, &ent.entry, &pw); if (ret == 0) { - ret = add_tl_data(out, KRB5_TL_PASSWORD, pw, strlen(pw) + 1); + (void) add_tl_data(out, KRB5_TL_PASSWORD, pw, strlen(pw) + 1); free(pw); } krb5_clear_error_message(context->context); } ret = hdb_entry_get_pkinit_acl(&ent.entry, &acl); if (ret == 0 && acl) { krb5_data buf; size_t len; ASN1_MALLOC_ENCODE(HDB_Ext_PKINIT_acl, buf.data, buf.length, acl, &len, ret); if (ret) { kadm5_free_principal_ent(context, out); goto out; } if (len != buf.length) krb5_abortx(context->context, "internal ASN.1 encoder error"); ret = add_tl_data(out, KRB5_TL_PKINIT_ACL, buf.data, buf.length); free(buf.data); if (ret) { kadm5_free_principal_ent(context, out); goto out; } } if(ret){ kadm5_free_principal_ent(context, out); goto out; } ret = hdb_entry_get_aliases(&ent.entry, &aliases); if (ret == 0 && aliases) { krb5_data buf; size_t len; ASN1_MALLOC_ENCODE(HDB_Ext_Aliases, buf.data, buf.length, aliases, &len, ret); if (ret) { kadm5_free_principal_ent(context, out); goto out; } if (len != buf.length) krb5_abortx(context->context, "internal ASN.1 encoder error"); ret = add_tl_data(out, KRB5_TL_ALIASES, buf.data, buf.length); free(buf.data); if (ret) { kadm5_free_principal_ent(context, out); goto out; } } if(ret){ kadm5_free_principal_ent(context, out); goto out; } } out: hdb_free_entry(context->context, &ent); return _kadm5_error_code(ret); } diff --git a/crypto/heimdal/lib/kadm5/init_c.c b/crypto/heimdal/lib/kadm5/init_c.c index 1623ed1a995d..5b83d2087a25 100644 --- a/crypto/heimdal/lib/kadm5/init_c.c +++ b/crypto/heimdal/lib/kadm5/init_c.c @@ -1,789 +1,789 @@ /* * Copyright (c) 1997 - 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 "kadm5_locl.h" #include #ifdef HAVE_SYS_SOCKET_H #include #endif #ifdef HAVE_NETINET_IN_H #include #endif #ifdef HAVE_NETDB_H #include #endif RCSID("$Id$"); static void set_funcs(kadm5_client_context *c) { #define SET(C, F) (C)->funcs.F = kadm5 ## _c_ ## F SET(c, chpass_principal); SET(c, chpass_principal_with_key); SET(c, create_principal); SET(c, delete_principal); SET(c, destroy); SET(c, flush); SET(c, get_principal); SET(c, get_principals); SET(c, get_privs); SET(c, modify_principal); SET(c, randkey_principal); SET(c, rename_principal); } kadm5_ret_t _kadm5_c_init_context(kadm5_client_context **ctx, kadm5_config_params *params, krb5_context context) { krb5_error_code ret; char *colon; *ctx = malloc(sizeof(**ctx)); if(*ctx == NULL) return ENOMEM; memset(*ctx, 0, sizeof(**ctx)); krb5_add_et_list (context, initialize_kadm5_error_table_r); set_funcs(*ctx); (*ctx)->context = context; if(params->mask & KADM5_CONFIG_REALM) { ret = 0; (*ctx)->realm = strdup(params->realm); if ((*ctx)->realm == NULL) ret = ENOMEM; } else ret = krb5_get_default_realm((*ctx)->context, &(*ctx)->realm); if (ret) { free(*ctx); return ret; } if(params->mask & KADM5_CONFIG_ADMIN_SERVER) (*ctx)->admin_server = strdup(params->admin_server); else { char **hostlist; ret = krb5_get_krb_admin_hst (context, &(*ctx)->realm, &hostlist); if (ret) { free((*ctx)->realm); free(*ctx); return ret; } (*ctx)->admin_server = strdup(*hostlist); krb5_free_krbhst (context, hostlist); } if ((*ctx)->admin_server == NULL) { free((*ctx)->realm); free(*ctx); return ENOMEM; } colon = strchr ((*ctx)->admin_server, ':'); if (colon != NULL) *colon++ = '\0'; (*ctx)->kadmind_port = 0; if(params->mask & KADM5_CONFIG_KADMIND_PORT) (*ctx)->kadmind_port = params->kadmind_port; else if (colon != NULL) { char *end; (*ctx)->kadmind_port = htons(strtol (colon, &end, 0)); } if ((*ctx)->kadmind_port == 0) (*ctx)->kadmind_port = krb5_getportbyname (context, "kerberos-adm", "tcp", 749); return 0; } static krb5_error_code get_kadm_ticket(krb5_context context, krb5_ccache id, krb5_principal client, const char *server_name) { krb5_error_code ret; krb5_creds in, *out; memset(&in, 0, sizeof(in)); in.client = client; ret = krb5_parse_name(context, server_name, &in.server); if(ret) return ret; ret = krb5_get_credentials(context, 0, id, &in, &out); if(ret == 0) krb5_free_creds(context, out); krb5_free_principal(context, in.server); return ret; } static krb5_error_code get_new_cache(krb5_context context, krb5_principal client, const char *password, krb5_prompter_fct prompter, const char *keytab, const char *server_name, krb5_ccache *ret_cache) { krb5_error_code ret; krb5_creds cred; krb5_get_init_creds_opt *opt; krb5_ccache id; ret = krb5_get_init_creds_opt_alloc (context, &opt); if (ret) return ret; krb5_get_init_creds_opt_set_default_flags(context, "kadmin", krb5_principal_get_realm(context, client), opt); krb5_get_init_creds_opt_set_forwardable (opt, FALSE); krb5_get_init_creds_opt_set_proxiable (opt, FALSE); if(password == NULL && prompter == NULL) { krb5_keytab kt; if(keytab == NULL) ret = krb5_kt_default(context, &kt); else ret = krb5_kt_resolve(context, keytab, &kt); if(ret) { krb5_get_init_creds_opt_free(context, opt); return ret; } ret = krb5_get_init_creds_keytab (context, &cred, client, kt, 0, server_name, opt); krb5_kt_close(context, kt); } else { ret = krb5_get_init_creds_password (context, &cred, client, password, prompter, NULL, 0, server_name, opt); } krb5_get_init_creds_opt_free(context, opt); switch(ret){ case 0: break; case KRB5_LIBOS_PWDINTR: /* don't print anything if it was just C-c:ed */ case KRB5KRB_AP_ERR_BAD_INTEGRITY: case KRB5KRB_AP_ERR_MODIFIED: return KADM5_BAD_PASSWORD; default: return ret; } ret = krb5_cc_new_unique(context, krb5_cc_type_memory, NULL, &id); if(ret) return ret; ret = krb5_cc_initialize (context, id, cred.client); if (ret) return ret; ret = krb5_cc_store_cred (context, id, &cred); if (ret) return ret; krb5_free_cred_contents (context, &cred); *ret_cache = id; return 0; } /* * Check the credential cache `id´ to figure out what principal to use * when talking to the kadmind. If there is a initial kadmin/admin@ * credential in the cache, use that client principal. Otherwise, use * the client principals first component and add /admin to the * principal. */ static krb5_error_code get_cache_principal(krb5_context context, krb5_ccache *id, krb5_principal *client) { krb5_error_code ret; const char *name, *inst; krb5_principal p1, p2; ret = krb5_cc_default(context, id); if(ret) { *id = NULL; return ret; } ret = krb5_cc_get_principal(context, *id, &p1); if(ret) { krb5_cc_close(context, *id); *id = NULL; return ret; } ret = krb5_make_principal(context, &p2, NULL, "kadmin", "admin", NULL); if (ret) { krb5_cc_close(context, *id); *id = NULL; krb5_free_principal(context, p1); return ret; } { krb5_creds in, *out; krb5_kdc_flags flags; flags.i = 0; memset(&in, 0, sizeof(in)); in.client = p1; in.server = p2; /* check for initial ticket kadmin/admin */ ret = krb5_get_credentials_with_flags(context, KRB5_GC_CACHED, flags, *id, &in, &out); krb5_free_principal(context, p2); if (ret == 0) { if (out->flags.b.initial) { *client = p1; krb5_free_creds(context, out); return 0; } krb5_free_creds(context, out); } } krb5_cc_close(context, *id); *id = NULL; name = krb5_principal_get_comp_string(context, p1, 0); inst = krb5_principal_get_comp_string(context, p1, 1); if(inst == NULL || strcmp(inst, "admin") != 0) { ret = krb5_make_principal(context, &p2, NULL, name, "admin", NULL); krb5_free_principal(context, p1); if(ret != 0) return ret; *client = p2; return 0; } *client = p1; return 0; } krb5_error_code _kadm5_c_get_cred_cache(krb5_context context, const char *client_name, const char *server_name, const char *password, krb5_prompter_fct prompter, const char *keytab, krb5_ccache ccache, krb5_ccache *ret_cache) { krb5_error_code ret; krb5_ccache id = NULL; krb5_principal default_client = NULL, client = NULL; /* treat empty password as NULL */ if(password && *password == '\0') password = NULL; if(server_name == NULL) server_name = KADM5_ADMIN_SERVICE; if(client_name != NULL) { ret = krb5_parse_name(context, client_name, &client); if(ret) return ret; } if(ccache != NULL) { id = ccache; ret = krb5_cc_get_principal(context, id, &client); if(ret) return ret; } else { /* get principal from default cache, ok if this doesn't work */ ret = get_cache_principal(context, &id, &default_client); if (ret) { /* * No client was specified by the caller and we cannot * determine the client from a credentials cache. */ const char *user; user = get_default_username (); if(user == NULL) { krb5_set_error_message(context, KADM5_FAILURE, "Unable to find local user name"); return KADM5_FAILURE; } ret = krb5_make_principal(context, &default_client, NULL, user, "admin", NULL); if(ret) return ret; } } /* * No client was specified by the caller, but we have a client * from the default credentials cache. */ if (client == NULL && default_client != NULL) client = default_client; if(id && client && (default_client == NULL || krb5_principal_compare(context, client, default_client) != 0)) { ret = get_kadm_ticket(context, id, client, server_name); if(ret == 0) { *ret_cache = id; krb5_free_principal(context, default_client); if (default_client != client) krb5_free_principal(context, client); return 0; } if(ccache != NULL) /* couldn't get ticket from cache */ return -1; } /* get creds via AS request */ if(id && (id != ccache)) krb5_cc_close(context, id); if (client != default_client) krb5_free_principal(context, default_client); ret = get_new_cache(context, client, password, prompter, keytab, server_name, ret_cache); krb5_free_principal(context, client); return ret; } static kadm5_ret_t kadm_connect(kadm5_client_context *ctx) { kadm5_ret_t ret; krb5_principal server; krb5_ccache cc; rk_socket_t s = rk_INVALID_SOCKET; struct addrinfo *ai, *a; struct addrinfo hints; int error; char portstr[NI_MAXSERV]; char *hostname, *slash; char *service_name; krb5_context context = ctx->context; memset (&hints, 0, sizeof(hints)); hints.ai_socktype = SOCK_STREAM; hints.ai_protocol = IPPROTO_TCP; snprintf (portstr, sizeof(portstr), "%u", ntohs(ctx->kadmind_port)); hostname = ctx->admin_server; slash = strchr (hostname, '/'); if (slash != NULL) hostname = slash + 1; error = getaddrinfo (hostname, portstr, &hints, &ai); if (error) { krb5_clear_error_message(context); return KADM5_BAD_SERVER_NAME; } for (a = ai; a != NULL; a = a->ai_next) { s = socket (a->ai_family, a->ai_socktype, a->ai_protocol); if (s < 0) continue; if (connect (s, a->ai_addr, a->ai_addrlen) < 0) { krb5_clear_error_message(context); krb5_warn (context, errno, "connect(%s)", hostname); rk_closesocket (s); continue; } break; } if (a == NULL) { freeaddrinfo (ai); krb5_clear_error_message(context); krb5_warnx (context, "failed to contact %s", hostname); return KADM5_FAILURE; } ret = _kadm5_c_get_cred_cache(context, ctx->client_name, ctx->service_name, NULL, ctx->prompter, ctx->keytab, ctx->ccache, &cc); if(ret) { freeaddrinfo (ai); rk_closesocket(s); return ret; } if (ctx->realm) asprintf(&service_name, "%s@%s", KADM5_ADMIN_SERVICE, ctx->realm); else asprintf(&service_name, "%s", KADM5_ADMIN_SERVICE); if (service_name == NULL) { freeaddrinfo (ai); rk_closesocket(s); krb5_clear_error_message(context); return ENOMEM; } ret = krb5_parse_name(context, service_name, &server); free(service_name); if(ret) { freeaddrinfo (ai); if(ctx->ccache == NULL) krb5_cc_close(context, cc); rk_closesocket(s); return ret; } ctx->ac = NULL; ret = krb5_sendauth(context, &ctx->ac, &s, KADMIN_APPL_VERSION, NULL, server, AP_OPTS_MUTUAL_REQUIRED, NULL, NULL, cc, NULL, NULL, NULL); if(ret == 0) { krb5_data params; kadm5_config_params p; memset(&p, 0, sizeof(p)); if(ctx->realm) { p.mask |= KADM5_CONFIG_REALM; p.realm = ctx->realm; } ret = _kadm5_marshal_params(context, &p, ¶ms); ret = krb5_write_priv_message(context, ctx->ac, &s, ¶ms); krb5_data_free(¶ms); if(ret) { freeaddrinfo (ai); rk_closesocket(s); if(ctx->ccache == NULL) krb5_cc_close(context, cc); return ret; } } else if(ret == KRB5_SENDAUTH_BADAPPLVERS) { rk_closesocket(s); s = socket (a->ai_family, a->ai_socktype, a->ai_protocol); if (s < 0) { freeaddrinfo (ai); krb5_clear_error_message(context); return errno; } if (connect (s, a->ai_addr, a->ai_addrlen) < 0) { rk_closesocket (s); freeaddrinfo (ai); krb5_clear_error_message(context); return errno; } ret = krb5_sendauth(context, &ctx->ac, &s, KADMIN_OLD_APPL_VERSION, NULL, server, AP_OPTS_MUTUAL_REQUIRED, NULL, NULL, cc, NULL, NULL, NULL); } freeaddrinfo (ai); if(ret) { rk_closesocket(s); return ret; } krb5_free_principal(context, server); if(ctx->ccache == NULL) krb5_cc_close(context, cc); ctx->sock = s; return 0; } kadm5_ret_t _kadm5_connect(void *handle) { kadm5_client_context *ctx = handle; if(ctx->sock == -1) return kadm_connect(ctx); return 0; } static kadm5_ret_t kadm5_c_init_with_context(krb5_context context, const char *client_name, const char *password, krb5_prompter_fct prompter, const char *keytab, krb5_ccache ccache, const char *service_name, kadm5_config_params *realm_params, unsigned long struct_version, unsigned long api_version, void **server_handle) { kadm5_ret_t ret; - kadm5_client_context *ctx; + kadm5_client_context *ctx = NULL; krb5_ccache cc; ret = _kadm5_c_init_context(&ctx, realm_params, context); if(ret) return ret; if(password != NULL && *password != '\0') { ret = _kadm5_c_get_cred_cache(context, client_name, service_name, password, prompter, keytab, ccache, &cc); if(ret) return ret; /* XXX */ ccache = cc; } if (client_name != NULL) ctx->client_name = strdup(client_name); else ctx->client_name = NULL; if (service_name != NULL) ctx->service_name = strdup(service_name); else ctx->service_name = NULL; ctx->prompter = prompter; ctx->keytab = keytab; ctx->ccache = ccache; /* maybe we should copy the params here */ ctx->sock = -1; *server_handle = ctx; return 0; } static kadm5_ret_t init_context(const char *client_name, const char *password, krb5_prompter_fct prompter, const char *keytab, krb5_ccache ccache, const char *service_name, kadm5_config_params *realm_params, unsigned long struct_version, unsigned long api_version, void **server_handle) { krb5_context context; kadm5_ret_t ret; kadm5_server_context *ctx; ret = krb5_init_context(&context); if (ret) return ret; ret = kadm5_c_init_with_context(context, client_name, password, prompter, keytab, ccache, service_name, realm_params, struct_version, api_version, server_handle); if(ret){ krb5_free_context(context); return ret; } ctx = *server_handle; ctx->my_context = 1; return 0; } kadm5_ret_t kadm5_c_init_with_password_ctx(krb5_context context, const char *client_name, const char *password, const char *service_name, kadm5_config_params *realm_params, unsigned long struct_version, unsigned long api_version, void **server_handle) { return kadm5_c_init_with_context(context, client_name, password, krb5_prompter_posix, NULL, NULL, service_name, realm_params, struct_version, api_version, server_handle); } kadm5_ret_t kadm5_c_init_with_password(const char *client_name, const char *password, const char *service_name, kadm5_config_params *realm_params, unsigned long struct_version, unsigned long api_version, void **server_handle) { return init_context(client_name, password, krb5_prompter_posix, NULL, NULL, service_name, realm_params, struct_version, api_version, server_handle); } kadm5_ret_t kadm5_c_init_with_skey_ctx(krb5_context context, const char *client_name, const char *keytab, const char *service_name, kadm5_config_params *realm_params, unsigned long struct_version, unsigned long api_version, void **server_handle) { return kadm5_c_init_with_context(context, client_name, NULL, NULL, keytab, NULL, service_name, realm_params, struct_version, api_version, server_handle); } kadm5_ret_t kadm5_c_init_with_skey(const char *client_name, const char *keytab, const char *service_name, kadm5_config_params *realm_params, unsigned long struct_version, unsigned long api_version, void **server_handle) { return init_context(client_name, NULL, NULL, keytab, NULL, service_name, realm_params, struct_version, api_version, server_handle); } kadm5_ret_t kadm5_c_init_with_creds_ctx(krb5_context context, const char *client_name, krb5_ccache ccache, const char *service_name, kadm5_config_params *realm_params, unsigned long struct_version, unsigned long api_version, void **server_handle) { return kadm5_c_init_with_context(context, client_name, NULL, NULL, NULL, ccache, service_name, realm_params, struct_version, api_version, server_handle); } kadm5_ret_t kadm5_c_init_with_creds(const char *client_name, krb5_ccache ccache, const char *service_name, kadm5_config_params *realm_params, unsigned long struct_version, unsigned long api_version, void **server_handle) { return init_context(client_name, NULL, NULL, NULL, ccache, service_name, realm_params, struct_version, api_version, server_handle); } #if 0 kadm5_ret_t kadm5_init(char *client_name, char *pass, char *service_name, kadm5_config_params *realm_params, unsigned long struct_version, unsigned long api_version, void **server_handle) { } #endif diff --git a/crypto/heimdal/lib/kadm5/ipropd_master.c b/crypto/heimdal/lib/kadm5/ipropd_master.c index e05e9ee55864..10d4202e0791 100644 --- a/crypto/heimdal/lib/kadm5/ipropd_master.c +++ b/crypto/heimdal/lib/kadm5/ipropd_master.c @@ -1,1030 +1,1033 @@ /* * 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 "iprop.h" #include static krb5_log_facility *log_facility; const char *slave_stats_file; const char *slave_time_missing = "2 min"; const char *slave_time_gone = "5 min"; static int time_before_missing; static int time_before_gone; const char *master_hostname; static krb5_socket_t make_signal_socket (krb5_context context) { #ifndef NO_UNIX_SOCKETS struct sockaddr_un addr; const char *fn; krb5_socket_t fd; fn = kadm5_log_signal_socket(context); fd = socket (AF_UNIX, SOCK_DGRAM, 0); if (fd < 0) krb5_err (context, 1, errno, "socket AF_UNIX"); memset (&addr, 0, sizeof(addr)); addr.sun_family = AF_UNIX; strlcpy (addr.sun_path, fn, sizeof(addr.sun_path)); unlink (addr.sun_path); if (bind (fd, (struct sockaddr *)&addr, sizeof(addr)) < 0) krb5_err (context, 1, errno, "bind %s", addr.sun_path); return fd; #else struct addrinfo *ai = NULL; krb5_socket_t fd; kadm5_log_signal_socket_info(context, 1, &ai); fd = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); if (rk_IS_BAD_SOCKET(fd)) krb5_err (context, 1, rk_SOCK_ERRNO, "socket AF=%d", ai->ai_family); if (rk_IS_SOCKET_ERROR( bind (fd, ai->ai_addr, ai->ai_addrlen) )) krb5_err (context, 1, rk_SOCK_ERRNO, "bind"); return fd; #endif } static krb5_socket_t make_listen_socket (krb5_context context, const char *port_str) { krb5_socket_t fd; int one = 1; struct sockaddr_in addr; fd = socket (AF_INET, SOCK_STREAM, 0); if (rk_IS_BAD_SOCKET(fd)) krb5_err (context, 1, rk_SOCK_ERRNO, "socket AF_INET"); setsockopt (fd, SOL_SOCKET, SO_REUSEADDR, (void *)&one, sizeof(one)); memset (&addr, 0, sizeof(addr)); addr.sin_family = AF_INET; if (port_str) { addr.sin_port = krb5_getportbyname (context, port_str, "tcp", 0); if (addr.sin_port == 0) { char *ptr; long port; port = strtol (port_str, &ptr, 10); if (port == 0 && ptr == port_str) krb5_errx (context, 1, "bad port `%s'", port_str); addr.sin_port = htons(port); } } else { addr.sin_port = krb5_getportbyname (context, IPROP_SERVICE, "tcp", IPROP_PORT); } if(bind(fd, (struct sockaddr *)&addr, sizeof(addr)) < 0) krb5_err (context, 1, errno, "bind"); if (listen(fd, SOMAXCONN) < 0) krb5_err (context, 1, errno, "listen"); return fd; } static krb5_socket_t make_listen6_socket (krb5_context context, const char *port_str) { krb5_socket_t fd; int one = 1; struct sockaddr_in6 addr; fd = socket (AF_INET6, SOCK_STREAM, 0); if (rk_IS_BAD_SOCKET(fd)) krb5_err (context, 1, rk_SOCK_ERRNO, "socket AF_INET6"); setsockopt (fd, SOL_SOCKET, SO_REUSEADDR, (void *)&one, sizeof(one)); memset (&addr, 0, sizeof(addr)); addr.sin6_family = AF_INET6; if (port_str) { addr.sin6_port = krb5_getportbyname (context, port_str, "tcp", 0); if (addr.sin6_port == 0) { char *ptr; long port; port = strtol (port_str, &ptr, 10); if (port == 0 && ptr == port_str) krb5_errx (context, 1, "bad port `%s'", port_str); addr.sin6_port = htons(port); } } else { addr.sin6_port = krb5_getportbyname (context, IPROP_SERVICE, "tcp", IPROP_PORT); } if(bind(fd, (struct sockaddr *)&addr, sizeof(addr)) < 0) krb5_err (context, 1, errno, "bind6"); if (listen(fd, SOMAXCONN) < 0) krb5_err (context, 1, errno, "listen6"); return fd; } #ifndef _SOCKADDR_UNION #define _SOCKADDR_UNION union sockaddr_union { struct sockaddr sa; struct sockaddr_in sin; struct sockaddr_in6 sin6; }; #endif /* _SOCKADDR_UNION */ struct slave { krb5_socket_t fd; union sockaddr_union addr; char *name; krb5_auth_context ac; uint32_t version; time_t seen; unsigned long flags; #define SLAVE_F_DEAD 0x1 #define SLAVE_F_AYT 0x2 struct slave *next; }; typedef struct slave slave; static int check_acl (krb5_context context, const char *name) { const char *fn; FILE *fp; char buf[256]; int ret = 1; char *slavefile = NULL; if (asprintf(&slavefile, "%s/slaves", hdb_db_dir(context)) == -1 || slavefile == NULL) errx(1, "out of memory"); fn = krb5_config_get_string_default(context, NULL, slavefile, "kdc", "iprop-acl", NULL); fp = fopen (fn, "r"); free(slavefile); if (fp == NULL) return 1; while (fgets(buf, sizeof(buf), fp) != NULL) { buf[strcspn(buf, "\r\n")] = '\0'; if (strcmp (buf, name) == 0) { ret = 0; break; } } fclose (fp); return ret; } static void slave_seen(slave *s) { s->flags &= ~SLAVE_F_AYT; s->seen = time(NULL); } static int slave_missing_p (slave *s) { if (time(NULL) > s->seen + time_before_missing) return 1; return 0; } static int slave_gone_p (slave *s) { if (time(NULL) > s->seen + time_before_gone) return 1; return 0; } static void slave_dead(krb5_context context, slave *s) { krb5_warnx(context, "slave %s dead", s->name); if (!rk_IS_BAD_SOCKET(s->fd)) { rk_closesocket (s->fd); s->fd = rk_INVALID_SOCKET; } s->flags |= SLAVE_F_DEAD; slave_seen(s); } static void remove_slave (krb5_context context, slave *s, slave **root) { slave **p; if (!rk_IS_BAD_SOCKET(s->fd)) rk_closesocket (s->fd); if (s->name) free (s->name); if (s->ac) krb5_auth_con_free (context, s->ac); for (p = root; *p; p = &(*p)->next) if (*p == s) { *p = s->next; break; } free (s); } static void add_slave (krb5_context context, krb5_keytab keytab, slave **root, krb5_socket_t fd) { krb5_principal server; krb5_error_code ret; slave *s; socklen_t addr_len; krb5_ticket *ticket = NULL; char hostname[128]; s = malloc(sizeof(*s)); if (s == NULL) { krb5_warnx (context, "add_slave: no memory"); return; } s->name = NULL; s->ac = NULL; addr_len = sizeof(s->addr.sin6); s->fd = accept (fd, (struct sockaddr *)&s->addr.sa, &addr_len); if (rk_IS_BAD_SOCKET(s->fd)) { krb5_warn (context, rk_SOCK_ERRNO, "accept"); goto error; } if (master_hostname) strlcpy(hostname, master_hostname, sizeof(hostname)); else gethostname(hostname, sizeof(hostname)); ret = krb5_sname_to_principal (context, hostname, IPROP_NAME, KRB5_NT_SRV_HST, &server); if (ret) { krb5_warn (context, ret, "krb5_sname_to_principal"); goto error; } ret = krb5_recvauth (context, &s->ac, &s->fd, IPROP_VERSION, server, 0, keytab, &ticket); krb5_free_principal (context, server); if (ret) { krb5_warn (context, ret, "krb5_recvauth"); goto error; } ret = krb5_unparse_name (context, ticket->client, &s->name); krb5_free_ticket (context, ticket); if (ret) { krb5_warn (context, ret, "krb5_unparse_name"); goto error; } if (check_acl (context, s->name)) { krb5_warnx (context, "%s not in acl", s->name); goto error; } { slave *l = *root; while (l) { if (strcmp(l->name, s->name) == 0) break; l = l->next; } if (l) { if (l->flags & SLAVE_F_DEAD) { remove_slave(context, l, root); } else { krb5_warnx (context, "second connection from %s", s->name); goto error; } } } krb5_warnx (context, "connection from %s", s->name); s->version = 0; s->flags = 0; slave_seen(s); s->next = *root; *root = s; return; error: remove_slave(context, s, root); } struct prop_context { krb5_auth_context auth_context; krb5_socket_t fd; }; static int prop_one (krb5_context context, HDB *db, hdb_entry_ex *entry, void *v) { krb5_error_code ret; krb5_storage *sp; krb5_data data; struct slave *s = (struct slave *)v; ret = hdb_entry2value (context, &entry->entry, &data); if (ret) return ret; ret = krb5_data_realloc (&data, data.length + 4); if (ret) { krb5_data_free (&data); return ret; } memmove ((char *)data.data + 4, data.data, data.length - 4); sp = krb5_storage_from_data(&data); if (sp == NULL) { krb5_data_free (&data); return ENOMEM; } krb5_store_int32(sp, ONE_PRINC); krb5_storage_free(sp); ret = krb5_write_priv_message (context, s->ac, &s->fd, &data); krb5_data_free (&data); return ret; } static int send_complete (krb5_context context, slave *s, const char *database, uint32_t current_version) { krb5_error_code ret; krb5_storage *sp; HDB *db; krb5_data data; char buf[8]; ret = hdb_create (context, &db, database); if (ret) krb5_err (context, 1, ret, "hdb_create: %s", database); ret = db->hdb_open (context, db, O_RDONLY, 0); if (ret) krb5_err (context, 1, ret, "db->open"); sp = krb5_storage_from_mem (buf, 4); if (sp == NULL) krb5_errx (context, 1, "krb5_storage_from_mem"); krb5_store_int32 (sp, TELL_YOU_EVERYTHING); krb5_storage_free (sp); data.data = buf; data.length = 4; ret = krb5_write_priv_message(context, s->ac, &s->fd, &data); if (ret) { krb5_warn (context, ret, "krb5_write_priv_message"); slave_dead(context, s); return ret; } ret = hdb_foreach (context, db, HDB_F_ADMIN_DATA, prop_one, s); if (ret) { krb5_warn (context, ret, "hdb_foreach"); slave_dead(context, s); return ret; } (*db->hdb_close)(context, db); (*db->hdb_destroy)(context, db); sp = krb5_storage_from_mem (buf, 8); if (sp == NULL) krb5_errx (context, 1, "krb5_storage_from_mem"); krb5_store_int32 (sp, NOW_YOU_HAVE); krb5_store_int32 (sp, current_version); krb5_storage_free (sp); data.length = 8; s->version = current_version; ret = krb5_write_priv_message(context, s->ac, &s->fd, &data); if (ret) { slave_dead(context, s); krb5_warn (context, ret, "krb5_write_priv_message"); return ret; } slave_seen(s); return 0; } static int send_are_you_there (krb5_context context, slave *s) { krb5_storage *sp; krb5_data data; char buf[4]; int ret; if (s->flags & (SLAVE_F_DEAD|SLAVE_F_AYT)) return 0; krb5_warnx(context, "slave %s missing, sending AYT", s->name); s->flags |= SLAVE_F_AYT; data.data = buf; data.length = 4; sp = krb5_storage_from_mem (buf, 4); if (sp == NULL) { krb5_warnx (context, "are_you_there: krb5_data_alloc"); slave_dead(context, s); return 1; } krb5_store_int32 (sp, ARE_YOU_THERE); krb5_storage_free (sp); ret = krb5_write_priv_message(context, s->ac, &s->fd, &data); if (ret) { krb5_warn (context, ret, "are_you_there: krb5_write_priv_message"); slave_dead(context, s); return 1; } return 0; } static int send_diffs (krb5_context context, slave *s, int log_fd, const char *database, uint32_t current_version) { krb5_storage *sp; uint32_t ver; time_t timestamp; enum kadm_ops op; uint32_t len; off_t right, left; krb5_data data; int ret = 0; if (s->version == current_version) { krb5_warnx(context, "slave %s in sync already at version %ld", s->name, (long)s->version); return 0; } if (s->flags & SLAVE_F_DEAD) return 0; /* if slave is a fresh client, starting over */ if (s->version == 0) { krb5_warnx(context, "sending complete log to fresh slave %s", s->name); return send_complete (context, s, database, current_version); } sp = kadm5_log_goto_end (log_fd); right = krb5_storage_seek(sp, 0, SEEK_CUR); for (;;) { ret = kadm5_log_previous (context, sp, &ver, ×tamp, &op, &len); if (ret) krb5_err(context, 1, ret, "send_diffs: failed to find previous entry"); left = krb5_storage_seek(sp, -16, SEEK_CUR); if (ver == s->version) return 0; if (ver == s->version + 1) break; if (left == 0) { krb5_storage_free(sp); krb5_warnx(context, "slave %s (version %lu) out of sync with master " "(first version in log %lu), sending complete database", s->name, (unsigned long)s->version, (unsigned long)ver); return send_complete (context, s, database, current_version); } } krb5_warnx(context, "syncing slave %s from version %lu to version %lu", s->name, (unsigned long)s->version, (unsigned long)current_version); ret = krb5_data_alloc (&data, right - left + 4); if (ret) { krb5_storage_free(sp); krb5_warn (context, ret, "send_diffs: krb5_data_alloc"); slave_dead(context, s); return 1; } krb5_storage_read (sp, (char *)data.data + 4, data.length - 4); krb5_storage_free(sp); sp = krb5_storage_from_data (&data); if (sp == NULL) { krb5_warnx (context, "send_diffs: krb5_storage_from_data"); slave_dead(context, s); return 1; } krb5_store_int32 (sp, FOR_YOU); krb5_storage_free(sp); ret = krb5_write_priv_message(context, s->ac, &s->fd, &data); krb5_data_free(&data); if (ret) { krb5_warn (context, ret, "send_diffs: krb5_write_priv_message"); slave_dead(context, s); return 1; } slave_seen(s); s->version = current_version; return 0; } static int process_msg (krb5_context context, slave *s, int log_fd, const char *database, uint32_t current_version) { int ret = 0; krb5_data out; krb5_storage *sp; int32_t tmp; ret = krb5_read_priv_message(context, s->ac, &s->fd, &out); if(ret) { krb5_warn (context, ret, "error reading message from %s", s->name); return 1; } sp = krb5_storage_from_mem (out.data, out.length); if (sp == NULL) { krb5_warnx (context, "process_msg: no memory"); krb5_data_free (&out); return 1; } if (krb5_ret_int32 (sp, &tmp) != 0) { krb5_warnx (context, "process_msg: client send too short command"); krb5_data_free (&out); return 1; } switch (tmp) { case I_HAVE : ret = krb5_ret_int32 (sp, &tmp); if (ret != 0) { krb5_warnx (context, "process_msg: client send too I_HAVE data"); break; } /* new started slave that have old log */ if (s->version == 0 && tmp != 0) { if (current_version < (uint32_t)tmp) { krb5_warnx (context, "Slave %s (version %lu) have later version " "the master (version %lu) OUT OF SYNC", s->name, (unsigned long)tmp, (unsigned long)current_version); } s->version = tmp; } if ((uint32_t)tmp < s->version) { krb5_warnx (context, "Slave claims to not have " "version we already sent to it"); } else { ret = send_diffs (context, s, log_fd, database, current_version); } break; case I_AM_HERE : break; case ARE_YOU_THERE: case FOR_YOU : default : krb5_warnx (context, "Ignoring command %d", tmp); break; } krb5_data_free (&out); krb5_storage_free (sp); slave_seen(s); return ret; } #define SLAVE_NAME "Name" #define SLAVE_ADDRESS "Address" #define SLAVE_VERSION "Version" #define SLAVE_STATUS "Status" #define SLAVE_SEEN "Last Seen" static FILE * open_stats(krb5_context context) { char *statfile = NULL; const char *fn; FILE *f; if (slave_stats_file) fn = slave_stats_file; else { asprintf(&statfile, "%s/slaves-stats", hdb_db_dir(context)); fn = krb5_config_get_string_default(context, NULL, statfile, "kdc", "iprop-stats", NULL); } f = fopen(fn, "w"); if (statfile) free(statfile); return f; } static void write_master_down(krb5_context context) { char str[100]; time_t t = time(NULL); FILE *fp; fp = open_stats(context); if (fp == NULL) return; krb5_format_time(context, t, str, sizeof(str), TRUE); fprintf(fp, "master down at %s\n", str); fclose(fp); } static void write_stats(krb5_context context, slave *slaves, uint32_t current_version) { char str[100]; rtbl_t tbl; time_t t = time(NULL); FILE *fp; fp = open_stats(context); if (fp == NULL) return; krb5_format_time(context, t, str, sizeof(str), TRUE); fprintf(fp, "Status for slaves, last updated: %s\n\n", str); fprintf(fp, "Master version: %lu\n\n", (unsigned long)current_version); tbl = rtbl_create(); if (tbl == NULL) { fclose(fp); return; } rtbl_add_column(tbl, SLAVE_NAME, 0); rtbl_add_column(tbl, SLAVE_ADDRESS, 0); rtbl_add_column(tbl, SLAVE_VERSION, RTBL_ALIGN_RIGHT); rtbl_add_column(tbl, SLAVE_STATUS, 0); rtbl_add_column(tbl, SLAVE_SEEN, 0); rtbl_set_prefix(tbl, " "); rtbl_set_column_prefix(tbl, SLAVE_NAME, ""); while (slaves) { krb5_address addr; krb5_error_code ret; rtbl_add_column_entry(tbl, SLAVE_NAME, slaves->name); ret = krb5_sockaddr2address (context, (struct sockaddr*)&slaves->addr.sa, &addr); if(ret == 0) { krb5_print_address(&addr, str, sizeof(str), NULL); krb5_free_address(context, &addr); rtbl_add_column_entry(tbl, SLAVE_ADDRESS, str); } else rtbl_add_column_entry(tbl, SLAVE_ADDRESS, ""); snprintf(str, sizeof(str), "%u", (unsigned)slaves->version); rtbl_add_column_entry(tbl, SLAVE_VERSION, str); if (slaves->flags & SLAVE_F_DEAD) rtbl_add_column_entry(tbl, SLAVE_STATUS, "Down"); else rtbl_add_column_entry(tbl, SLAVE_STATUS, "Up"); ret = krb5_format_time(context, slaves->seen, str, sizeof(str), TRUE); - rtbl_add_column_entry(tbl, SLAVE_SEEN, str); + if (ret) + rtbl_add_column_entry(tbl, SLAVE_SEEN, ""); + else + rtbl_add_column_entry(tbl, SLAVE_SEEN, str); slaves = slaves->next; } rtbl_format(tbl, fp); rtbl_destroy(tbl); fclose(fp); } static char sHDB[] = "HDB:"; static char *realm; static int version_flag; static int help_flag; static char *keytab_str = sHDB; static char *database; static char *config_file; static char *port_str; #ifdef SUPPORT_DETACH static int detach_from_console = 0; #endif static struct getargs args[] = { { "config-file", 'c', arg_string, &config_file, NULL, NULL }, { "realm", 'r', arg_string, &realm, NULL, NULL }, { "keytab", 'k', arg_string, &keytab_str, "keytab to get authentication from", "kspec" }, { "database", 'd', arg_string, &database, "database", "file"}, { "slave-stats-file", 0, arg_string, rk_UNCONST(&slave_stats_file), "file for slave status information", "file"}, { "time-missing", 0, arg_string, rk_UNCONST(&slave_time_missing), "time before slave is polled for presence", "time"}, { "time-gone", 0, arg_string, rk_UNCONST(&slave_time_gone), "time of inactivity after which a slave is considered gone", "time"}, { "port", 0, arg_string, &port_str, "port ipropd will listen to", "port"}, #ifdef SUPPORT_DETACH { "detach", 0, arg_flag, &detach_from_console, "detach from console", NULL }, #endif { "hostname", 0, arg_string, rk_UNCONST(&master_hostname), "hostname of master (if not same as hostname)", "hostname" }, { "version", 0, arg_flag, &version_flag, NULL, NULL }, { "help", 0, arg_flag, &help_flag, NULL, NULL } }; static int num_args = sizeof(args) / sizeof(args[0]); int main(int argc, char **argv) { krb5_error_code ret; krb5_context context; void *kadm_handle; kadm5_server_context *server_context; kadm5_config_params conf; krb5_socket_t signal_fd, listen_fd, listen6_fd; int log_fd; slave *slaves = NULL; uint32_t current_version = 0, old_version = 0; krb5_keytab keytab; int optidx; char **files; optidx = krb5_program_setup(&context, argc, argv, args, num_args, NULL); if(help_flag) krb5_std_usage(0, args, num_args); if(version_flag) { print_version(NULL); exit(0); } setup_signal(); if (config_file == NULL) { asprintf(&config_file, "%s/kdc.conf", hdb_db_dir(context)); if (config_file == NULL) errx(1, "out of memory"); } ret = krb5_prepend_config_files_default(config_file, &files); if (ret) krb5_err(context, 1, ret, "getting configuration files"); ret = krb5_set_config_files(context, files); krb5_free_config_files(files); if (ret) krb5_err(context, 1, ret, "reading configuration files"); time_before_gone = parse_time (slave_time_gone, "s"); if (time_before_gone < 0) krb5_errx (context, 1, "couldn't parse time: %s", slave_time_gone); time_before_missing = parse_time (slave_time_missing, "s"); if (time_before_missing < 0) krb5_errx (context, 1, "couldn't parse time: %s", slave_time_missing); #ifdef SUPPORT_DETACH if (detach_from_console) daemon(0, 0); #endif pidfile (NULL); krb5_openlog (context, "ipropd-master", &log_facility); krb5_set_warn_dest(context, log_facility); ret = krb5_kt_register(context, &hdb_kt_ops); if(ret) krb5_err(context, 1, ret, "krb5_kt_register"); ret = krb5_kt_resolve(context, keytab_str, &keytab); if(ret) krb5_err(context, 1, ret, "krb5_kt_resolve: %s", keytab_str); memset(&conf, 0, sizeof(conf)); if(realm) { conf.mask |= KADM5_CONFIG_REALM; conf.realm = realm; } ret = kadm5_init_with_skey_ctx (context, KADM5_ADMIN_SERVICE, NULL, KADM5_ADMIN_SERVICE, &conf, 0, 0, &kadm_handle); if (ret) krb5_err (context, 1, ret, "kadm5_init_with_password_ctx"); server_context = (kadm5_server_context *)kadm_handle; log_fd = open (server_context->log_context.log_file, O_RDONLY, 0); if (log_fd < 0) krb5_err (context, 1, errno, "open %s", server_context->log_context.log_file); signal_fd = make_signal_socket (context); listen_fd = make_listen_socket (context, port_str); listen6_fd = make_listen6_socket (context, port_str); kadm5_log_get_version_fd (log_fd, ¤t_version); krb5_warnx(context, "ipropd-master started at version: %lu", (unsigned long)current_version); while(exit_flag == 0){ slave *p; fd_set readset; int max_fd = 0; struct timeval to = {30, 0}; uint32_t vers; #ifndef NO_LIMIT_FD_SETSIZE if (signal_fd >= FD_SETSIZE || listen_fd >= FD_SETSIZE || listen6_fd >= FD_SETSIZE) krb5_errx (context, 1, "fd too large"); #endif FD_ZERO(&readset); FD_SET(signal_fd, &readset); max_fd = max(max_fd, signal_fd); FD_SET(listen_fd, &readset); max_fd = max(max_fd, listen_fd); FD_SET(listen6_fd, &readset); max_fd = max(max_fd, listen6_fd); for (p = slaves; p != NULL; p = p->next) { if (p->flags & SLAVE_F_DEAD) continue; FD_SET(p->fd, &readset); max_fd = max(max_fd, p->fd); } ret = select (max_fd + 1, &readset, NULL, NULL, &to); if (ret < 0) { if (errno == EINTR) continue; else krb5_err (context, 1, errno, "select"); } if (ret == 0) { old_version = current_version; kadm5_log_get_version_fd (log_fd, ¤t_version); if (current_version > old_version) { krb5_warnx(context, "Missed a signal, updating slaves %lu to %lu", (unsigned long)old_version, (unsigned long)current_version); for (p = slaves; p != NULL; p = p->next) { if (p->flags & SLAVE_F_DEAD) continue; send_diffs (context, p, log_fd, database, current_version); } } } if (ret && FD_ISSET(signal_fd, &readset)) { #ifndef NO_UNIX_SOCKETS struct sockaddr_un peer_addr; #else struct sockaddr_storage peer_addr; #endif socklen_t peer_len = sizeof(peer_addr); if(recvfrom(signal_fd, (void *)&vers, sizeof(vers), 0, (struct sockaddr *)&peer_addr, &peer_len) < 0) { krb5_warn (context, errno, "recvfrom"); continue; } --ret; assert(ret >= 0); old_version = current_version; kadm5_log_get_version_fd (log_fd, ¤t_version); if (current_version > old_version) { krb5_warnx(context, "Got a signal, updating slaves %lu to %lu", (unsigned long)old_version, (unsigned long)current_version); for (p = slaves; p != NULL; p = p->next) { if (p->flags & SLAVE_F_DEAD) continue; send_diffs (context, p, log_fd, database, current_version); } } else { krb5_warnx(context, "Got a signal, but no update in log version %lu", (unsigned long)current_version); } } for(p = slaves; p != NULL; p = p->next) { if (p->flags & SLAVE_F_DEAD) continue; if (ret && FD_ISSET(p->fd, &readset)) { --ret; assert(ret >= 0); if(process_msg (context, p, log_fd, database, current_version)) slave_dead(context, p); } else if (slave_gone_p (p)) slave_dead(context, p); else if (slave_missing_p (p)) send_are_you_there (context, p); } if (ret && FD_ISSET(listen6_fd, &readset)) { add_slave (context, keytab, &slaves, listen6_fd); --ret; assert(ret >= 0); } if (ret && FD_ISSET(listen_fd, &readset)) { add_slave (context, keytab, &slaves, listen_fd); --ret; assert(ret >= 0); } write_stats(context, slaves, current_version); } if(exit_flag == SIGINT || exit_flag == SIGTERM) krb5_warnx(context, "%s terminated", getprogname()); #ifdef SIGXCPU else if(exit_flag == SIGXCPU) krb5_warnx(context, "%s CPU time limit exceeded", getprogname()); #endif else krb5_warnx(context, "%s unexpected exit reason: %ld", getprogname(), (long)exit_flag); write_master_down(context); return 0; } diff --git a/crypto/heimdal/lib/kafs/afskrb5.c b/crypto/heimdal/lib/kafs/afskrb5.c index c04f43abbc25..919a6ee63542 100644 --- a/crypto/heimdal/lib/kafs/afskrb5.c +++ b/crypto/heimdal/lib/kafs/afskrb5.c @@ -1,357 +1,355 @@ /* * 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 "kafs_locl.h" struct krb5_kafs_data { krb5_context context; krb5_ccache id; krb5_const_realm realm; }; enum { KAFS_RXKAD_2B_KVNO = 213, KAFS_RXKAD_K5_KVNO = 256 }; static int v5_to_kt(krb5_creds *cred, uid_t uid, struct kafs_token *kt, int local524) { int kvno, ret; kt->ticket = NULL; /* check if des key */ if (cred->session.keyvalue.length != 8) return EINVAL; if (local524) { Ticket t; unsigned char *buf; size_t buf_len; size_t len; kvno = KAFS_RXKAD_2B_KVNO; ret = decode_Ticket(cred->ticket.data, cred->ticket.length, &t, &len); if (ret) return ret; if (t.tkt_vno != 5) return -1; ASN1_MALLOC_ENCODE(EncryptedData, buf, buf_len, &t.enc_part, &len, ret); free_Ticket(&t); if (ret) return ret; if(buf_len != len) { free(buf); return KRB5KRB_ERR_GENERIC; } kt->ticket = buf; kt->ticket_len = buf_len; } else { kvno = KAFS_RXKAD_K5_KVNO; kt->ticket = malloc(cred->ticket.length); if (kt->ticket == NULL) return ENOMEM; kt->ticket_len = cred->ticket.length; memcpy(kt->ticket, cred->ticket.data, kt->ticket_len); - - ret = 0; } /* * Build a struct ClearToken */ kt->ct.AuthHandle = kvno; memcpy(kt->ct.HandShakeKey, cred->session.keyvalue.data, 8); kt->ct.ViceId = uid; kt->ct.BeginTimestamp = cred->times.starttime; kt->ct.EndTimestamp = cred->times.endtime; _kafs_fixup_viceid(&kt->ct, uid); return 0; } static krb5_error_code v5_convert(krb5_context context, krb5_ccache id, krb5_creds *cred, uid_t uid, const char *cell, struct kafs_token *kt) { krb5_error_code ret; char *c, *val; c = strdup(cell); if (c == NULL) return ENOMEM; _kafs_foldup(c, c); krb5_appdefault_string (context, "libkafs", c, "afs-use-524", "2b", &val); free(c); if (strcasecmp(val, "local") == 0 || strcasecmp(val, "2b") == 0) ret = v5_to_kt(cred, uid, kt, 1); else ret = v5_to_kt(cred, uid, kt, 0); free(val); return ret; } /* * */ static int get_cred(struct kafs_data *data, const char *name, const char *inst, const char *realm, uid_t uid, struct kafs_token *kt) { krb5_error_code ret; krb5_creds in_creds, *out_creds; struct krb5_kafs_data *d = data->data; int invalid; memset(&in_creds, 0, sizeof(in_creds)); ret = krb5_make_principal(d->context, &in_creds.server, realm, name, inst, NULL); if(ret) return ret; ret = krb5_cc_get_principal(d->context, d->id, &in_creds.client); if(ret){ krb5_free_principal(d->context, in_creds.server); return ret; } in_creds.session.keytype = ETYPE_DES_CBC_CRC; /* check if des is disable, and in that case enable it for afs */ invalid = krb5_enctype_valid(d->context, in_creds.session.keytype); if (invalid) krb5_enctype_enable(d->context, in_creds.session.keytype); ret = krb5_get_credentials(d->context, 0, d->id, &in_creds, &out_creds); if (ret) { in_creds.session.keytype = ETYPE_DES_CBC_MD5; ret = krb5_get_credentials(d->context, 0, d->id, &in_creds, &out_creds); } if (invalid) krb5_enctype_disable(d->context, in_creds.session.keytype); krb5_free_principal(d->context, in_creds.server); krb5_free_principal(d->context, in_creds.client); if(ret) return ret; ret = v5_convert(d->context, d->id, out_creds, uid, (inst != NULL && inst[0] != '\0') ? inst : realm, kt); krb5_free_creds(d->context, out_creds); return ret; } static const char * get_error(struct kafs_data *data, int error) { struct krb5_kafs_data *d = data->data; return krb5_get_error_message(d->context, error); } static void free_error(struct kafs_data *data, const char *str) { struct krb5_kafs_data *d = data->data; krb5_free_error_message(d->context, str); } static krb5_error_code afslog_uid_int(struct kafs_data *data, const char *cell, const char *rh, uid_t uid, const char *homedir) { krb5_error_code ret; struct kafs_token kt; krb5_principal princ; const char *trealm; /* ticket realm */ struct krb5_kafs_data *d = data->data; if (cell == 0 || cell[0] == 0) return _kafs_afslog_all_local_cells (data, uid, homedir); ret = krb5_cc_get_principal (d->context, d->id, &princ); if (ret) return ret; trealm = krb5_principal_get_realm (d->context, princ); kt.ticket = NULL; ret = _kafs_get_cred(data, cell, d->realm, trealm, uid, &kt); krb5_free_principal (d->context, princ); if(ret == 0) { ret = kafs_settoken_rxkad(cell, &kt.ct, kt.ticket, kt.ticket_len); free(kt.ticket); } return ret; } static char * get_realm(struct kafs_data *data, const char *host) { struct krb5_kafs_data *d = data->data; krb5_realm *realms; char *r; if(krb5_get_host_realm(d->context, host, &realms)) return NULL; r = strdup(realms[0]); krb5_free_host_realm(d->context, realms); return r; } krb5_error_code krb5_afslog_uid_home(krb5_context context, krb5_ccache id, const char *cell, krb5_const_realm realm, uid_t uid, const char *homedir) { struct kafs_data kd; struct krb5_kafs_data d; krb5_error_code ret; kd.name = "krb5"; kd.afslog_uid = afslog_uid_int; kd.get_cred = get_cred; kd.get_realm = get_realm; kd.get_error = get_error; kd.free_error = free_error; kd.data = &d; if (context == NULL) { ret = krb5_init_context(&d.context); if (ret) return ret; } else d.context = context; if (id == NULL) { ret = krb5_cc_default(d.context, &d.id); if (ret) goto out; } else d.id = id; d.realm = realm; ret = afslog_uid_int(&kd, cell, 0, uid, homedir); if (id == NULL) krb5_cc_close(context, d.id); out: if (context == NULL) krb5_free_context(d.context); return ret; } krb5_error_code krb5_afslog_uid(krb5_context context, krb5_ccache id, const char *cell, krb5_const_realm realm, uid_t uid) { return krb5_afslog_uid_home (context, id, cell, realm, uid, NULL); } krb5_error_code krb5_afslog(krb5_context context, krb5_ccache id, const char *cell, krb5_const_realm realm) { return krb5_afslog_uid (context, id, cell, realm, getuid()); } krb5_error_code krb5_afslog_home(krb5_context context, krb5_ccache id, const char *cell, krb5_const_realm realm, const char *homedir) { return krb5_afslog_uid_home (context, id, cell, realm, getuid(), homedir); } /* * */ krb5_error_code krb5_realm_of_cell(const char *cell, char **realm) { struct kafs_data kd; kd.name = "krb5"; kd.get_realm = get_realm; kd.get_error = get_error; kd.free_error = free_error; return _kafs_realm_of_cell(&kd, cell, realm); } /* * */ int kafs_settoken5(krb5_context context, const char *cell, uid_t uid, krb5_creds *cred) { struct kafs_token kt; int ret; ret = v5_convert(context, NULL, cred, uid, cell, &kt); if (ret) return ret; ret = kafs_settoken_rxkad(cell, &kt.ct, kt.ticket, kt.ticket_len); free(kt.ticket); return ret; } diff --git a/crypto/heimdal/lib/krb5/acl.c b/crypto/heimdal/lib/krb5/acl.c index c94aae361b8e..cf8869a5e07b 100644 --- a/crypto/heimdal/lib/krb5/acl.c +++ b/crypto/heimdal/lib/krb5/acl.c @@ -1,295 +1,295 @@ /* * Copyright (c) 2000 - 2002, 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 "krb5_locl.h" #include struct acl_field { enum { acl_string, acl_fnmatch, acl_retval } type; union { const char *cstr; char **retv; } u; struct acl_field *next, **last; }; static void free_retv(struct acl_field *acl) { while(acl != NULL) { if (acl->type == acl_retval) { if (*acl->u.retv) free(*acl->u.retv); *acl->u.retv = NULL; } acl = acl->next; } } static void acl_free_list(struct acl_field *acl, int retv) { struct acl_field *next; if (retv) free_retv(acl); while(acl != NULL) { next = acl->next; free(acl); acl = next; } } static krb5_error_code acl_parse_format(krb5_context context, struct acl_field **acl_ret, const char *format, va_list ap) { const char *p; struct acl_field *acl = NULL, *tmp; for(p = format; *p != '\0'; p++) { tmp = malloc(sizeof(*tmp)); if(tmp == NULL) { krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", "")); acl_free_list(acl, 0); return ENOMEM; } if(*p == 's') { tmp->type = acl_string; tmp->u.cstr = va_arg(ap, const char*); } else if(*p == 'f') { tmp->type = acl_fnmatch; tmp->u.cstr = va_arg(ap, const char*); } else if(*p == 'r') { tmp->type = acl_retval; tmp->u.retv = va_arg(ap, char **); *tmp->u.retv = NULL; } else { krb5_set_error_message(context, EINVAL, N_("Unknown format specifier %c while " "parsing ACL", "specifier"), *p); acl_free_list(acl, 0); free(tmp); return EINVAL; } tmp->next = NULL; if(acl == NULL) acl = tmp; else *acl->last = tmp; acl->last = &tmp->next; } *acl_ret = acl; return 0; } static krb5_boolean acl_match_field(krb5_context context, const char *string, struct acl_field *field) { if(field->type == acl_string) { return !strcmp(field->u.cstr, string); } else if(field->type == acl_fnmatch) { return !fnmatch(field->u.cstr, string, 0); } else if(field->type == acl_retval) { *field->u.retv = strdup(string); return TRUE; } return FALSE; } static krb5_boolean acl_match_acl(krb5_context context, struct acl_field *acl, const char *string) { char buf[256]; while(strsep_copy(&string, " \t", buf, sizeof(buf)) != -1) { if(buf[0] == '\0') continue; /* skip ws */ if (acl == NULL) return FALSE; if(!acl_match_field(context, buf, acl)) { return FALSE; } acl = acl->next; } if (acl) return FALSE; return TRUE; } /** * krb5_acl_match_string matches ACL format against a string. * * The ACL format has three format specifiers: s, f, and r. Each * specifier will retrieve one argument from the variable arguments * for either matching or storing data. The input string is split up * using " " (space) and "\t" (tab) as a delimiter; multiple and "\t" * in a row are considered to be the same. * * List of format specifiers: * - s Matches a string using strcmp(3) (case sensitive). * - f Matches the string with fnmatch(3). Theflags * argument (the last argument) passed to the fnmatch function is 0. * - r Returns a copy of the string in the char ** passed in; the copy * must be freed with free(3). There is no need to free(3) the * string on error: the function will clean up and set the pointer * to NULL. * * @param context Kerberos 5 context * @param string string to match with * @param format format to match * @param ... parameter to format string * * @return Return an error code or 0. * * * @code * char *s; * * ret = krb5_acl_match_string(context, "foo", "s", "foo"); * if (ret) * krb5_errx(context, 1, "acl didn't match"); * ret = krb5_acl_match_string(context, "foo foo baz/kaka", * "ss", "foo", &s, "foo/\\*"); * if (ret) { * // no need to free(s) on error * assert(s == NULL); * krb5_errx(context, 1, "acl didn't match"); * } * free(s); * @endcode * * @sa krb5_acl_match_file * @ingroup krb5_support */ KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL krb5_acl_match_string(krb5_context context, const char *string, const char *format, ...) { krb5_error_code ret; krb5_boolean found; struct acl_field *acl; va_list ap; va_start(ap, format); ret = acl_parse_format(context, &acl, format, ap); va_end(ap); if(ret) return ret; found = acl_match_acl(context, acl, string); acl_free_list(acl, !found); if (found) { return 0; } else { krb5_set_error_message(context, EACCES, N_("ACL did not match", "")); return EACCES; } } /** * krb5_acl_match_file matches ACL format against each line in a file * using krb5_acl_match_string(). Lines starting with # are treated * like comments and ignored. * * @param context Kerberos 5 context. * @param file file with acl listed in the file. * @param format format to match. * @param ... parameter to format string. * * @return Return an error code or 0. * * @sa krb5_acl_match_string * @ingroup krb5_support */ KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL krb5_acl_match_file(krb5_context context, const char *file, const char *format, ...) { krb5_error_code ret; - struct acl_field *acl; + struct acl_field *acl = NULL; char buf[256]; va_list ap; FILE *f; krb5_boolean found; f = fopen(file, "r"); if(f == NULL) { int save_errno = errno; rk_strerror_r(save_errno, buf, sizeof(buf)); krb5_set_error_message(context, save_errno, N_("open(%s): %s", "file, errno"), file, buf); return save_errno; } rk_cloexec_file(f); va_start(ap, format); ret = acl_parse_format(context, &acl, format, ap); va_end(ap); if(ret) { fclose(f); return ret; } found = FALSE; while(fgets(buf, sizeof(buf), f)) { if(buf[0] == '#') continue; if(acl_match_acl(context, acl, buf)) { found = TRUE; break; } free_retv(acl); } fclose(f); acl_free_list(acl, !found); if (found) { return 0; } else { krb5_set_error_message(context, EACCES, N_("ACL did not match", "")); return EACCES; } } diff --git a/crypto/heimdal/lib/krb5/addr_families.c b/crypto/heimdal/lib/krb5/addr_families.c index 5d321a7e917d..6358b35b5974 100644 --- a/crypto/heimdal/lib/krb5/addr_families.c +++ b/crypto/heimdal/lib/krb5/addr_families.c @@ -1,1559 +1,1559 @@ /* * Copyright (c) 1997-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 "krb5_locl.h" struct addr_operations { int af; krb5_address_type atype; size_t max_sockaddr_size; krb5_error_code (*sockaddr2addr)(const struct sockaddr *, krb5_address *); krb5_error_code (*sockaddr2port)(const struct sockaddr *, int16_t *); void (*addr2sockaddr)(const krb5_address *, struct sockaddr *, krb5_socklen_t *sa_size, int port); void (*h_addr2sockaddr)(const char *, struct sockaddr *, krb5_socklen_t *, int); krb5_error_code (*h_addr2addr)(const char *, krb5_address *); krb5_boolean (*uninteresting)(const struct sockaddr *); krb5_boolean (*is_loopback)(const struct sockaddr *); void (*anyaddr)(struct sockaddr *, krb5_socklen_t *, int); int (*print_addr)(const krb5_address *, char *, size_t); int (*parse_addr)(krb5_context, const char*, krb5_address *); int (*order_addr)(krb5_context, const krb5_address*, const krb5_address*); int (*free_addr)(krb5_context, krb5_address*); int (*copy_addr)(krb5_context, const krb5_address*, krb5_address*); int (*mask_boundary)(krb5_context, const krb5_address*, unsigned long, krb5_address*, krb5_address*); }; /* * AF_INET - aka IPv4 implementation */ static krb5_error_code ipv4_sockaddr2addr (const struct sockaddr *sa, krb5_address *a) { const struct sockaddr_in *sin4 = (const struct sockaddr_in *)sa; unsigned char buf[4]; a->addr_type = KRB5_ADDRESS_INET; memcpy (buf, &sin4->sin_addr, 4); return krb5_data_copy(&a->address, buf, 4); } static krb5_error_code ipv4_sockaddr2port (const struct sockaddr *sa, int16_t *port) { const struct sockaddr_in *sin4 = (const struct sockaddr_in *)sa; *port = sin4->sin_port; return 0; } static void ipv4_addr2sockaddr (const krb5_address *a, struct sockaddr *sa, krb5_socklen_t *sa_size, int port) { struct sockaddr_in tmp; memset (&tmp, 0, sizeof(tmp)); tmp.sin_family = AF_INET; memcpy (&tmp.sin_addr, a->address.data, 4); tmp.sin_port = port; memcpy(sa, &tmp, min(sizeof(tmp), *sa_size)); *sa_size = sizeof(tmp); } static void ipv4_h_addr2sockaddr(const char *addr, struct sockaddr *sa, krb5_socklen_t *sa_size, int port) { struct sockaddr_in tmp; memset (&tmp, 0, sizeof(tmp)); tmp.sin_family = AF_INET; tmp.sin_port = port; tmp.sin_addr = *((const struct in_addr *)addr); memcpy(sa, &tmp, min(sizeof(tmp), *sa_size)); *sa_size = sizeof(tmp); } static krb5_error_code ipv4_h_addr2addr (const char *addr, krb5_address *a) { unsigned char buf[4]; a->addr_type = KRB5_ADDRESS_INET; memcpy(buf, addr, 4); return krb5_data_copy(&a->address, buf, 4); } /* * Are there any addresses that should be considered `uninteresting'? */ static krb5_boolean ipv4_uninteresting (const struct sockaddr *sa) { const struct sockaddr_in *sin4 = (const struct sockaddr_in *)sa; if (sin4->sin_addr.s_addr == INADDR_ANY) return TRUE; return FALSE; } static krb5_boolean ipv4_is_loopback (const struct sockaddr *sa) { const struct sockaddr_in *sin4 = (const struct sockaddr_in *)sa; if ((ntohl(sin4->sin_addr.s_addr) >> 24) == IN_LOOPBACKNET) return TRUE; return FALSE; } static void ipv4_anyaddr (struct sockaddr *sa, krb5_socklen_t *sa_size, int port) { struct sockaddr_in tmp; memset (&tmp, 0, sizeof(tmp)); tmp.sin_family = AF_INET; tmp.sin_port = port; tmp.sin_addr.s_addr = INADDR_ANY; memcpy(sa, &tmp, min(sizeof(tmp), *sa_size)); *sa_size = sizeof(tmp); } static int ipv4_print_addr (const krb5_address *addr, char *str, size_t len) { struct in_addr ia; memcpy (&ia, addr->address.data, 4); return snprintf (str, len, "IPv4:%s", inet_ntoa(ia)); } static int ipv4_parse_addr (krb5_context context, const char *address, krb5_address *addr) { const char *p; struct in_addr a; p = strchr(address, ':'); if(p) { p++; if(strncasecmp(address, "ip:", p - address) != 0 && strncasecmp(address, "ip4:", p - address) != 0 && strncasecmp(address, "ipv4:", p - address) != 0 && strncasecmp(address, "inet:", p - address) != 0) return -1; } else p = address; if(inet_aton(p, &a) == 0) return -1; addr->addr_type = KRB5_ADDRESS_INET; if(krb5_data_alloc(&addr->address, 4) != 0) return -1; _krb5_put_int(addr->address.data, ntohl(a.s_addr), addr->address.length); return 0; } static int ipv4_mask_boundary(krb5_context context, const krb5_address *inaddr, unsigned long len, krb5_address *low, krb5_address *high) { unsigned long ia; uint32_t l, h, m = 0xffffffff; if (len > 32) { krb5_set_error_message(context, KRB5_PROG_ATYPE_NOSUPP, N_("IPv4 prefix too large (%ld)", "len"), len); return KRB5_PROG_ATYPE_NOSUPP; } m = m << (32 - len); _krb5_get_int(inaddr->address.data, &ia, inaddr->address.length); l = ia & m; h = l | ~m; low->addr_type = KRB5_ADDRESS_INET; if(krb5_data_alloc(&low->address, 4) != 0) return -1; _krb5_put_int(low->address.data, l, low->address.length); high->addr_type = KRB5_ADDRESS_INET; if(krb5_data_alloc(&high->address, 4) != 0) { krb5_free_address(context, low); return -1; } _krb5_put_int(high->address.data, h, high->address.length); return 0; } /* * AF_INET6 - aka IPv6 implementation */ #ifdef HAVE_IPV6 static krb5_error_code ipv6_sockaddr2addr (const struct sockaddr *sa, krb5_address *a) { const struct sockaddr_in6 *sin6 = (const struct sockaddr_in6 *)sa; if (IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr)) { unsigned char buf[4]; a->addr_type = KRB5_ADDRESS_INET; #ifndef IN6_ADDR_V6_TO_V4 #ifdef IN6_EXTRACT_V4ADDR #define IN6_ADDR_V6_TO_V4(x) (&IN6_EXTRACT_V4ADDR(x)) #else #define IN6_ADDR_V6_TO_V4(x) ((const struct in_addr *)&(x)->s6_addr[12]) #endif #endif memcpy (buf, IN6_ADDR_V6_TO_V4(&sin6->sin6_addr), 4); return krb5_data_copy(&a->address, buf, 4); } else { a->addr_type = KRB5_ADDRESS_INET6; return krb5_data_copy(&a->address, &sin6->sin6_addr, sizeof(sin6->sin6_addr)); } } static krb5_error_code ipv6_sockaddr2port (const struct sockaddr *sa, int16_t *port) { const struct sockaddr_in6 *sin6 = (const struct sockaddr_in6 *)sa; *port = sin6->sin6_port; return 0; } static void ipv6_addr2sockaddr (const krb5_address *a, struct sockaddr *sa, krb5_socklen_t *sa_size, int port) { struct sockaddr_in6 tmp; memset (&tmp, 0, sizeof(tmp)); tmp.sin6_family = AF_INET6; memcpy (&tmp.sin6_addr, a->address.data, sizeof(tmp.sin6_addr)); tmp.sin6_port = port; memcpy(sa, &tmp, min(sizeof(tmp), *sa_size)); *sa_size = sizeof(tmp); } static void ipv6_h_addr2sockaddr(const char *addr, struct sockaddr *sa, krb5_socklen_t *sa_size, int port) { struct sockaddr_in6 tmp; memset (&tmp, 0, sizeof(tmp)); tmp.sin6_family = AF_INET6; tmp.sin6_port = port; tmp.sin6_addr = *((const struct in6_addr *)addr); memcpy(sa, &tmp, min(sizeof(tmp), *sa_size)); *sa_size = sizeof(tmp); } static krb5_error_code ipv6_h_addr2addr (const char *addr, krb5_address *a) { a->addr_type = KRB5_ADDRESS_INET6; return krb5_data_copy(&a->address, addr, sizeof(struct in6_addr)); } /* * */ static krb5_boolean ipv6_uninteresting (const struct sockaddr *sa) { const struct sockaddr_in6 *sin6 = (const struct sockaddr_in6 *)sa; const struct in6_addr *in6 = (const struct in6_addr *)&sin6->sin6_addr; return IN6_IS_ADDR_LINKLOCAL(in6) || IN6_IS_ADDR_V4COMPAT(in6); } static krb5_boolean ipv6_is_loopback (const struct sockaddr *sa) { const struct sockaddr_in6 *sin6 = (const struct sockaddr_in6 *)sa; const struct in6_addr *in6 = (const struct in6_addr *)&sin6->sin6_addr; return (IN6_IS_ADDR_LOOPBACK(in6)); } static void ipv6_anyaddr (struct sockaddr *sa, krb5_socklen_t *sa_size, int port) { struct sockaddr_in6 tmp; memset (&tmp, 0, sizeof(tmp)); tmp.sin6_family = AF_INET6; tmp.sin6_port = port; tmp.sin6_addr = in6addr_any; *sa_size = sizeof(tmp); } static int ipv6_print_addr (const krb5_address *addr, char *str, size_t len) { char buf[128], buf2[3]; if(inet_ntop(AF_INET6, addr->address.data, buf, sizeof(buf)) == NULL) { /* XXX this is pretty ugly, but better than abort() */ size_t i; unsigned char *p = addr->address.data; buf[0] = '\0'; for(i = 0; i < addr->address.length; i++) { snprintf(buf2, sizeof(buf2), "%02x", p[i]); if(i > 0 && (i & 1) == 0) strlcat(buf, ":", sizeof(buf)); strlcat(buf, buf2, sizeof(buf)); } } return snprintf(str, len, "IPv6:%s", buf); } static int ipv6_parse_addr (krb5_context context, const char *address, krb5_address *addr) { int ret; struct in6_addr in6; const char *p; p = strchr(address, ':'); if(p) { p++; if(strncasecmp(address, "ip6:", p - address) == 0 || strncasecmp(address, "ipv6:", p - address) == 0 || strncasecmp(address, "inet6:", p - address) == 0) address = p; } ret = inet_pton(AF_INET6, address, &in6.s6_addr); if(ret == 1) { addr->addr_type = KRB5_ADDRESS_INET6; ret = krb5_data_alloc(&addr->address, sizeof(in6.s6_addr)); if (ret) return -1; memcpy(addr->address.data, in6.s6_addr, sizeof(in6.s6_addr)); return 0; } return -1; } static int ipv6_mask_boundary(krb5_context context, const krb5_address *inaddr, unsigned long len, krb5_address *low, krb5_address *high) { struct in6_addr addr, laddr, haddr; uint32_t m; int i, sub_len; if (len > 128) { krb5_set_error_message(context, KRB5_PROG_ATYPE_NOSUPP, N_("IPv6 prefix too large (%ld)", "length"), len); return KRB5_PROG_ATYPE_NOSUPP; } if (inaddr->address.length != sizeof(addr)) { krb5_set_error_message(context, KRB5_PROG_ATYPE_NOSUPP, N_("IPv6 addr bad length", "")); return KRB5_PROG_ATYPE_NOSUPP; } memcpy(&addr, inaddr->address.data, inaddr->address.length); for (i = 0; i < 16; i++) { sub_len = min(8, len); m = 0xff << (8 - sub_len); laddr.s6_addr[i] = addr.s6_addr[i] & m; haddr.s6_addr[i] = (addr.s6_addr[i] & m) | ~m; if (len > 8) len -= 8; else len = 0; } low->addr_type = KRB5_ADDRESS_INET6; if (krb5_data_alloc(&low->address, sizeof(laddr.s6_addr)) != 0) return -1; memcpy(low->address.data, laddr.s6_addr, sizeof(laddr.s6_addr)); high->addr_type = KRB5_ADDRESS_INET6; if (krb5_data_alloc(&high->address, sizeof(haddr.s6_addr)) != 0) { krb5_free_address(context, low); return -1; } memcpy(high->address.data, haddr.s6_addr, sizeof(haddr.s6_addr)); return 0; } #endif /* IPv6 */ #ifndef HEIMDAL_SMALLER /* * table */ #define KRB5_ADDRESS_ARANGE (-100) struct arange { krb5_address low; krb5_address high; }; static int arange_parse_addr (krb5_context context, const char *address, krb5_address *addr) { char buf[1024], *p; krb5_address low0, high0; struct arange *a; krb5_error_code ret; if(strncasecmp(address, "RANGE:", 6) != 0) return -1; address += 6; p = strrchr(address, '/'); if (p) { krb5_addresses addrmask; char *q; long num; if (strlcpy(buf, address, sizeof(buf)) > sizeof(buf)) return -1; buf[p - address] = '\0'; ret = krb5_parse_address(context, buf, &addrmask); if (ret) return ret; if(addrmask.len != 1) { krb5_free_addresses(context, &addrmask); return -1; } address += p - address + 1; num = strtol(address, &q, 10); if (q == address || *q != '\0' || num < 0) { krb5_free_addresses(context, &addrmask); return -1; } ret = krb5_address_prefixlen_boundary(context, &addrmask.val[0], num, &low0, &high0); krb5_free_addresses(context, &addrmask); if (ret) return ret; } else { krb5_addresses low, high; strsep_copy(&address, "-", buf, sizeof(buf)); ret = krb5_parse_address(context, buf, &low); if(ret) return ret; if(low.len != 1) { krb5_free_addresses(context, &low); return -1; } strsep_copy(&address, "-", buf, sizeof(buf)); ret = krb5_parse_address(context, buf, &high); if(ret) { krb5_free_addresses(context, &low); return ret; } - if(high.len != 1 && high.val[0].addr_type != low.val[0].addr_type) { + if(high.len != 1 || high.val[0].addr_type != low.val[0].addr_type) { krb5_free_addresses(context, &low); krb5_free_addresses(context, &high); return -1; } ret = krb5_copy_address(context, &high.val[0], &high0); if (ret == 0) { ret = krb5_copy_address(context, &low.val[0], &low0); if (ret) krb5_free_address(context, &high0); } krb5_free_addresses(context, &low); krb5_free_addresses(context, &high); if (ret) return ret; } krb5_data_alloc(&addr->address, sizeof(*a)); addr->addr_type = KRB5_ADDRESS_ARANGE; a = addr->address.data; if(krb5_address_order(context, &low0, &high0) < 0) { a->low = low0; a->high = high0; } else { a->low = high0; a->high = low0; } return 0; } static int arange_free (krb5_context context, krb5_address *addr) { struct arange *a; a = addr->address.data; krb5_free_address(context, &a->low); krb5_free_address(context, &a->high); krb5_data_free(&addr->address); return 0; } static int arange_copy (krb5_context context, const krb5_address *inaddr, krb5_address *outaddr) { krb5_error_code ret; struct arange *i, *o; outaddr->addr_type = KRB5_ADDRESS_ARANGE; ret = krb5_data_alloc(&outaddr->address, sizeof(*o)); if(ret) return ret; i = inaddr->address.data; o = outaddr->address.data; ret = krb5_copy_address(context, &i->low, &o->low); if(ret) { krb5_data_free(&outaddr->address); return ret; } ret = krb5_copy_address(context, &i->high, &o->high); if(ret) { krb5_free_address(context, &o->low); krb5_data_free(&outaddr->address); return ret; } return 0; } static int arange_print_addr (const krb5_address *addr, char *str, size_t len) { struct arange *a; krb5_error_code ret; size_t l, size, ret_len; a = addr->address.data; l = strlcpy(str, "RANGE:", len); ret_len = l; if (l > len) l = len; size = l; ret = krb5_print_address (&a->low, str + size, len - size, &l); if (ret) return ret; ret_len += l; if (len - size > l) size += l; else size = len; l = strlcat(str + size, "-", len - size); ret_len += l; if (len - size > l) size += l; else size = len; ret = krb5_print_address (&a->high, str + size, len - size, &l); if (ret) return ret; ret_len += l; return ret_len; } static int arange_order_addr(krb5_context context, const krb5_address *addr1, const krb5_address *addr2) { int tmp1, tmp2, sign; struct arange *a; const krb5_address *a2; if(addr1->addr_type == KRB5_ADDRESS_ARANGE) { a = addr1->address.data; a2 = addr2; sign = 1; } else if(addr2->addr_type == KRB5_ADDRESS_ARANGE) { a = addr2->address.data; a2 = addr1; sign = -1; } else { abort(); UNREACHABLE(return 0); } if(a2->addr_type == KRB5_ADDRESS_ARANGE) { struct arange *b = a2->address.data; tmp1 = krb5_address_order(context, &a->low, &b->low); if(tmp1 != 0) return sign * tmp1; return sign * krb5_address_order(context, &a->high, &b->high); } else if(a2->addr_type == a->low.addr_type) { tmp1 = krb5_address_order(context, &a->low, a2); if(tmp1 > 0) return sign; tmp2 = krb5_address_order(context, &a->high, a2); if(tmp2 < 0) return -sign; return 0; } else { return sign * (addr1->addr_type - addr2->addr_type); } } #endif /* HEIMDAL_SMALLER */ static int addrport_print_addr (const krb5_address *addr, char *str, size_t len) { krb5_error_code ret; krb5_address addr1, addr2; uint16_t port = 0; size_t ret_len = 0, l, size = 0; krb5_storage *sp; sp = krb5_storage_from_data((krb5_data*)rk_UNCONST(&addr->address)); if (sp == NULL) return ENOMEM; /* for totally obscure reasons, these are not in network byteorder */ krb5_storage_set_byteorder(sp, KRB5_STORAGE_BYTEORDER_LE); krb5_storage_seek(sp, 2, SEEK_CUR); /* skip first two bytes */ krb5_ret_address(sp, &addr1); krb5_storage_seek(sp, 2, SEEK_CUR); /* skip two bytes */ krb5_ret_address(sp, &addr2); krb5_storage_free(sp); if(addr2.addr_type == KRB5_ADDRESS_IPPORT && addr2.address.length == 2) { unsigned long value; _krb5_get_int(addr2.address.data, &value, 2); port = value; } l = strlcpy(str, "ADDRPORT:", len); ret_len += l; if (len > l) size += l; else size = len; ret = krb5_print_address(&addr1, str + size, len - size, &l); if (ret) return ret; ret_len += l; if (len - size > l) size += l; else size = len; ret = snprintf(str + size, len - size, ",PORT=%u", port); if (ret < 0) return EINVAL; ret_len += ret; return ret_len; } static struct addr_operations at[] = { { AF_INET, KRB5_ADDRESS_INET, sizeof(struct sockaddr_in), ipv4_sockaddr2addr, ipv4_sockaddr2port, ipv4_addr2sockaddr, ipv4_h_addr2sockaddr, ipv4_h_addr2addr, ipv4_uninteresting, ipv4_is_loopback, ipv4_anyaddr, ipv4_print_addr, ipv4_parse_addr, NULL, NULL, NULL, ipv4_mask_boundary }, #ifdef HAVE_IPV6 { AF_INET6, KRB5_ADDRESS_INET6, sizeof(struct sockaddr_in6), ipv6_sockaddr2addr, ipv6_sockaddr2port, ipv6_addr2sockaddr, ipv6_h_addr2sockaddr, ipv6_h_addr2addr, ipv6_uninteresting, ipv6_is_loopback, ipv6_anyaddr, ipv6_print_addr, ipv6_parse_addr, NULL, NULL, NULL, ipv6_mask_boundary } , #endif #ifndef HEIMDAL_SMALLER /* fake address type */ { KRB5_ADDRESS_ARANGE, KRB5_ADDRESS_ARANGE, sizeof(struct arange), NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, arange_print_addr, arange_parse_addr, arange_order_addr, arange_free, arange_copy, NULL }, #endif { KRB5_ADDRESS_ADDRPORT, KRB5_ADDRESS_ADDRPORT, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, addrport_print_addr, NULL, NULL, NULL, NULL } }; static int num_addrs = sizeof(at) / sizeof(at[0]); static size_t max_sockaddr_size = 0; /* * generic functions */ static struct addr_operations * find_af(int af) { struct addr_operations *a; for (a = at; a < at + num_addrs; ++a) if (af == a->af) return a; return NULL; } static struct addr_operations * find_atype(krb5_address_type atype) { struct addr_operations *a; for (a = at; a < at + num_addrs; ++a) if (atype == a->atype) return a; return NULL; } /** * krb5_sockaddr2address stores a address a "struct sockaddr" sa in * the krb5_address addr. * * @param context a Keberos context * @param sa a struct sockaddr to extract the address from * @param addr an Kerberos 5 address to store the address in. * * @return Return an error code or 0. * * @ingroup krb5_address */ KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL krb5_sockaddr2address (krb5_context context, const struct sockaddr *sa, krb5_address *addr) { struct addr_operations *a = find_af(sa->sa_family); if (a == NULL) { krb5_set_error_message (context, KRB5_PROG_ATYPE_NOSUPP, N_("Address family %d not supported", ""), sa->sa_family); return KRB5_PROG_ATYPE_NOSUPP; } return (*a->sockaddr2addr)(sa, addr); } /** * krb5_sockaddr2port extracts a port (if possible) from a "struct * sockaddr. * * @param context a Keberos context * @param sa a struct sockaddr to extract the port from * @param port a pointer to an int16_t store the port in. * * @return Return an error code or 0. Will return * KRB5_PROG_ATYPE_NOSUPP in case address type is not supported. * * @ingroup krb5_address */ KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL krb5_sockaddr2port (krb5_context context, const struct sockaddr *sa, int16_t *port) { struct addr_operations *a = find_af(sa->sa_family); if (a == NULL) { krb5_set_error_message (context, KRB5_PROG_ATYPE_NOSUPP, N_("Address family %d not supported", ""), sa->sa_family); return KRB5_PROG_ATYPE_NOSUPP; } return (*a->sockaddr2port)(sa, port); } /** * krb5_addr2sockaddr sets the "struct sockaddr sockaddr" from addr * and port. The argument sa_size should initially contain the size of * the sa and after the call, it will contain the actual length of the * address. In case of the sa is too small to fit the whole address, * the up to *sa_size will be stored, and then *sa_size will be set to * the required length. * * @param context a Keberos context * @param addr the address to copy the from * @param sa the struct sockaddr that will be filled in * @param sa_size pointer to length of sa, and after the call, it will * contain the actual length of the address. * @param port set port in sa. * * @return Return an error code or 0. Will return * KRB5_PROG_ATYPE_NOSUPP in case address type is not supported. * * @ingroup krb5_address */ KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL krb5_addr2sockaddr (krb5_context context, const krb5_address *addr, struct sockaddr *sa, krb5_socklen_t *sa_size, int port) { struct addr_operations *a = find_atype(addr->addr_type); if (a == NULL) { krb5_set_error_message (context, KRB5_PROG_ATYPE_NOSUPP, N_("Address type %d not supported", "krb5_address type"), addr->addr_type); return KRB5_PROG_ATYPE_NOSUPP; } if (a->addr2sockaddr == NULL) { krb5_set_error_message (context, KRB5_PROG_ATYPE_NOSUPP, N_("Can't convert address type %d to sockaddr", ""), addr->addr_type); return KRB5_PROG_ATYPE_NOSUPP; } (*a->addr2sockaddr)(addr, sa, sa_size, port); return 0; } /** * krb5_max_sockaddr_size returns the max size of the .Li struct * sockaddr that the Kerberos library will return. * * @return Return an size_t of the maximum struct sockaddr. * * @ingroup krb5_address */ KRB5_LIB_FUNCTION size_t KRB5_LIB_CALL krb5_max_sockaddr_size (void) { if (max_sockaddr_size == 0) { struct addr_operations *a; for(a = at; a < at + num_addrs; ++a) max_sockaddr_size = max(max_sockaddr_size, a->max_sockaddr_size); } return max_sockaddr_size; } /** * krb5_sockaddr_uninteresting returns TRUE for all .Fa sa that the * kerberos library thinks are uninteresting. One example are link * local addresses. * * @param sa pointer to struct sockaddr that might be interesting. * * @return Return a non zero for uninteresting addresses. * * @ingroup krb5_address */ KRB5_LIB_FUNCTION krb5_boolean KRB5_LIB_CALL krb5_sockaddr_uninteresting(const struct sockaddr *sa) { struct addr_operations *a = find_af(sa->sa_family); if (a == NULL || a->uninteresting == NULL) return TRUE; return (*a->uninteresting)(sa); } KRB5_LIB_FUNCTION krb5_boolean KRB5_LIB_CALL krb5_sockaddr_is_loopback(const struct sockaddr *sa) { struct addr_operations *a = find_af(sa->sa_family); if (a == NULL || a->is_loopback == NULL) return TRUE; return (*a->is_loopback)(sa); } /** * krb5_h_addr2sockaddr initializes a "struct sockaddr sa" from af and * the "struct hostent" (see gethostbyname(3) ) h_addr_list * component. The argument sa_size should initially contain the size * of the sa, and after the call, it will contain the actual length of * the address. * * @param context a Keberos context * @param af addresses * @param addr address * @param sa returned struct sockaddr * @param sa_size size of sa * @param port port to set in sa. * * @return Return an error code or 0. * * @ingroup krb5_address */ KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL krb5_h_addr2sockaddr (krb5_context context, int af, const char *addr, struct sockaddr *sa, krb5_socklen_t *sa_size, int port) { struct addr_operations *a = find_af(af); if (a == NULL) { krb5_set_error_message (context, KRB5_PROG_ATYPE_NOSUPP, "Address family %d not supported", af); return KRB5_PROG_ATYPE_NOSUPP; } (*a->h_addr2sockaddr)(addr, sa, sa_size, port); return 0; } /** * krb5_h_addr2addr works like krb5_h_addr2sockaddr with the exception * that it operates on a krb5_address instead of a struct sockaddr. * * @param context a Keberos context * @param af address family * @param haddr host address from struct hostent. * @param addr returned krb5_address. * * @return Return an error code or 0. * * @ingroup krb5_address */ KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL krb5_h_addr2addr (krb5_context context, int af, const char *haddr, krb5_address *addr) { struct addr_operations *a = find_af(af); if (a == NULL) { krb5_set_error_message (context, KRB5_PROG_ATYPE_NOSUPP, N_("Address family %d not supported", ""), af); return KRB5_PROG_ATYPE_NOSUPP; } return (*a->h_addr2addr)(haddr, addr); } /** * krb5_anyaddr fills in a "struct sockaddr sa" that can be used to * bind(2) to. The argument sa_size should initially contain the size * of the sa, and after the call, it will contain the actual length * of the address. * * @param context a Keberos context * @param af address family * @param sa sockaddr * @param sa_size lenght of sa. * @param port for to fill into sa. * * @return Return an error code or 0. * * @ingroup krb5_address */ KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL krb5_anyaddr (krb5_context context, int af, struct sockaddr *sa, krb5_socklen_t *sa_size, int port) { struct addr_operations *a = find_af (af); if (a == NULL) { krb5_set_error_message (context, KRB5_PROG_ATYPE_NOSUPP, N_("Address family %d not supported", ""), af); return KRB5_PROG_ATYPE_NOSUPP; } (*a->anyaddr)(sa, sa_size, port); return 0; } /** * krb5_print_address prints the address in addr to the string string * that have the length len. If ret_len is not NULL, it will be filled * with the length of the string if size were unlimited (not including * the final NUL) . * * @param addr address to be printed * @param str pointer string to print the address into * @param len length that will fit into area pointed to by "str". * @param ret_len return length the str. * * @return Return an error code or 0. * * @ingroup krb5_address */ KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL krb5_print_address (const krb5_address *addr, char *str, size_t len, size_t *ret_len) { struct addr_operations *a = find_atype(addr->addr_type); int ret; if (a == NULL || a->print_addr == NULL) { char *s; int l; size_t i; s = str; l = snprintf(s, len, "TYPE_%d:", addr->addr_type); if (l < 0 || (size_t)l >= len) return EINVAL; s += l; len -= l; for(i = 0; i < addr->address.length; i++) { l = snprintf(s, len, "%02x", ((char*)addr->address.data)[i]); if (l < 0 || (size_t)l >= len) return EINVAL; len -= l; s += l; } if(ret_len != NULL) *ret_len = s - str; return 0; } ret = (*a->print_addr)(addr, str, len); if (ret < 0) return EINVAL; if(ret_len != NULL) *ret_len = ret; return 0; } /** * krb5_parse_address returns the resolved hostname in string to the * krb5_addresses addresses . * * @param context a Keberos context * @param string * @param addresses * * @return Return an error code or 0. * * @ingroup krb5_address */ KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL krb5_parse_address(krb5_context context, const char *string, krb5_addresses *addresses) { int i, n; struct addrinfo *ai, *a; int error; int save_errno; addresses->len = 0; addresses->val = NULL; for(i = 0; i < num_addrs; i++) { if(at[i].parse_addr) { krb5_address addr; if((*at[i].parse_addr)(context, string, &addr) == 0) { ALLOC_SEQ(addresses, 1); if (addresses->val == NULL) { krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", "")); return ENOMEM; } addresses->val[0] = addr; return 0; } } } error = getaddrinfo (string, NULL, NULL, &ai); if (error) { krb5_error_code ret2; save_errno = errno; ret2 = krb5_eai_to_heim_errno(error, save_errno); krb5_set_error_message (context, ret2, "%s: %s", string, gai_strerror(error)); return ret2; } n = 0; for (a = ai; a != NULL; a = a->ai_next) ++n; ALLOC_SEQ(addresses, n); if (addresses->val == NULL) { krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", "")); freeaddrinfo(ai); return ENOMEM; } addresses->len = 0; for (a = ai, i = 0; a != NULL; a = a->ai_next) { if (krb5_sockaddr2address (context, ai->ai_addr, &addresses->val[i])) continue; if(krb5_address_search(context, &addresses->val[i], addresses)) { krb5_free_address(context, &addresses->val[i]); continue; } i++; addresses->len = i; } freeaddrinfo (ai); return 0; } /** * krb5_address_order compares the addresses addr1 and addr2 so that * it can be used for sorting addresses. If the addresses are the same * address krb5_address_order will return 0. Behavies like memcmp(2). * * @param context a Keberos context * @param addr1 krb5_address to compare * @param addr2 krb5_address to compare * * @return < 0 if address addr1 in "less" then addr2. 0 if addr1 and * addr2 is the same address, > 0 if addr2 is "less" then addr1. * * @ingroup krb5_address */ KRB5_LIB_FUNCTION int KRB5_LIB_CALL krb5_address_order(krb5_context context, const krb5_address *addr1, const krb5_address *addr2) { /* this sucks; what if both addresses have order functions, which should we call? this works for now, though */ struct addr_operations *a; a = find_atype(addr1->addr_type); if(a == NULL) { krb5_set_error_message (context, KRB5_PROG_ATYPE_NOSUPP, N_("Address family %d not supported", ""), addr1->addr_type); return KRB5_PROG_ATYPE_NOSUPP; } if(a->order_addr != NULL) return (*a->order_addr)(context, addr1, addr2); a = find_atype(addr2->addr_type); if(a == NULL) { krb5_set_error_message (context, KRB5_PROG_ATYPE_NOSUPP, N_("Address family %d not supported", ""), addr2->addr_type); return KRB5_PROG_ATYPE_NOSUPP; } if(a->order_addr != NULL) return (*a->order_addr)(context, addr1, addr2); if(addr1->addr_type != addr2->addr_type) return addr1->addr_type - addr2->addr_type; if(addr1->address.length != addr2->address.length) return addr1->address.length - addr2->address.length; return memcmp (addr1->address.data, addr2->address.data, addr1->address.length); } /** * krb5_address_compare compares the addresses addr1 and addr2. * Returns TRUE if the two addresses are the same. * * @param context a Keberos context * @param addr1 address to compare * @param addr2 address to compare * * @return Return an TRUE is the address are the same FALSE if not * * @ingroup krb5_address */ KRB5_LIB_FUNCTION krb5_boolean KRB5_LIB_CALL krb5_address_compare(krb5_context context, const krb5_address *addr1, const krb5_address *addr2) { return krb5_address_order (context, addr1, addr2) == 0; } /** * krb5_address_search checks if the address addr is a member of the * address set list addrlist . * * @param context a Keberos context. * @param addr address to search for. * @param addrlist list of addresses to look in for addr. * * @return Return an error code or 0. * * @ingroup krb5_address */ KRB5_LIB_FUNCTION krb5_boolean KRB5_LIB_CALL krb5_address_search(krb5_context context, const krb5_address *addr, const krb5_addresses *addrlist) { size_t i; for (i = 0; i < addrlist->len; ++i) if (krb5_address_compare (context, addr, &addrlist->val[i])) return TRUE; return FALSE; } /** * krb5_free_address frees the data stored in the address that is * alloced with any of the krb5_address functions. * * @param context a Keberos context * @param address addresss to be freed. * * @return Return an error code or 0. * * @ingroup krb5_address */ KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL krb5_free_address(krb5_context context, krb5_address *address) { struct addr_operations *a = find_atype (address->addr_type); if(a != NULL && a->free_addr != NULL) return (*a->free_addr)(context, address); krb5_data_free (&address->address); memset(address, 0, sizeof(*address)); return 0; } /** * krb5_free_addresses frees the data stored in the address that is * alloced with any of the krb5_address functions. * * @param context a Keberos context * @param addresses addressses to be freed. * * @return Return an error code or 0. * * @ingroup krb5_address */ KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL krb5_free_addresses(krb5_context context, krb5_addresses *addresses) { size_t i; for(i = 0; i < addresses->len; i++) krb5_free_address(context, &addresses->val[i]); free(addresses->val); addresses->len = 0; addresses->val = NULL; return 0; } /** * krb5_copy_address copies the content of address * inaddr to outaddr. * * @param context a Keberos context * @param inaddr pointer to source address * @param outaddr pointer to destination address * * @return Return an error code or 0. * * @ingroup krb5_address */ KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL krb5_copy_address(krb5_context context, const krb5_address *inaddr, krb5_address *outaddr) { struct addr_operations *a = find_af (inaddr->addr_type); if(a != NULL && a->copy_addr != NULL) return (*a->copy_addr)(context, inaddr, outaddr); return copy_HostAddress(inaddr, outaddr); } /** * krb5_copy_addresses copies the content of addresses * inaddr to outaddr. * * @param context a Keberos context * @param inaddr pointer to source addresses * @param outaddr pointer to destination addresses * * @return Return an error code or 0. * * @ingroup krb5_address */ KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL krb5_copy_addresses(krb5_context context, const krb5_addresses *inaddr, krb5_addresses *outaddr) { size_t i; ALLOC_SEQ(outaddr, inaddr->len); if(inaddr->len > 0 && outaddr->val == NULL) return ENOMEM; for(i = 0; i < inaddr->len; i++) krb5_copy_address(context, &inaddr->val[i], &outaddr->val[i]); return 0; } /** * krb5_append_addresses adds the set of addresses in source to * dest. While copying the addresses, duplicates are also sorted out. * * @param context a Keberos context * @param dest destination of copy operation * @param source adresses that are going to be added to dest * * @return Return an error code or 0. * * @ingroup krb5_address */ KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL krb5_append_addresses(krb5_context context, krb5_addresses *dest, const krb5_addresses *source) { krb5_address *tmp; krb5_error_code ret; size_t i; if(source->len > 0) { tmp = realloc(dest->val, (dest->len + source->len) * sizeof(*tmp)); if(tmp == NULL) { krb5_set_error_message (context, ENOMEM, N_("malloc: out of memory", "")); return ENOMEM; } dest->val = tmp; for(i = 0; i < source->len; i++) { /* skip duplicates */ if(krb5_address_search(context, &source->val[i], dest)) continue; ret = krb5_copy_address(context, &source->val[i], &dest->val[dest->len]); if(ret) return ret; dest->len++; } } return 0; } /** * Create an address of type KRB5_ADDRESS_ADDRPORT from (addr, port) * * @param context a Keberos context * @param res built address from addr/port * @param addr address to use * @param port port to use * * @return Return an error code or 0. * * @ingroup krb5_address */ KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL krb5_make_addrport (krb5_context context, krb5_address **res, const krb5_address *addr, int16_t port) { krb5_error_code ret; size_t len = addr->address.length + 2 + 4 * 4; u_char *p; *res = malloc (sizeof(**res)); if (*res == NULL) { krb5_set_error_message (context, ENOMEM, N_("malloc: out of memory", "")); return ENOMEM; } (*res)->addr_type = KRB5_ADDRESS_ADDRPORT; ret = krb5_data_alloc (&(*res)->address, len); if (ret) { krb5_set_error_message (context, ret, N_("malloc: out of memory", "")); free (*res); *res = NULL; return ret; } p = (*res)->address.data; *p++ = 0; *p++ = 0; *p++ = (addr->addr_type ) & 0xFF; *p++ = (addr->addr_type >> 8) & 0xFF; *p++ = (addr->address.length ) & 0xFF; *p++ = (addr->address.length >> 8) & 0xFF; *p++ = (addr->address.length >> 16) & 0xFF; *p++ = (addr->address.length >> 24) & 0xFF; memcpy (p, addr->address.data, addr->address.length); p += addr->address.length; *p++ = 0; *p++ = 0; *p++ = (KRB5_ADDRESS_IPPORT ) & 0xFF; *p++ = (KRB5_ADDRESS_IPPORT >> 8) & 0xFF; *p++ = (2 ) & 0xFF; *p++ = (2 >> 8) & 0xFF; *p++ = (2 >> 16) & 0xFF; *p++ = (2 >> 24) & 0xFF; memcpy (p, &port, 2); return 0; } /** * Calculate the boundary addresses of `inaddr'/`prefixlen' and store * them in `low' and `high'. * * @param context a Keberos context * @param inaddr address in prefixlen that the bondery searched * @param prefixlen width of boundery * @param low lowest address * @param high highest address * * @return Return an error code or 0. * * @ingroup krb5_address */ KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL krb5_address_prefixlen_boundary(krb5_context context, const krb5_address *inaddr, unsigned long prefixlen, krb5_address *low, krb5_address *high) { struct addr_operations *a = find_atype (inaddr->addr_type); if(a != NULL && a->mask_boundary != NULL) return (*a->mask_boundary)(context, inaddr, prefixlen, low, high); krb5_set_error_message(context, KRB5_PROG_ATYPE_NOSUPP, N_("Address family %d doesn't support " "address mask operation", ""), inaddr->addr_type); return KRB5_PROG_ATYPE_NOSUPP; } diff --git a/crypto/heimdal/lib/krb5/context.c b/crypto/heimdal/lib/krb5/context.c index 99bf1b419b0a..86bfe539b974 100644 --- a/crypto/heimdal/lib/krb5/context.c +++ b/crypto/heimdal/lib/krb5/context.c @@ -1,1516 +1,1516 @@ /* * Copyright (c) 1997 - 2010 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" #include #include #define INIT_FIELD(C, T, E, D, F) \ (C)->E = krb5_config_get_ ## T ## _default ((C), NULL, (D), \ "libdefaults", F, NULL) #define INIT_FLAG(C, O, V, D, F) \ do { \ if (krb5_config_get_bool_default((C), NULL, (D),"libdefaults", F, NULL)) { \ (C)->O |= V; \ } \ } while(0) /* * Set the list of etypes `ret_etypes' from the configuration variable * `name' */ static krb5_error_code set_etypes (krb5_context context, const char *name, krb5_enctype **ret_enctypes) { char **etypes_str; krb5_enctype *etypes = NULL; etypes_str = krb5_config_get_strings(context, NULL, "libdefaults", name, NULL); if(etypes_str){ int i, j, k; for(i = 0; etypes_str[i]; i++); etypes = malloc((i+1) * sizeof(*etypes)); if (etypes == NULL) { krb5_config_free_strings (etypes_str); krb5_set_error_message (context, ENOMEM, N_("malloc: out of memory", "")); return ENOMEM; } for(j = 0, k = 0; j < i; j++) { krb5_enctype e; if(krb5_string_to_enctype(context, etypes_str[j], &e) != 0) continue; if (krb5_enctype_valid(context, e) != 0) continue; etypes[k++] = e; } etypes[k] = ETYPE_NULL; krb5_config_free_strings(etypes_str); } *ret_enctypes = etypes; return 0; } /* * read variables from the configuration file and set in `context' */ static krb5_error_code init_context_from_config_file(krb5_context context) { krb5_error_code ret; const char * tmp; char **s; - krb5_enctype *tmptypes; + krb5_enctype *tmptypes = NULL; INIT_FIELD(context, time, max_skew, 5 * 60, "clockskew"); INIT_FIELD(context, time, kdc_timeout, 3, "kdc_timeout"); INIT_FIELD(context, int, max_retries, 3, "max_retries"); INIT_FIELD(context, string, http_proxy, NULL, "http_proxy"); ret = krb5_config_get_bool_default(context, NULL, FALSE, "libdefaults", "allow_weak_crypto", NULL); if (ret) { krb5_enctype_enable(context, ETYPE_DES_CBC_CRC); krb5_enctype_enable(context, ETYPE_DES_CBC_MD4); krb5_enctype_enable(context, ETYPE_DES_CBC_MD5); krb5_enctype_enable(context, ETYPE_DES_CBC_NONE); krb5_enctype_enable(context, ETYPE_DES_CFB64_NONE); krb5_enctype_enable(context, ETYPE_DES_PCBC_NONE); } ret = set_etypes (context, "default_etypes", &tmptypes); if(ret) return ret; free(context->etypes); context->etypes = tmptypes; ret = set_etypes (context, "default_etypes_des", &tmptypes); if(ret) return ret; free(context->etypes_des); context->etypes_des = tmptypes; ret = set_etypes (context, "default_as_etypes", &tmptypes); if(ret) return ret; free(context->as_etypes); context->as_etypes = tmptypes; ret = set_etypes (context, "default_tgs_etypes", &tmptypes); if(ret) return ret; free(context->tgs_etypes); context->tgs_etypes = tmptypes; ret = set_etypes (context, "permitted_enctypes", &tmptypes); if(ret) return ret; free(context->permitted_enctypes); context->permitted_enctypes = tmptypes; /* default keytab name */ tmp = NULL; if(!issuid()) tmp = getenv("KRB5_KTNAME"); if(tmp != NULL) context->default_keytab = tmp; else INIT_FIELD(context, string, default_keytab, KEYTAB_DEFAULT, "default_keytab_name"); INIT_FIELD(context, string, default_keytab_modify, NULL, "default_keytab_modify_name"); INIT_FIELD(context, string, time_fmt, "%Y-%m-%dT%H:%M:%S", "time_format"); INIT_FIELD(context, string, date_fmt, "%Y-%m-%d", "date_format"); INIT_FIELD(context, bool, log_utc, FALSE, "log_utc"); /* init dns-proxy slime */ tmp = krb5_config_get_string(context, NULL, "libdefaults", "dns_proxy", NULL); if(tmp) roken_gethostby_setup(context->http_proxy, tmp); krb5_free_host_realm (context, context->default_realms); context->default_realms = NULL; { krb5_addresses addresses; char **adr, **a; krb5_set_extra_addresses(context, NULL); adr = krb5_config_get_strings(context, NULL, "libdefaults", "extra_addresses", NULL); memset(&addresses, 0, sizeof(addresses)); for(a = adr; a && *a; a++) { ret = krb5_parse_address(context, *a, &addresses); if (ret == 0) { krb5_add_extra_addresses(context, &addresses); krb5_free_addresses(context, &addresses); } } krb5_config_free_strings(adr); krb5_set_ignore_addresses(context, NULL); adr = krb5_config_get_strings(context, NULL, "libdefaults", "ignore_addresses", NULL); memset(&addresses, 0, sizeof(addresses)); for(a = adr; a && *a; a++) { ret = krb5_parse_address(context, *a, &addresses); if (ret == 0) { krb5_add_ignore_addresses(context, &addresses); krb5_free_addresses(context, &addresses); } } krb5_config_free_strings(adr); } INIT_FIELD(context, bool, scan_interfaces, TRUE, "scan_interfaces"); INIT_FIELD(context, int, fcache_vno, 0, "fcache_version"); /* prefer dns_lookup_kdc over srv_lookup. */ INIT_FIELD(context, bool, srv_lookup, TRUE, "srv_lookup"); INIT_FIELD(context, bool, srv_lookup, context->srv_lookup, "dns_lookup_kdc"); INIT_FIELD(context, int, large_msg_size, 1400, "large_message_size"); INIT_FLAG(context, flags, KRB5_CTX_F_DNS_CANONICALIZE_HOSTNAME, TRUE, "dns_canonicalize_hostname"); INIT_FLAG(context, flags, KRB5_CTX_F_CHECK_PAC, TRUE, "check_pac"); context->default_cc_name = NULL; context->default_cc_name_set = 0; s = krb5_config_get_strings(context, NULL, "logging", "krb5", NULL); if(s) { char **p; krb5_initlog(context, "libkrb5", &context->debug_dest); for(p = s; *p; p++) krb5_addlog_dest(context, context->debug_dest, *p); krb5_config_free_strings(s); } tmp = krb5_config_get_string(context, NULL, "libdefaults", "check-rd-req-server", NULL); if (tmp == NULL && !issuid()) tmp = getenv("KRB5_CHECK_RD_REQ_SERVER"); if(tmp) { if (strcasecmp(tmp, "ignore") == 0) context->flags |= KRB5_CTX_F_RD_REQ_IGNORE; } return 0; } static krb5_error_code cc_ops_register(krb5_context context) { context->cc_ops = NULL; context->num_cc_ops = 0; #ifndef KCM_IS_API_CACHE krb5_cc_register(context, &krb5_acc_ops, TRUE); #endif krb5_cc_register(context, &krb5_fcc_ops, TRUE); krb5_cc_register(context, &krb5_mcc_ops, TRUE); #ifdef HAVE_SCC krb5_cc_register(context, &krb5_scc_ops, TRUE); #endif #ifdef HAVE_KCM #ifdef KCM_IS_API_CACHE krb5_cc_register(context, &krb5_akcm_ops, TRUE); #endif krb5_cc_register(context, &krb5_kcm_ops, TRUE); #endif _krb5_load_ccache_plugins(context); return 0; } static krb5_error_code cc_ops_copy(krb5_context context, const krb5_context src_context) { const krb5_cc_ops **cc_ops; context->cc_ops = NULL; context->num_cc_ops = 0; if (src_context->num_cc_ops == 0) return 0; cc_ops = malloc(sizeof(cc_ops[0]) * src_context->num_cc_ops); if (cc_ops == NULL) { krb5_set_error_message(context, KRB5_CC_NOMEM, N_("malloc: out of memory", "")); return KRB5_CC_NOMEM; } memcpy(rk_UNCONST(cc_ops), src_context->cc_ops, sizeof(cc_ops[0]) * src_context->num_cc_ops); context->cc_ops = cc_ops; context->num_cc_ops = src_context->num_cc_ops; return 0; } static krb5_error_code kt_ops_register(krb5_context context) { context->num_kt_types = 0; context->kt_types = NULL; krb5_kt_register (context, &krb5_fkt_ops); krb5_kt_register (context, &krb5_wrfkt_ops); krb5_kt_register (context, &krb5_javakt_ops); krb5_kt_register (context, &krb5_mkt_ops); #ifndef HEIMDAL_SMALLER krb5_kt_register (context, &krb5_akf_ops); #endif krb5_kt_register (context, &krb5_any_ops); return 0; } static krb5_error_code kt_ops_copy(krb5_context context, const krb5_context src_context) { context->num_kt_types = 0; context->kt_types = NULL; if (src_context->num_kt_types == 0) return 0; context->kt_types = malloc(sizeof(context->kt_types[0]) * src_context->num_kt_types); if (context->kt_types == NULL) { krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", "")); return ENOMEM; } context->num_kt_types = src_context->num_kt_types; memcpy(context->kt_types, src_context->kt_types, sizeof(context->kt_types[0]) * src_context->num_kt_types); return 0; } static const char *sysplugin_dirs[] = { LIBDIR "/plugin/krb5", #ifdef __APPLE__ "/Library/KerberosPlugins/KerberosFrameworkPlugins", "/System/Library/KerberosPlugins/KerberosFrameworkPlugins", #endif NULL }; static void init_context_once(void *ctx) { krb5_context context = ctx; _krb5_load_plugins(context, "krb5", sysplugin_dirs); bindtextdomain(HEIMDAL_TEXTDOMAIN, HEIMDAL_LOCALEDIR); } /** * Initializes the context structure and reads the configuration file * /etc/krb5.conf. The structure should be freed by calling * krb5_free_context() when it is no longer being used. * * @param context pointer to returned context * * @return Returns 0 to indicate success. Otherwise an errno code is * returned. Failure means either that something bad happened during * initialization (typically ENOMEM) or that Kerberos should not be * used ENXIO. * * @ingroup krb5 */ KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL krb5_init_context(krb5_context *context) { static heim_base_once_t init_context = HEIM_BASE_ONCE_INIT; krb5_context p; krb5_error_code ret; char **files; *context = NULL; p = calloc(1, sizeof(*p)); if(!p) return ENOMEM; p->mutex = malloc(sizeof(HEIMDAL_MUTEX)); if (p->mutex == NULL) { free(p); return ENOMEM; } HEIMDAL_MUTEX_init(p->mutex); p->flags |= KRB5_CTX_F_HOMEDIR_ACCESS; ret = krb5_get_default_config_files(&files); if(ret) goto out; ret = krb5_set_config_files(p, files); krb5_free_config_files(files); if(ret) goto out; /* init error tables */ krb5_init_ets(p); cc_ops_register(p); kt_ops_register(p); #ifdef PKINIT ret = hx509_context_init(&p->hx509ctx); if (ret) goto out; #endif if (rk_SOCK_INIT()) p->flags |= KRB5_CTX_F_SOCKETS_INITIALIZED; out: if(ret) { krb5_free_context(p); p = NULL; } else { heim_base_once_f(&init_context, p, init_context_once); } *context = p; return ret; } #ifndef HEIMDAL_SMALLER KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL krb5_get_permitted_enctypes(krb5_context context, krb5_enctype **etypes) { return krb5_get_default_in_tkt_etypes(context, KRB5_PDU_NONE, etypes); } /* * */ static krb5_error_code copy_etypes (krb5_context context, krb5_enctype *enctypes, krb5_enctype **ret_enctypes) { unsigned int i; for (i = 0; enctypes[i]; i++) ; i++; *ret_enctypes = malloc(sizeof(ret_enctypes[0]) * i); if (*ret_enctypes == NULL) { krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", "")); return ENOMEM; } memcpy(*ret_enctypes, enctypes, sizeof(ret_enctypes[0]) * i); return 0; } /** * Make a copy for the Kerberos 5 context, the new krb5_context shoud * be freed with krb5_free_context(). * * @param context the Kerberos context to copy * @param out the copy of the Kerberos, set to NULL error. * * @return Returns 0 to indicate success. Otherwise an kerberos et * error code is returned, see krb5_get_error_message(). * * @ingroup krb5 */ KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL krb5_copy_context(krb5_context context, krb5_context *out) { krb5_error_code ret; krb5_context p; *out = NULL; p = calloc(1, sizeof(*p)); if (p == NULL) { krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", "")); return ENOMEM; } p->mutex = malloc(sizeof(HEIMDAL_MUTEX)); if (p->mutex == NULL) { krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", "")); free(p); return ENOMEM; } HEIMDAL_MUTEX_init(p->mutex); if (context->default_cc_name) p->default_cc_name = strdup(context->default_cc_name); if (context->default_cc_name_env) p->default_cc_name_env = strdup(context->default_cc_name_env); if (context->etypes) { ret = copy_etypes(context, context->etypes, &p->etypes); if (ret) goto out; } if (context->etypes_des) { ret = copy_etypes(context, context->etypes_des, &p->etypes_des); if (ret) goto out; } if (context->default_realms) { ret = krb5_copy_host_realm(context, context->default_realms, &p->default_realms); if (ret) goto out; } ret = _krb5_config_copy(context, context->cf, &p->cf); if (ret) goto out; /* XXX should copy */ krb5_init_ets(p); cc_ops_copy(p, context); kt_ops_copy(p, context); #if 0 /* XXX */ if(context->warn_dest != NULL) ; if(context->debug_dest != NULL) ; #endif ret = krb5_set_extra_addresses(p, context->extra_addresses); if (ret) goto out; ret = krb5_set_extra_addresses(p, context->ignore_addresses); if (ret) goto out; ret = _krb5_copy_send_to_kdc_func(p, context); if (ret) goto out; *out = p; return 0; out: krb5_free_context(p); return ret; } #endif /** * Frees the krb5_context allocated by krb5_init_context(). * * @param context context to be freed. * * @ingroup krb5 */ KRB5_LIB_FUNCTION void KRB5_LIB_CALL krb5_free_context(krb5_context context) { if (context->default_cc_name) free(context->default_cc_name); if (context->default_cc_name_env) free(context->default_cc_name_env); free(context->etypes); free(context->etypes_des); krb5_free_host_realm (context, context->default_realms); krb5_config_file_free (context, context->cf); free_error_table (context->et_list); free(rk_UNCONST(context->cc_ops)); free(context->kt_types); krb5_clear_error_message(context); if(context->warn_dest != NULL) krb5_closelog(context, context->warn_dest); if(context->debug_dest != NULL) krb5_closelog(context, context->debug_dest); krb5_set_extra_addresses(context, NULL); krb5_set_ignore_addresses(context, NULL); krb5_set_send_to_kdc_func(context, NULL, NULL); #ifdef PKINIT if (context->hx509ctx) hx509_context_free(&context->hx509ctx); #endif HEIMDAL_MUTEX_destroy(context->mutex); free(context->mutex); if (context->flags & KRB5_CTX_F_SOCKETS_INITIALIZED) { rk_SOCK_EXIT(); } memset(context, 0, sizeof(*context)); free(context); } /** * Reinit the context from a new set of filenames. * * @param context context to add configuration too. * @param filenames array of filenames, end of list is indicated with a NULL filename. * * @return Returns 0 to indicate success. Otherwise an kerberos et * error code is returned, see krb5_get_error_message(). * * @ingroup krb5 */ KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL krb5_set_config_files(krb5_context context, char **filenames) { krb5_error_code ret; krb5_config_binding *tmp = NULL; while(filenames != NULL && *filenames != NULL && **filenames != '\0') { ret = krb5_config_parse_file_multi(context, *filenames, &tmp); if(ret != 0 && ret != ENOENT && ret != EACCES && ret != EPERM) { krb5_config_file_free(context, tmp); return ret; } filenames++; } #if 0 /* with this enabled and if there are no config files, Kerberos is considererd disabled */ if(tmp == NULL) return ENXIO; #endif #ifdef _WIN32 _krb5_load_config_from_registry(context, &tmp); #endif krb5_config_file_free(context, context->cf); context->cf = tmp; ret = init_context_from_config_file(context); return ret; } static krb5_error_code add_file(char ***pfilenames, int *len, char *file) { char **pp = *pfilenames; int i; for(i = 0; i < *len; i++) { if(strcmp(pp[i], file) == 0) { free(file); return 0; } } pp = realloc(*pfilenames, (*len + 2) * sizeof(*pp)); if (pp == NULL) { free(file); return ENOMEM; } pp[*len] = file; pp[*len + 1] = NULL; *pfilenames = pp; *len += 1; return 0; } /* * `pq' isn't free, it's up the the caller */ KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL krb5_prepend_config_files(const char *filelist, char **pq, char ***ret_pp) { krb5_error_code ret; const char *p, *q; char **pp; int len; char *fn; pp = NULL; len = 0; p = filelist; while(1) { ssize_t l; q = p; l = strsep_copy(&q, PATH_SEP, NULL, 0); if(l == -1) break; fn = malloc(l + 1); if(fn == NULL) { krb5_free_config_files(pp); return ENOMEM; } (void)strsep_copy(&p, PATH_SEP, fn, l + 1); ret = add_file(&pp, &len, fn); if (ret) { krb5_free_config_files(pp); return ret; } } if (pq != NULL) { int i; for (i = 0; pq[i] != NULL; i++) { fn = strdup(pq[i]); if (fn == NULL) { krb5_free_config_files(pp); return ENOMEM; } ret = add_file(&pp, &len, fn); if (ret) { krb5_free_config_files(pp); return ret; } } } *ret_pp = pp; return 0; } /** * Prepend the filename to the global configuration list. * * @param filelist a filename to add to the default list of filename * @param pfilenames return array of filenames, should be freed with krb5_free_config_files(). * * @return Returns 0 to indicate success. Otherwise an kerberos et * error code is returned, see krb5_get_error_message(). * * @ingroup krb5 */ KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL krb5_prepend_config_files_default(const char *filelist, char ***pfilenames) { krb5_error_code ret; char **defpp, **pp = NULL; ret = krb5_get_default_config_files(&defpp); if (ret) return ret; ret = krb5_prepend_config_files(filelist, defpp, &pp); krb5_free_config_files(defpp); if (ret) { return ret; } *pfilenames = pp; return 0; } #ifdef _WIN32 /** * Checks the registry for configuration file location * * Kerberos for Windows and other legacy Kerberos applications expect * to find the configuration file location in the * SOFTWARE\MIT\Kerberos registry key under the value "config". */ char * _krb5_get_default_config_config_files_from_registry() { static const char * KeyName = "Software\\MIT\\Kerberos"; char *config_file = NULL; LONG rcode; HKEY key; rcode = RegOpenKeyEx(HKEY_CURRENT_USER, KeyName, 0, KEY_READ, &key); if (rcode == ERROR_SUCCESS) { config_file = _krb5_parse_reg_value_as_multi_string(NULL, key, "config", REG_NONE, 0, PATH_SEP); RegCloseKey(key); } if (config_file) return config_file; rcode = RegOpenKeyEx(HKEY_LOCAL_MACHINE, KeyName, 0, KEY_READ, &key); if (rcode == ERROR_SUCCESS) { config_file = _krb5_parse_reg_value_as_multi_string(NULL, key, "config", REG_NONE, 0, PATH_SEP); RegCloseKey(key); } return config_file; } #endif /** * Get the global configuration list. * * @param pfilenames return array of filenames, should be freed with krb5_free_config_files(). * * @return Returns 0 to indicate success. Otherwise an kerberos et * error code is returned, see krb5_get_error_message(). * * @ingroup krb5 */ KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL krb5_get_default_config_files(char ***pfilenames) { const char *files = NULL; if (pfilenames == NULL) return EINVAL; if(!issuid()) files = getenv("KRB5_CONFIG"); #ifdef _WIN32 if (files == NULL) { char * reg_files; reg_files = _krb5_get_default_config_config_files_from_registry(); if (reg_files != NULL) { krb5_error_code code; code = krb5_prepend_config_files(reg_files, NULL, pfilenames); free(reg_files); return code; } } #endif if (files == NULL) files = krb5_config_file; return krb5_prepend_config_files(files, NULL, pfilenames); } /** * Free a list of configuration files. * * @param filenames list, terminated with a NULL pointer, to be * freed. NULL is an valid argument. * * @return Returns 0 to indicate success. Otherwise an kerberos et * error code is returned, see krb5_get_error_message(). * * @ingroup krb5 */ KRB5_LIB_FUNCTION void KRB5_LIB_CALL krb5_free_config_files(char **filenames) { char **p; for(p = filenames; p && *p != NULL; p++) free(*p); free(filenames); } /** * Returns the list of Kerberos encryption types sorted in order of * most preferred to least preferred encryption type. Note that some * encryption types might be disabled, so you need to check with * krb5_enctype_valid() before using the encryption type. * * @return list of enctypes, terminated with ETYPE_NULL. Its a static * array completed into the Kerberos library so the content doesn't * need to be freed. * * @ingroup krb5 */ KRB5_LIB_FUNCTION const krb5_enctype * KRB5_LIB_CALL krb5_kerberos_enctypes(krb5_context context) { static const krb5_enctype p[] = { ETYPE_AES256_CTS_HMAC_SHA1_96, ETYPE_AES128_CTS_HMAC_SHA1_96, ETYPE_DES3_CBC_SHA1, ETYPE_DES3_CBC_MD5, ETYPE_ARCFOUR_HMAC_MD5, ETYPE_DES_CBC_MD5, ETYPE_DES_CBC_MD4, ETYPE_DES_CBC_CRC, ETYPE_NULL }; return p; } /* * */ static krb5_error_code copy_enctypes(krb5_context context, const krb5_enctype *in, krb5_enctype **out) { krb5_enctype *p = NULL; size_t m, n; for (n = 0; in[n]; n++) ; n++; ALLOC(p, n); if(p == NULL) return krb5_enomem(context); for (n = 0, m = 0; in[n]; n++) { if (krb5_enctype_valid(context, in[n]) != 0) continue; p[m++] = in[n]; } p[m] = KRB5_ENCTYPE_NULL; if (m == 0) { free(p); krb5_set_error_message (context, KRB5_PROG_ETYPE_NOSUPP, N_("no valid enctype set", "")); return KRB5_PROG_ETYPE_NOSUPP; } *out = p; return 0; } /* * set `etype' to a malloced list of the default enctypes */ static krb5_error_code default_etypes(krb5_context context, krb5_enctype **etype) { const krb5_enctype *p = krb5_kerberos_enctypes(context); return copy_enctypes(context, p, etype); } /** * Set the default encryption types that will be use in communcation * with the KDC, clients and servers. * * @param context Kerberos 5 context. * @param etypes Encryption types, array terminated with ETYPE_NULL (0). * * @return Returns 0 to indicate success. Otherwise an kerberos et * error code is returned, see krb5_get_error_message(). * * @ingroup krb5 */ KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL krb5_set_default_in_tkt_etypes(krb5_context context, const krb5_enctype *etypes) { krb5_error_code ret; krb5_enctype *p = NULL; if(etypes) { ret = copy_enctypes(context, etypes, &p); if (ret) return ret; } if(context->etypes) free(context->etypes); context->etypes = p; return 0; } /** * Get the default encryption types that will be use in communcation * with the KDC, clients and servers. * * @param context Kerberos 5 context. * @param etypes Encryption types, array terminated with * ETYPE_NULL(0), caller should free array with krb5_xfree(): * * @return Returns 0 to indicate success. Otherwise an kerberos et * error code is returned, see krb5_get_error_message(). * * @ingroup krb5 */ KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL krb5_get_default_in_tkt_etypes(krb5_context context, krb5_pdu pdu_type, krb5_enctype **etypes) { krb5_enctype *enctypes = NULL; krb5_error_code ret; krb5_enctype *p; heim_assert(pdu_type == KRB5_PDU_AS_REQUEST || pdu_type == KRB5_PDU_TGS_REQUEST || pdu_type == KRB5_PDU_NONE, "pdu contant not as expected"); if (pdu_type == KRB5_PDU_AS_REQUEST && context->as_etypes != NULL) enctypes = context->as_etypes; else if (pdu_type == KRB5_PDU_TGS_REQUEST && context->tgs_etypes != NULL) enctypes = context->tgs_etypes; else if (context->etypes != NULL) enctypes = context->etypes; if (enctypes != NULL) { ret = copy_enctypes(context, enctypes, &p); if (ret) return ret; } else { ret = default_etypes(context, &p); if (ret) return ret; } *etypes = p; return 0; } /** * Init the built-in ets in the Kerberos library. * * @param context kerberos context to add the ets too * * @ingroup krb5 */ KRB5_LIB_FUNCTION void KRB5_LIB_CALL krb5_init_ets(krb5_context context) { if(context->et_list == NULL){ krb5_add_et_list(context, initialize_krb5_error_table_r); krb5_add_et_list(context, initialize_asn1_error_table_r); krb5_add_et_list(context, initialize_heim_error_table_r); krb5_add_et_list(context, initialize_k524_error_table_r); #ifdef COM_ERR_BINDDOMAIN_krb5 bindtextdomain(COM_ERR_BINDDOMAIN_krb5, HEIMDAL_LOCALEDIR); bindtextdomain(COM_ERR_BINDDOMAIN_asn1, HEIMDAL_LOCALEDIR); bindtextdomain(COM_ERR_BINDDOMAIN_heim, HEIMDAL_LOCALEDIR); bindtextdomain(COM_ERR_BINDDOMAIN_k524, HEIMDAL_LOCALEDIR); #endif #ifdef PKINIT krb5_add_et_list(context, initialize_hx_error_table_r); #ifdef COM_ERR_BINDDOMAIN_hx bindtextdomain(COM_ERR_BINDDOMAIN_hx, HEIMDAL_LOCALEDIR); #endif #endif } } /** * Make the kerberos library default to the admin KDC. * * @param context Kerberos 5 context. * @param flag boolean flag to select if the use the admin KDC or not. * * @ingroup krb5 */ KRB5_LIB_FUNCTION void KRB5_LIB_CALL krb5_set_use_admin_kdc (krb5_context context, krb5_boolean flag) { context->use_admin_kdc = flag; } /** * Make the kerberos library default to the admin KDC. * * @param context Kerberos 5 context. * * @return boolean flag to telling the context will use admin KDC as the default KDC. * * @ingroup krb5 */ KRB5_LIB_FUNCTION krb5_boolean KRB5_LIB_CALL krb5_get_use_admin_kdc (krb5_context context) { return context->use_admin_kdc; } /** * Add extra address to the address list that the library will add to * the client's address list when communicating with the KDC. * * @param context Kerberos 5 context. * @param addresses addreses to add * * @return Returns 0 to indicate success. Otherwise an kerberos et * error code is returned, see krb5_get_error_message(). * * @ingroup krb5 */ KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL krb5_add_extra_addresses(krb5_context context, krb5_addresses *addresses) { if(context->extra_addresses) return krb5_append_addresses(context, context->extra_addresses, addresses); else return krb5_set_extra_addresses(context, addresses); } /** * Set extra address to the address list that the library will add to * the client's address list when communicating with the KDC. * * @param context Kerberos 5 context. * @param addresses addreses to set * * @return Returns 0 to indicate success. Otherwise an kerberos et * error code is returned, see krb5_get_error_message(). * * @ingroup krb5 */ KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL krb5_set_extra_addresses(krb5_context context, const krb5_addresses *addresses) { if(context->extra_addresses) krb5_free_addresses(context, context->extra_addresses); if(addresses == NULL) { if(context->extra_addresses != NULL) { free(context->extra_addresses); context->extra_addresses = NULL; } return 0; } if(context->extra_addresses == NULL) { context->extra_addresses = malloc(sizeof(*context->extra_addresses)); if(context->extra_addresses == NULL) { krb5_set_error_message (context, ENOMEM, N_("malloc: out of memory", "")); return ENOMEM; } } return krb5_copy_addresses(context, addresses, context->extra_addresses); } /** * Get extra address to the address list that the library will add to * the client's address list when communicating with the KDC. * * @param context Kerberos 5 context. * @param addresses addreses to set * * @return Returns 0 to indicate success. Otherwise an kerberos et * error code is returned, see krb5_get_error_message(). * * @ingroup krb5 */ KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL krb5_get_extra_addresses(krb5_context context, krb5_addresses *addresses) { if(context->extra_addresses == NULL) { memset(addresses, 0, sizeof(*addresses)); return 0; } return krb5_copy_addresses(context,context->extra_addresses, addresses); } /** * Add extra addresses to ignore when fetching addresses from the * underlaying operating system. * * @param context Kerberos 5 context. * @param addresses addreses to ignore * * @return Returns 0 to indicate success. Otherwise an kerberos et * error code is returned, see krb5_get_error_message(). * * @ingroup krb5 */ KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL krb5_add_ignore_addresses(krb5_context context, krb5_addresses *addresses) { if(context->ignore_addresses) return krb5_append_addresses(context, context->ignore_addresses, addresses); else return krb5_set_ignore_addresses(context, addresses); } /** * Set extra addresses to ignore when fetching addresses from the * underlaying operating system. * * @param context Kerberos 5 context. * @param addresses addreses to ignore * * @return Returns 0 to indicate success. Otherwise an kerberos et * error code is returned, see krb5_get_error_message(). * * @ingroup krb5 */ KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL krb5_set_ignore_addresses(krb5_context context, const krb5_addresses *addresses) { if(context->ignore_addresses) krb5_free_addresses(context, context->ignore_addresses); if(addresses == NULL) { if(context->ignore_addresses != NULL) { free(context->ignore_addresses); context->ignore_addresses = NULL; } return 0; } if(context->ignore_addresses == NULL) { context->ignore_addresses = malloc(sizeof(*context->ignore_addresses)); if(context->ignore_addresses == NULL) { krb5_set_error_message (context, ENOMEM, N_("malloc: out of memory", "")); return ENOMEM; } } return krb5_copy_addresses(context, addresses, context->ignore_addresses); } /** * Get extra addresses to ignore when fetching addresses from the * underlaying operating system. * * @param context Kerberos 5 context. * @param addresses list addreses ignored * * @return Returns 0 to indicate success. Otherwise an kerberos et * error code is returned, see krb5_get_error_message(). * * @ingroup krb5 */ KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL krb5_get_ignore_addresses(krb5_context context, krb5_addresses *addresses) { if(context->ignore_addresses == NULL) { memset(addresses, 0, sizeof(*addresses)); return 0; } return krb5_copy_addresses(context, context->ignore_addresses, addresses); } /** * Set version of fcache that the library should use. * * @param context Kerberos 5 context. * @param version version number. * * @return Returns 0 to indicate success. Otherwise an kerberos et * error code is returned, see krb5_get_error_message(). * * @ingroup krb5 */ KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL krb5_set_fcache_version(krb5_context context, int version) { context->fcache_vno = version; return 0; } /** * Get version of fcache that the library should use. * * @param context Kerberos 5 context. * @param version version number. * * @return Returns 0 to indicate success. Otherwise an kerberos et * error code is returned, see krb5_get_error_message(). * * @ingroup krb5 */ KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL krb5_get_fcache_version(krb5_context context, int *version) { *version = context->fcache_vno; return 0; } /** * Runtime check if the Kerberos library was complied with thread support. * * @return TRUE if the library was compiled with thread support, FALSE if not. * * @ingroup krb5 */ KRB5_LIB_FUNCTION krb5_boolean KRB5_LIB_CALL krb5_is_thread_safe(void) { #ifdef ENABLE_PTHREAD_SUPPORT return TRUE; #else return FALSE; #endif } /** * Set if the library should use DNS to canonicalize hostnames. * * @param context Kerberos 5 context. * @param flag if its dns canonicalizion is used or not. * * @ingroup krb5 */ KRB5_LIB_FUNCTION void KRB5_LIB_CALL krb5_set_dns_canonicalize_hostname (krb5_context context, krb5_boolean flag) { if (flag) context->flags |= KRB5_CTX_F_DNS_CANONICALIZE_HOSTNAME; else context->flags &= ~KRB5_CTX_F_DNS_CANONICALIZE_HOSTNAME; } /** * Get if the library uses DNS to canonicalize hostnames. * * @param context Kerberos 5 context. * * @return return non zero if the library uses DNS to canonicalize hostnames. * * @ingroup krb5 */ KRB5_LIB_FUNCTION krb5_boolean KRB5_LIB_CALL krb5_get_dns_canonicalize_hostname (krb5_context context) { return (context->flags & KRB5_CTX_F_DNS_CANONICALIZE_HOSTNAME) ? 1 : 0; } /** * Get current offset in time to the KDC. * * @param context Kerberos 5 context. * @param sec seconds part of offset. * @param usec micro seconds part of offset. * * @return returns zero * * @ingroup krb5 */ KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL krb5_get_kdc_sec_offset (krb5_context context, int32_t *sec, int32_t *usec) { if (sec) *sec = context->kdc_sec_offset; if (usec) *usec = context->kdc_usec_offset; return 0; } /** * Set current offset in time to the KDC. * * @param context Kerberos 5 context. * @param sec seconds part of offset. * @param usec micro seconds part of offset. * * @return returns zero * * @ingroup krb5 */ KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL krb5_set_kdc_sec_offset (krb5_context context, int32_t sec, int32_t usec) { context->kdc_sec_offset = sec; if (usec >= 0) context->kdc_usec_offset = usec; return 0; } /** * Get max time skew allowed. * * @param context Kerberos 5 context. * * @return timeskew in seconds. * * @ingroup krb5 */ KRB5_LIB_FUNCTION time_t KRB5_LIB_CALL krb5_get_max_time_skew (krb5_context context) { return context->max_skew; } /** * Set max time skew allowed. * * @param context Kerberos 5 context. * @param t timeskew in seconds. * * @ingroup krb5 */ KRB5_LIB_FUNCTION void KRB5_LIB_CALL krb5_set_max_time_skew (krb5_context context, time_t t) { context->max_skew = t; } /* * Init encryption types in len, val with etypes. * * @param context Kerberos 5 context. * @param pdu_type type of pdu * @param len output length of val. * @param val output array of enctypes. * @param etypes etypes to set val and len to, if NULL, use default enctypes. * @return Returns 0 to indicate success. Otherwise an kerberos et * error code is returned, see krb5_get_error_message(). * * @ingroup krb5 */ KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL _krb5_init_etype(krb5_context context, krb5_pdu pdu_type, unsigned *len, krb5_enctype **val, const krb5_enctype *etypes) { krb5_error_code ret; if (etypes == NULL) ret = krb5_get_default_in_tkt_etypes(context, pdu_type, val); else ret = copy_enctypes(context, etypes, val); if (ret) return ret; if (len) { *len = 0; while ((*val)[*len] != KRB5_ENCTYPE_NULL) (*len)++; } return 0; } /* * Allow homedir accces */ static HEIMDAL_MUTEX homedir_mutex = HEIMDAL_MUTEX_INITIALIZER; static krb5_boolean allow_homedir = TRUE; krb5_boolean _krb5_homedir_access(krb5_context context) { krb5_boolean allow; #ifdef HAVE_GETEUID /* is never allowed for root */ if (geteuid() == 0) return FALSE; #endif if (context && (context->flags & KRB5_CTX_F_HOMEDIR_ACCESS) == 0) return FALSE; HEIMDAL_MUTEX_lock(&homedir_mutex); allow = allow_homedir; HEIMDAL_MUTEX_unlock(&homedir_mutex); return allow; } /** * Enable and disable home directory access on either the global state * or the krb5_context state. By calling krb5_set_home_dir_access() * with context set to NULL, the global state is configured otherwise * the state for the krb5_context is modified. * * For home directory access to be allowed, both the global state and * the krb5_context state have to be allowed. * * Administrator (root user), never uses the home directory. * * @param context a Kerberos 5 context or NULL * @param allow allow if TRUE home directory * @return the old value * * @ingroup krb5 */ KRB5_LIB_FUNCTION krb5_boolean KRB5_LIB_CALL krb5_set_home_dir_access(krb5_context context, krb5_boolean allow) { krb5_boolean old; if (context) { old = (context->flags & KRB5_CTX_F_HOMEDIR_ACCESS) ? TRUE : FALSE; if (allow) context->flags |= KRB5_CTX_F_HOMEDIR_ACCESS; else context->flags &= ~KRB5_CTX_F_HOMEDIR_ACCESS; } else { HEIMDAL_MUTEX_lock(&homedir_mutex); old = allow_homedir; allow_homedir = allow; HEIMDAL_MUTEX_unlock(&homedir_mutex); } return old; } diff --git a/crypto/heimdal/lib/krb5/deprecated.c b/crypto/heimdal/lib/krb5/deprecated.c index 1d44d21b1706..e7c0105ebf7f 100644 --- a/crypto/heimdal/lib/krb5/deprecated.c +++ b/crypto/heimdal/lib/krb5/deprecated.c @@ -1,609 +1,607 @@ /* * Copyright (c) 1997 - 2009 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" #undef __attribute__ #define __attribute__(x) #ifndef HEIMDAL_SMALLER /** * Same as krb5_data_free(). MIT compat. * * Deprecated: use krb5_data_free(). * * @param context Kerberos 5 context. * @param data krb5_data to free. * * @ingroup krb5_deprecated */ KRB5_LIB_FUNCTION void KRB5_LIB_CALL krb5_free_data_contents(krb5_context context, krb5_data *data) KRB5_DEPRECATED_FUNCTION("Use X instead") { krb5_data_free(data); } /** * Deprecated: keytypes doesn't exists, they are really enctypes. * * @ingroup krb5_deprecated */ KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL krb5_keytype_to_enctypes_default (krb5_context context, krb5_keytype keytype, unsigned *len, krb5_enctype **val) KRB5_DEPRECATED_FUNCTION("Use X instead") { unsigned int i, n; krb5_enctype *ret; if (keytype != KEYTYPE_DES || context->etypes_des == NULL) return krb5_keytype_to_enctypes (context, keytype, len, val); for (n = 0; context->etypes_des[n]; ++n) ; ret = malloc (n * sizeof(*ret)); if (ret == NULL && n != 0) { krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", "")); return ENOMEM; } for (i = 0; i < n; ++i) ret[i] = context->etypes_des[i]; *len = n; *val = ret; return 0; } static struct { const char *name; krb5_keytype type; } keys[] = { { "null", ENCTYPE_NULL }, { "des", ETYPE_DES_CBC_CRC }, { "des3", ETYPE_OLD_DES3_CBC_SHA1 }, { "aes-128", ETYPE_AES128_CTS_HMAC_SHA1_96 }, { "aes-256", ETYPE_AES256_CTS_HMAC_SHA1_96 }, { "arcfour", ETYPE_ARCFOUR_HMAC_MD5 }, { "arcfour-56", ETYPE_ARCFOUR_HMAC_MD5_56 } }; static int num_keys = sizeof(keys) / sizeof(keys[0]); /** * Deprecated: keytypes doesn't exists, they are really enctypes in * most cases, use krb5_enctype_to_string(). * * @ingroup krb5_deprecated */ KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL krb5_keytype_to_string(krb5_context context, krb5_keytype keytype, char **string) KRB5_DEPRECATED_FUNCTION("Use X instead") { const char *name = NULL; int i; for(i = 0; i < num_keys; i++) { if(keys[i].type == keytype) { name = keys[i].name; break; } } if(i >= num_keys) { krb5_set_error_message(context, KRB5_PROG_KEYTYPE_NOSUPP, "key type %d not supported", keytype); return KRB5_PROG_KEYTYPE_NOSUPP; } *string = strdup(name); if(*string == NULL) { krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", "")); return ENOMEM; } return 0; } /** * Deprecated: keytypes doesn't exists, they are really enctypes in * most cases, use krb5_string_to_enctype(). * * @ingroup krb5_deprecated */ KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL krb5_string_to_keytype(krb5_context context, const char *string, krb5_keytype *keytype) KRB5_DEPRECATED_FUNCTION("Use X instead") { char *end; int i; for(i = 0; i < num_keys; i++) if(strcasecmp(keys[i].name, string) == 0){ *keytype = keys[i].type; return 0; } /* check if the enctype is a number */ *keytype = strtol(string, &end, 0); if(*end == '\0' && *keytype != 0) { if (krb5_enctype_valid(context, *keytype) == 0) return 0; } krb5_set_error_message(context, KRB5_PROG_KEYTYPE_NOSUPP, "key type %s not supported", string); return KRB5_PROG_KEYTYPE_NOSUPP; } /** * Deprecated: use krb5_get_init_creds() and friends. * * @ingroup krb5_deprecated */ KRB5_LIB_FUNCTION krb5_error_code KRB5_CALLCONV krb5_password_key_proc (krb5_context context, krb5_enctype type, krb5_salt salt, krb5_const_pointer keyseed, krb5_keyblock **key) KRB5_DEPRECATED_FUNCTION("Use X instead") { krb5_error_code ret; const char *password = (const char *)keyseed; char buf[BUFSIZ]; *key = malloc (sizeof (**key)); if (*key == NULL) { krb5_set_error_message(context, ENOMEM, "malloc: out of memory"); return ENOMEM; } if (password == NULL) { if(UI_UTIL_read_pw_string (buf, sizeof(buf), "Password: ", 0)) { free (*key); krb5_clear_error_message(context); return KRB5_LIBOS_PWDINTR; } password = buf; } ret = krb5_string_to_key_salt (context, type, password, salt, *key); memset (buf, 0, sizeof(buf)); return ret; } /** * Deprecated: use krb5_get_init_creds() and friends. * * @ingroup krb5_deprecated */ KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL krb5_get_in_tkt_with_password (krb5_context context, krb5_flags options, krb5_addresses *addrs, const krb5_enctype *etypes, const krb5_preauthtype *pre_auth_types, const char *password, krb5_ccache ccache, krb5_creds *creds, krb5_kdc_rep *ret_as_reply) KRB5_DEPRECATED_FUNCTION("Use X instead") { return krb5_get_in_tkt (context, options, addrs, etypes, pre_auth_types, krb5_password_key_proc, password, NULL, NULL, creds, ccache, ret_as_reply); } static krb5_error_code KRB5_CALLCONV krb5_skey_key_proc (krb5_context context, krb5_enctype type, krb5_salt salt, krb5_const_pointer keyseed, krb5_keyblock **key) { return krb5_copy_keyblock (context, keyseed, key); } /** * Deprecated: use krb5_get_init_creds() and friends. * * @ingroup krb5_deprecated */ KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL krb5_get_in_tkt_with_skey (krb5_context context, krb5_flags options, krb5_addresses *addrs, const krb5_enctype *etypes, const krb5_preauthtype *pre_auth_types, const krb5_keyblock *key, krb5_ccache ccache, krb5_creds *creds, krb5_kdc_rep *ret_as_reply) KRB5_DEPRECATED_FUNCTION("Use X instead") { if(key == NULL) return krb5_get_in_tkt_with_keytab (context, options, addrs, etypes, pre_auth_types, NULL, ccache, creds, ret_as_reply); else return krb5_get_in_tkt (context, options, addrs, etypes, pre_auth_types, krb5_skey_key_proc, key, NULL, NULL, creds, ccache, ret_as_reply); } /** * Deprecated: use krb5_get_init_creds() and friends. * * @ingroup krb5_deprecated */ KRB5_LIB_FUNCTION krb5_error_code KRB5_CALLCONV krb5_keytab_key_proc (krb5_context context, krb5_enctype enctype, krb5_salt salt, krb5_const_pointer keyseed, krb5_keyblock **key) KRB5_DEPRECATED_FUNCTION("Use X instead") { krb5_keytab_key_proc_args *args = rk_UNCONST(keyseed); krb5_keytab keytab = args->keytab; krb5_principal principal = args->principal; krb5_error_code ret; krb5_keytab real_keytab; krb5_keytab_entry entry; if(keytab == NULL) krb5_kt_default(context, &real_keytab); else real_keytab = keytab; ret = krb5_kt_get_entry (context, real_keytab, principal, 0, enctype, &entry); + if (ret == 0) { + ret = krb5_copy_keyblock (context, &entry.keyblock, key); + krb5_kt_free_entry(context, &entry); + } if (keytab == NULL) krb5_kt_close (context, real_keytab); - - if (ret) - return ret; - - ret = krb5_copy_keyblock (context, &entry.keyblock, key); - krb5_kt_free_entry(context, &entry); return ret; } /** * Deprecated: use krb5_get_init_creds() and friends. * * @ingroup krb5_deprecated */ KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL krb5_get_in_tkt_with_keytab (krb5_context context, krb5_flags options, krb5_addresses *addrs, const krb5_enctype *etypes, const krb5_preauthtype *pre_auth_types, krb5_keytab keytab, krb5_ccache ccache, krb5_creds *creds, krb5_kdc_rep *ret_as_reply) KRB5_DEPRECATED_FUNCTION("Use X instead") { krb5_keytab_key_proc_args a; a.principal = creds->client; a.keytab = keytab; return krb5_get_in_tkt (context, options, addrs, etypes, pre_auth_types, krb5_keytab_key_proc, &a, NULL, NULL, creds, ccache, ret_as_reply); } /** * Generate a new ccache of type `ops' in `id'. * * Deprecated: use krb5_cc_new_unique() instead. * * @return Return an error code or 0, see krb5_get_error_message(). * * @ingroup krb5_ccache */ KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL krb5_cc_gen_new(krb5_context context, const krb5_cc_ops *ops, krb5_ccache *id) KRB5_DEPRECATED_FUNCTION("Use X instead") { return krb5_cc_new_unique(context, ops->prefix, NULL, id); } /** * Deprecated: use krb5_principal_get_realm() * * @ingroup krb5_deprecated */ KRB5_LIB_FUNCTION krb5_realm * KRB5_LIB_CALL krb5_princ_realm(krb5_context context, krb5_principal principal) KRB5_DEPRECATED_FUNCTION("Use X instead") { return &principal->realm; } /** * Deprecated: use krb5_principal_set_realm() * * @ingroup krb5_deprecated */ KRB5_LIB_FUNCTION void KRB5_LIB_CALL krb5_princ_set_realm(krb5_context context, krb5_principal principal, krb5_realm *realm) KRB5_DEPRECATED_FUNCTION("Use X instead") { principal->realm = *realm; } /** * Deprecated: use krb5_free_cred_contents() * * @ingroup krb5_deprecated */ /* keep this for compatibility with older code */ KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL krb5_free_creds_contents (krb5_context context, krb5_creds *c) KRB5_DEPRECATED_FUNCTION("Use X instead") { return krb5_free_cred_contents (context, c); } /** * Free the error message returned by krb5_get_error_string(). * * Deprecated: use krb5_free_error_message() * * @param context Kerberos context * @param str error message to free * * @ingroup krb5_deprecated */ KRB5_LIB_FUNCTION void KRB5_LIB_CALL krb5_free_error_string(krb5_context context, char *str) KRB5_DEPRECATED_FUNCTION("Use X instead") { krb5_free_error_message(context, str); } /** * Set the error message returned by krb5_get_error_string(). * * Deprecated: use krb5_get_error_message() * * @param context Kerberos context * @param fmt error message to free * * @return Return an error code or 0. * * @ingroup krb5_deprecated */ KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL krb5_set_error_string(krb5_context context, const char *fmt, ...) __attribute__((format (printf, 2, 3))) KRB5_DEPRECATED_FUNCTION("Use X instead") { va_list ap; va_start(ap, fmt); krb5_vset_error_message (context, 0, fmt, ap); va_end(ap); return 0; } /** * Set the error message returned by krb5_get_error_string(), * deprecated, use krb5_set_error_message(). * * Deprecated: use krb5_vset_error_message() * * @param context Kerberos context * @param msg error message to free * * @return Return an error code or 0. * * @ingroup krb5_deprecated */ KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL krb5_vset_error_string(krb5_context context, const char *fmt, va_list args) __attribute__ ((format (printf, 2, 0))) KRB5_DEPRECATED_FUNCTION("Use X instead") { krb5_vset_error_message(context, 0, fmt, args); return 0; } /** * Clear the error message returned by krb5_get_error_string(). * * Deprecated: use krb5_clear_error_message() * * @param context Kerberos context * * @ingroup krb5_deprecated */ KRB5_LIB_FUNCTION void KRB5_LIB_CALL krb5_clear_error_string(krb5_context context) KRB5_DEPRECATED_FUNCTION("Use X instead") { krb5_clear_error_message(context); } /** * Deprecated: use krb5_get_credentials_with_flags(). * * @ingroup krb5_deprecated */ KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL krb5_get_cred_from_kdc_opt(krb5_context context, krb5_ccache ccache, krb5_creds *in_creds, krb5_creds **out_creds, krb5_creds ***ret_tgts, krb5_flags flags) KRB5_DEPRECATED_FUNCTION("Use X instead") { krb5_kdc_flags f; f.i = flags; return _krb5_get_cred_kdc_any(context, f, ccache, in_creds, NULL, NULL, out_creds, ret_tgts); } /** * Deprecated: use krb5_get_credentials_with_flags(). * * @ingroup krb5_deprecated */ KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL krb5_get_cred_from_kdc(krb5_context context, krb5_ccache ccache, krb5_creds *in_creds, krb5_creds **out_creds, krb5_creds ***ret_tgts) KRB5_DEPRECATED_FUNCTION("Use X instead") { return krb5_get_cred_from_kdc_opt(context, ccache, in_creds, out_creds, ret_tgts, 0); } /** * Deprecated: use krb5_xfree(). * * @ingroup krb5_deprecated */ KRB5_LIB_FUNCTION void KRB5_LIB_CALL krb5_free_unparsed_name(krb5_context context, char *str) KRB5_DEPRECATED_FUNCTION("Use X instead") { krb5_xfree(str); } /** * Deprecated: use krb5_generate_subkey_extended() * * @ingroup krb5_deprecated */ KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL krb5_generate_subkey(krb5_context context, const krb5_keyblock *key, krb5_keyblock **subkey) KRB5_DEPRECATED_FUNCTION("Use X instead") { return krb5_generate_subkey_extended(context, key, ETYPE_NULL, subkey); } /** * Deprecated: use krb5_auth_con_getremoteseqnumber() * * @ingroup krb5_deprecated */ KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL krb5_auth_getremoteseqnumber(krb5_context context, krb5_auth_context auth_context, int32_t *seqnumber) KRB5_DEPRECATED_FUNCTION("Use X instead") { *seqnumber = auth_context->remote_seqnumber; return 0; } #endif /* HEIMDAL_SMALLER */ diff --git a/crypto/heimdal/lib/krb5/init_creds_pw.c b/crypto/heimdal/lib/krb5/init_creds_pw.c index 37f4147c372a..9f7c2f3eea3e 100644 --- a/crypto/heimdal/lib/krb5/init_creds_pw.c +++ b/crypto/heimdal/lib/krb5/init_creds_pw.c @@ -1,2150 +1,2148 @@ /* * Copyright (c) 1997 - 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 "krb5_locl.h" typedef struct krb5_get_init_creds_ctx { KDCOptions flags; krb5_creds cred; krb5_addresses *addrs; krb5_enctype *etypes; krb5_preauthtype *pre_auth_types; char *in_tkt_service; unsigned nonce; unsigned pk_nonce; krb5_data req_buffer; AS_REQ as_req; int pa_counter; /* password and keytab_data is freed on completion */ char *password; krb5_keytab_key_proc_args *keytab_data; krb5_pointer *keyseed; krb5_s2k_proc keyproc; krb5_get_init_creds_tristate req_pac; krb5_pk_init_ctx pk_init_ctx; int ic_flags; int used_pa_types; #define USED_PKINIT 1 #define USED_PKINIT_W2K 2 #define USED_ENC_TS_GUESS 4 #define USED_ENC_TS_INFO 8 METHOD_DATA md; KRB_ERROR error; AS_REP as_rep; EncKDCRepPart enc_part; krb5_prompter_fct prompter; void *prompter_data; struct pa_info_data *ppaid; } krb5_get_init_creds_ctx; struct pa_info_data { krb5_enctype etype; krb5_salt salt; krb5_data *s2kparams; }; static void free_paid(krb5_context context, struct pa_info_data *ppaid) { krb5_free_salt(context, ppaid->salt); if (ppaid->s2kparams) krb5_free_data(context, ppaid->s2kparams); } static krb5_error_code KRB5_CALLCONV default_s2k_func(krb5_context context, krb5_enctype type, krb5_const_pointer keyseed, krb5_salt salt, krb5_data *s2kparms, krb5_keyblock **key) { krb5_error_code ret; krb5_data password; krb5_data opaque; _krb5_debug(context, 5, "krb5_get_init_creds: using default_s2k_func"); password.data = rk_UNCONST(keyseed); password.length = strlen(keyseed); if (s2kparms) opaque = *s2kparms; else krb5_data_zero(&opaque); *key = malloc(sizeof(**key)); if (*key == NULL) return ENOMEM; ret = krb5_string_to_key_data_salt_opaque(context, type, password, salt, opaque, *key); if (ret) { free(*key); *key = NULL; } return ret; } static void free_init_creds_ctx(krb5_context context, krb5_init_creds_context ctx) { if (ctx->etypes) free(ctx->etypes); if (ctx->pre_auth_types) free (ctx->pre_auth_types); if (ctx->in_tkt_service) free(ctx->in_tkt_service); if (ctx->keytab_data) free(ctx->keytab_data); if (ctx->password) { memset(ctx->password, 0, strlen(ctx->password)); free(ctx->password); } krb5_data_free(&ctx->req_buffer); krb5_free_cred_contents(context, &ctx->cred); free_METHOD_DATA(&ctx->md); free_AS_REP(&ctx->as_rep); free_EncKDCRepPart(&ctx->enc_part); free_KRB_ERROR(&ctx->error); free_AS_REQ(&ctx->as_req); if (ctx->ppaid) { free_paid(context, ctx->ppaid); free(ctx->ppaid); } memset(ctx, 0, sizeof(*ctx)); } static int get_config_time (krb5_context context, const char *realm, const char *name, int def) { int ret; ret = krb5_config_get_time (context, NULL, "realms", realm, name, NULL); if (ret >= 0) return ret; ret = krb5_config_get_time (context, NULL, "libdefaults", name, NULL); if (ret >= 0) return ret; return def; } static krb5_error_code init_cred (krb5_context context, krb5_creds *cred, krb5_principal client, krb5_deltat start_time, krb5_get_init_creds_opt *options) { krb5_error_code ret; int tmp; krb5_timestamp now; krb5_timeofday (context, &now); memset (cred, 0, sizeof(*cred)); if (client) krb5_copy_principal(context, client, &cred->client); else { ret = krb5_get_default_principal (context, &cred->client); if (ret) goto out; } if (start_time) cred->times.starttime = now + start_time; if (options->flags & KRB5_GET_INIT_CREDS_OPT_TKT_LIFE) tmp = options->tkt_life; else tmp = 10 * 60 * 60; cred->times.endtime = now + tmp; if ((options->flags & KRB5_GET_INIT_CREDS_OPT_RENEW_LIFE) && options->renew_life > 0) { cred->times.renew_till = now + options->renew_life; } return 0; out: krb5_free_cred_contents (context, cred); return ret; } /* * Print a message (str) to the user about the expiration in `lr' */ static void report_expiration (krb5_context context, krb5_prompter_fct prompter, krb5_data *data, const char *str, time_t now) { char *p = NULL; if (asprintf(&p, "%s%s", str, ctime(&now)) < 0 || p == NULL) return; (*prompter)(context, data, NULL, p, 0, NULL); free(p); } /* * Check the context, and in the case there is a expiration warning, * use the prompter to print the warning. * * @param context A Kerberos 5 context. * @param options An GIC options structure * @param ctx The krb5_init_creds_context check for expiration. */ static krb5_error_code process_last_request(krb5_context context, krb5_get_init_creds_opt *options, krb5_init_creds_context ctx) { krb5_const_realm realm; LastReq *lr; krb5_boolean reported = FALSE; krb5_timestamp sec; time_t t; size_t i; /* * First check if there is a API consumer. */ realm = krb5_principal_get_realm (context, ctx->cred.client); lr = &ctx->enc_part.last_req; if (options && options->opt_private && options->opt_private->lr.func) { krb5_last_req_entry **lre; lre = calloc(lr->len + 1, sizeof(**lre)); if (lre == NULL) { krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", "")); return ENOMEM; } for (i = 0; i < lr->len; i++) { lre[i] = calloc(1, sizeof(*lre[i])); if (lre[i] == NULL) break; lre[i]->lr_type = lr->val[i].lr_type; lre[i]->value = lr->val[i].lr_value; } (*options->opt_private->lr.func)(context, lre, options->opt_private->lr.ctx); for (i = 0; i < lr->len; i++) free(lre[i]); free(lre); } /* * Now check if we should prompt the user */ if (ctx->prompter == NULL) return 0; krb5_timeofday (context, &sec); t = sec + get_config_time (context, realm, "warn_pwexpire", 7 * 24 * 60 * 60); for (i = 0; i < lr->len; ++i) { if (lr->val[i].lr_value <= t) { switch (abs(lr->val[i].lr_type)) { case LR_PW_EXPTIME : report_expiration(context, ctx->prompter, ctx->prompter_data, "Your password will expire at ", lr->val[i].lr_value); reported = TRUE; break; case LR_ACCT_EXPTIME : report_expiration(context, ctx->prompter, ctx->prompter_data, "Your account will expire at ", lr->val[i].lr_value); reported = TRUE; break; } } } if (!reported && ctx->enc_part.key_expiration && *ctx->enc_part.key_expiration <= t) { report_expiration(context, ctx->prompter, ctx->prompter_data, "Your password/account will expire at ", *ctx->enc_part.key_expiration); } return 0; } static krb5_addresses no_addrs = { 0, NULL }; static krb5_error_code get_init_creds_common(krb5_context context, krb5_principal client, krb5_deltat start_time, krb5_get_init_creds_opt *options, krb5_init_creds_context ctx) { krb5_get_init_creds_opt *default_opt = NULL; krb5_error_code ret; krb5_enctype *etypes; krb5_preauthtype *pre_auth_types; memset(ctx, 0, sizeof(*ctx)); if (options == NULL) { const char *realm = krb5_principal_get_realm(context, client); krb5_get_init_creds_opt_alloc (context, &default_opt); options = default_opt; krb5_get_init_creds_opt_set_default_flags(context, NULL, realm, options); } if (options->opt_private) { if (options->opt_private->password) { ret = krb5_init_creds_set_password(context, ctx, options->opt_private->password); if (ret) goto out; } ctx->keyproc = options->opt_private->key_proc; ctx->req_pac = options->opt_private->req_pac; ctx->pk_init_ctx = options->opt_private->pk_init_ctx; ctx->ic_flags = options->opt_private->flags; } else ctx->req_pac = KRB5_INIT_CREDS_TRISTATE_UNSET; if (ctx->keyproc == NULL) ctx->keyproc = default_s2k_func; /* Enterprise name implicitly turns on canonicalize */ if ((ctx->ic_flags & KRB5_INIT_CREDS_CANONICALIZE) || krb5_principal_get_type(context, client) == KRB5_NT_ENTERPRISE_PRINCIPAL) ctx->flags.canonicalize = 1; ctx->pre_auth_types = NULL; ctx->addrs = NULL; ctx->etypes = NULL; ctx->pre_auth_types = NULL; ret = init_cred(context, &ctx->cred, client, start_time, options); if (ret) { if (default_opt) krb5_get_init_creds_opt_free(context, default_opt); return ret; } ret = krb5_init_creds_set_service(context, ctx, NULL); if (ret) goto out; if (options->flags & KRB5_GET_INIT_CREDS_OPT_FORWARDABLE) ctx->flags.forwardable = options->forwardable; if (options->flags & KRB5_GET_INIT_CREDS_OPT_PROXIABLE) ctx->flags.proxiable = options->proxiable; if (start_time) ctx->flags.postdated = 1; if (ctx->cred.times.renew_till) ctx->flags.renewable = 1; if (options->flags & KRB5_GET_INIT_CREDS_OPT_ADDRESS_LIST) { ctx->addrs = options->address_list; } else if (options->opt_private) { switch (options->opt_private->addressless) { case KRB5_INIT_CREDS_TRISTATE_UNSET: #if KRB5_ADDRESSLESS_DEFAULT == TRUE ctx->addrs = &no_addrs; #else ctx->addrs = NULL; #endif break; case KRB5_INIT_CREDS_TRISTATE_FALSE: ctx->addrs = NULL; break; case KRB5_INIT_CREDS_TRISTATE_TRUE: ctx->addrs = &no_addrs; break; } } if (options->flags & KRB5_GET_INIT_CREDS_OPT_ETYPE_LIST) { if (ctx->etypes) free(ctx->etypes); etypes = malloc((options->etype_list_length + 1) * sizeof(krb5_enctype)); if (etypes == NULL) { ret = ENOMEM; krb5_set_error_message(context, ret, N_("malloc: out of memory", "")); goto out; } memcpy (etypes, options->etype_list, options->etype_list_length * sizeof(krb5_enctype)); etypes[options->etype_list_length] = ETYPE_NULL; ctx->etypes = etypes; } if (options->flags & KRB5_GET_INIT_CREDS_OPT_PREAUTH_LIST) { pre_auth_types = malloc((options->preauth_list_length + 1) * sizeof(krb5_preauthtype)); if (pre_auth_types == NULL) { ret = ENOMEM; krb5_set_error_message(context, ret, N_("malloc: out of memory", "")); goto out; } memcpy (pre_auth_types, options->preauth_list, options->preauth_list_length * sizeof(krb5_preauthtype)); pre_auth_types[options->preauth_list_length] = KRB5_PADATA_NONE; ctx->pre_auth_types = pre_auth_types; } if (options->flags & KRB5_GET_INIT_CREDS_OPT_ANONYMOUS) ctx->flags.request_anonymous = options->anonymous; if (default_opt) krb5_get_init_creds_opt_free(context, default_opt); return 0; out: if (default_opt) krb5_get_init_creds_opt_free(context, default_opt); return ret; } static krb5_error_code change_password (krb5_context context, krb5_principal client, const char *password, char *newpw, size_t newpw_sz, krb5_prompter_fct prompter, void *data, krb5_get_init_creds_opt *old_options) { krb5_prompt prompts[2]; krb5_error_code ret; krb5_creds cpw_cred; char buf1[BUFSIZ], buf2[BUFSIZ]; krb5_data password_data[2]; int result_code; krb5_data result_code_string; krb5_data result_string; char *p; krb5_get_init_creds_opt *options; memset (&cpw_cred, 0, sizeof(cpw_cred)); ret = krb5_get_init_creds_opt_alloc(context, &options); if (ret) return ret; krb5_get_init_creds_opt_set_tkt_life (options, 60); krb5_get_init_creds_opt_set_forwardable (options, FALSE); krb5_get_init_creds_opt_set_proxiable (options, FALSE); if (old_options && old_options->flags & KRB5_GET_INIT_CREDS_OPT_PREAUTH_LIST) krb5_get_init_creds_opt_set_preauth_list (options, old_options->preauth_list, old_options->preauth_list_length); krb5_data_zero (&result_code_string); krb5_data_zero (&result_string); ret = krb5_get_init_creds_password (context, &cpw_cred, client, password, prompter, data, 0, "kadmin/changepw", options); krb5_get_init_creds_opt_free(context, options); if (ret) goto out; for(;;) { password_data[0].data = buf1; password_data[0].length = sizeof(buf1); prompts[0].hidden = 1; prompts[0].prompt = "New password: "; prompts[0].reply = &password_data[0]; prompts[0].type = KRB5_PROMPT_TYPE_NEW_PASSWORD; password_data[1].data = buf2; password_data[1].length = sizeof(buf2); prompts[1].hidden = 1; prompts[1].prompt = "Repeat new password: "; prompts[1].reply = &password_data[1]; prompts[1].type = KRB5_PROMPT_TYPE_NEW_PASSWORD_AGAIN; ret = (*prompter) (context, data, NULL, "Changing password", 2, prompts); if (ret) { memset (buf1, 0, sizeof(buf1)); memset (buf2, 0, sizeof(buf2)); goto out; } if (strcmp (buf1, buf2) == 0) break; memset (buf1, 0, sizeof(buf1)); memset (buf2, 0, sizeof(buf2)); } ret = krb5_set_password (context, &cpw_cred, buf1, client, &result_code, &result_code_string, &result_string); if (ret) goto out; if (asprintf(&p, "%s: %.*s\n", result_code ? "Error" : "Success", (int)result_string.length, result_string.length > 0 ? (char*)result_string.data : "") < 0) { ret = ENOMEM; goto out; } /* return the result */ (*prompter) (context, data, NULL, p, 0, NULL); free (p); if (result_code == 0) { strlcpy (newpw, buf1, newpw_sz); ret = 0; } else { ret = ENOTTY; krb5_set_error_message(context, ret, N_("failed changing password", "")); } out: memset (buf1, 0, sizeof(buf1)); memset (buf2, 0, sizeof(buf2)); krb5_data_free (&result_string); krb5_data_free (&result_code_string); krb5_free_cred_contents (context, &cpw_cred); return ret; } KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL krb5_keyblock_key_proc (krb5_context context, krb5_keytype type, krb5_data *salt, krb5_const_pointer keyseed, krb5_keyblock **key) { return krb5_copy_keyblock (context, keyseed, key); } /* * */ static krb5_error_code init_as_req (krb5_context context, KDCOptions opts, const krb5_creds *creds, const krb5_addresses *addrs, const krb5_enctype *etypes, AS_REQ *a) { krb5_error_code ret; memset(a, 0, sizeof(*a)); a->pvno = 5; a->msg_type = krb_as_req; a->req_body.kdc_options = opts; a->req_body.cname = malloc(sizeof(*a->req_body.cname)); if (a->req_body.cname == NULL) { ret = ENOMEM; krb5_set_error_message(context, ret, N_("malloc: out of memory", "")); goto fail; } a->req_body.sname = malloc(sizeof(*a->req_body.sname)); if (a->req_body.sname == NULL) { ret = ENOMEM; krb5_set_error_message(context, ret, N_("malloc: out of memory", "")); goto fail; } ret = _krb5_principal2principalname (a->req_body.cname, creds->client); if (ret) goto fail; ret = copy_Realm(&creds->client->realm, &a->req_body.realm); if (ret) goto fail; ret = _krb5_principal2principalname (a->req_body.sname, creds->server); if (ret) goto fail; if(creds->times.starttime) { a->req_body.from = malloc(sizeof(*a->req_body.from)); if (a->req_body.from == NULL) { ret = ENOMEM; krb5_set_error_message(context, ret, N_("malloc: out of memory", "")); goto fail; } *a->req_body.from = creds->times.starttime; } if(creds->times.endtime){ ALLOC(a->req_body.till, 1); *a->req_body.till = creds->times.endtime; } if(creds->times.renew_till){ a->req_body.rtime = malloc(sizeof(*a->req_body.rtime)); if (a->req_body.rtime == NULL) { ret = ENOMEM; krb5_set_error_message(context, ret, N_("malloc: out of memory", "")); goto fail; } *a->req_body.rtime = creds->times.renew_till; } a->req_body.nonce = 0; ret = _krb5_init_etype(context, KRB5_PDU_AS_REQUEST, &a->req_body.etype.len, &a->req_body.etype.val, etypes); if (ret) goto fail; /* * This means no addresses */ if (addrs && addrs->len == 0) { a->req_body.addresses = NULL; } else { a->req_body.addresses = malloc(sizeof(*a->req_body.addresses)); if (a->req_body.addresses == NULL) { ret = ENOMEM; krb5_set_error_message(context, ret, N_("malloc: out of memory", "")); goto fail; } if (addrs) ret = krb5_copy_addresses(context, addrs, a->req_body.addresses); else { ret = krb5_get_all_client_addrs (context, a->req_body.addresses); if(ret == 0 && a->req_body.addresses->len == 0) { free(a->req_body.addresses); a->req_body.addresses = NULL; } } if (ret) goto fail; } a->req_body.enc_authorization_data = NULL; a->req_body.additional_tickets = NULL; a->padata = NULL; return 0; fail: free_AS_REQ(a); memset(a, 0, sizeof(*a)); return ret; } static krb5_error_code set_paid(struct pa_info_data *paid, krb5_context context, krb5_enctype etype, krb5_salttype salttype, void *salt_string, size_t salt_len, krb5_data *s2kparams) { paid->etype = etype; paid->salt.salttype = salttype; paid->salt.saltvalue.data = malloc(salt_len + 1); if (paid->salt.saltvalue.data == NULL) { krb5_clear_error_message(context); return ENOMEM; } memcpy(paid->salt.saltvalue.data, salt_string, salt_len); ((char *)paid->salt.saltvalue.data)[salt_len] = '\0'; paid->salt.saltvalue.length = salt_len; if (s2kparams) { krb5_error_code ret; ret = krb5_copy_data(context, s2kparams, &paid->s2kparams); if (ret) { krb5_clear_error_message(context); krb5_free_salt(context, paid->salt); return ret; } } else paid->s2kparams = NULL; return 0; } static struct pa_info_data * pa_etype_info2(krb5_context context, const krb5_principal client, const AS_REQ *asreq, struct pa_info_data *paid, heim_octet_string *data) { krb5_error_code ret; ETYPE_INFO2 e; size_t sz; size_t i, j; memset(&e, 0, sizeof(e)); ret = decode_ETYPE_INFO2(data->data, data->length, &e, &sz); if (ret) goto out; if (e.len == 0) goto out; for (j = 0; j < asreq->req_body.etype.len; j++) { for (i = 0; i < e.len; i++) { if (asreq->req_body.etype.val[j] == e.val[i].etype) { krb5_salt salt; if (e.val[i].salt == NULL) ret = krb5_get_pw_salt(context, client, &salt); else { salt.saltvalue.data = *e.val[i].salt; salt.saltvalue.length = strlen(*e.val[i].salt); ret = 0; } if (ret == 0) ret = set_paid(paid, context, e.val[i].etype, KRB5_PW_SALT, salt.saltvalue.data, salt.saltvalue.length, e.val[i].s2kparams); if (e.val[i].salt == NULL) krb5_free_salt(context, salt); if (ret == 0) { free_ETYPE_INFO2(&e); return paid; } } } } out: free_ETYPE_INFO2(&e); return NULL; } static struct pa_info_data * pa_etype_info(krb5_context context, const krb5_principal client, const AS_REQ *asreq, struct pa_info_data *paid, heim_octet_string *data) { krb5_error_code ret; ETYPE_INFO e; size_t sz; size_t i, j; memset(&e, 0, sizeof(e)); ret = decode_ETYPE_INFO(data->data, data->length, &e, &sz); if (ret) goto out; if (e.len == 0) goto out; for (j = 0; j < asreq->req_body.etype.len; j++) { for (i = 0; i < e.len; i++) { if (asreq->req_body.etype.val[j] == e.val[i].etype) { krb5_salt salt; salt.salttype = KRB5_PW_SALT; if (e.val[i].salt == NULL) ret = krb5_get_pw_salt(context, client, &salt); else { salt.saltvalue = *e.val[i].salt; ret = 0; } if (e.val[i].salttype) salt.salttype = *e.val[i].salttype; if (ret == 0) { ret = set_paid(paid, context, e.val[i].etype, salt.salttype, salt.saltvalue.data, salt.saltvalue.length, NULL); if (e.val[i].salt == NULL) krb5_free_salt(context, salt); } if (ret == 0) { free_ETYPE_INFO(&e); return paid; } } } } out: free_ETYPE_INFO(&e); return NULL; } static struct pa_info_data * pa_pw_or_afs3_salt(krb5_context context, const krb5_principal client, const AS_REQ *asreq, struct pa_info_data *paid, heim_octet_string *data) { krb5_error_code ret; if (paid->etype == ENCTYPE_NULL) return NULL; ret = set_paid(paid, context, paid->etype, paid->salt.salttype, data->data, data->length, NULL); if (ret) return NULL; return paid; } struct pa_info { krb5_preauthtype type; struct pa_info_data *(*salt_info)(krb5_context, const krb5_principal, const AS_REQ *, struct pa_info_data *, heim_octet_string *); }; static struct pa_info pa_prefs[] = { { KRB5_PADATA_ETYPE_INFO2, pa_etype_info2 }, { KRB5_PADATA_ETYPE_INFO, pa_etype_info }, { KRB5_PADATA_PW_SALT, pa_pw_or_afs3_salt }, { KRB5_PADATA_AFS3_SALT, pa_pw_or_afs3_salt } }; static PA_DATA * find_pa_data(const METHOD_DATA *md, unsigned type) { size_t i; if (md == NULL) return NULL; for (i = 0; i < md->len; i++) if (md->val[i].padata_type == type) return &md->val[i]; return NULL; } static struct pa_info_data * process_pa_info(krb5_context context, const krb5_principal client, const AS_REQ *asreq, struct pa_info_data *paid, METHOD_DATA *md) { struct pa_info_data *p = NULL; size_t i; for (i = 0; p == NULL && i < sizeof(pa_prefs)/sizeof(pa_prefs[0]); i++) { PA_DATA *pa = find_pa_data(md, pa_prefs[i].type); if (pa == NULL) continue; paid->salt.salttype = (krb5_salttype)pa_prefs[i].type; p = (*pa_prefs[i].salt_info)(context, client, asreq, paid, &pa->padata_value); } return p; } static krb5_error_code make_pa_enc_timestamp(krb5_context context, METHOD_DATA *md, krb5_enctype etype, krb5_keyblock *key) { PA_ENC_TS_ENC p; unsigned char *buf; size_t buf_size; size_t len = 0; EncryptedData encdata; krb5_error_code ret; int32_t usec; int usec2; krb5_crypto crypto; krb5_us_timeofday (context, &p.patimestamp, &usec); usec2 = usec; p.pausec = &usec2; ASN1_MALLOC_ENCODE(PA_ENC_TS_ENC, buf, buf_size, &p, &len, ret); if (ret) return ret; if(buf_size != len) krb5_abortx(context, "internal error in ASN.1 encoder"); ret = krb5_crypto_init(context, key, 0, &crypto); if (ret) { free(buf); return ret; } ret = krb5_encrypt_EncryptedData(context, crypto, KRB5_KU_PA_ENC_TIMESTAMP, buf, len, 0, &encdata); free(buf); krb5_crypto_destroy(context, crypto); if (ret) return ret; ASN1_MALLOC_ENCODE(EncryptedData, buf, buf_size, &encdata, &len, ret); free_EncryptedData(&encdata); if (ret) return ret; if(buf_size != len) krb5_abortx(context, "internal error in ASN.1 encoder"); ret = krb5_padata_add(context, md, KRB5_PADATA_ENC_TIMESTAMP, buf, len); if (ret) free(buf); return ret; } static krb5_error_code add_enc_ts_padata(krb5_context context, METHOD_DATA *md, krb5_principal client, krb5_s2k_proc keyproc, krb5_const_pointer keyseed, krb5_enctype *enctypes, unsigned netypes, krb5_salt *salt, krb5_data *s2kparams) { krb5_error_code ret; krb5_salt salt2; krb5_enctype *ep; size_t i; if(salt == NULL) { /* default to standard salt */ ret = krb5_get_pw_salt (context, client, &salt2); if (ret) return ret; salt = &salt2; } if (!enctypes) { enctypes = context->etypes; netypes = 0; for (ep = enctypes; *ep != ETYPE_NULL; ep++) netypes++; } for (i = 0; i < netypes; ++i) { krb5_keyblock *key; _krb5_debug(context, 5, "krb5_get_init_creds: using ENC-TS with enctype %d", enctypes[i]); ret = (*keyproc)(context, enctypes[i], keyseed, *salt, s2kparams, &key); if (ret) continue; ret = make_pa_enc_timestamp (context, md, enctypes[i], key); krb5_free_keyblock (context, key); if (ret) return ret; } if(salt == &salt2) krb5_free_salt(context, salt2); return 0; } static krb5_error_code pa_data_to_md_ts_enc(krb5_context context, const AS_REQ *a, const krb5_principal client, krb5_get_init_creds_ctx *ctx, struct pa_info_data *ppaid, METHOD_DATA *md) { if (ctx->keyproc == NULL || ctx->keyseed == NULL) return 0; if (ppaid) { add_enc_ts_padata(context, md, client, ctx->keyproc, ctx->keyseed, &ppaid->etype, 1, &ppaid->salt, ppaid->s2kparams); } else { krb5_salt salt; _krb5_debug(context, 5, "krb5_get_init_creds: pa-info not found, guessing salt"); /* make a v5 salted pa-data */ add_enc_ts_padata(context, md, client, ctx->keyproc, ctx->keyseed, a->req_body.etype.val, a->req_body.etype.len, NULL, NULL); /* make a v4 salted pa-data */ salt.salttype = KRB5_PW_SALT; krb5_data_zero(&salt.saltvalue); add_enc_ts_padata(context, md, client, ctx->keyproc, ctx->keyseed, a->req_body.etype.val, a->req_body.etype.len, &salt, NULL); } return 0; } static krb5_error_code pa_data_to_key_plain(krb5_context context, const krb5_principal client, krb5_get_init_creds_ctx *ctx, krb5_salt salt, krb5_data *s2kparams, krb5_enctype etype, krb5_keyblock **key) { krb5_error_code ret; ret = (*ctx->keyproc)(context, etype, ctx->keyseed, salt, s2kparams, key); return ret; } static krb5_error_code pa_data_to_md_pkinit(krb5_context context, const AS_REQ *a, const krb5_principal client, int win2k, krb5_get_init_creds_ctx *ctx, METHOD_DATA *md) { if (ctx->pk_init_ctx == NULL) return 0; #ifdef PKINIT return _krb5_pk_mk_padata(context, ctx->pk_init_ctx, ctx->ic_flags, win2k, &a->req_body, ctx->pk_nonce, md); #else krb5_set_error_message(context, EINVAL, N_("no support for PKINIT compiled in", "")); return EINVAL; #endif } static krb5_error_code pa_data_add_pac_request(krb5_context context, krb5_get_init_creds_ctx *ctx, METHOD_DATA *md) { size_t len = 0, length; krb5_error_code ret; PA_PAC_REQUEST req; void *buf; switch (ctx->req_pac) { case KRB5_INIT_CREDS_TRISTATE_UNSET: return 0; /* don't bother */ case KRB5_INIT_CREDS_TRISTATE_TRUE: req.include_pac = 1; break; case KRB5_INIT_CREDS_TRISTATE_FALSE: req.include_pac = 0; } ASN1_MALLOC_ENCODE(PA_PAC_REQUEST, buf, length, &req, &len, ret); if (ret) return ret; if(len != length) krb5_abortx(context, "internal error in ASN.1 encoder"); ret = krb5_padata_add(context, md, KRB5_PADATA_PA_PAC_REQUEST, buf, len); if (ret) free(buf); return 0; } /* * Assumes caller always will free `out_md', even on error. */ static krb5_error_code process_pa_data_to_md(krb5_context context, const krb5_creds *creds, const AS_REQ *a, krb5_get_init_creds_ctx *ctx, METHOD_DATA *in_md, METHOD_DATA **out_md, krb5_prompter_fct prompter, void *prompter_data) { krb5_error_code ret; ALLOC(*out_md, 1); if (*out_md == NULL) { krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", "")); return ENOMEM; } (*out_md)->len = 0; (*out_md)->val = NULL; if (_krb5_have_debug(context, 5)) { unsigned i; _krb5_debug(context, 5, "KDC send %d patypes", in_md->len); for (i = 0; i < in_md->len; i++) _krb5_debug(context, 5, "KDC send PA-DATA type: %d", in_md->val[i].padata_type); } /* * Make sure we don't sent both ENC-TS and PK-INIT pa data, no * need to expose our password protecting our PKCS12 key. */ if (ctx->pk_init_ctx) { _krb5_debug(context, 5, "krb5_get_init_creds: " "prepareing PKINIT padata (%s)", (ctx->used_pa_types & USED_PKINIT_W2K) ? "win2k" : "ietf"); if (ctx->used_pa_types & USED_PKINIT_W2K) { krb5_set_error_message(context, KRB5_GET_IN_TKT_LOOP, "Already tried pkinit, looping"); return KRB5_GET_IN_TKT_LOOP; } ret = pa_data_to_md_pkinit(context, a, creds->client, (ctx->used_pa_types & USED_PKINIT), ctx, *out_md); if (ret) return ret; if (ctx->used_pa_types & USED_PKINIT) ctx->used_pa_types |= USED_PKINIT_W2K; else ctx->used_pa_types |= USED_PKINIT; } else if (in_md->len != 0) { struct pa_info_data *paid, *ppaid; unsigned flag; paid = calloc(1, sizeof(*paid)); paid->etype = ENCTYPE_NULL; ppaid = process_pa_info(context, creds->client, a, paid, in_md); if (ppaid) flag = USED_ENC_TS_INFO; else flag = USED_ENC_TS_GUESS; if (ctx->used_pa_types & flag) { if (ppaid) free_paid(context, ppaid); krb5_set_error_message(context, KRB5_GET_IN_TKT_LOOP, "Already tried ENC-TS-%s, looping", flag == USED_ENC_TS_INFO ? "info" : "guess"); return KRB5_GET_IN_TKT_LOOP; } pa_data_to_md_ts_enc(context, a, creds->client, ctx, ppaid, *out_md); ctx->used_pa_types |= flag; if (ppaid) { if (ctx->ppaid) { free_paid(context, ctx->ppaid); free(ctx->ppaid); } ctx->ppaid = ppaid; } else free(paid); } pa_data_add_pac_request(context, ctx, *out_md); if ((*out_md)->len == 0) { free(*out_md); *out_md = NULL; } return 0; } static krb5_error_code process_pa_data_to_key(krb5_context context, krb5_get_init_creds_ctx *ctx, krb5_creds *creds, AS_REQ *a, AS_REP *rep, const krb5_krbhst_info *hi, krb5_keyblock **key) { struct pa_info_data paid, *ppaid = NULL; krb5_error_code ret; krb5_enctype etype; PA_DATA *pa; memset(&paid, 0, sizeof(paid)); etype = rep->enc_part.etype; if (rep->padata) { paid.etype = etype; ppaid = process_pa_info(context, creds->client, a, &paid, rep->padata); } if (ppaid == NULL) ppaid = ctx->ppaid; if (ppaid == NULL) { ret = krb5_get_pw_salt (context, creds->client, &paid.salt); if (ret) return ret; paid.etype = etype; paid.s2kparams = NULL; ppaid = &paid; } pa = NULL; if (rep->padata) { int idx = 0; pa = krb5_find_padata(rep->padata->val, rep->padata->len, KRB5_PADATA_PK_AS_REP, &idx); if (pa == NULL) { idx = 0; pa = krb5_find_padata(rep->padata->val, rep->padata->len, KRB5_PADATA_PK_AS_REP_19, &idx); } } if (pa && ctx->pk_init_ctx) { #ifdef PKINIT _krb5_debug(context, 5, "krb5_get_init_creds: using PKINIT"); ret = _krb5_pk_rd_pa_reply(context, a->req_body.realm, ctx->pk_init_ctx, etype, hi, ctx->pk_nonce, &ctx->req_buffer, pa, key); #else ret = EINVAL; krb5_set_error_message(context, ret, N_("no support for PKINIT compiled in", "")); #endif } else if (ctx->keyseed) { _krb5_debug(context, 5, "krb5_get_init_creds: using keyproc"); ret = pa_data_to_key_plain(context, creds->client, ctx, ppaid->salt, ppaid->s2kparams, etype, key); } else { ret = EINVAL; krb5_set_error_message(context, ret, N_("No usable pa data type", "")); } free_paid(context, &paid); return ret; } /** * Start a new context to get a new initial credential. * * @param context A Kerberos 5 context. * @param client The Kerberos principal to get the credential for, if * NULL is given, the default principal is used as determined by * krb5_get_default_principal(). * @param prompter * @param prompter_data * @param start_time the time the ticket should start to be valid or 0 for now. * @param options a options structure, can be NULL for default options. * @param rctx A new allocated free with krb5_init_creds_free(). * * @return 0 for success or an Kerberos 5 error code, see krb5_get_error_message(). * * @ingroup krb5_credential */ KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL krb5_init_creds_init(krb5_context context, krb5_principal client, krb5_prompter_fct prompter, void *prompter_data, krb5_deltat start_time, krb5_get_init_creds_opt *options, krb5_init_creds_context *rctx) { krb5_init_creds_context ctx; krb5_error_code ret; *rctx = NULL; ctx = calloc(1, sizeof(*ctx)); if (ctx == NULL) { krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", "")); return ENOMEM; } ret = get_init_creds_common(context, client, start_time, options, ctx); if (ret) { free(ctx); return ret; } /* Set a new nonce. */ krb5_generate_random_block (&ctx->nonce, sizeof(ctx->nonce)); ctx->nonce &= 0x7fffffff; /* XXX these just needs to be the same when using Windows PK-INIT */ ctx->pk_nonce = ctx->nonce; ctx->prompter = prompter; ctx->prompter_data = prompter_data; *rctx = ctx; return ret; } /** * Sets the service that the is requested. This call is only neede for * special initial tickets, by default the a krbtgt is fetched in the default realm. * * @param context a Kerberos 5 context. * @param ctx a krb5_init_creds_context context. * @param service the service given as a string, for example * "kadmind/admin". If NULL, the default krbtgt in the clients * realm is set. * * @return 0 for success, or an Kerberos 5 error code, see krb5_get_error_message(). * @ingroup krb5_credential */ KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL krb5_init_creds_set_service(krb5_context context, krb5_init_creds_context ctx, const char *service) { krb5_const_realm client_realm; krb5_principal principal; krb5_error_code ret; client_realm = krb5_principal_get_realm (context, ctx->cred.client); if (service) { ret = krb5_parse_name (context, service, &principal); if (ret) return ret; krb5_principal_set_realm (context, principal, client_realm); } else { ret = krb5_make_principal(context, &principal, client_realm, KRB5_TGS_NAME, client_realm, NULL); if (ret) return ret; } /* * This is for Windows RODC that are picky about what name type * the server principal have, and the really strange part is that * they are picky about the AS-REQ name type and not the TGS-REQ * later. Oh well. */ if (krb5_principal_is_krbtgt(context, principal)) krb5_principal_set_type(context, principal, KRB5_NT_SRV_INST); krb5_free_principal(context, ctx->cred.server); ctx->cred.server = principal; return 0; } /** * Sets the password that will use for the request. * * @param context a Kerberos 5 context. * @param ctx ctx krb5_init_creds_context context. * @param password the password to use. * * @return 0 for success, or an Kerberos 5 error code, see krb5_get_error_message(). * @ingroup krb5_credential */ KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL krb5_init_creds_set_password(krb5_context context, krb5_init_creds_context ctx, const char *password) { if (ctx->password) { memset(ctx->password, 0, strlen(ctx->password)); free(ctx->password); } if (password) { ctx->password = strdup(password); if (ctx->password == NULL) { krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", "")); return ENOMEM; } ctx->keyseed = (void *) ctx->password; } else { ctx->keyseed = NULL; ctx->password = NULL; } return 0; } static krb5_error_code KRB5_CALLCONV keytab_key_proc(krb5_context context, krb5_enctype enctype, krb5_const_pointer keyseed, krb5_salt salt, krb5_data *s2kparms, krb5_keyblock **key) { krb5_keytab_key_proc_args *args = rk_UNCONST(keyseed); krb5_keytab keytab = args->keytab; krb5_principal principal = args->principal; krb5_error_code ret; krb5_keytab real_keytab; krb5_keytab_entry entry; if(keytab == NULL) krb5_kt_default(context, &real_keytab); else real_keytab = keytab; ret = krb5_kt_get_entry (context, real_keytab, principal, 0, enctype, &entry); + if (ret == 0) { + ret = krb5_copy_keyblock(context, &entry.keyblock, key); + krb5_kt_free_entry(context, &entry); + } if (keytab == NULL) krb5_kt_close (context, real_keytab); - - if (ret) - return ret; - - ret = krb5_copy_keyblock (context, &entry.keyblock, key); - krb5_kt_free_entry(context, &entry); return ret; } /** * Set the keytab to use for authentication. * * @param context a Kerberos 5 context. * @param ctx ctx krb5_init_creds_context context. * @param keytab the keytab to read the key from. * * @return 0 for success, or an Kerberos 5 error code, see krb5_get_error_message(). * @ingroup krb5_credential */ KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL krb5_init_creds_set_keytab(krb5_context context, krb5_init_creds_context ctx, krb5_keytab keytab) { krb5_keytab_key_proc_args *a; krb5_keytab_entry entry; krb5_kt_cursor cursor; krb5_enctype *etypes = NULL; krb5_error_code ret; size_t netypes = 0; int kvno = 0; a = malloc(sizeof(*a)); if (a == NULL) { krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", "")); return ENOMEM; } a->principal = ctx->cred.client; a->keytab = keytab; ctx->keytab_data = a; ctx->keyseed = (void *)a; ctx->keyproc = keytab_key_proc; /* * We need to the KDC what enctypes we support for this keytab, * esp if the keytab is really a password based entry, then the * KDC might have more enctypes in the database then what we have * in the keytab. */ ret = krb5_kt_start_seq_get(context, keytab, &cursor); if(ret) goto out; while(krb5_kt_next_entry(context, keytab, &entry, &cursor) == 0){ void *ptr; if (!krb5_principal_compare(context, entry.principal, ctx->cred.client)) goto next; /* check if we ahve this kvno already */ if (entry.vno > kvno) { /* remove old list of etype */ if (etypes) free(etypes); etypes = NULL; netypes = 0; kvno = entry.vno; } else if (entry.vno != kvno) goto next; /* check if enctype is supported */ if (krb5_enctype_valid(context, entry.keyblock.keytype) != 0) goto next; /* add enctype to supported list */ ptr = realloc(etypes, sizeof(etypes[0]) * (netypes + 2)); if (ptr == NULL) goto next; etypes = ptr; etypes[netypes] = entry.keyblock.keytype; etypes[netypes + 1] = ETYPE_NULL; netypes++; next: krb5_kt_free_entry(context, &entry); } krb5_kt_end_seq_get(context, keytab, &cursor); if (etypes) { if (ctx->etypes) free(ctx->etypes); ctx->etypes = etypes; } out: return 0; } static krb5_error_code KRB5_CALLCONV keyblock_key_proc(krb5_context context, krb5_enctype enctype, krb5_const_pointer keyseed, krb5_salt salt, krb5_data *s2kparms, krb5_keyblock **key) { return krb5_copy_keyblock (context, keyseed, key); } KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL krb5_init_creds_set_keyblock(krb5_context context, krb5_init_creds_context ctx, krb5_keyblock *keyblock) { ctx->keyseed = (void *)keyblock; ctx->keyproc = keyblock_key_proc; return 0; } /** * The core loop if krb5_get_init_creds() function family. Create the * packets and have the caller send them off to the KDC. * * If the caller want all work been done for them, use * krb5_init_creds_get() instead. * * @param context a Kerberos 5 context. * @param ctx ctx krb5_init_creds_context context. * @param in input data from KDC, first round it should be reset by krb5_data_zer(). * @param out reply to KDC. * @param hostinfo KDC address info, first round it can be NULL. * @param flags status of the round, if * KRB5_INIT_CREDS_STEP_FLAG_CONTINUE is set, continue one more round. * * @return 0 for success, or an Kerberos 5 error code, see * krb5_get_error_message(). * * @ingroup krb5_credential */ KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL krb5_init_creds_step(krb5_context context, krb5_init_creds_context ctx, krb5_data *in, krb5_data *out, krb5_krbhst_info *hostinfo, unsigned int *flags) { krb5_error_code ret; size_t len = 0; size_t size; krb5_data_zero(out); if (ctx->as_req.req_body.cname == NULL) { ret = init_as_req(context, ctx->flags, &ctx->cred, ctx->addrs, ctx->etypes, &ctx->as_req); if (ret) { free_init_creds_ctx(context, ctx); return ret; } } #define MAX_PA_COUNTER 10 if (ctx->pa_counter > MAX_PA_COUNTER) { krb5_set_error_message(context, KRB5_GET_IN_TKT_LOOP, N_("Looping %d times while getting " "initial credentials", ""), ctx->pa_counter); return KRB5_GET_IN_TKT_LOOP; } ctx->pa_counter++; _krb5_debug(context, 5, "krb5_get_init_creds: loop %d", ctx->pa_counter); /* Lets process the input packet */ if (in && in->length) { krb5_kdc_rep rep; memset(&rep, 0, sizeof(rep)); _krb5_debug(context, 5, "krb5_get_init_creds: processing input"); ret = decode_AS_REP(in->data, in->length, &rep.kdc_rep, &size); if (ret == 0) { krb5_keyblock *key = NULL; unsigned eflags = EXTRACT_TICKET_AS_REQ | EXTRACT_TICKET_TIMESYNC; if (ctx->flags.canonicalize) { eflags |= EXTRACT_TICKET_ALLOW_SERVER_MISMATCH; eflags |= EXTRACT_TICKET_MATCH_REALM; } if (ctx->ic_flags & KRB5_INIT_CREDS_NO_C_CANON_CHECK) eflags |= EXTRACT_TICKET_ALLOW_CNAME_MISMATCH; ret = process_pa_data_to_key(context, ctx, &ctx->cred, &ctx->as_req, &rep.kdc_rep, hostinfo, &key); if (ret) { free_AS_REP(&rep.kdc_rep); goto out; } _krb5_debug(context, 5, "krb5_get_init_creds: extracting ticket"); ret = _krb5_extract_ticket(context, &rep, &ctx->cred, key, NULL, KRB5_KU_AS_REP_ENC_PART, NULL, ctx->nonce, eflags, NULL, NULL); krb5_free_keyblock(context, key); *flags = 0; if (ret == 0) ret = copy_EncKDCRepPart(&rep.enc_part, &ctx->enc_part); free_AS_REP(&rep.kdc_rep); free_EncASRepPart(&rep.enc_part); return ret; } else { /* let's try to parse it as a KRB-ERROR */ _krb5_debug(context, 5, "krb5_get_init_creds: got an error"); free_KRB_ERROR(&ctx->error); ret = krb5_rd_error(context, in, &ctx->error); if(ret && in->length && ((char*)in->data)[0] == 4) ret = KRB5KRB_AP_ERR_V4_REPLY; if (ret) { _krb5_debug(context, 5, "krb5_get_init_creds: failed to read error"); goto out; } ret = krb5_error_from_rd_error(context, &ctx->error, &ctx->cred); _krb5_debug(context, 5, "krb5_get_init_creds: KRB-ERROR %d", ret); /* * If no preauth was set and KDC requires it, give it one * more try. */ if (ret == KRB5KDC_ERR_PREAUTH_REQUIRED) { free_METHOD_DATA(&ctx->md); memset(&ctx->md, 0, sizeof(ctx->md)); if (ctx->error.e_data) { ret = decode_METHOD_DATA(ctx->error.e_data->data, ctx->error.e_data->length, &ctx->md, NULL); if (ret) krb5_set_error_message(context, ret, N_("Failed to decode METHOD-DATA", "")); } else { krb5_set_error_message(context, ret, N_("Preauth required but no preauth " "options send by KDC", "")); } } else if (ret == KRB5KRB_AP_ERR_SKEW && context->kdc_sec_offset == 0) { /* * Try adapt to timeskrew when we are using pre-auth, and * if there was a time skew, try again. */ krb5_set_real_time(context, ctx->error.stime, -1); if (context->kdc_sec_offset) ret = 0; _krb5_debug(context, 10, "init_creds: err skew updateing kdc offset to %d", context->kdc_sec_offset); ctx->used_pa_types = 0; } else if (ret == KRB5_KDC_ERR_WRONG_REALM && ctx->flags.canonicalize) { /* client referal to a new realm */ if (ctx->error.crealm == NULL) { krb5_set_error_message(context, ret, N_("Got a client referral, not but no realm", "")); goto out; } _krb5_debug(context, 5, "krb5_get_init_creds: got referal to realm %s", *ctx->error.crealm); ret = krb5_principal_set_realm(context, ctx->cred.client, *ctx->error.crealm); ctx->used_pa_types = 0; } if (ret) goto out; } } if (ctx->as_req.padata) { free_METHOD_DATA(ctx->as_req.padata); free(ctx->as_req.padata); ctx->as_req.padata = NULL; } /* Set a new nonce. */ ctx->as_req.req_body.nonce = ctx->nonce; /* fill_in_md_data */ ret = process_pa_data_to_md(context, &ctx->cred, &ctx->as_req, ctx, &ctx->md, &ctx->as_req.padata, ctx->prompter, ctx->prompter_data); if (ret) goto out; krb5_data_free(&ctx->req_buffer); ASN1_MALLOC_ENCODE(AS_REQ, ctx->req_buffer.data, ctx->req_buffer.length, &ctx->as_req, &len, ret); if (ret) goto out; if(len != ctx->req_buffer.length) krb5_abortx(context, "internal error in ASN.1 encoder"); out->data = ctx->req_buffer.data; out->length = ctx->req_buffer.length; *flags = KRB5_INIT_CREDS_STEP_FLAG_CONTINUE; return 0; out: return ret; } /** * Extract the newly acquired credentials from krb5_init_creds_context * context. * * @param context A Kerberos 5 context. * @param ctx * @param cred credentials, free with krb5_free_cred_contents(). * * @return 0 for sucess or An Kerberos error code, see krb5_get_error_message(). */ KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL krb5_init_creds_get_creds(krb5_context context, krb5_init_creds_context ctx, krb5_creds *cred) { return krb5_copy_creds_contents(context, &ctx->cred, cred); } /** * Get the last error from the transaction. * * @return Returns 0 or an error code * * @ingroup krb5_credential */ KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL krb5_init_creds_get_error(krb5_context context, krb5_init_creds_context ctx, KRB_ERROR *error) { krb5_error_code ret; ret = copy_KRB_ERROR(&ctx->error, error); if (ret) krb5_set_error_message(context, ret, N_("malloc: out of memory", "")); return ret; } /** * Free the krb5_init_creds_context allocated by krb5_init_creds_init(). * * @param context A Kerberos 5 context. * @param ctx The krb5_init_creds_context to free. * * @ingroup krb5_credential */ KRB5_LIB_FUNCTION void KRB5_LIB_CALL krb5_init_creds_free(krb5_context context, krb5_init_creds_context ctx) { free_init_creds_ctx(context, ctx); free(ctx); } /** * Get new credentials as setup by the krb5_init_creds_context. * * @param context A Kerberos 5 context. * @param ctx The krb5_init_creds_context to process. * * @ingroup krb5_credential */ KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL krb5_init_creds_get(krb5_context context, krb5_init_creds_context ctx) { krb5_sendto_ctx stctx = NULL; krb5_krbhst_info *hostinfo = NULL; krb5_error_code ret; krb5_data in, out; unsigned int flags = 0; krb5_data_zero(&in); krb5_data_zero(&out); ret = krb5_sendto_ctx_alloc(context, &stctx); if (ret) goto out; krb5_sendto_ctx_set_func(stctx, _krb5_kdc_retry, NULL); while (1) { flags = 0; ret = krb5_init_creds_step(context, ctx, &in, &out, hostinfo, &flags); krb5_data_free(&in); if (ret) goto out; if ((flags & 1) == 0) break; ret = krb5_sendto_context (context, stctx, &out, ctx->cred.client->realm, &in); if (ret) goto out; } out: if (stctx) krb5_sendto_ctx_free(context, stctx); return ret; } /** * Get new credentials using password. * * @ingroup krb5_credential */ KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL krb5_get_init_creds_password(krb5_context context, krb5_creds *creds, krb5_principal client, const char *password, krb5_prompter_fct prompter, void *data, krb5_deltat start_time, const char *in_tkt_service, krb5_get_init_creds_opt *options) { krb5_init_creds_context ctx; char buf[BUFSIZ]; krb5_error_code ret; int chpw = 0; again: ret = krb5_init_creds_init(context, client, prompter, data, start_time, options, &ctx); if (ret) goto out; ret = krb5_init_creds_set_service(context, ctx, in_tkt_service); if (ret) goto out; if (prompter != NULL && ctx->password == NULL && password == NULL) { krb5_prompt prompt; krb5_data password_data; char *p, *q; krb5_unparse_name (context, client, &p); asprintf (&q, "%s's Password: ", p); free (p); prompt.prompt = q; password_data.data = buf; password_data.length = sizeof(buf); prompt.hidden = 1; prompt.reply = &password_data; prompt.type = KRB5_PROMPT_TYPE_PASSWORD; ret = (*prompter) (context, data, NULL, NULL, 1, &prompt); free (q); if (ret) { memset (buf, 0, sizeof(buf)); ret = KRB5_LIBOS_PWDINTR; krb5_clear_error_message (context); goto out; } password = password_data.data; } if (password) { ret = krb5_init_creds_set_password(context, ctx, password); if (ret) goto out; } ret = krb5_init_creds_get(context, ctx); if (ret == 0) process_last_request(context, options, ctx); if (ret == KRB5KDC_ERR_KEY_EXPIRED && chpw == 0) { char buf2[1024]; /* try to avoid recursion */ if (in_tkt_service != NULL && strcmp(in_tkt_service, "kadmin/changepw") == 0) goto out; /* don't try to change password where then where none */ if (prompter == NULL) goto out; ret = change_password (context, client, ctx->password, buf2, sizeof(buf), prompter, data, options); if (ret) goto out; chpw = 1; krb5_init_creds_free(context, ctx); goto again; } out: if (ret == 0) krb5_init_creds_get_creds(context, ctx, creds); if (ctx) krb5_init_creds_free(context, ctx); memset(buf, 0, sizeof(buf)); return ret; } /** * Get new credentials using keyblock. * * @ingroup krb5_credential */ KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL krb5_get_init_creds_keyblock(krb5_context context, krb5_creds *creds, krb5_principal client, krb5_keyblock *keyblock, krb5_deltat start_time, const char *in_tkt_service, krb5_get_init_creds_opt *options) { krb5_init_creds_context ctx; krb5_error_code ret; memset(creds, 0, sizeof(*creds)); ret = krb5_init_creds_init(context, client, NULL, NULL, start_time, options, &ctx); if (ret) goto out; ret = krb5_init_creds_set_service(context, ctx, in_tkt_service); if (ret) goto out; ret = krb5_init_creds_set_keyblock(context, ctx, keyblock); if (ret) goto out; ret = krb5_init_creds_get(context, ctx); if (ret == 0) process_last_request(context, options, ctx); out: if (ret == 0) krb5_init_creds_get_creds(context, ctx, creds); if (ctx) krb5_init_creds_free(context, ctx); return ret; } /** * Get new credentials using keytab. * * @ingroup krb5_credential */ KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL krb5_get_init_creds_keytab(krb5_context context, krb5_creds *creds, krb5_principal client, krb5_keytab keytab, krb5_deltat start_time, const char *in_tkt_service, krb5_get_init_creds_opt *options) { krb5_init_creds_context ctx; krb5_error_code ret; memset(creds, 0, sizeof(*creds)); ret = krb5_init_creds_init(context, client, NULL, NULL, start_time, options, &ctx); if (ret) goto out; ret = krb5_init_creds_set_service(context, ctx, in_tkt_service); if (ret) goto out; ret = krb5_init_creds_set_keytab(context, ctx, keytab); if (ret) goto out; ret = krb5_init_creds_get(context, ctx); if (ret == 0) process_last_request(context, options, ctx); out: if (ret == 0) krb5_init_creds_get_creds(context, ctx, creds); if (ctx) krb5_init_creds_free(context, ctx); return ret; } diff --git a/crypto/heimdal/lib/krb5/keytab.c b/crypto/heimdal/lib/krb5/keytab.c index 8ca515f2133d..862d988d7a1e 100644 --- a/crypto/heimdal/lib/krb5/keytab.c +++ b/crypto/heimdal/lib/krb5/keytab.c @@ -1,893 +1,898 @@ /* * Copyright (c) 1997 - 2005 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" /** * @page krb5_keytab_intro The keytab handing functions * @section section_krb5_keytab Kerberos Keytabs * * See the library functions here: @ref krb5_keytab * * Keytabs are long term key storage for servers, their equvalment of * password files. * * Normally the only function that useful for server are to specify * what keytab to use to other core functions like krb5_rd_req() * krb5_kt_resolve(), and krb5_kt_close(). * * @subsection krb5_keytab_names Keytab names * * A keytab name is on the form type:residual. The residual part is * specific to each keytab-type. * * When a keytab-name is resolved, the type is matched with an internal * list of keytab types. If there is no matching keytab type, * the default keytab is used. The current default type is FILE. * * The default value can be changed in the configuration file * /etc/krb5.conf by setting the variable * [defaults]default_keytab_name. * * The keytab types that are implemented in Heimdal are: * - file * store the keytab in a file, the type's name is FILE . The * residual part is a filename. For compatibility with other * Kerberos implemtation WRFILE and JAVA14 is also accepted. WRFILE * has the same format as FILE. JAVA14 have a format that is * compatible with older versions of MIT kerberos and SUN's Java * based installation. They store a truncted kvno, so when the knvo * excess 255, they are truncted in this format. * * - keytab * store the keytab in a AFS keyfile (usually /usr/afs/etc/KeyFile ), * the type's name is AFSKEYFILE. The residual part is a filename. * * - memory * The keytab is stored in a memory segment. This allows sensitive * and/or temporary data not to be stored on disk. The type's name * is MEMORY. Each MEMORY keytab is referenced counted by and * opened by the residual name, so two handles can point to the * same memory area. When the last user closes using krb5_kt_close() * the keytab, the keys in they keytab is memset() to zero and freed * and can no longer be looked up by name. * * * @subsection krb5_keytab_example Keytab example * * This is a minimalistic version of ktutil. * * @code int main (int argc, char **argv) { krb5_context context; krb5_keytab keytab; krb5_kt_cursor cursor; krb5_keytab_entry entry; krb5_error_code ret; char *principal; if (krb5_init_context (&context) != 0) errx(1, "krb5_context"); ret = krb5_kt_default (context, &keytab); if (ret) krb5_err(context, 1, ret, "krb5_kt_default"); ret = krb5_kt_start_seq_get(context, keytab, &cursor); if (ret) krb5_err(context, 1, ret, "krb5_kt_start_seq_get"); while((ret = krb5_kt_next_entry(context, keytab, &entry, &cursor)) == 0){ krb5_unparse_name(context, entry.principal, &principal); printf("principal: %s\n", principal); free(principal); krb5_kt_free_entry(context, &entry); } ret = krb5_kt_end_seq_get(context, keytab, &cursor); if (ret) krb5_err(context, 1, ret, "krb5_kt_end_seq_get"); ret = krb5_kt_close(context, keytab); if (ret) krb5_err(context, 1, ret, "krb5_kt_close"); krb5_free_context(context); return 0; } * @endcode * */ /** * Register a new keytab backend. * * @param context a Keberos context. * @param ops a backend to register. * * @return Return an error code or 0, see krb5_get_error_message(). * * @ingroup krb5_keytab */ KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL krb5_kt_register(krb5_context context, const krb5_kt_ops *ops) { struct krb5_keytab_data *tmp; if (strlen(ops->prefix) > KRB5_KT_PREFIX_MAX_LEN - 1) { krb5_set_error_message(context, KRB5_KT_BADNAME, N_("can't register cache type, prefix too long", "")); return KRB5_KT_BADNAME; } tmp = realloc(context->kt_types, (context->num_kt_types + 1) * sizeof(*context->kt_types)); if(tmp == NULL) { krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", "")); return ENOMEM; } memcpy(&tmp[context->num_kt_types], ops, sizeof(tmp[context->num_kt_types])); context->kt_types = tmp; context->num_kt_types++; return 0; } static const char * keytab_name(const char *name, const char **type, size_t *type_len) { const char *residual; residual = strchr(name, ':'); if (residual == NULL || name[0] == '/' #ifdef _WIN32 /* Avoid treating : as a keytab type * specification */ || name + 1 == residual #endif ) { *type = "FILE"; *type_len = strlen(*type); residual = name; } else { *type = name; *type_len = residual - name; residual++; } return residual; } /** * Resolve the keytab name (of the form `type:residual') in `name' * into a keytab in `id'. * * @param context a Keberos context. * @param name name to resolve * @param id resulting keytab, free with krb5_kt_close(). * * @return Return an error code or 0, see krb5_get_error_message(). * * @ingroup krb5_keytab */ KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL krb5_kt_resolve(krb5_context context, const char *name, krb5_keytab *id) { krb5_keytab k; int i; const char *type, *residual; size_t type_len; krb5_error_code ret; residual = keytab_name(name, &type, &type_len); for(i = 0; i < context->num_kt_types; i++) { if(strncasecmp(type, context->kt_types[i].prefix, type_len) == 0) break; } if(i == context->num_kt_types) { krb5_set_error_message(context, KRB5_KT_UNKNOWN_TYPE, N_("unknown keytab type %.*s", "type"), (int)type_len, type); return KRB5_KT_UNKNOWN_TYPE; } k = malloc (sizeof(*k)); if (k == NULL) { krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", "")); return ENOMEM; } memcpy(k, &context->kt_types[i], sizeof(*k)); k->data = NULL; ret = (*k->resolve)(context, residual, k); if(ret) { free(k); k = NULL; } *id = k; return ret; } /** * copy the name of the default keytab into `name'. * * @param context a Keberos context. * @param name buffer where the name will be written * @param namesize length of name * * @return Return an error code or 0, see krb5_get_error_message(). * * @ingroup krb5_keytab */ KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL krb5_kt_default_name(krb5_context context, char *name, size_t namesize) { if (strlcpy (name, context->default_keytab, namesize) >= namesize) { krb5_clear_error_message (context); return KRB5_CONFIG_NOTENUFSPACE; } return 0; } /** * Copy the name of the default modify keytab into `name'. * * @param context a Keberos context. * @param name buffer where the name will be written * @param namesize length of name * * @return Return an error code or 0, see krb5_get_error_message(). * * @ingroup krb5_keytab */ KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL krb5_kt_default_modify_name(krb5_context context, char *name, size_t namesize) { const char *kt = NULL; if(context->default_keytab_modify == NULL) { if(strncasecmp(context->default_keytab, "ANY:", 4) != 0) kt = context->default_keytab; else { size_t len = strcspn(context->default_keytab + 4, ","); if(len >= namesize) { krb5_clear_error_message(context); return KRB5_CONFIG_NOTENUFSPACE; } strlcpy(name, context->default_keytab + 4, namesize); name[len] = '\0'; return 0; } } else kt = context->default_keytab_modify; if (strlcpy (name, kt, namesize) >= namesize) { krb5_clear_error_message (context); return KRB5_CONFIG_NOTENUFSPACE; } return 0; } /** * Set `id' to the default keytab. * * @param context a Keberos context. * @param id the new default keytab. * * @return Return an error code or 0, see krb5_get_error_message(). * * @ingroup krb5_keytab */ KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL krb5_kt_default(krb5_context context, krb5_keytab *id) { return krb5_kt_resolve (context, context->default_keytab, id); } /** * Read the key identified by `(principal, vno, enctype)' from the * keytab in `keyprocarg' (the default if == NULL) into `*key'. * * @param context a Keberos context. * @param keyprocarg * @param principal * @param vno * @param enctype * @param key * * @return Return an error code or 0, see krb5_get_error_message(). * * @ingroup krb5_keytab */ KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL krb5_kt_read_service_key(krb5_context context, krb5_pointer keyprocarg, krb5_principal principal, krb5_kvno vno, krb5_enctype enctype, krb5_keyblock **key) { - krb5_keytab keytab; + krb5_keytab keytab = NULL; /* Quiet lint */ krb5_keytab_entry entry; krb5_error_code ret; + memset(&entry, 0, sizeof(entry)); if (keyprocarg) ret = krb5_kt_resolve (context, keyprocarg, &keytab); else ret = krb5_kt_default (context, &keytab); if (ret) return ret; ret = krb5_kt_get_entry (context, keytab, principal, vno, enctype, &entry); + if (ret == 0) { + ret = krb5_copy_keyblock (context, &entry.keyblock, key); + krb5_kt_free_entry(context, &entry); + } krb5_kt_close (context, keytab); - if (ret) - return ret; - ret = krb5_copy_keyblock (context, &entry.keyblock, key); - krb5_kt_free_entry(context, &entry); return ret; } /** * Return the type of the `keytab' in the string `prefix of length * `prefixsize'. * * @param context a Keberos context. * @param keytab the keytab to get the prefix for * @param prefix prefix buffer * @param prefixsize length of prefix buffer * * @return Return an error code or 0, see krb5_get_error_message(). * * @ingroup krb5_keytab */ KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL krb5_kt_get_type(krb5_context context, krb5_keytab keytab, char *prefix, size_t prefixsize) { strlcpy(prefix, keytab->prefix, prefixsize); return 0; } /** * Retrieve the name of the keytab `keytab' into `name', `namesize' * * @param context a Keberos context. * @param keytab the keytab to get the name for. * @param name name buffer. * @param namesize size of name buffer. * * @return Return an error code or 0, see krb5_get_error_message(). * * @ingroup krb5_keytab */ KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL krb5_kt_get_name(krb5_context context, krb5_keytab keytab, char *name, size_t namesize) { return (*keytab->get_name)(context, keytab, name, namesize); } /** * Retrieve the full name of the keytab `keytab' and store the name in * `str'. * * @param context a Keberos context. * @param keytab keytab to get name for. * @param str the name of the keytab name, usee krb5_xfree() to free * the string. On error, *str is set to NULL. * * @return Return an error code or 0, see krb5_get_error_message(). * * @ingroup krb5_keytab */ KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL krb5_kt_get_full_name(krb5_context context, krb5_keytab keytab, char **str) { char type[KRB5_KT_PREFIX_MAX_LEN]; char name[MAXPATHLEN]; krb5_error_code ret; *str = NULL; ret = krb5_kt_get_type(context, keytab, type, sizeof(type)); if (ret) return ret; ret = krb5_kt_get_name(context, keytab, name, sizeof(name)); if (ret) return ret; if (asprintf(str, "%s:%s", type, name) == -1) { krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", "")); *str = NULL; return ENOMEM; } return 0; } /** * Finish using the keytab in `id'. All resources will be released, * even on errors. * * @param context a Keberos context. * @param id keytab to close. * * @return Return an error code or 0, see krb5_get_error_message(). * * @ingroup krb5_keytab */ KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL krb5_kt_close(krb5_context context, krb5_keytab id) { - krb5_error_code ret; + krb5_error_code ret = 0; - ret = (*id->close)(context, id); - memset(id, 0, sizeof(*id)); - free(id); + if (id) { + ret = (id->close)(context, id); + memset(id, 0, sizeof(*id)); + free(id); + } return ret; } /** * Destroy (remove) the keytab in `id'. All resources will be released, * even on errors, does the equvalment of krb5_kt_close() on the resources. * * @param context a Keberos context. * @param id keytab to destroy. * * @return Return an error code or 0, see krb5_get_error_message(). * * @ingroup krb5_keytab */ KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL krb5_kt_destroy(krb5_context context, krb5_keytab id) { krb5_error_code ret; ret = (*id->destroy)(context, id); krb5_kt_close(context, id); return ret; } /* * Match any aliases in keytab `entry' with `principal'. */ static krb5_boolean compare_aliseses(krb5_context context, krb5_keytab_entry *entry, krb5_const_principal principal) { unsigned int i; if (entry->aliases == NULL) return FALSE; for (i = 0; i < entry->aliases->len; i++) if (krb5_principal_compare(context, &entry->aliases->val[i], principal)) return TRUE; return FALSE; } /** * Compare `entry' against `principal, vno, enctype'. * Any of `principal, vno, enctype' might be 0 which acts as a wildcard. * Return TRUE if they compare the same, FALSE otherwise. * * @param context a Keberos context. * @param entry an entry to match with. * @param principal principal to match, NULL matches all principals. * @param vno key version to match, 0 matches all key version numbers. * @param enctype encryption type to match, 0 matches all encryption types. * * @return Return TRUE or match, FALSE if not matched. * * @ingroup krb5_keytab */ KRB5_LIB_FUNCTION krb5_boolean KRB5_LIB_CALL krb5_kt_compare(krb5_context context, krb5_keytab_entry *entry, krb5_const_principal principal, krb5_kvno vno, krb5_enctype enctype) { if(principal != NULL && !(krb5_principal_compare(context, entry->principal, principal) || compare_aliseses(context, entry, principal))) return FALSE; if(vno && vno != entry->vno) return FALSE; if(enctype && enctype != entry->keyblock.keytype) return FALSE; return TRUE; } krb5_error_code _krb5_kt_principal_not_found(krb5_context context, krb5_error_code ret, krb5_keytab id, krb5_const_principal principal, krb5_enctype enctype, int kvno) { char princ[256], kvno_str[25], *kt_name; char *enctype_str = NULL; krb5_unparse_name_fixed (context, principal, princ, sizeof(princ)); krb5_kt_get_full_name (context, id, &kt_name); krb5_enctype_to_string(context, enctype, &enctype_str); if (kvno) snprintf(kvno_str, sizeof(kvno_str), "(kvno %d)", kvno); else kvno_str[0] = '\0'; krb5_set_error_message (context, ret, N_("Failed to find %s%s in keytab %s (%s)", "principal, kvno, keytab file, enctype"), princ, kvno_str, kt_name ? kt_name : "unknown keytab", enctype_str ? enctype_str : "unknown enctype"); free(kt_name); free(enctype_str); return ret; } /** * Retrieve the keytab entry for `principal, kvno, enctype' into `entry' * from the keytab `id'. Matching is done like krb5_kt_compare(). * * @param context a Keberos context. * @param id a keytab. * @param principal principal to match, NULL matches all principals. * @param kvno key version to match, 0 matches all key version numbers. * @param enctype encryption type to match, 0 matches all encryption types. * @param entry the returned entry, free with krb5_kt_free_entry(). * * @return Return an error code or 0, see krb5_get_error_message(). * * @ingroup krb5_keytab */ KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL krb5_kt_get_entry(krb5_context context, krb5_keytab id, krb5_const_principal principal, krb5_kvno kvno, krb5_enctype enctype, krb5_keytab_entry *entry) { krb5_keytab_entry tmp; krb5_error_code ret; krb5_kt_cursor cursor; if(id->get) return (*id->get)(context, id, principal, kvno, enctype, entry); + memset(&tmp, 0, sizeof(tmp)); ret = krb5_kt_start_seq_get (context, id, &cursor); if (ret) { /* This is needed for krb5_verify_init_creds, but keep error * string from previous error for the human. */ context->error_code = KRB5_KT_NOTFOUND; return KRB5_KT_NOTFOUND; } entry->vno = 0; while (krb5_kt_next_entry(context, id, &tmp, &cursor) == 0) { if (krb5_kt_compare(context, &tmp, principal, 0, enctype)) { /* the file keytab might only store the lower 8 bits of the kvno, so only compare those bits */ if (kvno == tmp.vno || (tmp.vno < 256 && kvno % 256 == tmp.vno)) { krb5_kt_copy_entry_contents (context, &tmp, entry); krb5_kt_free_entry (context, &tmp); krb5_kt_end_seq_get(context, id, &cursor); return 0; } else if (kvno == 0 && tmp.vno > entry->vno) { if (entry->vno) krb5_kt_free_entry (context, entry); krb5_kt_copy_entry_contents (context, &tmp, entry); } } krb5_kt_free_entry(context, &tmp); } krb5_kt_end_seq_get (context, id, &cursor); if (entry->vno == 0) return _krb5_kt_principal_not_found(context, KRB5_KT_NOTFOUND, id, principal, enctype, kvno); return 0; } /** * Copy the contents of `in' into `out'. * * @param context a Keberos context. * @param in the keytab entry to copy. * @param out the copy of the keytab entry, free with krb5_kt_free_entry(). * * @return Return an error code or 0, see krb5_get_error_message(). * * @ingroup krb5_keytab */ KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL krb5_kt_copy_entry_contents(krb5_context context, const krb5_keytab_entry *in, krb5_keytab_entry *out) { krb5_error_code ret; memset(out, 0, sizeof(*out)); - out->vno = in->vno; ret = krb5_copy_principal (context, in->principal, &out->principal); if (ret) - goto fail; + return ret; ret = krb5_copy_keyblock_contents (context, &in->keyblock, &out->keyblock); - if (ret) - goto fail; + if (ret) { + krb5_free_principal(context, out->principal); + memset(out, 0, sizeof(*out)); + return ret; + } + out->vno = in->vno; out->timestamp = in->timestamp; return 0; -fail: - krb5_kt_free_entry (context, out); - return ret; } /** * Free the contents of `entry'. * * @param context a Keberos context. * @param entry the entry to free * * @return Return an error code or 0, see krb5_get_error_message(). * * @ingroup krb5_keytab */ KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL krb5_kt_free_entry(krb5_context context, krb5_keytab_entry *entry) { krb5_free_principal (context, entry->principal); krb5_free_keyblock_contents (context, &entry->keyblock); memset(entry, 0, sizeof(*entry)); return 0; } /** * Set `cursor' to point at the beginning of `id'. * * @param context a Keberos context. * @param id a keytab. * @param cursor a newly allocated cursor, free with krb5_kt_end_seq_get(). * * @return Return an error code or 0, see krb5_get_error_message(). * * @ingroup krb5_keytab */ KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL krb5_kt_start_seq_get(krb5_context context, krb5_keytab id, krb5_kt_cursor *cursor) { if(id->start_seq_get == NULL) { krb5_set_error_message(context, HEIM_ERR_OPNOTSUPP, N_("start_seq_get is not supported " "in the %s keytab type", ""), id->prefix); return HEIM_ERR_OPNOTSUPP; } return (*id->start_seq_get)(context, id, cursor); } /** * Get the next entry from keytab, advance the cursor. On last entry * the function will return KRB5_KT_END. * * @param context a Keberos context. * @param id a keytab. * @param entry the returned entry, free with krb5_kt_free_entry(). * @param cursor the cursor of the iteration. * * @return Return an error code or 0, see krb5_get_error_message(). * * @ingroup krb5_keytab */ KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL krb5_kt_next_entry(krb5_context context, krb5_keytab id, krb5_keytab_entry *entry, krb5_kt_cursor *cursor) { if(id->next_entry == NULL) { krb5_set_error_message(context, HEIM_ERR_OPNOTSUPP, N_("next_entry is not supported in the %s " " keytab", ""), id->prefix); return HEIM_ERR_OPNOTSUPP; } return (*id->next_entry)(context, id, entry, cursor); } /** * Release all resources associated with `cursor'. * * @param context a Keberos context. * @param id a keytab. * @param cursor the cursor to free. * * @return Return an error code or 0, see krb5_get_error_message(). * * @ingroup krb5_keytab */ KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL krb5_kt_end_seq_get(krb5_context context, krb5_keytab id, krb5_kt_cursor *cursor) { if(id->end_seq_get == NULL) { krb5_set_error_message(context, HEIM_ERR_OPNOTSUPP, "end_seq_get is not supported in the %s " " keytab", id->prefix); return HEIM_ERR_OPNOTSUPP; } return (*id->end_seq_get)(context, id, cursor); } /** * Add the entry in `entry' to the keytab `id'. * * @param context a Keberos context. * @param id a keytab. * @param entry the entry to add * * @return Return an error code or 0, see krb5_get_error_message(). * * @ingroup krb5_keytab */ KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL krb5_kt_add_entry(krb5_context context, krb5_keytab id, krb5_keytab_entry *entry) { if(id->add == NULL) { krb5_set_error_message(context, KRB5_KT_NOWRITE, N_("Add is not supported in the %s keytab", ""), id->prefix); return KRB5_KT_NOWRITE; } entry->timestamp = time(NULL); return (*id->add)(context, id,entry); } /** * Remove an entry from the keytab, matching is done using * krb5_kt_compare(). * @param context a Keberos context. * @param id a keytab. * @param entry the entry to remove * * @return Return an error code or 0, see krb5_get_error_message(). * * @ingroup krb5_keytab */ KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL krb5_kt_remove_entry(krb5_context context, krb5_keytab id, krb5_keytab_entry *entry) { if(id->remove == NULL) { krb5_set_error_message(context, KRB5_KT_NOWRITE, N_("Remove is not supported in the %s keytab", ""), id->prefix); return KRB5_KT_NOWRITE; } return (*id->remove)(context, id, entry); } /** * Return true if the keytab exists and have entries * * @param context a Keberos context. * @param id a keytab. * * @return Return an error code or 0, see krb5_get_error_message(). * * @ingroup krb5_keytab */ KRB5_LIB_FUNCTION krb5_boolean KRB5_LIB_CALL krb5_kt_have_content(krb5_context context, krb5_keytab id) { krb5_keytab_entry entry; krb5_kt_cursor cursor; krb5_error_code ret; char *name; + memset(&entry, 0, sizeof(entry)); ret = krb5_kt_start_seq_get(context, id, &cursor); if (ret) goto notfound; ret = krb5_kt_next_entry(context, id, &entry, &cursor); krb5_kt_end_seq_get(context, id, &cursor); if (ret) goto notfound; krb5_kt_free_entry(context, &entry); return 0; notfound: ret = krb5_kt_get_full_name(context, id, &name); if (ret == 0) { krb5_set_error_message(context, KRB5_KT_NOTFOUND, N_("No entry in keytab: %s", ""), name); free(name); } return KRB5_KT_NOTFOUND; } diff --git a/crypto/heimdal/lib/krb5/krb5.h b/crypto/heimdal/lib/krb5/krb5.h index 2d555ea0604f..9c9b68f6d8c9 100644 --- a/crypto/heimdal/lib/krb5/krb5.h +++ b/crypto/heimdal/lib/krb5/krb5.h @@ -1,916 +1,935 @@ /* * Copyright (c) 1997 - 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. */ /* $Id$ */ #ifndef __KRB5_H__ #define __KRB5_H__ #include #include #include #include #include #include #include /* name confusion with MIT */ #ifndef KRB5KDC_ERR_KEY_EXP #define KRB5KDC_ERR_KEY_EXP KRB5KDC_ERR_KEY_EXPIRED #endif #ifdef _WIN32 #define KRB5_CALLCONV __stdcall #else #define KRB5_CALLCONV #endif /* simple constants */ #ifndef TRUE #define TRUE 1 #define FALSE 0 #endif typedef int krb5_boolean; typedef int32_t krb5_error_code; typedef int32_t krb5_kvno; typedef uint32_t krb5_flags; typedef void *krb5_pointer; typedef const void *krb5_const_pointer; struct krb5_crypto_data; typedef struct krb5_crypto_data *krb5_crypto; struct krb5_get_creds_opt_data; typedef struct krb5_get_creds_opt_data *krb5_get_creds_opt; struct krb5_digest_data; typedef struct krb5_digest_data *krb5_digest; struct krb5_ntlm_data; typedef struct krb5_ntlm_data *krb5_ntlm; struct krb5_pac_data; typedef struct krb5_pac_data *krb5_pac; typedef struct krb5_rd_req_in_ctx_data *krb5_rd_req_in_ctx; typedef struct krb5_rd_req_out_ctx_data *krb5_rd_req_out_ctx; typedef CKSUMTYPE krb5_cksumtype; typedef Checksum krb5_checksum; typedef ENCTYPE krb5_enctype; typedef struct krb5_get_init_creds_ctx *krb5_init_creds_context; typedef heim_octet_string krb5_data; /* PKINIT related forward declarations */ struct ContentInfo; struct krb5_pk_identity; struct krb5_pk_cert; /* krb5_enc_data is a mit compat structure */ typedef struct krb5_enc_data { krb5_enctype enctype; krb5_kvno kvno; krb5_data ciphertext; } krb5_enc_data; /* alternative names */ enum { ENCTYPE_NULL = KRB5_ENCTYPE_NULL, ENCTYPE_DES_CBC_CRC = KRB5_ENCTYPE_DES_CBC_CRC, ENCTYPE_DES_CBC_MD4 = KRB5_ENCTYPE_DES_CBC_MD4, ENCTYPE_DES_CBC_MD5 = KRB5_ENCTYPE_DES_CBC_MD5, ENCTYPE_DES3_CBC_MD5 = KRB5_ENCTYPE_DES3_CBC_MD5, ENCTYPE_OLD_DES3_CBC_SHA1 = KRB5_ENCTYPE_OLD_DES3_CBC_SHA1, ENCTYPE_SIGN_DSA_GENERATE = KRB5_ENCTYPE_SIGN_DSA_GENERATE, ENCTYPE_ENCRYPT_RSA_PRIV = KRB5_ENCTYPE_ENCRYPT_RSA_PRIV, ENCTYPE_ENCRYPT_RSA_PUB = KRB5_ENCTYPE_ENCRYPT_RSA_PUB, ENCTYPE_DES3_CBC_SHA1 = KRB5_ENCTYPE_DES3_CBC_SHA1, ENCTYPE_AES128_CTS_HMAC_SHA1_96 = KRB5_ENCTYPE_AES128_CTS_HMAC_SHA1_96, ENCTYPE_AES256_CTS_HMAC_SHA1_96 = KRB5_ENCTYPE_AES256_CTS_HMAC_SHA1_96, ENCTYPE_ARCFOUR_HMAC = KRB5_ENCTYPE_ARCFOUR_HMAC_MD5, ENCTYPE_ARCFOUR_HMAC_MD5 = KRB5_ENCTYPE_ARCFOUR_HMAC_MD5, ENCTYPE_ARCFOUR_HMAC_MD5_56 = KRB5_ENCTYPE_ARCFOUR_HMAC_MD5_56, ENCTYPE_ENCTYPE_PK_CROSS = KRB5_ENCTYPE_ENCTYPE_PK_CROSS, ENCTYPE_DES_CBC_NONE = KRB5_ENCTYPE_DES_CBC_NONE, ENCTYPE_DES3_CBC_NONE = KRB5_ENCTYPE_DES3_CBC_NONE, ENCTYPE_DES_CFB64_NONE = KRB5_ENCTYPE_DES_CFB64_NONE, ENCTYPE_DES_PCBC_NONE = KRB5_ENCTYPE_DES_PCBC_NONE, ETYPE_NULL = KRB5_ENCTYPE_NULL, ETYPE_DES_CBC_CRC = KRB5_ENCTYPE_DES_CBC_CRC, ETYPE_DES_CBC_MD4 = KRB5_ENCTYPE_DES_CBC_MD4, ETYPE_DES_CBC_MD5 = KRB5_ENCTYPE_DES_CBC_MD5, ETYPE_DES3_CBC_MD5 = KRB5_ENCTYPE_DES3_CBC_MD5, ETYPE_OLD_DES3_CBC_SHA1 = KRB5_ENCTYPE_OLD_DES3_CBC_SHA1, ETYPE_SIGN_DSA_GENERATE = KRB5_ENCTYPE_SIGN_DSA_GENERATE, ETYPE_ENCRYPT_RSA_PRIV = KRB5_ENCTYPE_ENCRYPT_RSA_PRIV, ETYPE_ENCRYPT_RSA_PUB = KRB5_ENCTYPE_ENCRYPT_RSA_PUB, ETYPE_DES3_CBC_SHA1 = KRB5_ENCTYPE_DES3_CBC_SHA1, ETYPE_AES128_CTS_HMAC_SHA1_96 = KRB5_ENCTYPE_AES128_CTS_HMAC_SHA1_96, ETYPE_AES256_CTS_HMAC_SHA1_96 = KRB5_ENCTYPE_AES256_CTS_HMAC_SHA1_96, ETYPE_ARCFOUR_HMAC_MD5 = KRB5_ENCTYPE_ARCFOUR_HMAC_MD5, ETYPE_ARCFOUR_HMAC_MD5_56 = KRB5_ENCTYPE_ARCFOUR_HMAC_MD5_56, ETYPE_ENCTYPE_PK_CROSS = KRB5_ENCTYPE_ENCTYPE_PK_CROSS, ETYPE_ARCFOUR_MD4 = KRB5_ENCTYPE_ARCFOUR_MD4, ETYPE_ARCFOUR_HMAC_OLD = KRB5_ENCTYPE_ARCFOUR_HMAC_OLD, ETYPE_ARCFOUR_HMAC_OLD_EXP = KRB5_ENCTYPE_ARCFOUR_HMAC_OLD_EXP, ETYPE_DES_CBC_NONE = KRB5_ENCTYPE_DES_CBC_NONE, ETYPE_DES3_CBC_NONE = KRB5_ENCTYPE_DES3_CBC_NONE, ETYPE_DES_CFB64_NONE = KRB5_ENCTYPE_DES_CFB64_NONE, ETYPE_DES_PCBC_NONE = KRB5_ENCTYPE_DES_PCBC_NONE, ETYPE_DIGEST_MD5_NONE = KRB5_ENCTYPE_DIGEST_MD5_NONE, ETYPE_CRAM_MD5_NONE = KRB5_ENCTYPE_CRAM_MD5_NONE }; /* PDU types */ typedef enum krb5_pdu { KRB5_PDU_ERROR = 0, KRB5_PDU_TICKET = 1, KRB5_PDU_AS_REQUEST = 2, KRB5_PDU_AS_REPLY = 3, KRB5_PDU_TGS_REQUEST = 4, KRB5_PDU_TGS_REPLY = 5, KRB5_PDU_AP_REQUEST = 6, KRB5_PDU_AP_REPLY = 7, KRB5_PDU_KRB_SAFE = 8, KRB5_PDU_KRB_PRIV = 9, KRB5_PDU_KRB_CRED = 10, KRB5_PDU_NONE = 11 /* See krb5_get_permitted_enctypes() */ } krb5_pdu; typedef PADATA_TYPE krb5_preauthtype; typedef enum krb5_key_usage { KRB5_KU_PA_ENC_TIMESTAMP = 1, /* AS-REQ PA-ENC-TIMESTAMP padata timestamp, encrypted with the client key (section 5.4.1) */ KRB5_KU_TICKET = 2, /* AS-REP Ticket and TGS-REP Ticket (includes tgs session key or application session key), encrypted with the service key (section 5.4.2) */ KRB5_KU_AS_REP_ENC_PART = 3, /* AS-REP encrypted part (includes tgs session key or application session key), encrypted with the client key (section 5.4.2) */ KRB5_KU_TGS_REQ_AUTH_DAT_SESSION = 4, /* TGS-REQ KDC-REQ-BODY AuthorizationData, encrypted with the tgs session key (section 5.4.1) */ KRB5_KU_TGS_REQ_AUTH_DAT_SUBKEY = 5, /* TGS-REQ KDC-REQ-BODY AuthorizationData, encrypted with the tgs authenticator subkey (section 5.4.1) */ KRB5_KU_TGS_REQ_AUTH_CKSUM = 6, /* TGS-REQ PA-TGS-REQ padata AP-REQ Authenticator cksum, keyed with the tgs session key (sections 5.3.2, 5.4.1) */ KRB5_KU_TGS_REQ_AUTH = 7, /* TGS-REQ PA-TGS-REQ padata AP-REQ Authenticator (includes tgs authenticator subkey), encrypted with the tgs session key (section 5.3.2) */ KRB5_KU_TGS_REP_ENC_PART_SESSION = 8, /* TGS-REP encrypted part (includes application session key), encrypted with the tgs session key (section 5.4.2) */ KRB5_KU_TGS_REP_ENC_PART_SUB_KEY = 9, /* TGS-REP encrypted part (includes application session key), encrypted with the tgs authenticator subkey (section 5.4.2) */ KRB5_KU_AP_REQ_AUTH_CKSUM = 10, /* AP-REQ Authenticator cksum, keyed with the application session key (section 5.3.2) */ KRB5_KU_AP_REQ_AUTH = 11, /* AP-REQ Authenticator (includes application authenticator subkey), encrypted with the application session key (section 5.3.2) */ KRB5_KU_AP_REQ_ENC_PART = 12, /* AP-REP encrypted part (includes application session subkey), encrypted with the application session key (section 5.5.2) */ KRB5_KU_KRB_PRIV = 13, /* KRB-PRIV encrypted part, encrypted with a key chosen by the application (section 5.7.1) */ KRB5_KU_KRB_CRED = 14, /* KRB-CRED encrypted part, encrypted with a key chosen by the application (section 5.8.1) */ KRB5_KU_KRB_SAFE_CKSUM = 15, /* KRB-SAFE cksum, keyed with a key chosen by the application (section 5.6.1) */ KRB5_KU_OTHER_ENCRYPTED = 16, /* Data which is defined in some specification outside of Kerberos to be encrypted using an RFC1510 encryption type. */ KRB5_KU_OTHER_CKSUM = 17, /* Data which is defined in some specification outside of Kerberos to be checksummed using an RFC1510 checksum type. */ KRB5_KU_KRB_ERROR = 18, /* Krb-error checksum */ KRB5_KU_AD_KDC_ISSUED = 19, /* AD-KDCIssued checksum */ KRB5_KU_MANDATORY_TICKET_EXTENSION = 20, /* Checksum for Mandatory Ticket Extensions */ KRB5_KU_AUTH_DATA_TICKET_EXTENSION = 21, /* Checksum in Authorization Data in Ticket Extensions */ KRB5_KU_USAGE_SEAL = 22, /* seal in GSSAPI krb5 mechanism */ KRB5_KU_USAGE_SIGN = 23, /* sign in GSSAPI krb5 mechanism */ KRB5_KU_USAGE_SEQ = 24, /* SEQ in GSSAPI krb5 mechanism */ KRB5_KU_USAGE_ACCEPTOR_SEAL = 22, /* acceptor sign in GSSAPI CFX krb5 mechanism */ KRB5_KU_USAGE_ACCEPTOR_SIGN = 23, /* acceptor seal in GSSAPI CFX krb5 mechanism */ KRB5_KU_USAGE_INITIATOR_SEAL = 24, /* initiator sign in GSSAPI CFX krb5 mechanism */ KRB5_KU_USAGE_INITIATOR_SIGN = 25, /* initiator seal in GSSAPI CFX krb5 mechanism */ KRB5_KU_PA_SERVER_REFERRAL_DATA = 22, /* encrypted server referral data */ KRB5_KU_SAM_CHECKSUM = 25, /* Checksum for the SAM-CHECKSUM field */ KRB5_KU_SAM_ENC_TRACK_ID = 26, /* Encryption of the SAM-TRACK-ID field */ KRB5_KU_PA_SERVER_REFERRAL = 26, /* Keyusage for the server referral in a TGS req */ KRB5_KU_SAM_ENC_NONCE_SAD = 27, /* Encryption of the SAM-NONCE-OR-SAD field */ KRB5_KU_PA_PKINIT_KX = 44, /* Encryption type of the kdc session contribution in pk-init */ KRB5_KU_AS_REQ = 56, /* Checksum of over the AS-REQ send by the KDC in PA-REQ-ENC-PA-REP */ KRB5_KU_DIGEST_ENCRYPT = -18, /* Encryption key usage used in the digest encryption field */ KRB5_KU_DIGEST_OPAQUE = -19, /* Checksum key usage used in the digest opaque field */ KRB5_KU_KRB5SIGNEDPATH = -21, /* Checksum key usage on KRB5SignedPath */ KRB5_KU_CANONICALIZED_NAMES = -23 /* Checksum key usage on PA-CANONICALIZED */ } krb5_key_usage; typedef krb5_key_usage krb5_keyusage; typedef enum krb5_salttype { KRB5_PW_SALT = KRB5_PADATA_PW_SALT, KRB5_AFS3_SALT = KRB5_PADATA_AFS3_SALT }krb5_salttype; typedef struct krb5_salt { krb5_salttype salttype; krb5_data saltvalue; } krb5_salt; typedef ETYPE_INFO krb5_preauthinfo; typedef struct { krb5_preauthtype type; krb5_preauthinfo info; /* list of preauthinfo for this type */ } krb5_preauthdata_entry; typedef struct krb5_preauthdata { unsigned len; krb5_preauthdata_entry *val; }krb5_preauthdata; typedef enum krb5_address_type { KRB5_ADDRESS_INET = 2, KRB5_ADDRESS_NETBIOS = 20, KRB5_ADDRESS_INET6 = 24, KRB5_ADDRESS_ADDRPORT = 256, KRB5_ADDRESS_IPPORT = 257 } krb5_address_type; enum { AP_OPTS_USE_SESSION_KEY = 1, AP_OPTS_MUTUAL_REQUIRED = 2, AP_OPTS_USE_SUBKEY = 4 /* library internal */ }; typedef HostAddress krb5_address; typedef HostAddresses krb5_addresses; typedef krb5_enctype krb5_keytype; enum krb5_keytype_old { KEYTYPE_NULL = ETYPE_NULL, KEYTYPE_DES = ETYPE_DES_CBC_CRC, KEYTYPE_DES3 = ETYPE_OLD_DES3_CBC_SHA1, KEYTYPE_AES128 = ETYPE_AES128_CTS_HMAC_SHA1_96, KEYTYPE_AES256 = ETYPE_AES256_CTS_HMAC_SHA1_96, KEYTYPE_ARCFOUR = ETYPE_ARCFOUR_HMAC_MD5, KEYTYPE_ARCFOUR_56 = ETYPE_ARCFOUR_HMAC_MD5_56 }; typedef EncryptionKey krb5_keyblock; typedef AP_REQ krb5_ap_req; struct krb5_cc_ops; #ifdef _WIN32 #define KRB5_USE_PATH_TOKENS 1 #endif #ifdef KRB5_USE_PATH_TOKENS #define KRB5_DEFAULT_CCFILE_ROOT "%{TEMP}/krb5cc_" #else #define KRB5_DEFAULT_CCFILE_ROOT "/tmp/krb5cc_" #endif #define KRB5_DEFAULT_CCROOT "FILE:" KRB5_DEFAULT_CCFILE_ROOT #define KRB5_ACCEPT_NULL_ADDRESSES(C) \ krb5_config_get_bool_default((C), NULL, TRUE, \ "libdefaults", "accept_null_addresses", \ NULL) typedef void *krb5_cc_cursor; typedef struct krb5_cccol_cursor_data *krb5_cccol_cursor; typedef struct krb5_ccache_data { const struct krb5_cc_ops *ops; krb5_data data; }krb5_ccache_data; typedef struct krb5_ccache_data *krb5_ccache; typedef struct krb5_context_data *krb5_context; typedef Realm krb5_realm; typedef const char *krb5_const_realm; /* stupid language */ #define krb5_realm_length(r) strlen(r) #define krb5_realm_data(r) (r) typedef Principal krb5_principal_data; typedef struct Principal *krb5_principal; typedef const struct Principal *krb5_const_principal; typedef struct Principals *krb5_principals; typedef time_t krb5_deltat; typedef time_t krb5_timestamp; typedef struct krb5_times { krb5_timestamp authtime; krb5_timestamp starttime; krb5_timestamp endtime; krb5_timestamp renew_till; } krb5_times; typedef union { TicketFlags b; krb5_flags i; } krb5_ticket_flags; /* options for krb5_get_in_tkt() */ #define KDC_OPT_FORWARDABLE (1 << 1) #define KDC_OPT_FORWARDED (1 << 2) #define KDC_OPT_PROXIABLE (1 << 3) #define KDC_OPT_PROXY (1 << 4) #define KDC_OPT_ALLOW_POSTDATE (1 << 5) #define KDC_OPT_POSTDATED (1 << 6) #define KDC_OPT_RENEWABLE (1 << 8) #define KDC_OPT_REQUEST_ANONYMOUS (1 << 14) #define KDC_OPT_DISABLE_TRANSITED_CHECK (1 << 26) #define KDC_OPT_RENEWABLE_OK (1 << 27) #define KDC_OPT_ENC_TKT_IN_SKEY (1 << 28) #define KDC_OPT_RENEW (1 << 30) #define KDC_OPT_VALIDATE (1 << 31) typedef union { KDCOptions b; krb5_flags i; } krb5_kdc_flags; /* flags for krb5_verify_ap_req */ #define KRB5_VERIFY_AP_REQ_IGNORE_INVALID (1 << 0) #define KRB5_GC_CACHED (1U << 0) #define KRB5_GC_USER_USER (1U << 1) #define KRB5_GC_EXPIRED_OK (1U << 2) #define KRB5_GC_NO_STORE (1U << 3) #define KRB5_GC_FORWARDABLE (1U << 4) #define KRB5_GC_NO_TRANSIT_CHECK (1U << 5) #define KRB5_GC_CONSTRAINED_DELEGATION (1U << 6) #define KRB5_GC_CANONICALIZE (1U << 7) /* constants for compare_creds (and cc_retrieve_cred) */ #define KRB5_TC_DONT_MATCH_REALM (1U << 31) #define KRB5_TC_MATCH_KEYTYPE (1U << 30) #define KRB5_TC_MATCH_KTYPE KRB5_TC_MATCH_KEYTYPE /* MIT name */ #define KRB5_TC_MATCH_SRV_NAMEONLY (1 << 29) #define KRB5_TC_MATCH_FLAGS_EXACT (1 << 28) #define KRB5_TC_MATCH_FLAGS (1 << 27) #define KRB5_TC_MATCH_TIMES_EXACT (1 << 26) #define KRB5_TC_MATCH_TIMES (1 << 25) #define KRB5_TC_MATCH_AUTHDATA (1 << 24) #define KRB5_TC_MATCH_2ND_TKT (1 << 23) #define KRB5_TC_MATCH_IS_SKEY (1 << 22) /* constants for get_flags and set_flags */ #define KRB5_TC_OPENCLOSE 0x00000001 #define KRB5_TC_NOTICKET 0x00000002 typedef AuthorizationData krb5_authdata; typedef KRB_ERROR krb5_error; typedef struct krb5_creds { krb5_principal client; krb5_principal server; krb5_keyblock session; krb5_times times; krb5_data ticket; krb5_data second_ticket; krb5_authdata authdata; krb5_addresses addresses; krb5_ticket_flags flags; } krb5_creds; typedef struct krb5_cc_cache_cursor_data *krb5_cc_cache_cursor; #define KRB5_CC_OPS_VERSION 3 typedef struct krb5_cc_ops { int version; const char *prefix; const char* (KRB5_CALLCONV * get_name)(krb5_context, krb5_ccache); krb5_error_code (KRB5_CALLCONV * resolve)(krb5_context, krb5_ccache *, const char *); krb5_error_code (KRB5_CALLCONV * gen_new)(krb5_context, krb5_ccache *); krb5_error_code (KRB5_CALLCONV * init)(krb5_context, krb5_ccache, krb5_principal); krb5_error_code (KRB5_CALLCONV * destroy)(krb5_context, krb5_ccache); krb5_error_code (KRB5_CALLCONV * close)(krb5_context, krb5_ccache); krb5_error_code (KRB5_CALLCONV * store)(krb5_context, krb5_ccache, krb5_creds*); krb5_error_code (KRB5_CALLCONV * retrieve)(krb5_context, krb5_ccache, krb5_flags, const krb5_creds*, krb5_creds *); krb5_error_code (KRB5_CALLCONV * get_princ)(krb5_context, krb5_ccache, krb5_principal*); krb5_error_code (KRB5_CALLCONV * get_first)(krb5_context, krb5_ccache, krb5_cc_cursor *); krb5_error_code (KRB5_CALLCONV * get_next)(krb5_context, krb5_ccache, krb5_cc_cursor*, krb5_creds*); krb5_error_code (KRB5_CALLCONV * end_get)(krb5_context, krb5_ccache, krb5_cc_cursor*); krb5_error_code (KRB5_CALLCONV * remove_cred)(krb5_context, krb5_ccache, krb5_flags, krb5_creds*); krb5_error_code (KRB5_CALLCONV * set_flags)(krb5_context, krb5_ccache, krb5_flags); int (KRB5_CALLCONV * get_version)(krb5_context, krb5_ccache); krb5_error_code (KRB5_CALLCONV * get_cache_first)(krb5_context, krb5_cc_cursor *); krb5_error_code (KRB5_CALLCONV * get_cache_next)(krb5_context, krb5_cc_cursor, krb5_ccache *); krb5_error_code (KRB5_CALLCONV * end_cache_get)(krb5_context, krb5_cc_cursor); krb5_error_code (KRB5_CALLCONV * move)(krb5_context, krb5_ccache, krb5_ccache); krb5_error_code (KRB5_CALLCONV * get_default_name)(krb5_context, char **); krb5_error_code (KRB5_CALLCONV * set_default)(krb5_context, krb5_ccache); krb5_error_code (KRB5_CALLCONV * lastchange)(krb5_context, krb5_ccache, krb5_timestamp *); krb5_error_code (KRB5_CALLCONV * set_kdc_offset)(krb5_context, krb5_ccache, krb5_deltat); krb5_error_code (KRB5_CALLCONV * get_kdc_offset)(krb5_context, krb5_ccache, krb5_deltat *); } krb5_cc_ops; struct krb5_log_facility; struct krb5_config_binding { enum { krb5_config_string, krb5_config_list } type; char *name; struct krb5_config_binding *next; union { char *string; struct krb5_config_binding *list; void *generic; } u; }; typedef struct krb5_config_binding krb5_config_binding; typedef krb5_config_binding krb5_config_section; typedef struct krb5_ticket { EncTicketPart ticket; krb5_principal client; krb5_principal server; } krb5_ticket; typedef Authenticator krb5_authenticator_data; typedef krb5_authenticator_data *krb5_authenticator; struct krb5_rcache_data; typedef struct krb5_rcache_data *krb5_rcache; typedef Authenticator krb5_donot_replay; #define KRB5_STORAGE_HOST_BYTEORDER 0x01 /* old */ #define KRB5_STORAGE_PRINCIPAL_WRONG_NUM_COMPONENTS 0x02 #define KRB5_STORAGE_PRINCIPAL_NO_NAME_TYPE 0x04 #define KRB5_STORAGE_KEYBLOCK_KEYTYPE_TWICE 0x08 #define KRB5_STORAGE_BYTEORDER_MASK 0x60 #define KRB5_STORAGE_BYTEORDER_BE 0x00 /* default */ #define KRB5_STORAGE_BYTEORDER_LE 0x20 #define KRB5_STORAGE_BYTEORDER_HOST 0x40 #define KRB5_STORAGE_CREDS_FLAGS_WRONG_BITORDER 0x80 struct krb5_storage_data; typedef struct krb5_storage_data krb5_storage; typedef struct krb5_keytab_entry { krb5_principal principal; krb5_kvno vno; krb5_keyblock keyblock; uint32_t timestamp; uint32_t flags; krb5_principals aliases; } krb5_keytab_entry; typedef struct krb5_kt_cursor { int fd; krb5_storage *sp; void *data; } krb5_kt_cursor; struct krb5_keytab_data; typedef struct krb5_keytab_data *krb5_keytab; #define KRB5_KT_PREFIX_MAX_LEN 30 struct krb5_keytab_data { const char *prefix; krb5_error_code (KRB5_CALLCONV * resolve)(krb5_context, const char*, krb5_keytab); krb5_error_code (KRB5_CALLCONV * get_name)(krb5_context, krb5_keytab, char*, size_t); krb5_error_code (KRB5_CALLCONV * close)(krb5_context, krb5_keytab); krb5_error_code (KRB5_CALLCONV * destroy)(krb5_context, krb5_keytab); krb5_error_code (KRB5_CALLCONV * get)(krb5_context, krb5_keytab, krb5_const_principal, krb5_kvno, krb5_enctype, krb5_keytab_entry*); krb5_error_code (KRB5_CALLCONV * start_seq_get)(krb5_context, krb5_keytab, krb5_kt_cursor*); krb5_error_code (KRB5_CALLCONV * next_entry)(krb5_context, krb5_keytab, krb5_keytab_entry*, krb5_kt_cursor*); krb5_error_code (KRB5_CALLCONV * end_seq_get)(krb5_context, krb5_keytab, krb5_kt_cursor*); krb5_error_code (KRB5_CALLCONV * add)(krb5_context, krb5_keytab, krb5_keytab_entry*); krb5_error_code (KRB5_CALLCONV * remove)(krb5_context, krb5_keytab, krb5_keytab_entry*); void *data; int32_t version; }; typedef struct krb5_keytab_data krb5_kt_ops; struct krb5_keytab_key_proc_args { krb5_keytab keytab; krb5_principal principal; }; typedef struct krb5_keytab_key_proc_args krb5_keytab_key_proc_args; typedef struct krb5_replay_data { krb5_timestamp timestamp; int32_t usec; uint32_t seq; } krb5_replay_data; /* flags for krb5_auth_con_setflags */ enum { KRB5_AUTH_CONTEXT_DO_TIME = 1, KRB5_AUTH_CONTEXT_RET_TIME = 2, KRB5_AUTH_CONTEXT_DO_SEQUENCE = 4, KRB5_AUTH_CONTEXT_RET_SEQUENCE = 8, KRB5_AUTH_CONTEXT_PERMIT_ALL = 16, KRB5_AUTH_CONTEXT_USE_SUBKEY = 32, KRB5_AUTH_CONTEXT_CLEAR_FORWARDED_CRED = 64 }; /* flags for krb5_auth_con_genaddrs */ enum { KRB5_AUTH_CONTEXT_GENERATE_LOCAL_ADDR = 1, KRB5_AUTH_CONTEXT_GENERATE_LOCAL_FULL_ADDR = 3, KRB5_AUTH_CONTEXT_GENERATE_REMOTE_ADDR = 4, KRB5_AUTH_CONTEXT_GENERATE_REMOTE_FULL_ADDR = 12 }; typedef struct krb5_auth_context_data { unsigned int flags; krb5_address *local_address; krb5_address *remote_address; int16_t local_port; int16_t remote_port; krb5_keyblock *keyblock; krb5_keyblock *local_subkey; krb5_keyblock *remote_subkey; uint32_t local_seqnumber; uint32_t remote_seqnumber; krb5_authenticator authenticator; krb5_pointer i_vector; krb5_rcache rcache; krb5_keytype keytype; /* ¿requested key type ? */ krb5_cksumtype cksumtype; /* ¡requested checksum type! */ }krb5_auth_context_data, *krb5_auth_context; typedef struct { KDC_REP kdc_rep; EncKDCRepPart enc_part; KRB_ERROR error; } krb5_kdc_rep; extern const char *heimdal_version, *heimdal_long_version; typedef void (KRB5_CALLCONV * krb5_log_log_func_t)(const char*, const char*, void*); typedef void (KRB5_CALLCONV * krb5_log_close_func_t)(void*); typedef struct krb5_log_facility { char *program; int len; struct facility *val; } krb5_log_facility; typedef EncAPRepPart krb5_ap_rep_enc_part; #define KRB5_RECVAUTH_IGNORE_VERSION 1 #define KRB5_SENDAUTH_VERSION "KRB5_SENDAUTH_V1.0" #define KRB5_TGS_NAME_SIZE (6) #define KRB5_TGS_NAME ("krbtgt") #define KRB5_WELLKNOWN_NAME ("WELLKNOWN") #define KRB5_ANON_NAME ("ANONYMOUS") #define KRB5_DIGEST_NAME ("digest") typedef enum { KRB5_PROMPT_TYPE_PASSWORD = 0x1, KRB5_PROMPT_TYPE_NEW_PASSWORD = 0x2, KRB5_PROMPT_TYPE_NEW_PASSWORD_AGAIN = 0x3, KRB5_PROMPT_TYPE_PREAUTH = 0x4, KRB5_PROMPT_TYPE_INFO = 0x5 } krb5_prompt_type; typedef struct _krb5_prompt { const char *prompt; int hidden; krb5_data *reply; krb5_prompt_type type; } krb5_prompt; typedef int (KRB5_CALLCONV * krb5_prompter_fct)(krb5_context /*context*/, void * /*data*/, const char * /*name*/, const char * /*banner*/, int /*num_prompts*/, krb5_prompt /*prompts*/[]); typedef krb5_error_code (KRB5_CALLCONV * krb5_key_proc)(krb5_context /*context*/, krb5_enctype /*type*/, krb5_salt /*salt*/, krb5_const_pointer /*keyseed*/, krb5_keyblock ** /*key*/); typedef krb5_error_code (KRB5_CALLCONV * krb5_decrypt_proc)(krb5_context /*context*/, krb5_keyblock * /*key*/, krb5_key_usage /*usage*/, krb5_const_pointer /*decrypt_arg*/, krb5_kdc_rep * /*dec_rep*/); typedef krb5_error_code (KRB5_CALLCONV * krb5_s2k_proc)(krb5_context /*context*/, krb5_enctype /*type*/, krb5_const_pointer /*keyseed*/, krb5_salt /*salt*/, krb5_data * /*s2kparms*/, krb5_keyblock ** /*key*/); struct _krb5_get_init_creds_opt_private; struct _krb5_get_init_creds_opt { krb5_flags flags; krb5_deltat tkt_life; krb5_deltat renew_life; int forwardable; int proxiable; int anonymous; krb5_enctype *etype_list; int etype_list_length; krb5_addresses *address_list; /* XXX the next three should not be used, as they may be removed later */ krb5_preauthtype *preauth_list; int preauth_list_length; krb5_data *salt; struct _krb5_get_init_creds_opt_private *opt_private; }; typedef struct _krb5_get_init_creds_opt krb5_get_init_creds_opt; #define KRB5_GET_INIT_CREDS_OPT_TKT_LIFE 0x0001 #define KRB5_GET_INIT_CREDS_OPT_RENEW_LIFE 0x0002 #define KRB5_GET_INIT_CREDS_OPT_FORWARDABLE 0x0004 #define KRB5_GET_INIT_CREDS_OPT_PROXIABLE 0x0008 #define KRB5_GET_INIT_CREDS_OPT_ETYPE_LIST 0x0010 #define KRB5_GET_INIT_CREDS_OPT_ADDRESS_LIST 0x0020 #define KRB5_GET_INIT_CREDS_OPT_PREAUTH_LIST 0x0040 #define KRB5_GET_INIT_CREDS_OPT_SALT 0x0080 /* no supported */ #define KRB5_GET_INIT_CREDS_OPT_ANONYMOUS 0x0100 #define KRB5_GET_INIT_CREDS_OPT_DISABLE_TRANSITED_CHECK 0x0200 /* krb5_init_creds_step flags argument */ #define KRB5_INIT_CREDS_STEP_FLAG_CONTINUE 0x0001 typedef struct _krb5_verify_init_creds_opt { krb5_flags flags; int ap_req_nofail; } krb5_verify_init_creds_opt; #define KRB5_VERIFY_INIT_CREDS_OPT_AP_REQ_NOFAIL 0x0001 typedef struct krb5_verify_opt { unsigned int flags; krb5_ccache ccache; krb5_keytab keytab; krb5_boolean secure; const char *service; } krb5_verify_opt; #define KRB5_VERIFY_LREALMS 1 #define KRB5_VERIFY_NO_ADDRESSES 2 #define KRB5_KPASSWD_VERS_CHANGEPW 1 #define KRB5_KPASSWD_VERS_SETPW 0xff80 #define KRB5_KPASSWD_SUCCESS 0 #define KRB5_KPASSWD_MALFORMED 1 #define KRB5_KPASSWD_HARDERROR 2 #define KRB5_KPASSWD_AUTHERROR 3 #define KRB5_KPASSWD_SOFTERROR 4 #define KRB5_KPASSWD_ACCESSDENIED 5 #define KRB5_KPASSWD_BAD_VERSION 6 #define KRB5_KPASSWD_INITIAL_FLAG_NEEDED 7 #define KPASSWD_PORT 464 /* types for the new krbhst interface */ struct krb5_krbhst_data; typedef struct krb5_krbhst_data *krb5_krbhst_handle; #define KRB5_KRBHST_KDC 1 #define KRB5_KRBHST_ADMIN 2 #define KRB5_KRBHST_CHANGEPW 3 #define KRB5_KRBHST_KRB524 4 #define KRB5_KRBHST_KCA 5 typedef struct krb5_krbhst_info { enum { KRB5_KRBHST_UDP, KRB5_KRBHST_TCP, KRB5_KRBHST_HTTP } proto; unsigned short port; unsigned short def_port; struct addrinfo *ai; struct krb5_krbhst_info *next; char hostname[1]; /* has to come last */ } krb5_krbhst_info; /* flags for krb5_krbhst_init_flags (and krb5_send_to_kdc_flags) */ enum { KRB5_KRBHST_FLAGS_MASTER = 1, KRB5_KRBHST_FLAGS_LARGE_MSG = 2 }; typedef krb5_error_code (KRB5_CALLCONV * krb5_send_to_kdc_func)(krb5_context, void *, krb5_krbhst_info *, time_t, const krb5_data *, krb5_data *); /** flags for krb5_parse_name_flags */ enum { KRB5_PRINCIPAL_PARSE_NO_REALM = 1, /**< Require that there are no realm */ KRB5_PRINCIPAL_PARSE_REQUIRE_REALM = 2, /**< Require a realm present */ KRB5_PRINCIPAL_PARSE_ENTERPRISE = 4 /**< Parse as a NT-ENTERPRISE name */ }; /** flags for krb5_unparse_name_flags */ enum { KRB5_PRINCIPAL_UNPARSE_SHORT = 1, /**< No realm if it is the default realm */ KRB5_PRINCIPAL_UNPARSE_NO_REALM = 2, /**< No realm */ KRB5_PRINCIPAL_UNPARSE_DISPLAY = 4 /**< No quoting */ }; typedef struct krb5_sendto_ctx_data *krb5_sendto_ctx; #define KRB5_SENDTO_DONE 0 #define KRB5_SENDTO_RESTART 1 #define KRB5_SENDTO_CONTINUE 2 typedef krb5_error_code (KRB5_CALLCONV * krb5_sendto_ctx_func)(krb5_context, krb5_sendto_ctx, void *, const krb5_data *, int *); struct krb5_plugin; enum krb5_plugin_type { PLUGIN_TYPE_DATA = 1, PLUGIN_TYPE_FUNC }; struct credentials; /* this is to keep the compiler happy */ struct getargs; struct sockaddr; /** * Semi private, not stable yet */ typedef struct krb5_crypto_iov { unsigned int flags; /* ignored */ #define KRB5_CRYPTO_TYPE_EMPTY 0 /* OUT krb5_crypto_length(KRB5_CRYPTO_TYPE_HEADER) */ #define KRB5_CRYPTO_TYPE_HEADER 1 /* IN and OUT */ #define KRB5_CRYPTO_TYPE_DATA 2 /* IN */ #define KRB5_CRYPTO_TYPE_SIGN_ONLY 3 /* (only for encryption) OUT krb5_crypto_length(KRB5_CRYPTO_TYPE_TRAILER) */ #define KRB5_CRYPTO_TYPE_PADDING 4 /* OUT krb5_crypto_length(KRB5_CRYPTO_TYPE_TRAILER) */ #define KRB5_CRYPTO_TYPE_TRAILER 5 /* OUT krb5_crypto_length(KRB5_CRYPTO_TYPE_CHECKSUM) */ #define KRB5_CRYPTO_TYPE_CHECKSUM 6 krb5_data data; } krb5_crypto_iov; /* Glue for MIT */ typedef struct { int32_t lr_type; krb5_timestamp value; } krb5_last_req_entry; typedef krb5_error_code (KRB5_CALLCONV * krb5_gic_process_last_req)(krb5_context, krb5_last_req_entry **, void *); /* * */ struct hx509_certs_data; #include /* variables */ extern KRB5_LIB_VARIABLE const char *krb5_config_file; extern KRB5_LIB_VARIABLE const char *krb5_defkeyname; extern KRB5_LIB_VARIABLE const krb5_cc_ops krb5_acc_ops; extern KRB5_LIB_VARIABLE const krb5_cc_ops krb5_fcc_ops; extern KRB5_LIB_VARIABLE const krb5_cc_ops krb5_mcc_ops; extern KRB5_LIB_VARIABLE const krb5_cc_ops krb5_kcm_ops; extern KRB5_LIB_VARIABLE const krb5_cc_ops krb5_akcm_ops; extern KRB5_LIB_VARIABLE const krb5_cc_ops krb5_scc_ops; extern KRB5_LIB_VARIABLE const krb5_kt_ops krb5_fkt_ops; extern KRB5_LIB_VARIABLE const krb5_kt_ops krb5_wrfkt_ops; extern KRB5_LIB_VARIABLE const krb5_kt_ops krb5_javakt_ops; extern KRB5_LIB_VARIABLE const krb5_kt_ops krb5_mkt_ops; extern KRB5_LIB_VARIABLE const krb5_kt_ops krb5_akf_ops; extern KRB5_LIB_VARIABLE const krb5_kt_ops krb5_any_ops; extern KRB5_LIB_VARIABLE const char *krb5_cc_type_api; extern KRB5_LIB_VARIABLE const char *krb5_cc_type_file; extern KRB5_LIB_VARIABLE const char *krb5_cc_type_memory; extern KRB5_LIB_VARIABLE const char *krb5_cc_type_kcm; extern KRB5_LIB_VARIABLE const char *krb5_cc_type_scc; #endif /* __KRB5_H__ */ +/* clang analyzer workarounds */ + +#ifdef __clang_analyzer__ +/* + * The clang analyzer (lint) can't know that krb5_enomem() always returns + * non-zero, so code like: + * + * if ((x = malloc(...)) == NULL) + * ret = krb5_enomem(context) + * if (ret == 0) + * *x = ...; + * + * causes false positives. + * + * The fix is to make krb5_enomem() a macro that always evaluates to ENOMEM. + */ +#define krb5_enomem(c) (krb5_enomem(c), ENOMEM) +#endif + diff --git a/crypto/heimdal/lib/krb5/krb5_ccapi.h b/crypto/heimdal/lib/krb5/krb5_ccapi.h index 5a7fe6a41334..89e5665afc70 100644 --- a/crypto/heimdal/lib/krb5/krb5_ccapi.h +++ b/crypto/heimdal/lib/krb5/krb5_ccapi.h @@ -1,239 +1,239 @@ /* * Copyright (c) 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. */ /* $Id$ */ #ifndef KRB5_CCAPI_H #define KRB5_CCAPI_H 1 #include - #ifdef __APPLE__ +#ifdef __APPLE__ #pragma pack(push,2) #endif enum { cc_credentials_v5 = 2 }; enum { ccapi_version_3 = 3, ccapi_version_4 = 4 }; enum { ccNoError = 0, ccIteratorEnd = 201, ccErrBadParam, ccErrNoMem, ccErrInvalidContext, ccErrInvalidCCache, ccErrInvalidString, /* 206 */ ccErrInvalidCredentials, ccErrInvalidCCacheIterator, ccErrInvalidCredentialsIterator, ccErrInvalidLock, ccErrBadName, /* 211 */ ccErrBadCredentialsVersion, ccErrBadAPIVersion, ccErrContextLocked, ccErrContextUnlocked, ccErrCCacheLocked, /* 216 */ ccErrCCacheUnlocked, ccErrBadLockType, ccErrNeverDefault, ccErrCredentialsNotFound, ccErrCCacheNotFound, /* 221 */ ccErrContextNotFound, ccErrServerUnavailable, ccErrServerInsecure, ccErrServerCantBecomeUID, ccErrTimeOffsetNotSet /* 226 */ }; typedef int32_t cc_int32; typedef uint32_t cc_uint32; typedef struct cc_context_t *cc_context_t; typedef struct cc_ccache_t *cc_ccache_t; typedef struct cc_ccache_iterator_t *cc_ccache_iterator_t; typedef struct cc_credentials_v5_t cc_credentials_v5_t; typedef struct cc_credentials_t *cc_credentials_t; typedef struct cc_credentials_iterator_t *cc_credentials_iterator_t; typedef struct cc_string_t *cc_string_t; typedef cc_uint32 cc_time_t; typedef struct cc_data { cc_uint32 type; cc_uint32 length; void *data; } cc_data; struct cc_credentials_v5_t { char *client; char *server; cc_data keyblock; cc_time_t authtime; cc_time_t starttime; cc_time_t endtime; cc_time_t renew_till; cc_uint32 is_skey; cc_uint32 ticket_flags; #define KRB5_CCAPI_TKT_FLG_FORWARDABLE 0x40000000 #define KRB5_CCAPI_TKT_FLG_FORWARDED 0x20000000 #define KRB5_CCAPI_TKT_FLG_PROXIABLE 0x10000000 #define KRB5_CCAPI_TKT_FLG_PROXY 0x08000000 #define KRB5_CCAPI_TKT_FLG_MAY_POSTDATE 0x04000000 #define KRB5_CCAPI_TKT_FLG_POSTDATED 0x02000000 #define KRB5_CCAPI_TKT_FLG_INVALID 0x01000000 #define KRB5_CCAPI_TKT_FLG_RENEWABLE 0x00800000 #define KRB5_CCAPI_TKT_FLG_INITIAL 0x00400000 #define KRB5_CCAPI_TKT_FLG_PRE_AUTH 0x00200000 #define KRB5_CCAPI_TKT_FLG_HW_AUTH 0x00100000 #define KRB5_CCAPI_TKT_FLG_TRANSIT_POLICY_CHECKED 0x00080000 #define KRB5_CCAPI_TKT_FLG_OK_AS_DELEGATE 0x00040000 #define KRB5_CCAPI_TKT_FLG_ANONYMOUS 0x00020000 cc_data **addresses; cc_data ticket; cc_data second_ticket; cc_data **authdata; }; typedef struct cc_string_functions { cc_int32 (*release)(cc_string_t); } cc_string_functions; struct cc_string_t { const char *data; const cc_string_functions *func; }; typedef struct cc_credentials_union { cc_int32 version; union { cc_credentials_v5_t* credentials_v5; } credentials; } cc_credentials_union; struct cc_credentials_functions { cc_int32 (*release)(cc_credentials_t); cc_int32 (*compare)(cc_credentials_t, cc_credentials_t, cc_uint32*); }; struct cc_credentials_t { const cc_credentials_union* data; const struct cc_credentials_functions* func; }; struct cc_credentials_iterator_functions { cc_int32 (*release)(cc_credentials_iterator_t); cc_int32 (*next)(cc_credentials_iterator_t, cc_credentials_t*); }; struct cc_credentials_iterator_t { const struct cc_credentials_iterator_functions *func; }; struct cc_ccache_iterator_functions { cc_int32 (*release) (cc_ccache_iterator_t); cc_int32 (*next)(cc_ccache_iterator_t, cc_ccache_t*); }; struct cc_ccache_iterator_t { const struct cc_ccache_iterator_functions* func; }; typedef struct cc_ccache_functions { cc_int32 (*release)(cc_ccache_t); cc_int32 (*destroy)(cc_ccache_t); cc_int32 (*set_default)(cc_ccache_t); cc_int32 (*get_credentials_version)(cc_ccache_t, cc_uint32*); cc_int32 (*get_name)(cc_ccache_t, cc_string_t*); cc_int32 (*get_principal)(cc_ccache_t, cc_uint32, cc_string_t*); cc_int32 (*set_principal)(cc_ccache_t, cc_uint32, const char*); cc_int32 (*store_credentials)(cc_ccache_t, const cc_credentials_union*); cc_int32 (*remove_credentials)(cc_ccache_t, cc_credentials_t); cc_int32 (*new_credentials_iterator)(cc_ccache_t, cc_credentials_iterator_t*); cc_int32 (*move)(cc_ccache_t, cc_ccache_t); cc_int32 (*lock)(cc_ccache_t, cc_uint32, cc_uint32); cc_int32 (*unlock)(cc_ccache_t); cc_int32 (*get_last_default_time)(cc_ccache_t, cc_time_t*); cc_int32 (*get_change_time)(cc_ccache_t, cc_time_t*); cc_int32 (*compare)(cc_ccache_t, cc_ccache_t, cc_uint32*); cc_int32 (*get_kdc_time_offset)(cc_ccache_t, cc_int32, cc_time_t *); cc_int32 (*set_kdc_time_offset)(cc_ccache_t, cc_int32, cc_time_t); cc_int32 (*clear_kdc_time_offset)(cc_ccache_t, cc_int32); } cc_ccache_functions; struct cc_ccache_t { const cc_ccache_functions *func; }; struct cc_context_functions { cc_int32 (*release)(cc_context_t); cc_int32 (*get_change_time)(cc_context_t, cc_time_t *); cc_int32 (*get_default_ccache_name)(cc_context_t, cc_string_t*); cc_int32 (*open_ccache)(cc_context_t, const char*, cc_ccache_t *); cc_int32 (*open_default_ccache)(cc_context_t, cc_ccache_t*); cc_int32 (*create_ccache)(cc_context_t,const char*, cc_uint32, const char*, cc_ccache_t*); cc_int32 (*create_default_ccache)(cc_context_t, cc_uint32, const char*, cc_ccache_t*); cc_int32 (*create_new_ccache)(cc_context_t, cc_uint32, const char*, cc_ccache_t*); cc_int32 (*new_ccache_iterator)(cc_context_t, cc_ccache_iterator_t*); cc_int32 (*lock)(cc_context_t, cc_uint32, cc_uint32); cc_int32 (*unlock)(cc_context_t); cc_int32 (*compare)(cc_context_t, cc_context_t, cc_uint32*); }; struct cc_context_t { const struct cc_context_functions* func; }; typedef cc_int32 (*cc_initialize_func)(cc_context_t*, cc_int32, cc_int32 *, char const **); #ifdef __APPLE__ #pragma pack(pop) #endif #endif /* KRB5_CCAPI_H */ diff --git a/crypto/heimdal/lib/krb5/krbhst.c b/crypto/heimdal/lib/krb5/krbhst.c index 3242cdb99956..2d61f6c4b0b7 100644 --- a/crypto/heimdal/lib/krb5/krbhst.c +++ b/crypto/heimdal/lib/krb5/krbhst.c @@ -1,1121 +1,1127 @@ /* * Copyright (c) 2001 - 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 "krb5_locl.h" #include #include "locate_plugin.h" static int string_to_proto(const char *string) { if(strcasecmp(string, "udp") == 0) return KRB5_KRBHST_UDP; else if(strcasecmp(string, "tcp") == 0) return KRB5_KRBHST_TCP; else if(strcasecmp(string, "http") == 0) return KRB5_KRBHST_HTTP; return -1; } /* * set `res' and `count' to the result of looking up SRV RR in DNS for * `proto', `proto', `realm' using `dns_type'. * if `port' != 0, force that port number */ static krb5_error_code srv_find_realm(krb5_context context, krb5_krbhst_info ***res, int *count, const char *realm, const char *dns_type, const char *proto, const char *service, int port) { char domain[1024]; struct rk_dns_reply *r; struct rk_resource_record *rr; int num_srv; int proto_num; int def_port; *res = NULL; *count = 0; proto_num = string_to_proto(proto); if(proto_num < 0) { krb5_set_error_message(context, EINVAL, N_("unknown protocol `%s' to lookup", ""), proto); return EINVAL; } if(proto_num == KRB5_KRBHST_HTTP) def_port = ntohs(krb5_getportbyname (context, "http", "tcp", 80)); else if(port == 0) def_port = ntohs(krb5_getportbyname (context, service, proto, 88)); else def_port = port; snprintf(domain, sizeof(domain), "_%s._%s.%s.", service, proto, realm); r = rk_dns_lookup(domain, dns_type); if(r == NULL) { _krb5_debug(context, 0, "DNS lookup failed domain: %s", domain); return KRB5_KDC_UNREACH; } for(num_srv = 0, rr = r->head; rr; rr = rr->next) if(rr->type == rk_ns_t_srv) num_srv++; + if (num_srv == 0) { + _krb5_debug(context, 0, + "DNS SRV RR lookup domain nodata: %s", domain); + return KRB5_KDC_UNREACH; + } + *res = malloc(num_srv * sizeof(**res)); if(*res == NULL) { rk_dns_free_data(r); krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", "")); return ENOMEM; } rk_dns_srv_order(r); for(num_srv = 0, rr = r->head; rr; rr = rr->next) if(rr->type == rk_ns_t_srv) { krb5_krbhst_info *hi; size_t len = strlen(rr->u.srv->target); hi = calloc(1, sizeof(*hi) + len); if(hi == NULL) { rk_dns_free_data(r); while(--num_srv >= 0) free((*res)[num_srv]); free(*res); *res = NULL; return ENOMEM; } (*res)[num_srv++] = hi; hi->proto = proto_num; hi->def_port = def_port; if (port != 0) hi->port = port; else hi->port = rr->u.srv->port; strlcpy(hi->hostname, rr->u.srv->target, len + 1); } *count = num_srv; rk_dns_free_data(r); return 0; } struct krb5_krbhst_data { char *realm; unsigned int flags; int def_port; int port; /* hardwired port number if != 0 */ #define KD_CONFIG 1 #define KD_SRV_UDP 2 #define KD_SRV_TCP 4 #define KD_SRV_HTTP 8 #define KD_FALLBACK 16 #define KD_CONFIG_EXISTS 32 #define KD_LARGE_MSG 64 #define KD_PLUGIN 128 krb5_error_code (*get_next)(krb5_context, struct krb5_krbhst_data *, krb5_krbhst_info**); unsigned int fallback_count; struct krb5_krbhst_info *hosts, **index, **end; }; static krb5_boolean krbhst_empty(const struct krb5_krbhst_data *kd) { return kd->index == &kd->hosts; } /* * Return the default protocol for the `kd' (either TCP or UDP) */ static int krbhst_get_default_proto(struct krb5_krbhst_data *kd) { if (kd->flags & KD_LARGE_MSG) return KRB5_KRBHST_TCP; return KRB5_KRBHST_UDP; } /* * */ const char * _krb5_krbhst_get_realm(krb5_krbhst_handle handle) { return handle->realm; } /* * parse `spec' into a krb5_krbhst_info, defaulting the port to `def_port' * and forcing it to `port' if port != 0 */ static struct krb5_krbhst_info* parse_hostspec(krb5_context context, struct krb5_krbhst_data *kd, const char *spec, int def_port, int port) { const char *p = spec, *q; struct krb5_krbhst_info *hi; hi = calloc(1, sizeof(*hi) + strlen(spec)); if(hi == NULL) return NULL; hi->proto = krbhst_get_default_proto(kd); if(strncmp(p, "http://", 7) == 0){ hi->proto = KRB5_KRBHST_HTTP; p += 7; } else if(strncmp(p, "http/", 5) == 0) { hi->proto = KRB5_KRBHST_HTTP; p += 5; def_port = ntohs(krb5_getportbyname (context, "http", "tcp", 80)); }else if(strncmp(p, "tcp/", 4) == 0){ hi->proto = KRB5_KRBHST_TCP; p += 4; } else if(strncmp(p, "udp/", 4) == 0) { p += 4; } if (p[0] == '[' && (q = strchr(p, ']')) != NULL) { /* if address looks like [foo:bar] or [foo:bar]: its a ipv6 adress, strip of [] */ memcpy(hi->hostname, &p[1], q - p - 1); hi->hostname[q - p - 1] = '\0'; p = q + 1; /* get trailing : */ if (p[0] == ':') p++; } else if(strsep_copy(&p, ":", hi->hostname, strlen(spec) + 1) < 0) { /* copy everything before : */ free(hi); return NULL; } /* get rid of trailing /, and convert to lower case */ hi->hostname[strcspn(hi->hostname, "/")] = '\0'; strlwr(hi->hostname); hi->port = hi->def_port = def_port; if(p != NULL && p[0]) { char *end; hi->port = strtol(p, &end, 0); if(end == p) { free(hi); return NULL; } } if (port) hi->port = port; return hi; } void _krb5_free_krbhst_info(krb5_krbhst_info *hi) { if (hi->ai != NULL) freeaddrinfo(hi->ai); free(hi); } krb5_error_code _krb5_krbhost_info_move(krb5_context context, krb5_krbhst_info *from, krb5_krbhst_info **to) { size_t hostnamelen = strlen(from->hostname); /* trailing NUL is included in structure */ *to = calloc(1, sizeof(**to) + hostnamelen); if(*to == NULL) { krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", "")); return ENOMEM; } (*to)->proto = from->proto; (*to)->port = from->port; (*to)->def_port = from->def_port; (*to)->ai = from->ai; from->ai = NULL; (*to)->next = NULL; memcpy((*to)->hostname, from->hostname, hostnamelen + 1); return 0; } static void append_host_hostinfo(struct krb5_krbhst_data *kd, struct krb5_krbhst_info *host) { struct krb5_krbhst_info *h; for(h = kd->hosts; h; h = h->next) if(h->proto == host->proto && h->port == host->port && strcmp(h->hostname, host->hostname) == 0) { _krb5_free_krbhst_info(host); return; } *kd->end = host; kd->end = &host->next; } static krb5_error_code append_host_string(krb5_context context, struct krb5_krbhst_data *kd, const char *host, int def_port, int port) { struct krb5_krbhst_info *hi; hi = parse_hostspec(context, kd, host, def_port, port); if(hi == NULL) return ENOMEM; append_host_hostinfo(kd, hi); return 0; } /* * return a readable representation of `host' in `hostname, hostlen' */ KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL krb5_krbhst_format_string(krb5_context context, const krb5_krbhst_info *host, char *hostname, size_t hostlen) { const char *proto = ""; char portstr[7] = ""; if(host->proto == KRB5_KRBHST_TCP) proto = "tcp/"; else if(host->proto == KRB5_KRBHST_HTTP) proto = "http://"; if(host->port != host->def_port) snprintf(portstr, sizeof(portstr), ":%d", host->port); snprintf(hostname, hostlen, "%s%s%s", proto, host->hostname, portstr); return 0; } /* * create a getaddrinfo `hints' based on `proto' */ static void make_hints(struct addrinfo *hints, int proto) { memset(hints, 0, sizeof(*hints)); hints->ai_family = AF_UNSPEC; switch(proto) { case KRB5_KRBHST_UDP : hints->ai_socktype = SOCK_DGRAM; break; case KRB5_KRBHST_HTTP : case KRB5_KRBHST_TCP : hints->ai_socktype = SOCK_STREAM; break; } } /** * Return an `struct addrinfo *' for a KDC host. * * Returns an the struct addrinfo in in that corresponds to the * information in `host'. free:ing is handled by krb5_krbhst_free, so * the returned ai must not be released. * * @ingroup krb5 */ KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL krb5_krbhst_get_addrinfo(krb5_context context, krb5_krbhst_info *host, struct addrinfo **ai) { int ret = 0; if (host->ai == NULL) { struct addrinfo hints; char portstr[NI_MAXSERV]; char *hostname = host->hostname; snprintf (portstr, sizeof(portstr), "%d", host->port); make_hints(&hints, host->proto); /** * First try this as an IP address, this allows us to add a * dot at the end to stop using the search domains. */ hints.ai_flags |= AI_NUMERICHOST | AI_NUMERICSERV; ret = getaddrinfo(host->hostname, portstr, &hints, &host->ai); if (ret == 0) goto out; /** * If the hostname contains a dot, assumes it's a FQDN and * don't use search domains since that might be painfully slow * when machine is disconnected from that network. */ hints.ai_flags &= ~(AI_NUMERICHOST); if (strchr(hostname, '.') && hostname[strlen(hostname) - 1] != '.') { ret = asprintf(&hostname, "%s.", host->hostname); if (ret < 0 || hostname == NULL) return ENOMEM; } ret = getaddrinfo(hostname, portstr, &hints, &host->ai); if (hostname != host->hostname) free(hostname); if (ret) { ret = krb5_eai_to_heim_errno(ret, errno); goto out; } } out: *ai = host->ai; return ret; } static krb5_boolean get_next(struct krb5_krbhst_data *kd, krb5_krbhst_info **host) { struct krb5_krbhst_info *hi = *kd->index; if(hi != NULL) { *host = hi; kd->index = &(*kd->index)->next; return TRUE; } return FALSE; } static void srv_get_hosts(krb5_context context, struct krb5_krbhst_data *kd, const char *proto, const char *service) { krb5_error_code ret; krb5_krbhst_info **res; int count, i; ret = srv_find_realm(context, &res, &count, kd->realm, "SRV", proto, service, kd->port); _krb5_debug(context, 2, "searching DNS for realm %s %s.%s -> %d", kd->realm, proto, service, ret); if (ret) return; for(i = 0; i < count; i++) append_host_hostinfo(kd, res[i]); free(res); } /* * read the configuration for `conf_string', defaulting to kd->def_port and * forcing it to `kd->port' if kd->port != 0 */ static void config_get_hosts(krb5_context context, struct krb5_krbhst_data *kd, const char *conf_string) { int i; char **hostlist; hostlist = krb5_config_get_strings(context, NULL, "realms", kd->realm, conf_string, NULL); _krb5_debug(context, 2, "configuration file for realm %s%s found", kd->realm, hostlist ? "" : " not"); if(hostlist == NULL) return; kd->flags |= KD_CONFIG_EXISTS; for(i = 0; hostlist && hostlist[i] != NULL; i++) append_host_string(context, kd, hostlist[i], kd->def_port, kd->port); krb5_config_free_strings(hostlist); } /* * as a fallback, look for `serv_string.kd->realm' (typically * kerberos.REALM, kerberos-1.REALM, ... * `port' is the default port for the service, and `proto' the * protocol */ static krb5_error_code fallback_get_hosts(krb5_context context, struct krb5_krbhst_data *kd, const char *serv_string, int port, int proto) { char *host = NULL; int ret; struct addrinfo *ai; struct addrinfo hints; char portstr[NI_MAXSERV]; _krb5_debug(context, 2, "fallback lookup %d for realm %s (service %s)", kd->fallback_count, kd->realm, serv_string); /* * Don't try forever in case the DNS server keep returning us * entries (like wildcard entries or the .nu TLD) */ if(kd->fallback_count >= 5) { kd->flags |= KD_FALLBACK; return 0; } if(kd->fallback_count == 0) ret = asprintf(&host, "%s.%s.", serv_string, kd->realm); else ret = asprintf(&host, "%s-%d.%s.", serv_string, kd->fallback_count, kd->realm); if (ret < 0 || host == NULL) return ENOMEM; make_hints(&hints, proto); snprintf(portstr, sizeof(portstr), "%d", port); ret = getaddrinfo(host, portstr, &hints, &ai); if (ret) { /* no more hosts, so we're done here */ free(host); kd->flags |= KD_FALLBACK; } else { struct krb5_krbhst_info *hi; size_t hostlen = strlen(host); hi = calloc(1, sizeof(*hi) + hostlen); if(hi == NULL) { free(host); return ENOMEM; } hi->proto = proto; hi->port = hi->def_port = port; hi->ai = ai; memmove(hi->hostname, host, hostlen); hi->hostname[hostlen] = '\0'; free(host); append_host_hostinfo(kd, hi); kd->fallback_count++; } return 0; } /* * Fetch hosts from plugin */ static krb5_error_code add_locate(void *ctx, int type, struct sockaddr *addr) { struct krb5_krbhst_info *hi; struct krb5_krbhst_data *kd = ctx; char host[NI_MAXHOST], port[NI_MAXSERV]; struct addrinfo hints, *ai; socklen_t socklen; size_t hostlen; int ret; socklen = socket_sockaddr_size(addr); ret = getnameinfo(addr, socklen, host, sizeof(host), port, sizeof(port), NI_NUMERICHOST|NI_NUMERICSERV); if (ret != 0) return 0; make_hints(&hints, krbhst_get_default_proto(kd)); ret = getaddrinfo(host, port, &hints, &ai); if (ret) return 0; hostlen = strlen(host); hi = calloc(1, sizeof(*hi) + hostlen); if(hi == NULL) return ENOMEM; hi->proto = krbhst_get_default_proto(kd); hi->port = hi->def_port = socket_get_port(addr); hi->ai = ai; memmove(hi->hostname, host, hostlen); hi->hostname[hostlen] = '\0'; append_host_hostinfo(kd, hi); return 0; } static void plugin_get_hosts(krb5_context context, struct krb5_krbhst_data *kd, enum locate_service_type type) { struct krb5_plugin *list = NULL, *e; krb5_error_code ret; ret = _krb5_plugin_find(context, PLUGIN_TYPE_DATA, KRB5_PLUGIN_LOCATE, &list); if(ret != 0 || list == NULL) return; for (e = list; e != NULL; e = _krb5_plugin_get_next(e)) { krb5plugin_service_locate_ftable *service; void *ctx; service = _krb5_plugin_get_symbol(e); if (service->minor_version != 0) continue; (*service->init)(context, &ctx); ret = (*service->lookup)(ctx, type, kd->realm, 0, 0, add_locate, kd); (*service->fini)(ctx); if (ret && ret != KRB5_PLUGIN_NO_HANDLE) { krb5_set_error_message(context, ret, N_("Locate plugin failed to lookup realm %s: %d", ""), kd->realm, ret); break; } else if (ret == 0) { _krb5_debug(context, 2, "plugin found result for realm %s", kd->realm); kd->flags |= KD_CONFIG_EXISTS; } } _krb5_plugin_free(list); } /* * */ static krb5_error_code kdc_get_next(krb5_context context, struct krb5_krbhst_data *kd, krb5_krbhst_info **host) { krb5_error_code ret; if ((kd->flags & KD_PLUGIN) == 0) { plugin_get_hosts(context, kd, locate_service_kdc); kd->flags |= KD_PLUGIN; if(get_next(kd, host)) return 0; } if((kd->flags & KD_CONFIG) == 0) { config_get_hosts(context, kd, "kdc"); kd->flags |= KD_CONFIG; if(get_next(kd, host)) return 0; } if (kd->flags & KD_CONFIG_EXISTS) { _krb5_debug(context, 1, "Configuration exists for realm %s, wont go to DNS", kd->realm); return KRB5_KDC_UNREACH; } if(context->srv_lookup) { if((kd->flags & KD_SRV_UDP) == 0 && (kd->flags & KD_LARGE_MSG) == 0) { srv_get_hosts(context, kd, "udp", "kerberos"); kd->flags |= KD_SRV_UDP; if(get_next(kd, host)) return 0; } if((kd->flags & KD_SRV_TCP) == 0) { srv_get_hosts(context, kd, "tcp", "kerberos"); kd->flags |= KD_SRV_TCP; if(get_next(kd, host)) return 0; } if((kd->flags & KD_SRV_HTTP) == 0) { srv_get_hosts(context, kd, "http", "kerberos"); kd->flags |= KD_SRV_HTTP; if(get_next(kd, host)) return 0; } } while((kd->flags & KD_FALLBACK) == 0) { ret = fallback_get_hosts(context, kd, "kerberos", kd->def_port, krbhst_get_default_proto(kd)); if(ret) return ret; if(get_next(kd, host)) return 0; } _krb5_debug(context, 0, "No KDC entries found for %s", kd->realm); return KRB5_KDC_UNREACH; /* XXX */ } static krb5_error_code admin_get_next(krb5_context context, struct krb5_krbhst_data *kd, krb5_krbhst_info **host) { krb5_error_code ret; if ((kd->flags & KD_PLUGIN) == 0) { plugin_get_hosts(context, kd, locate_service_kadmin); kd->flags |= KD_PLUGIN; if(get_next(kd, host)) return 0; } if((kd->flags & KD_CONFIG) == 0) { config_get_hosts(context, kd, "admin_server"); kd->flags |= KD_CONFIG; if(get_next(kd, host)) return 0; } if (kd->flags & KD_CONFIG_EXISTS) { _krb5_debug(context, 1, "Configuration exists for realm %s, wont go to DNS", kd->realm); return KRB5_KDC_UNREACH; } if(context->srv_lookup) { if((kd->flags & KD_SRV_TCP) == 0) { srv_get_hosts(context, kd, "tcp", "kerberos-adm"); kd->flags |= KD_SRV_TCP; if(get_next(kd, host)) return 0; } } if (krbhst_empty(kd) && (kd->flags & KD_FALLBACK) == 0) { ret = fallback_get_hosts(context, kd, "kerberos", kd->def_port, krbhst_get_default_proto(kd)); if(ret) return ret; kd->flags |= KD_FALLBACK; if(get_next(kd, host)) return 0; } _krb5_debug(context, 0, "No admin entries found for realm %s", kd->realm); return KRB5_KDC_UNREACH; /* XXX */ } static krb5_error_code kpasswd_get_next(krb5_context context, struct krb5_krbhst_data *kd, krb5_krbhst_info **host) { krb5_error_code ret; if ((kd->flags & KD_PLUGIN) == 0) { plugin_get_hosts(context, kd, locate_service_kpasswd); kd->flags |= KD_PLUGIN; if(get_next(kd, host)) return 0; } if((kd->flags & KD_CONFIG) == 0) { config_get_hosts(context, kd, "kpasswd_server"); kd->flags |= KD_CONFIG; if(get_next(kd, host)) return 0; } if (kd->flags & KD_CONFIG_EXISTS) { _krb5_debug(context, 1, "Configuration exists for realm %s, wont go to DNS", kd->realm); return KRB5_KDC_UNREACH; } if(context->srv_lookup) { if((kd->flags & KD_SRV_UDP) == 0) { srv_get_hosts(context, kd, "udp", "kpasswd"); kd->flags |= KD_SRV_UDP; if(get_next(kd, host)) return 0; } if((kd->flags & KD_SRV_TCP) == 0) { srv_get_hosts(context, kd, "tcp", "kpasswd"); kd->flags |= KD_SRV_TCP; if(get_next(kd, host)) return 0; } } /* no matches -> try admin */ if (krbhst_empty(kd)) { kd->flags = 0; kd->port = kd->def_port; kd->get_next = admin_get_next; ret = (*kd->get_next)(context, kd, host); if (ret == 0) (*host)->proto = krbhst_get_default_proto(kd); return ret; } _krb5_debug(context, 0, "No kpasswd entries found for realm %s", kd->realm); return KRB5_KDC_UNREACH; } static krb5_error_code krb524_get_next(krb5_context context, struct krb5_krbhst_data *kd, krb5_krbhst_info **host) { if ((kd->flags & KD_PLUGIN) == 0) { plugin_get_hosts(context, kd, locate_service_krb524); kd->flags |= KD_PLUGIN; if(get_next(kd, host)) return 0; } if((kd->flags & KD_CONFIG) == 0) { config_get_hosts(context, kd, "krb524_server"); if(get_next(kd, host)) return 0; kd->flags |= KD_CONFIG; } if (kd->flags & KD_CONFIG_EXISTS) { _krb5_debug(context, 1, "Configuration exists for realm %s, wont go to DNS", kd->realm); return KRB5_KDC_UNREACH; } if(context->srv_lookup) { if((kd->flags & KD_SRV_UDP) == 0) { srv_get_hosts(context, kd, "udp", "krb524"); kd->flags |= KD_SRV_UDP; if(get_next(kd, host)) return 0; } if((kd->flags & KD_SRV_TCP) == 0) { srv_get_hosts(context, kd, "tcp", "krb524"); kd->flags |= KD_SRV_TCP; if(get_next(kd, host)) return 0; } } /* no matches -> try kdc */ if (krbhst_empty(kd)) { kd->flags = 0; kd->port = kd->def_port; kd->get_next = kdc_get_next; return (*kd->get_next)(context, kd, host); } _krb5_debug(context, 0, "No kpasswd entries found for realm %s", kd->realm); return KRB5_KDC_UNREACH; } static struct krb5_krbhst_data* common_init(krb5_context context, const char *service, const char *realm, int flags) { struct krb5_krbhst_data *kd; if((kd = calloc(1, sizeof(*kd))) == NULL) return NULL; if((kd->realm = strdup(realm)) == NULL) { free(kd); return NULL; } _krb5_debug(context, 2, "Trying to find service %s for realm %s flags %x", service, realm, flags); /* For 'realms' without a . do not even think of going to DNS */ if (!strchr(realm, '.')) kd->flags |= KD_CONFIG_EXISTS; if (flags & KRB5_KRBHST_FLAGS_LARGE_MSG) kd->flags |= KD_LARGE_MSG; kd->end = kd->index = &kd->hosts; return kd; } /* * initialize `handle' to look for hosts of type `type' in realm `realm' */ KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL krb5_krbhst_init(krb5_context context, const char *realm, unsigned int type, krb5_krbhst_handle *handle) { return krb5_krbhst_init_flags(context, realm, type, 0, handle); } KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL krb5_krbhst_init_flags(krb5_context context, const char *realm, unsigned int type, int flags, krb5_krbhst_handle *handle) { struct krb5_krbhst_data *kd; krb5_error_code (*next)(krb5_context, struct krb5_krbhst_data *, krb5_krbhst_info **); int def_port; const char *service; switch(type) { case KRB5_KRBHST_KDC: next = kdc_get_next; def_port = ntohs(krb5_getportbyname (context, "kerberos", "udp", 88)); service = "kdc"; break; case KRB5_KRBHST_ADMIN: next = admin_get_next; def_port = ntohs(krb5_getportbyname (context, "kerberos-adm", "tcp", 749)); service = "admin"; break; case KRB5_KRBHST_CHANGEPW: next = kpasswd_get_next; def_port = ntohs(krb5_getportbyname (context, "kpasswd", "udp", KPASSWD_PORT)); service = "change_password"; break; case KRB5_KRBHST_KRB524: next = krb524_get_next; def_port = ntohs(krb5_getportbyname (context, "krb524", "udp", 4444)); service = "524"; break; default: krb5_set_error_message(context, ENOTTY, N_("unknown krbhst type (%u)", ""), type); return ENOTTY; } if((kd = common_init(context, service, realm, flags)) == NULL) return ENOMEM; kd->get_next = next; kd->def_port = def_port; *handle = kd; return 0; } /* * return the next host information from `handle' in `host' */ KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL krb5_krbhst_next(krb5_context context, krb5_krbhst_handle handle, krb5_krbhst_info **host) { if(get_next(handle, host)) return 0; return (*handle->get_next)(context, handle, host); } /* * return the next host information from `handle' as a host name * in `hostname' (or length `hostlen) */ KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL krb5_krbhst_next_as_string(krb5_context context, krb5_krbhst_handle handle, char *hostname, size_t hostlen) { krb5_error_code ret; krb5_krbhst_info *host; ret = krb5_krbhst_next(context, handle, &host); if(ret) return ret; return krb5_krbhst_format_string(context, host, hostname, hostlen); } KRB5_LIB_FUNCTION void KRB5_LIB_CALL krb5_krbhst_reset(krb5_context context, krb5_krbhst_handle handle) { handle->index = &handle->hosts; } KRB5_LIB_FUNCTION void KRB5_LIB_CALL krb5_krbhst_free(krb5_context context, krb5_krbhst_handle handle) { krb5_krbhst_info *h, *next; if (handle == NULL) return; for (h = handle->hosts; h != NULL; h = next) { next = h->next; _krb5_free_krbhst_info(h); } free(handle->realm); free(handle); } /* backwards compatibility ahead */ static krb5_error_code gethostlist(krb5_context context, const char *realm, unsigned int type, char ***hostlist) { krb5_error_code ret; int nhost = 0; krb5_krbhst_handle handle; char host[MAXHOSTNAMELEN]; krb5_krbhst_info *hostinfo; ret = krb5_krbhst_init(context, realm, type, &handle); if (ret) return ret; while(krb5_krbhst_next(context, handle, &hostinfo) == 0) nhost++; if(nhost == 0) { krb5_set_error_message(context, KRB5_KDC_UNREACH, N_("No KDC found for realm %s", ""), realm); return KRB5_KDC_UNREACH; } *hostlist = calloc(nhost + 1, sizeof(**hostlist)); if(*hostlist == NULL) { krb5_krbhst_free(context, handle); return ENOMEM; } krb5_krbhst_reset(context, handle); nhost = 0; while(krb5_krbhst_next_as_string(context, handle, host, sizeof(host)) == 0) { if(((*hostlist)[nhost++] = strdup(host)) == NULL) { krb5_free_krbhst(context, *hostlist); krb5_krbhst_free(context, handle); return ENOMEM; } } (*hostlist)[nhost] = NULL; krb5_krbhst_free(context, handle); return 0; } /* * return an malloced list of kadmin-hosts for `realm' in `hostlist' */ KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL krb5_get_krb_admin_hst (krb5_context context, const krb5_realm *realm, char ***hostlist) { return gethostlist(context, *realm, KRB5_KRBHST_ADMIN, hostlist); } /* * return an malloced list of changepw-hosts for `realm' in `hostlist' */ KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL krb5_get_krb_changepw_hst (krb5_context context, const krb5_realm *realm, char ***hostlist) { return gethostlist(context, *realm, KRB5_KRBHST_CHANGEPW, hostlist); } /* * return an malloced list of 524-hosts for `realm' in `hostlist' */ KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL krb5_get_krb524hst (krb5_context context, const krb5_realm *realm, char ***hostlist) { return gethostlist(context, *realm, KRB5_KRBHST_KRB524, hostlist); } /* * return an malloced list of KDC's for `realm' in `hostlist' */ KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL krb5_get_krbhst (krb5_context context, const krb5_realm *realm, char ***hostlist) { return gethostlist(context, *realm, KRB5_KRBHST_KDC, hostlist); } /* * free all the memory allocated in `hostlist' */ KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL krb5_free_krbhst (krb5_context context, char **hostlist) { char **p; for (p = hostlist; *p; ++p) free (*p); free (hostlist); return 0; } diff --git a/crypto/heimdal/lib/krb5/pac.c b/crypto/heimdal/lib/krb5/pac.c index 91f68d5e00e7..1fe86e536beb 100644 --- a/crypto/heimdal/lib/krb5/pac.c +++ b/crypto/heimdal/lib/krb5/pac.c @@ -1,1145 +1,1267 @@ /* * 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 "krb5_locl.h" #include struct PAC_INFO_BUFFER { uint32_t type; uint32_t buffersize; uint32_t offset_hi; uint32_t offset_lo; }; struct PACTYPE { uint32_t numbuffers; uint32_t version; struct PAC_INFO_BUFFER buffers[1]; }; struct krb5_pac_data { struct PACTYPE *pac; krb5_data data; struct PAC_INFO_BUFFER *server_checksum; struct PAC_INFO_BUFFER *privsvr_checksum; struct PAC_INFO_BUFFER *logon_name; }; #define PAC_ALIGNMENT 8 #define PACTYPE_SIZE 8 #define PAC_INFO_BUFFER_SIZE 16 #define PAC_SERVER_CHECKSUM 6 #define PAC_PRIVSVR_CHECKSUM 7 #define PAC_LOGON_NAME 10 #define PAC_CONSTRAINED_DELEGATION 11 #define CHECK(r,f,l) \ do { \ if (((r) = f ) != 0) { \ krb5_clear_error_message(context); \ goto l; \ } \ } while(0) static const char zeros[PAC_ALIGNMENT] = { 0 }; /* * HMAC-MD5 checksum over any key (needed for the PAC routines) */ static krb5_error_code HMAC_MD5_any_checksum(krb5_context context, const krb5_keyblock *key, const void *data, size_t len, unsigned usage, Checksum *result) { struct _krb5_key_data local_key; krb5_error_code ret; memset(&local_key, 0, sizeof(local_key)); ret = krb5_copy_keyblock(context, key, &local_key.key); if (ret) return ret; ret = krb5_data_alloc (&result->checksum, 16); if (ret) { krb5_free_keyblock(context, local_key.key); return ret; } result->cksumtype = CKSUMTYPE_HMAC_MD5; ret = _krb5_HMAC_MD5_checksum(context, &local_key, data, len, usage, result); if (ret) krb5_data_free(&result->checksum); krb5_free_keyblock(context, local_key.key); return ret; } +static krb5_error_code pac_header_size(krb5_context context, + uint32_t num_buffers, + uint32_t *result) +{ + krb5_error_code ret; + uint32_t header_size; + + /* Guard against integer overflow on 32-bit systems. */ + if (num_buffers > 1000) { + ret = EINVAL; + krb5_set_error_message(context, ret, "PAC has too many buffers"); + return ret; + } + header_size = PAC_INFO_BUFFER_SIZE * num_buffers; + + /* Guard against integer overflow on 32-bit systems. */ + if (header_size > UINT32_MAX - PACTYPE_SIZE) { + ret = EINVAL; + krb5_set_error_message(context, ret, "PAC has too many buffers"); + return ret; + } + header_size += PACTYPE_SIZE; + + *result = header_size; + + return 0; +} + +static krb5_error_code pac_aligned_size(krb5_context context, + uint32_t size, + uint32_t *aligned_size) +{ + krb5_error_code ret; + + /* Guard against integer overflow on 32-bit systems. */ + if (size > UINT32_MAX - (PAC_ALIGNMENT - 1)) { + ret = EINVAL; + krb5_set_error_message(context, ret, "integer overrun"); + return ret; + } + size += PAC_ALIGNMENT - 1; + + /* align to PAC_ALIGNMENT */ + size = (size / PAC_ALIGNMENT) * PAC_ALIGNMENT; + + *aligned_size = size; + + return 0; +} + /* * */ KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL krb5_pac_parse(krb5_context context, const void *ptr, size_t len, krb5_pac *pac) { krb5_error_code ret; krb5_pac p; krb5_storage *sp = NULL; uint32_t i, tmp, tmp2, header_end; p = calloc(1, sizeof(*p)); if (p == NULL) { ret = krb5_enomem(context); goto out; } sp = krb5_storage_from_readonly_mem(ptr, len); if (sp == NULL) { ret = krb5_enomem(context); goto out; } krb5_storage_set_flags(sp, KRB5_STORAGE_BYTEORDER_LE); CHECK(ret, krb5_ret_uint32(sp, &tmp), out); CHECK(ret, krb5_ret_uint32(sp, &tmp2), out); if (tmp < 1) { ret = EINVAL; /* Too few buffers */ krb5_set_error_message(context, ret, N_("PAC have too few buffer", "")); goto out; } if (tmp2 != 0) { ret = EINVAL; /* Wrong version */ krb5_set_error_message(context, ret, N_("PAC have wrong version %d", ""), (int)tmp2); goto out; } - p->pac = calloc(1, - sizeof(*p->pac) + (sizeof(p->pac->buffers[0]) * (tmp - 1))); + ret = pac_header_size(context, tmp, &header_end); + if (ret) { + return ret; + } + + p->pac = calloc(1, header_end); if (p->pac == NULL) { ret = krb5_enomem(context); goto out; } p->pac->numbuffers = tmp; p->pac->version = tmp2; - header_end = PACTYPE_SIZE + (PAC_INFO_BUFFER_SIZE * p->pac->numbuffers); if (header_end > len) { ret = EINVAL; goto out; } for (i = 0; i < p->pac->numbuffers; i++) { CHECK(ret, krb5_ret_uint32(sp, &p->pac->buffers[i].type), out); CHECK(ret, krb5_ret_uint32(sp, &p->pac->buffers[i].buffersize), out); CHECK(ret, krb5_ret_uint32(sp, &p->pac->buffers[i].offset_lo), out); CHECK(ret, krb5_ret_uint32(sp, &p->pac->buffers[i].offset_hi), out); /* consistency checks */ if (p->pac->buffers[i].offset_lo & (PAC_ALIGNMENT - 1)) { ret = EINVAL; krb5_set_error_message(context, ret, N_("PAC out of allignment", "")); goto out; } if (p->pac->buffers[i].offset_hi) { ret = EINVAL; krb5_set_error_message(context, ret, N_("PAC high offset set", "")); goto out; } if (p->pac->buffers[i].offset_lo > len) { ret = EINVAL; krb5_set_error_message(context, ret, N_("PAC offset off end", "")); goto out; } if (p->pac->buffers[i].offset_lo < header_end) { ret = EINVAL; krb5_set_error_message(context, ret, N_("PAC offset inside header: %lu %lu", ""), (unsigned long)p->pac->buffers[i].offset_lo, (unsigned long)header_end); goto out; } if (p->pac->buffers[i].buffersize > len - p->pac->buffers[i].offset_lo){ ret = EINVAL; krb5_set_error_message(context, ret, N_("PAC length off end", "")); goto out; } /* let save pointer to data we need later */ if (p->pac->buffers[i].type == PAC_SERVER_CHECKSUM) { if (p->server_checksum) { ret = EINVAL; krb5_set_error_message(context, ret, N_("PAC have two server checksums", "")); goto out; } p->server_checksum = &p->pac->buffers[i]; } else if (p->pac->buffers[i].type == PAC_PRIVSVR_CHECKSUM) { if (p->privsvr_checksum) { ret = EINVAL; krb5_set_error_message(context, ret, N_("PAC have two KDC checksums", "")); goto out; } p->privsvr_checksum = &p->pac->buffers[i]; } else if (p->pac->buffers[i].type == PAC_LOGON_NAME) { if (p->logon_name) { ret = EINVAL; krb5_set_error_message(context, ret, N_("PAC have two logon names", "")); goto out; } p->logon_name = &p->pac->buffers[i]; } } ret = krb5_data_copy(&p->data, ptr, len); if (ret) goto out; krb5_storage_free(sp); *pac = p; return 0; out: if (sp) krb5_storage_free(sp); if (p) { if (p->pac) free(p->pac); free(p); } *pac = NULL; return ret; } KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL krb5_pac_init(krb5_context context, krb5_pac *pac) { krb5_error_code ret; krb5_pac p; p = calloc(1, sizeof(*p)); if (p == NULL) { return krb5_enomem(context); } p->pac = calloc(1, sizeof(*p->pac)); if (p->pac == NULL) { free(p); return krb5_enomem(context); } ret = krb5_data_alloc(&p->data, PACTYPE_SIZE); if (ret) { free (p->pac); free(p); return krb5_enomem(context); } *pac = p; return 0; } KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL krb5_pac_add_buffer(krb5_context context, krb5_pac p, uint32_t type, const krb5_data *data) { krb5_error_code ret; void *ptr; - size_t len, offset, header_end, old_end; + uint32_t unaligned_len, num_buffers, len, offset, header_end, old_end; uint32_t i; - len = p->pac->numbuffers; + if (data->length > UINT32_MAX) { + ret = EINVAL; + krb5_set_error_message(context, ret, "integer overrun"); + return ret; + } + + num_buffers = p->pac->numbuffers; + + if (num_buffers >= UINT32_MAX) { + ret = EINVAL; + krb5_set_error_message(context, ret, "integer overrun"); + return ret; + } + ret = pac_header_size(context, num_buffers + 1, &header_end); + if (ret) { + return ret; + } - ptr = realloc(p->pac, - sizeof(*p->pac) + (sizeof(p->pac->buffers[0]) * len)); + ptr = realloc(p->pac, header_end); if (ptr == NULL) return krb5_enomem(context); p->pac = ptr; - for (i = 0; i < len; i++) + for (i = 0; i < num_buffers; i++) { + if (p->pac->buffers[i].offset_lo > UINT32_MAX - PAC_INFO_BUFFER_SIZE) { + ret = EINVAL; + krb5_set_error_message(context, ret, "integer overrun"); + return ret; + } + p->pac->buffers[i].offset_lo += PAC_INFO_BUFFER_SIZE; + } + if (p->data.length > UINT32_MAX - PAC_INFO_BUFFER_SIZE) { + ret = EINVAL; + krb5_set_error_message(context, ret, "integer overrun"); + return ret; + } offset = p->data.length + PAC_INFO_BUFFER_SIZE; - p->pac->buffers[len].type = type; - p->pac->buffers[len].buffersize = data->length; - p->pac->buffers[len].offset_lo = offset; - p->pac->buffers[len].offset_hi = 0; + p->pac->buffers[num_buffers].type = type; + p->pac->buffers[num_buffers].buffersize = data->length; + p->pac->buffers[num_buffers].offset_lo = offset; + p->pac->buffers[num_buffers].offset_hi = 0; old_end = p->data.length; - len = p->data.length + data->length + PAC_INFO_BUFFER_SIZE; - if (len < p->data.length) { + if (offset > UINT32_MAX - data->length) { krb5_set_error_message(context, EINVAL, "integer overrun"); return EINVAL; } + unaligned_len = offset + data->length; - /* align to PAC_ALIGNMENT */ - len = ((len + PAC_ALIGNMENT - 1) / PAC_ALIGNMENT) * PAC_ALIGNMENT; + ret = pac_aligned_size(context, unaligned_len, &len); + if (ret) + return ret; ret = krb5_data_realloc(&p->data, len); if (ret) { krb5_set_error_message(context, ret, N_("malloc: out of memory", "")); return ret; } /* * make place for new PAC INFO BUFFER header */ - header_end = PACTYPE_SIZE + (PAC_INFO_BUFFER_SIZE * p->pac->numbuffers); + header_end -= PAC_INFO_BUFFER_SIZE; memmove((unsigned char *)p->data.data + header_end + PAC_INFO_BUFFER_SIZE, (unsigned char *)p->data.data + header_end , old_end - header_end); memset((unsigned char *)p->data.data + header_end, 0, PAC_INFO_BUFFER_SIZE); /* * copy in new data part */ memcpy((unsigned char *)p->data.data + offset, data->data, data->length); memset((unsigned char *)p->data.data + offset + data->length, - 0, p->data.length - offset - data->length); + 0, p->data.length - unaligned_len); p->pac->numbuffers += 1; return 0; } /** * Get the PAC buffer of specific type from the pac. * * @param context Kerberos 5 context. * @param p the pac structure returned by krb5_pac_parse(). * @param type type of buffer to get * @param data return data, free with krb5_data_free(). * * @return Returns 0 to indicate success. Otherwise an kerberos et * error code is returned, see krb5_get_error_message(). * * @ingroup krb5_pac */ KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL krb5_pac_get_buffer(krb5_context context, krb5_pac p, uint32_t type, krb5_data *data) { krb5_error_code ret; uint32_t i; for (i = 0; i < p->pac->numbuffers; i++) { - const size_t len = p->pac->buffers[i].buffersize; - const size_t offset = p->pac->buffers[i].offset_lo; + const uint32_t len = p->pac->buffers[i].buffersize; + const uint32_t offset = p->pac->buffers[i].offset_lo; if (p->pac->buffers[i].type != type) continue; ret = krb5_data_copy(data, (unsigned char *)p->data.data + offset, len); if (ret) { krb5_set_error_message(context, ret, N_("malloc: out of memory", "")); return ret; } return 0; } krb5_set_error_message(context, ENOENT, "No PAC buffer of type %lu was found", (unsigned long)type); return ENOENT; } /* * */ KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL krb5_pac_get_types(krb5_context context, krb5_pac p, size_t *len, uint32_t **types) { size_t i; *types = calloc(p->pac->numbuffers, sizeof(*types)); if (*types == NULL) { *len = 0; return krb5_enomem(context); } for (i = 0; i < p->pac->numbuffers; i++) (*types)[i] = p->pac->buffers[i].type; *len = p->pac->numbuffers; return 0; } /* * */ KRB5_LIB_FUNCTION void KRB5_LIB_CALL krb5_pac_free(krb5_context context, krb5_pac pac) { krb5_data_free(&pac->data); free(pac->pac); free(pac); } /* * */ static krb5_error_code verify_checksum(krb5_context context, const struct PAC_INFO_BUFFER *sig, const krb5_data *data, void *ptr, size_t len, const krb5_keyblock *key) { krb5_storage *sp = NULL; uint32_t type; krb5_error_code ret; Checksum cksum; memset(&cksum, 0, sizeof(cksum)); sp = krb5_storage_from_mem((char *)data->data + sig->offset_lo, sig->buffersize); if (sp == NULL) return krb5_enomem(context); krb5_storage_set_flags(sp, KRB5_STORAGE_BYTEORDER_LE); CHECK(ret, krb5_ret_uint32(sp, &type), out); cksum.cksumtype = type; cksum.checksum.length = sig->buffersize - krb5_storage_seek(sp, 0, SEEK_CUR); cksum.checksum.data = malloc(cksum.checksum.length); if (cksum.checksum.data == NULL) { ret = krb5_enomem(context); goto out; } ret = krb5_storage_read(sp, cksum.checksum.data, cksum.checksum.length); if (ret != (int)cksum.checksum.length) { ret = EINVAL; krb5_set_error_message(context, ret, "PAC checksum missing checksum"); goto out; } if (!krb5_checksum_is_keyed(context, cksum.cksumtype)) { ret = EINVAL; krb5_set_error_message(context, ret, "Checksum type %d not keyed", cksum.cksumtype); goto out; } /* If the checksum is HMAC-MD5, the checksum type is not tied to * the key type, instead the HMAC-MD5 checksum is applied blindly * on whatever key is used for this connection, avoiding issues * with unkeyed checksums on des-cbc-md5 and des-cbc-crc. See * http://comments.gmane.org/gmane.comp.encryption.kerberos.devel/8743 * for the same issue in MIT, and * http://blogs.msdn.com/b/openspecification/archive/2010/01/01/verifying-the-server-signature-in-kerberos-privilege-account-certificate.aspx * for Microsoft's explaination */ if (cksum.cksumtype == CKSUMTYPE_HMAC_MD5) { Checksum local_checksum; memset(&local_checksum, 0, sizeof(local_checksum)); ret = HMAC_MD5_any_checksum(context, key, ptr, len, KRB5_KU_OTHER_CKSUM, &local_checksum); if (ret != 0 || krb5_data_ct_cmp(&local_checksum.checksum, &cksum.checksum) != 0) { ret = KRB5KRB_AP_ERR_BAD_INTEGRITY; krb5_set_error_message(context, ret, N_("PAC integrity check failed for " "hmac-md5 checksum", "")); } krb5_data_free(&local_checksum.checksum); } else { krb5_crypto crypto = NULL; ret = krb5_crypto_init(context, key, 0, &crypto); if (ret) goto out; ret = krb5_verify_checksum(context, crypto, KRB5_KU_OTHER_CKSUM, ptr, len, &cksum); krb5_crypto_destroy(context, crypto); } free(cksum.checksum.data); krb5_storage_free(sp); return ret; out: if (cksum.checksum.data) free(cksum.checksum.data); if (sp) krb5_storage_free(sp); return ret; } static krb5_error_code create_checksum(krb5_context context, const krb5_keyblock *key, uint32_t cksumtype, void *data, size_t datalen, void *sig, size_t siglen) { krb5_crypto crypto = NULL; krb5_error_code ret; Checksum cksum; /* If the checksum is HMAC-MD5, the checksum type is not tied to * the key type, instead the HMAC-MD5 checksum is applied blindly * on whatever key is used for this connection, avoiding issues * with unkeyed checksums on des-cbc-md5 and des-cbc-crc. See * http://comments.gmane.org/gmane.comp.encryption.kerberos.devel/8743 * for the same issue in MIT, and * http://blogs.msdn.com/b/openspecification/archive/2010/01/01/verifying-the-server-signature-in-kerberos-privilege-account-certificate.aspx * for Microsoft's explaination */ if (cksumtype == (uint32_t)CKSUMTYPE_HMAC_MD5) { ret = HMAC_MD5_any_checksum(context, key, data, datalen, KRB5_KU_OTHER_CKSUM, &cksum); } else { ret = krb5_crypto_init(context, key, 0, &crypto); if (ret) return ret; ret = krb5_create_checksum(context, crypto, KRB5_KU_OTHER_CKSUM, 0, data, datalen, &cksum); krb5_crypto_destroy(context, crypto); if (ret) return ret; } if (cksum.checksum.length != siglen) { krb5_set_error_message(context, EINVAL, "pac checksum wrong length"); free_Checksum(&cksum); return EINVAL; } memcpy(sig, cksum.checksum.data, siglen); free_Checksum(&cksum); return 0; } /* * */ #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 krb5_error_code verify_logonname(krb5_context context, const struct PAC_INFO_BUFFER *logon_name, const krb5_data *data, time_t authtime, krb5_const_principal principal) { krb5_error_code ret; krb5_principal p2; uint32_t time1, time2; krb5_storage *sp; uint16_t len; char *s; sp = krb5_storage_from_readonly_mem((const char *)data->data + logon_name->offset_lo, logon_name->buffersize); if (sp == NULL) return krb5_enomem(context); krb5_storage_set_flags(sp, KRB5_STORAGE_BYTEORDER_LE); CHECK(ret, krb5_ret_uint32(sp, &time1), out); CHECK(ret, krb5_ret_uint32(sp, &time2), out); { uint64_t t1, t2; t1 = unix2nttime(authtime); t2 = ((uint64_t)time2 << 32) | time1; if (t1 != t2) { krb5_storage_free(sp); krb5_set_error_message(context, EINVAL, "PAC timestamp mismatch"); return EINVAL; } } CHECK(ret, krb5_ret_uint16(sp, &len), out); if (len == 0) { krb5_storage_free(sp); krb5_set_error_message(context, EINVAL, "PAC logon name length missing"); return EINVAL; } s = malloc(len); if (s == NULL) { krb5_storage_free(sp); return krb5_enomem(context); } ret = krb5_storage_read(sp, s, len); if (ret != len) { krb5_storage_free(sp); krb5_set_error_message(context, EINVAL, "Failed to read PAC logon name"); return EINVAL; } krb5_storage_free(sp); { size_t ucs2len = len / 2; uint16_t *ucs2; size_t u8len; unsigned int flags = WIND_RW_LE; ucs2 = malloc(sizeof(ucs2[0]) * ucs2len); if (ucs2 == NULL) return krb5_enomem(context); ret = wind_ucs2read(s, len, &flags, ucs2, &ucs2len); free(s); if (ret) { free(ucs2); krb5_set_error_message(context, ret, "Failed to convert string to UCS-2"); return ret; } ret = wind_ucs2utf8_length(ucs2, ucs2len, &u8len); if (ret) { free(ucs2); krb5_set_error_message(context, ret, "Failed to count length of UCS-2 string"); return ret; } u8len += 1; /* Add space for NUL */ s = malloc(u8len); if (s == NULL) { free(ucs2); return krb5_enomem(context); } ret = wind_ucs2utf8(ucs2, ucs2len, s, &u8len); free(ucs2); if (ret) { free(s); krb5_set_error_message(context, ret, "Failed to convert to UTF-8"); return ret; } } ret = krb5_parse_name_flags(context, s, KRB5_PRINCIPAL_PARSE_NO_REALM, &p2); free(s); if (ret) return ret; if (krb5_principal_compare_any_realm(context, principal, p2) != TRUE) { ret = EINVAL; krb5_set_error_message(context, ret, "PAC logon name mismatch"); } krb5_free_principal(context, p2); return ret; out: return ret; } /* * */ static krb5_error_code build_logon_name(krb5_context context, time_t authtime, krb5_const_principal principal, krb5_data *logon) { krb5_error_code ret; krb5_storage *sp; uint64_t t; char *s, *s2; size_t s2_len; t = unix2nttime(authtime); krb5_data_zero(logon); sp = krb5_storage_emem(); if (sp == NULL) return krb5_enomem(context); krb5_storage_set_flags(sp, KRB5_STORAGE_BYTEORDER_LE); CHECK(ret, krb5_store_uint32(sp, t & 0xffffffff), out); CHECK(ret, krb5_store_uint32(sp, t >> 32), out); ret = krb5_unparse_name_flags(context, principal, KRB5_PRINCIPAL_UNPARSE_NO_REALM, &s); if (ret) goto out; { size_t ucs2_len; uint16_t *ucs2; unsigned int flags; ret = wind_utf8ucs2_length(s, &ucs2_len); if (ret) { free(s); krb5_set_error_message(context, ret, "Failed to count length of UTF-8 string"); return ret; } ucs2 = malloc(sizeof(ucs2[0]) * ucs2_len); if (ucs2 == NULL) { free(s); return krb5_enomem(context); } ret = wind_utf8ucs2(s, ucs2, &ucs2_len); free(s); if (ret) { free(ucs2); krb5_set_error_message(context, ret, "Failed to convert string to UCS-2"); return ret; } s2_len = (ucs2_len + 1) * 2; s2 = malloc(s2_len); if (ucs2 == NULL) { free(ucs2); return krb5_enomem(context); } flags = WIND_RW_LE; ret = wind_ucs2write(ucs2, ucs2_len, &flags, s2, &s2_len); free(ucs2); if (ret) { free(s2); krb5_set_error_message(context, ret, "Failed to write to UCS-2 buffer"); return ret; } /* * we do not want zero termination */ s2_len = ucs2_len * 2; } CHECK(ret, krb5_store_uint16(sp, s2_len), out); ret = krb5_storage_write(sp, s2, s2_len); free(s2); if (ret != (int)s2_len) { ret = krb5_enomem(context); goto out; } ret = krb5_storage_to_data(sp, logon); if (ret) goto out; krb5_storage_free(sp); return 0; out: krb5_storage_free(sp); return ret; } /** * Verify the PAC. * * @param context Kerberos 5 context. * @param pac the pac structure returned by krb5_pac_parse(). * @param authtime The time of the ticket the PAC belongs to. * @param principal the principal to verify. * @param server The service key, most always be given. * @param privsvr The KDC key, may be given. * @return Returns 0 to indicate success. Otherwise an kerberos et * error code is returned, see krb5_get_error_message(). * * @ingroup krb5_pac */ KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL krb5_pac_verify(krb5_context context, const krb5_pac pac, time_t authtime, krb5_const_principal principal, const krb5_keyblock *server, const krb5_keyblock *privsvr) { krb5_error_code ret; if (pac->server_checksum == NULL) { krb5_set_error_message(context, EINVAL, "PAC missing server checksum"); return EINVAL; } if (pac->privsvr_checksum == NULL) { krb5_set_error_message(context, EINVAL, "PAC missing kdc checksum"); return EINVAL; } if (pac->logon_name == NULL) { krb5_set_error_message(context, EINVAL, "PAC missing logon name"); return EINVAL; } ret = verify_logonname(context, pac->logon_name, &pac->data, authtime, principal); if (ret) return ret; /* * in the service case, clean out data option of the privsvr and * server checksum before checking the checksum. */ { krb5_data *copy; ret = krb5_copy_data(context, &pac->data, ©); if (ret) return ret; if (pac->server_checksum->buffersize < 4) return EINVAL; if (pac->privsvr_checksum->buffersize < 4) return EINVAL; memset((char *)copy->data + pac->server_checksum->offset_lo + 4, 0, pac->server_checksum->buffersize - 4); memset((char *)copy->data + pac->privsvr_checksum->offset_lo + 4, 0, pac->privsvr_checksum->buffersize - 4); ret = verify_checksum(context, pac->server_checksum, &pac->data, copy->data, copy->length, server); krb5_free_data(context, copy); if (ret) return ret; } if (privsvr) { /* The priv checksum covers the server checksum */ ret = verify_checksum(context, pac->privsvr_checksum, &pac->data, (char *)pac->data.data + pac->server_checksum->offset_lo + 4, pac->server_checksum->buffersize - 4, privsvr); if (ret) return ret; } return 0; } /* * */ static krb5_error_code fill_zeros(krb5_context context, krb5_storage *sp, size_t len) { ssize_t sret; size_t l; while (len) { l = len; if (l > sizeof(zeros)) l = sizeof(zeros); sret = krb5_storage_write(sp, zeros, l); if (sret <= 0) return krb5_enomem(context); len -= sret; } return 0; } static krb5_error_code pac_checksum(krb5_context context, const krb5_keyblock *key, uint32_t *cksumtype, size_t *cksumsize) { krb5_cksumtype cktype; krb5_error_code ret; krb5_crypto crypto = NULL; ret = krb5_crypto_init(context, key, 0, &crypto); if (ret) return ret; ret = krb5_crypto_get_checksum_type(context, crypto, &cktype); krb5_crypto_destroy(context, crypto); if (ret) return ret; if (krb5_checksum_is_keyed(context, cktype) == FALSE) { *cksumtype = CKSUMTYPE_HMAC_MD5; *cksumsize = 16; } ret = krb5_checksumsize(context, cktype, cksumsize); if (ret) return ret; *cksumtype = (uint32_t)cktype; return 0; } krb5_error_code _krb5_pac_sign(krb5_context context, krb5_pac p, time_t authtime, krb5_principal principal, const krb5_keyblock *server_key, const krb5_keyblock *priv_key, krb5_data *data) { krb5_error_code ret; krb5_storage *sp = NULL, *spdata = NULL; uint32_t end; size_t server_size, priv_size; uint32_t server_offset = 0, priv_offset = 0; uint32_t server_cksumtype = 0, priv_cksumtype = 0; - int num = 0; - size_t i; + uint32_t num = 0; + uint32_t i; krb5_data logon, d; krb5_data_zero(&logon); if (p->logon_name == NULL) num++; if (p->server_checksum == NULL) num++; if (p->privsvr_checksum == NULL) num++; if (num) { void *ptr; - - ptr = realloc(p->pac, sizeof(*p->pac) + (sizeof(p->pac->buffers[0]) * (p->pac->numbuffers + num - 1))); + uint32_t len; + + if (p->pac->numbuffers > UINT32_MAX - num) { + ret = EINVAL; + krb5_set_error_message(context, ret, "integer overrun"); + goto out; + } + ret = pac_header_size(context, p->pac->numbuffers + num, &len); + if (ret) + goto out; + + ptr = realloc(p->pac, len); if (ptr == NULL) return krb5_enomem(context); p->pac = ptr; if (p->logon_name == NULL) { p->logon_name = &p->pac->buffers[p->pac->numbuffers++]; memset(p->logon_name, 0, sizeof(*p->logon_name)); p->logon_name->type = PAC_LOGON_NAME; } if (p->server_checksum == NULL) { p->server_checksum = &p->pac->buffers[p->pac->numbuffers++]; memset(p->server_checksum, 0, sizeof(*p->server_checksum)); p->server_checksum->type = PAC_SERVER_CHECKSUM; } if (p->privsvr_checksum == NULL) { p->privsvr_checksum = &p->pac->buffers[p->pac->numbuffers++]; memset(p->privsvr_checksum, 0, sizeof(*p->privsvr_checksum)); p->privsvr_checksum->type = PAC_PRIVSVR_CHECKSUM; } } /* Calculate LOGON NAME */ ret = build_logon_name(context, authtime, principal, &logon); if (ret) goto out; /* Set lengths for checksum */ ret = pac_checksum(context, server_key, &server_cksumtype, &server_size); if (ret) goto out; ret = pac_checksum(context, priv_key, &priv_cksumtype, &priv_size); if (ret) goto out; /* Encode PAC */ sp = krb5_storage_emem(); if (sp == NULL) return krb5_enomem(context); krb5_storage_set_flags(sp, KRB5_STORAGE_BYTEORDER_LE); spdata = krb5_storage_emem(); if (spdata == NULL) { krb5_storage_free(sp); return krb5_enomem(context); } krb5_storage_set_flags(spdata, KRB5_STORAGE_BYTEORDER_LE); CHECK(ret, krb5_store_uint32(sp, p->pac->numbuffers), out); CHECK(ret, krb5_store_uint32(sp, p->pac->version), out); - end = PACTYPE_SIZE + (PAC_INFO_BUFFER_SIZE * p->pac->numbuffers); + ret = pac_header_size(context, p->pac->numbuffers, &end); + if (ret) + goto out; for (i = 0; i < p->pac->numbuffers; i++) { uint32_t len; size_t sret; void *ptr = NULL; /* store data */ if (p->pac->buffers[i].type == PAC_SERVER_CHECKSUM) { + if (server_size > UINT32_MAX - 4) { + ret = EINVAL; + krb5_set_error_message(context, ret, "integer overrun"); + goto out; + } + if (end > UINT32_MAX - 4) { + ret = EINVAL; + krb5_set_error_message(context, ret, "integer overrun"); + goto out; + } len = server_size + 4; server_offset = end + 4; CHECK(ret, krb5_store_uint32(spdata, server_cksumtype), out); CHECK(ret, fill_zeros(context, spdata, server_size), out); } else if (p->pac->buffers[i].type == PAC_PRIVSVR_CHECKSUM) { + if (priv_size > UINT32_MAX - 4) { + ret = EINVAL; + krb5_set_error_message(context, ret, "integer overrun"); + goto out; + } + if (end > UINT32_MAX - 4) { + ret = EINVAL; + krb5_set_error_message(context, ret, "integer overrun"); + goto out; + } len = priv_size + 4; priv_offset = end + 4; CHECK(ret, krb5_store_uint32(spdata, priv_cksumtype), out); CHECK(ret, fill_zeros(context, spdata, priv_size), out); } else if (p->pac->buffers[i].type == PAC_LOGON_NAME) { len = krb5_storage_write(spdata, logon.data, logon.length); if (logon.length != len) { ret = EINVAL; goto out; } } else { len = p->pac->buffers[i].buffersize; ptr = (char *)p->data.data + p->pac->buffers[i].offset_lo; sret = krb5_storage_write(spdata, ptr, len); if (sret != len) { ret = krb5_enomem(context); goto out; } /* XXX if not aligned, fill_zeros */ } /* write header */ CHECK(ret, krb5_store_uint32(sp, p->pac->buffers[i].type), out); CHECK(ret, krb5_store_uint32(sp, len), out); CHECK(ret, krb5_store_uint32(sp, end), out); CHECK(ret, krb5_store_uint32(sp, 0), out); /* advance data endpointer and align */ { - int32_t e; + uint32_t e; + if (end > UINT32_MAX - len) { + ret = EINVAL; + krb5_set_error_message(context, ret, "integer overrun"); + goto out; + } end += len; - e = ((end + PAC_ALIGNMENT - 1) / PAC_ALIGNMENT) * PAC_ALIGNMENT; - if ((int32_t)end != e) { + + ret = pac_aligned_size(context, end, &e); + if (ret) + goto out; + + if (end != e) { CHECK(ret, fill_zeros(context, spdata, e - end), out); } end = e; } } /* assert (server_offset != 0 && priv_offset != 0); */ /* export PAC */ ret = krb5_storage_to_data(spdata, &d); if (ret) { krb5_set_error_message(context, ret, N_("malloc: out of memory", "")); goto out; } ret = krb5_storage_write(sp, d.data, d.length); if (ret != (int)d.length) { krb5_data_free(&d); ret = krb5_enomem(context); goto out; } krb5_data_free(&d); ret = krb5_storage_to_data(sp, &d); if (ret) { ret = krb5_enomem(context); goto out; } /* sign */ ret = create_checksum(context, server_key, server_cksumtype, d.data, d.length, (char *)d.data + server_offset, server_size); if (ret) { krb5_data_free(&d); goto out; } ret = create_checksum(context, priv_key, priv_cksumtype, (char *)d.data + server_offset, server_size, (char *)d.data + priv_offset, priv_size); if (ret) { krb5_data_free(&d); goto out; } /* done */ *data = d; krb5_data_free(&logon); krb5_storage_free(sp); krb5_storage_free(spdata); return 0; out: krb5_data_free(&logon); if (sp) krb5_storage_free(sp); if (spdata) krb5_storage_free(spdata); return ret; } diff --git a/crypto/heimdal/lib/krb5/rd_req.c b/crypto/heimdal/lib/krb5/rd_req.c index 21daeb596b55..7130a07efc6f 100644 --- a/crypto/heimdal/lib/krb5/rd_req.c +++ b/crypto/heimdal/lib/krb5/rd_req.c @@ -1,1079 +1,1078 @@ /* * Copyright (c) 1997 - 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 "krb5_locl.h" static krb5_error_code decrypt_tkt_enc_part (krb5_context context, krb5_keyblock *key, EncryptedData *enc_part, EncTicketPart *decr_part) { krb5_error_code ret; krb5_data plain; size_t len; krb5_crypto crypto; ret = krb5_crypto_init(context, key, 0, &crypto); if (ret) return ret; ret = krb5_decrypt_EncryptedData (context, crypto, KRB5_KU_TICKET, enc_part, &plain); krb5_crypto_destroy(context, crypto); if (ret) return ret; ret = decode_EncTicketPart(plain.data, plain.length, decr_part, &len); if (ret) krb5_set_error_message(context, ret, N_("Failed to decode encrypted " "ticket part", "")); krb5_data_free (&plain); return ret; } static krb5_error_code decrypt_authenticator (krb5_context context, EncryptionKey *key, EncryptedData *enc_part, Authenticator *authenticator, krb5_key_usage usage) { krb5_error_code ret; krb5_data plain; size_t len; krb5_crypto crypto; ret = krb5_crypto_init(context, key, 0, &crypto); if (ret) return ret; ret = krb5_decrypt_EncryptedData (context, crypto, usage /* KRB5_KU_AP_REQ_AUTH */, enc_part, &plain); /* for backwards compatibility, also try the old usage */ if (ret && usage == KRB5_KU_TGS_REQ_AUTH) ret = krb5_decrypt_EncryptedData (context, crypto, KRB5_KU_AP_REQ_AUTH, enc_part, &plain); krb5_crypto_destroy(context, crypto); if (ret) return ret; ret = decode_Authenticator(plain.data, plain.length, authenticator, &len); krb5_data_free (&plain); return ret; } KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL krb5_decode_ap_req(krb5_context context, const krb5_data *inbuf, krb5_ap_req *ap_req) { krb5_error_code ret; size_t len; ret = decode_AP_REQ(inbuf->data, inbuf->length, ap_req, &len); if (ret) return ret; if (ap_req->pvno != 5){ free_AP_REQ(ap_req); krb5_clear_error_message (context); return KRB5KRB_AP_ERR_BADVERSION; } if (ap_req->msg_type != krb_ap_req){ free_AP_REQ(ap_req); krb5_clear_error_message (context); return KRB5KRB_AP_ERR_MSG_TYPE; } if (ap_req->ticket.tkt_vno != 5){ free_AP_REQ(ap_req); krb5_clear_error_message (context); return KRB5KRB_AP_ERR_BADVERSION; } return 0; } static krb5_error_code check_transited(krb5_context context, Ticket *ticket, EncTicketPart *enc) { char **realms; unsigned int num_realms, n; krb5_error_code ret; /* * Windows 2000 and 2003 uses this inside their TGT so it's normaly * not seen by others, however, samba4 joined with a Windows AD as * a Domain Controller gets exposed to this. */ if(enc->transited.tr_type == 0 && enc->transited.contents.length == 0) return 0; if(enc->transited.tr_type != DOMAIN_X500_COMPRESS) return KRB5KDC_ERR_TRTYPE_NOSUPP; if(enc->transited.contents.length == 0) return 0; ret = krb5_domain_x500_decode(context, enc->transited.contents, &realms, &num_realms, enc->crealm, ticket->realm); if(ret) return ret; ret = krb5_check_transited(context, enc->crealm, ticket->realm, realms, num_realms, NULL); for (n = 0; n < num_realms; n++) free(realms[n]); free(realms); return ret; } static krb5_error_code find_etypelist(krb5_context context, krb5_auth_context auth_context, EtypeList *etypes) { krb5_error_code ret; krb5_authdata *ad; krb5_authdata adIfRelevant; unsigned i; memset(&adIfRelevant, 0, sizeof(adIfRelevant)); etypes->len = 0; etypes->val = NULL; ad = auth_context->authenticator->authorization_data; if (ad == NULL) return 0; for (i = 0; i < ad->len; i++) { if (ad->val[i].ad_type == KRB5_AUTHDATA_IF_RELEVANT) { ret = decode_AD_IF_RELEVANT(ad->val[i].ad_data.data, ad->val[i].ad_data.length, &adIfRelevant, NULL); if (ret) return ret; if (adIfRelevant.len == 1 && adIfRelevant.val[0].ad_type == KRB5_AUTHDATA_GSS_API_ETYPE_NEGOTIATION) { break; } free_AD_IF_RELEVANT(&adIfRelevant); adIfRelevant.len = 0; } } if (adIfRelevant.len == 0) return 0; ret = decode_EtypeList(adIfRelevant.val[0].ad_data.data, adIfRelevant.val[0].ad_data.length, etypes, NULL); if (ret) krb5_clear_error_message(context); free_AD_IF_RELEVANT(&adIfRelevant); return ret; } KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL krb5_decrypt_ticket(krb5_context context, Ticket *ticket, krb5_keyblock *key, EncTicketPart *out, krb5_flags flags) { EncTicketPart t; krb5_error_code ret; ret = decrypt_tkt_enc_part (context, key, &ticket->enc_part, &t); if (ret) return ret; { krb5_timestamp now; time_t start = t.authtime; krb5_timeofday (context, &now); if(t.starttime) start = *t.starttime; if(start - now > context->max_skew || (t.flags.invalid && !(flags & KRB5_VERIFY_AP_REQ_IGNORE_INVALID))) { free_EncTicketPart(&t); krb5_clear_error_message (context); return KRB5KRB_AP_ERR_TKT_NYV; } if(now - t.endtime > context->max_skew) { free_EncTicketPart(&t); krb5_clear_error_message (context); return KRB5KRB_AP_ERR_TKT_EXPIRED; } if(!t.flags.transited_policy_checked) { ret = check_transited(context, ticket, &t); if(ret) { free_EncTicketPart(&t); return ret; } } } if(out) *out = t; else free_EncTicketPart(&t); return 0; } KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL krb5_verify_authenticator_checksum(krb5_context context, krb5_auth_context ac, void *data, size_t len) { krb5_error_code ret; krb5_keyblock *key; krb5_authenticator authenticator; krb5_crypto crypto; ret = krb5_auth_con_getauthenticator (context, ac, &authenticator); if(ret) return ret; if(authenticator->cksum == NULL) { krb5_free_authenticator(context, &authenticator); return -17; } ret = krb5_auth_con_getkey(context, ac, &key); if(ret) { krb5_free_authenticator(context, &authenticator); return ret; } ret = krb5_crypto_init(context, key, 0, &crypto); if(ret) goto out; ret = krb5_verify_checksum (context, crypto, KRB5_KU_AP_REQ_AUTH_CKSUM, data, len, authenticator->cksum); krb5_crypto_destroy(context, crypto); out: krb5_free_authenticator(context, &authenticator); krb5_free_keyblock(context, key); return ret; } KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL krb5_verify_ap_req(krb5_context context, krb5_auth_context *auth_context, krb5_ap_req *ap_req, krb5_const_principal server, krb5_keyblock *keyblock, krb5_flags flags, krb5_flags *ap_req_options, krb5_ticket **ticket) { return krb5_verify_ap_req2 (context, auth_context, ap_req, server, keyblock, flags, ap_req_options, ticket, KRB5_KU_AP_REQ_AUTH); } KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL krb5_verify_ap_req2(krb5_context context, krb5_auth_context *auth_context, krb5_ap_req *ap_req, krb5_const_principal server, krb5_keyblock *keyblock, krb5_flags flags, krb5_flags *ap_req_options, krb5_ticket **ticket, krb5_key_usage usage) { krb5_ticket *t; krb5_auth_context ac; krb5_error_code ret; EtypeList etypes; if (ticket) *ticket = NULL; if (auth_context && *auth_context) { ac = *auth_context; } else { ret = krb5_auth_con_init (context, &ac); if (ret) return ret; } t = calloc(1, sizeof(*t)); if (t == NULL) { ret = ENOMEM; krb5_clear_error_message (context); goto out; } if (ap_req->ap_options.use_session_key && ac->keyblock){ ret = krb5_decrypt_ticket(context, &ap_req->ticket, ac->keyblock, &t->ticket, flags); krb5_free_keyblock(context, ac->keyblock); ac->keyblock = NULL; }else ret = krb5_decrypt_ticket(context, &ap_req->ticket, keyblock, &t->ticket, flags); if(ret) goto out; ret = _krb5_principalname2krb5_principal(context, &t->server, ap_req->ticket.sname, ap_req->ticket.realm); if (ret) goto out; ret = _krb5_principalname2krb5_principal(context, &t->client, t->ticket.cname, t->ticket.crealm); if (ret) goto out; ret = decrypt_authenticator (context, &t->ticket.key, &ap_req->authenticator, ac->authenticator, usage); if (ret) goto out; { krb5_principal p1, p2; krb5_boolean res; _krb5_principalname2krb5_principal(context, &p1, ac->authenticator->cname, ac->authenticator->crealm); _krb5_principalname2krb5_principal(context, &p2, t->ticket.cname, t->ticket.crealm); res = krb5_principal_compare (context, p1, p2); krb5_free_principal (context, p1); krb5_free_principal (context, p2); if (!res) { ret = KRB5KRB_AP_ERR_BADMATCH; krb5_clear_error_message (context); goto out; } } /* check addresses */ if (t->ticket.caddr && ac->remote_address && !krb5_address_search (context, ac->remote_address, t->ticket.caddr)) { ret = KRB5KRB_AP_ERR_BADADDR; krb5_clear_error_message (context); goto out; } /* check timestamp in authenticator */ { krb5_timestamp now; krb5_timeofday (context, &now); if (abs(ac->authenticator->ctime - now) > context->max_skew) { ret = KRB5KRB_AP_ERR_SKEW; krb5_clear_error_message (context); goto out; } } if (ac->authenticator->seq_number) krb5_auth_con_setremoteseqnumber(context, ac, *ac->authenticator->seq_number); /* XXX - Xor sequence numbers */ if (ac->authenticator->subkey) { ret = krb5_auth_con_setremotesubkey(context, ac, ac->authenticator->subkey); if (ret) goto out; } ret = find_etypelist(context, ac, &etypes); if (ret) goto out; ac->keytype = ETYPE_NULL; if (etypes.val) { size_t i; for (i = 0; i < etypes.len; i++) { if (krb5_enctype_valid(context, etypes.val[i]) == 0) { ac->keytype = etypes.val[i]; break; } } } /* save key */ ret = krb5_copy_keyblock(context, &t->ticket.key, &ac->keyblock); if (ret) goto out; if (ap_req_options) { *ap_req_options = 0; if (ac->keytype != ETYPE_NULL) *ap_req_options |= AP_OPTS_USE_SUBKEY; if (ap_req->ap_options.use_session_key) *ap_req_options |= AP_OPTS_USE_SESSION_KEY; if (ap_req->ap_options.mutual_required) *ap_req_options |= AP_OPTS_MUTUAL_REQUIRED; } if(ticket) *ticket = t; else krb5_free_ticket (context, t); if (auth_context) { if (*auth_context == NULL) *auth_context = ac; } else krb5_auth_con_free (context, ac); free_EtypeList(&etypes); return 0; out: if (t) krb5_free_ticket (context, t); if (auth_context == NULL || *auth_context == NULL) krb5_auth_con_free (context, ac); return ret; } /* * */ struct krb5_rd_req_in_ctx_data { krb5_keytab keytab; krb5_keyblock *keyblock; krb5_boolean check_pac; }; struct krb5_rd_req_out_ctx_data { krb5_keyblock *keyblock; krb5_flags ap_req_options; krb5_ticket *ticket; krb5_principal server; }; /** * Allocate a krb5_rd_req_in_ctx as an input parameter to * krb5_rd_req_ctx(). The caller should free the context with * krb5_rd_req_in_ctx_free() when done with the context. * * @param context Keberos 5 context. * @param ctx in ctx to krb5_rd_req_ctx(). * * @return Kerberos 5 error code, see krb5_get_error_message(). * * @ingroup krb5_auth */ KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL krb5_rd_req_in_ctx_alloc(krb5_context context, krb5_rd_req_in_ctx *ctx) { *ctx = calloc(1, sizeof(**ctx)); if (*ctx == NULL) { krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", "")); return ENOMEM; } (*ctx)->check_pac = (context->flags & KRB5_CTX_F_CHECK_PAC) ? 1 : 0; return 0; } /** * Set the keytab that krb5_rd_req_ctx() will use. * * @param context Keberos 5 context. * @param in in ctx to krb5_rd_req_ctx(). * @param keytab keytab that krb5_rd_req_ctx() will use, only copy the * pointer, so the caller must free they keytab after * krb5_rd_req_in_ctx_free() is called. * * @return Kerberos 5 error code, see krb5_get_error_message(). * * @ingroup krb5_auth */ KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL krb5_rd_req_in_set_keytab(krb5_context context, krb5_rd_req_in_ctx in, krb5_keytab keytab) { in->keytab = keytab; return 0; } /** * Set if krb5_rq_red() is going to check the Windows PAC or not * * @param context Keberos 5 context. * @param in krb5_rd_req_in_ctx to check the option on. * @param flag flag to select if to check the pac (TRUE) or not (FALSE). * * @return Kerberos 5 error code, see krb5_get_error_message(). * * @ingroup krb5_auth */ KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL krb5_rd_req_in_set_pac_check(krb5_context context, krb5_rd_req_in_ctx in, krb5_boolean flag) { in->check_pac = flag; return 0; } KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL krb5_rd_req_in_set_keyblock(krb5_context context, krb5_rd_req_in_ctx in, krb5_keyblock *keyblock) { in->keyblock = keyblock; /* XXX should make copy */ return 0; } KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL krb5_rd_req_out_get_ap_req_options(krb5_context context, krb5_rd_req_out_ctx out, krb5_flags *ap_req_options) { *ap_req_options = out->ap_req_options; return 0; } KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL krb5_rd_req_out_get_ticket(krb5_context context, krb5_rd_req_out_ctx out, krb5_ticket **ticket) { return krb5_copy_ticket(context, out->ticket, ticket); } KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL krb5_rd_req_out_get_keyblock(krb5_context context, krb5_rd_req_out_ctx out, krb5_keyblock **keyblock) { return krb5_copy_keyblock(context, out->keyblock, keyblock); } /** * Get the principal that was used in the request from the * client. Might not match whats in the ticket if krb5_rd_req_ctx() * searched in the keytab for a matching key. * * @param context a Kerberos 5 context. * @param out a krb5_rd_req_out_ctx from krb5_rd_req_ctx(). * @param principal return principal, free with krb5_free_principal(). * * @ingroup krb5_auth */ KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL krb5_rd_req_out_get_server(krb5_context context, krb5_rd_req_out_ctx out, krb5_principal *principal) { return krb5_copy_principal(context, out->server, principal); } KRB5_LIB_FUNCTION void KRB5_LIB_CALL krb5_rd_req_in_ctx_free(krb5_context context, krb5_rd_req_in_ctx ctx) { free(ctx); } /** * Free the krb5_rd_req_out_ctx. * * @param context Keberos 5 context. * @param ctx krb5_rd_req_out_ctx context to free. * * @ingroup krb5_auth */ KRB5_LIB_FUNCTION void KRB5_LIB_CALL krb5_rd_req_out_ctx_free(krb5_context context, krb5_rd_req_out_ctx ctx) { if (ctx->ticket) krb5_free_ticket(context, ctx->ticket); if (ctx->keyblock) krb5_free_keyblock(context, ctx->keyblock); if (ctx->server) krb5_free_principal(context, ctx->server); free(ctx); } /* * */ KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL krb5_rd_req(krb5_context context, krb5_auth_context *auth_context, const krb5_data *inbuf, krb5_const_principal server, krb5_keytab keytab, krb5_flags *ap_req_options, krb5_ticket **ticket) { krb5_error_code ret; krb5_rd_req_in_ctx in; krb5_rd_req_out_ctx out; ret = krb5_rd_req_in_ctx_alloc(context, &in); if (ret) return ret; ret = krb5_rd_req_in_set_keytab(context, in, keytab); if (ret) { krb5_rd_req_in_ctx_free(context, in); return ret; } ret = krb5_rd_req_ctx(context, auth_context, inbuf, server, in, &out); krb5_rd_req_in_ctx_free(context, in); if (ret) return ret; if (ap_req_options) *ap_req_options = out->ap_req_options; if (ticket) { ret = krb5_copy_ticket(context, out->ticket, ticket); if (ret) goto out; } out: krb5_rd_req_out_ctx_free(context, out); return ret; } /* * */ KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL krb5_rd_req_with_keyblock(krb5_context context, krb5_auth_context *auth_context, const krb5_data *inbuf, krb5_const_principal server, krb5_keyblock *keyblock, krb5_flags *ap_req_options, krb5_ticket **ticket) { krb5_error_code ret; krb5_rd_req_in_ctx in; krb5_rd_req_out_ctx out; ret = krb5_rd_req_in_ctx_alloc(context, &in); if (ret) return ret; ret = krb5_rd_req_in_set_keyblock(context, in, keyblock); if (ret) { krb5_rd_req_in_ctx_free(context, in); return ret; } ret = krb5_rd_req_ctx(context, auth_context, inbuf, server, in, &out); krb5_rd_req_in_ctx_free(context, in); if (ret) return ret; if (ap_req_options) *ap_req_options = out->ap_req_options; if (ticket) { ret = krb5_copy_ticket(context, out->ticket, ticket); if (ret) goto out; } out: krb5_rd_req_out_ctx_free(context, out); return ret; } /* * */ static krb5_error_code get_key_from_keytab(krb5_context context, krb5_ap_req *ap_req, krb5_const_principal server, krb5_keytab keytab, krb5_keyblock **out_key) { krb5_keytab_entry entry; krb5_error_code ret; int kvno; krb5_keytab real_keytab; if(keytab == NULL) krb5_kt_default(context, &real_keytab); else real_keytab = keytab; if (ap_req->ticket.enc_part.kvno) kvno = *ap_req->ticket.enc_part.kvno; else kvno = 0; ret = krb5_kt_get_entry (context, real_keytab, server, kvno, ap_req->ticket.enc_part.etype, &entry); - if(ret) - goto out; - ret = krb5_copy_keyblock(context, &entry.keyblock, out_key); - krb5_kt_free_entry (context, &entry); -out: + if(ret == 0) { + ret = krb5_copy_keyblock(context, &entry.keyblock, out_key); + krb5_kt_free_entry(context, &entry); + } if(keytab == NULL) krb5_kt_close(context, real_keytab); return ret; } /** * The core server function that verify application authentication * requests from clients. * * @param context Keberos 5 context. * @param auth_context the authentication context, can be NULL, then * default values for the authentication context will used. * @param inbuf the (AP-REQ) authentication buffer * * @param server the server with authenticate as, if NULL the function * will try to find any available credential in the keytab * that will verify the reply. The function will prefer the * server the server client specified in the AP-REQ, but if * there is no mach, it will try all keytab entries for a * match. This have serious performance issues for larger keytabs. * * @param inctx control the behavior of the function, if NULL, the * default behavior is used. * @param outctx the return outctx, free with krb5_rd_req_out_ctx_free(). * @return Kerberos 5 error code, see krb5_get_error_message(). * * @ingroup krb5_auth */ KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL krb5_rd_req_ctx(krb5_context context, krb5_auth_context *auth_context, const krb5_data *inbuf, krb5_const_principal server, krb5_rd_req_in_ctx inctx, krb5_rd_req_out_ctx *outctx) { krb5_error_code ret; krb5_ap_req ap_req; krb5_rd_req_out_ctx o = NULL; krb5_keytab id = NULL, keytab = NULL; krb5_principal service = NULL; *outctx = NULL; o = calloc(1, sizeof(*o)); if (o == NULL) { krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", "")); return ENOMEM; } if (*auth_context == NULL) { ret = krb5_auth_con_init(context, auth_context); if (ret) goto out; } ret = krb5_decode_ap_req(context, inbuf, &ap_req); if(ret) goto out; /* Save that principal that was in the request */ ret = _krb5_principalname2krb5_principal(context, &o->server, ap_req.ticket.sname, ap_req.ticket.realm); if (ret) goto out; if (ap_req.ap_options.use_session_key && (*auth_context)->keyblock == NULL) { ret = KRB5KRB_AP_ERR_NOKEY; krb5_set_error_message(context, ret, N_("krb5_rd_req: user to user auth " "without session key given", "")); goto out; } if (inctx && inctx->keytab) id = inctx->keytab; if((*auth_context)->keyblock){ ret = krb5_copy_keyblock(context, (*auth_context)->keyblock, &o->keyblock); if (ret) goto out; } else if(inctx && inctx->keyblock){ ret = krb5_copy_keyblock(context, inctx->keyblock, &o->keyblock); if (ret) goto out; } else { if(id == NULL) { krb5_kt_default(context, &keytab); id = keytab; } if (id == NULL) goto out; if (server == NULL) { ret = _krb5_principalname2krb5_principal(context, &service, ap_req.ticket.sname, ap_req.ticket.realm); if (ret) goto out; server = service; } ret = get_key_from_keytab(context, &ap_req, server, id, &o->keyblock); if (ret) { /* If caller specified a server, fail. */ if (service == NULL && (context->flags & KRB5_CTX_F_RD_REQ_IGNORE) == 0) goto out; /* Otherwise, fall back to iterating over the keytab. This * have serious performace issues for larger keytab. */ o->keyblock = NULL; } } if (o->keyblock) { /* * We got an exact keymatch, use that. */ ret = krb5_verify_ap_req2(context, auth_context, &ap_req, server, o->keyblock, 0, &o->ap_req_options, &o->ticket, KRB5_KU_AP_REQ_AUTH); if (ret) goto out; } else { /* * Interate over keytab to find a key that can decrypt the request. */ krb5_keytab_entry entry; krb5_kt_cursor cursor; int done = 0, kvno = 0; memset(&cursor, 0, sizeof(cursor)); if (ap_req.ticket.enc_part.kvno) kvno = *ap_req.ticket.enc_part.kvno; ret = krb5_kt_start_seq_get(context, id, &cursor); if (ret) goto out; done = 0; while (!done) { krb5_principal p; ret = krb5_kt_next_entry(context, id, &entry, &cursor); if (ret) { _krb5_kt_principal_not_found(context, ret, id, o->server, ap_req.ticket.enc_part.etype, kvno); goto out; } if (entry.keyblock.keytype != ap_req.ticket.enc_part.etype) { krb5_kt_free_entry (context, &entry); continue; } ret = krb5_verify_ap_req2(context, auth_context, &ap_req, server, &entry.keyblock, 0, &o->ap_req_options, &o->ticket, KRB5_KU_AP_REQ_AUTH); if (ret) { krb5_kt_free_entry (context, &entry); continue; } /* * Found a match, save the keyblock for PAC processing, * and update the service principal in the ticket to match * whatever is in the keytab. */ ret = krb5_copy_keyblock(context, &entry.keyblock, &o->keyblock); if (ret) { krb5_kt_free_entry (context, &entry); goto out; } ret = krb5_copy_principal(context, entry.principal, &p); if (ret) { krb5_kt_free_entry (context, &entry); goto out; } krb5_free_principal(context, o->ticket->server); o->ticket->server = p; krb5_kt_free_entry (context, &entry); done = 1; } krb5_kt_end_seq_get (context, id, &cursor); } /* If there is a PAC, verify its server signature */ if (inctx == NULL || inctx->check_pac) { krb5_pac pac; krb5_data data; ret = krb5_ticket_get_authorization_data_type(context, o->ticket, KRB5_AUTHDATA_WIN2K_PAC, &data); if (ret == 0) { ret = krb5_pac_parse(context, data.data, data.length, &pac); krb5_data_free(&data); if (ret) goto out; ret = krb5_pac_verify(context, pac, o->ticket->ticket.authtime, o->ticket->client, o->keyblock, NULL); krb5_pac_free(context, pac); if (ret) goto out; } else ret = 0; } out: if (ret || outctx == NULL) { krb5_rd_req_out_ctx_free(context, o); } else *outctx = o; free_AP_REQ(&ap_req); if (service) krb5_free_principal(context, service); if (keytab) krb5_kt_close(context, keytab); return ret; } diff --git a/crypto/heimdal/lib/krb5/test_store.c b/crypto/heimdal/lib/krb5/test_store.c index 6b930775c0cc..746e3509f51d 100644 --- a/crypto/heimdal/lib/krb5/test_store.c +++ b/crypto/heimdal/lib/krb5/test_store.c @@ -1,337 +1,337 @@ /* * 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 KTH 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 KTH AND ITS 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 KTH OR ITS 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" #include static void test_int8(krb5_context context, krb5_storage *sp) { krb5_error_code ret; int i; int8_t val[] = { 0, 1, -1, 128, -127 }, v; krb5_storage_truncate(sp, 0); for (i = 0; i < sizeof(val[0])/sizeof(val); i++) { ret = krb5_store_int8(sp, val[i]); if (ret) krb5_err(context, 1, ret, "krb5_store_int8"); krb5_storage_seek(sp, 0, SEEK_SET); ret = krb5_ret_int8(sp, &v); if (ret) krb5_err(context, 1, ret, "krb5_ret_int8"); if (v != val[i]) krb5_errx(context, 1, "store and ret mismatch"); } } static void test_int16(krb5_context context, krb5_storage *sp) { krb5_error_code ret; int i; int16_t val[] = { - 0, 1, -1, 32768, -32767 + 0, 1, -1, 32767, -32768 }, v; krb5_storage_truncate(sp, 0); for (i = 0; i < sizeof(val[0])/sizeof(val); i++) { ret = krb5_store_int16(sp, val[i]); if (ret) krb5_err(context, 1, ret, "krb5_store_int16"); krb5_storage_seek(sp, 0, SEEK_SET); ret = krb5_ret_int16(sp, &v); if (ret) krb5_err(context, 1, ret, "krb5_ret_int16"); if (v != val[i]) krb5_errx(context, 1, "store and ret mismatch"); } } static void test_int32(krb5_context context, krb5_storage *sp) { krb5_error_code ret; int i; int32_t val[] = { 0, 1, -1, 2147483647, -2147483646 }, v; krb5_storage_truncate(sp, 0); for (i = 0; i < sizeof(val[0])/sizeof(val); i++) { ret = krb5_store_int32(sp, val[i]); if (ret) krb5_err(context, 1, ret, "krb5_store_int32"); krb5_storage_seek(sp, 0, SEEK_SET); ret = krb5_ret_int32(sp, &v); if (ret) krb5_err(context, 1, ret, "krb5_ret_int32"); if (v != val[i]) krb5_errx(context, 1, "store and ret mismatch"); } } static void test_uint8(krb5_context context, krb5_storage *sp) { krb5_error_code ret; int i; uint8_t val[] = { 0, 1, 255 }, v; krb5_storage_truncate(sp, 0); for (i = 0; i < sizeof(val[0])/sizeof(val); i++) { ret = krb5_store_uint8(sp, val[i]); if (ret) krb5_err(context, 1, ret, "krb5_store_uint8"); krb5_storage_seek(sp, 0, SEEK_SET); ret = krb5_ret_uint8(sp, &v); if (ret) krb5_err(context, 1, ret, "krb5_ret_uint8"); if (v != val[i]) krb5_errx(context, 1, "store and ret mismatch"); } } static void test_uint16(krb5_context context, krb5_storage *sp) { krb5_error_code ret; int i; uint16_t val[] = { 0, 1, 65535 }, v; krb5_storage_truncate(sp, 0); for (i = 0; i < sizeof(val[0])/sizeof(val); i++) { ret = krb5_store_uint16(sp, val[i]); if (ret) krb5_err(context, 1, ret, "krb5_store_uint16"); krb5_storage_seek(sp, 0, SEEK_SET); ret = krb5_ret_uint16(sp, &v); if (ret) krb5_err(context, 1, ret, "krb5_ret_uint16"); if (v != val[i]) krb5_errx(context, 1, "store and ret mismatch"); } } static void test_uint32(krb5_context context, krb5_storage *sp) { krb5_error_code ret; int i; uint32_t val[] = { 0, 1, 4294967295UL }, v; krb5_storage_truncate(sp, 0); for (i = 0; i < sizeof(val[0])/sizeof(val); i++) { ret = krb5_store_uint32(sp, val[i]); if (ret) krb5_err(context, 1, ret, "krb5_store_uint32"); krb5_storage_seek(sp, 0, SEEK_SET); ret = krb5_ret_uint32(sp, &v); if (ret) krb5_err(context, 1, ret, "krb5_ret_uint32"); if (v != val[i]) krb5_errx(context, 1, "store and ret mismatch"); } } static void test_storage(krb5_context context, krb5_storage *sp) { test_int8(context, sp); test_int16(context, sp); test_int32(context, sp); test_uint8(context, sp); test_uint16(context, sp); test_uint32(context, sp); } static void test_truncate(krb5_context context, krb5_storage *sp, int fd) { struct stat sb; krb5_store_string(sp, "hej"); krb5_storage_truncate(sp, 2); if (fstat(fd, &sb) != 0) krb5_err(context, 1, errno, "fstat"); if (sb.st_size != 2) krb5_errx(context, 1, "length not 2"); krb5_storage_truncate(sp, 1024); if (fstat(fd, &sb) != 0) krb5_err(context, 1, errno, "fstat"); if (sb.st_size != 1024) krb5_errx(context, 1, "length not 2"); } static void check_too_large(krb5_context context, krb5_storage *sp) { uint32_t too_big_sizes[] = { INT_MAX, INT_MAX / 2, INT_MAX / 4, INT_MAX / 8 + 1}; krb5_error_code ret; krb5_data data; size_t n; for (n = 0; n < sizeof(too_big_sizes) / sizeof(too_big_sizes); n++) { krb5_storage_truncate(sp, 0); krb5_store_uint32(sp, too_big_sizes[n]); krb5_storage_seek(sp, 0, SEEK_SET); ret = krb5_ret_data(sp, &data); if (ret != HEIM_ERR_TOO_BIG) errx(1, "not too big: %lu", (unsigned long)n); } } /* * */ static int version_flag = 0; static int help_flag = 0; static struct getargs args[] = { {"version", 0, arg_flag, &version_flag, "print version", NULL }, {"help", 0, arg_flag, &help_flag, NULL, NULL } }; static void usage (int ret) { arg_printusage (args, sizeof(args)/sizeof(*args), NULL, ""); exit (ret); } int main(int argc, char **argv) { krb5_context context; krb5_error_code ret; int fd, optidx = 0; krb5_storage *sp; const char *fn = "test-store-data"; setprogname(argv[0]); if(getarg(args, sizeof(args) / sizeof(args[0]), argc, argv, &optidx)) usage(1); if (help_flag) usage (0); if(version_flag){ print_version(NULL); exit(0); } argc -= optidx; argv += optidx; ret = krb5_init_context (&context); if (ret) errx (1, "krb5_init_context failed: %d", ret); /* * Test encoding/decoding of primotive types on diffrent backends */ sp = krb5_storage_emem(); if (sp == NULL) krb5_errx(context, 1, "krb5_storage_emem: no mem"); test_storage(context, sp); check_too_large(context, sp); krb5_storage_free(sp); fd = open(fn, O_RDWR|O_CREAT|O_TRUNC, 0600); if (fd < 0) krb5_err(context, 1, errno, "open(%s)", fn); sp = krb5_storage_from_fd(fd); close(fd); if (sp == NULL) krb5_errx(context, 1, "krb5_storage_from_fd: %s no mem", fn); test_storage(context, sp); krb5_storage_free(sp); unlink(fn); /* * test truncate behavior */ fd = open(fn, O_RDWR|O_CREAT|O_TRUNC, 0600); if (fd < 0) krb5_err(context, 1, errno, "open(%s)", fn); sp = krb5_storage_from_fd(fd); if (sp == NULL) krb5_errx(context, 1, "krb5_storage_from_fd: %s no mem", fn); test_truncate(context, sp, fd); krb5_storage_free(sp); close(fd); unlink(fn); krb5_free_context(context); return 0; } diff --git a/crypto/heimdal/lib/krb5/transited.c b/crypto/heimdal/lib/krb5/transited.c index 5e21987bca91..09e540a2bedc 100644 --- a/crypto/heimdal/lib/krb5/transited.c +++ b/crypto/heimdal/lib/krb5/transited.c @@ -1,490 +1,493 @@ /* * Copyright (c) 1997 - 2001, 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 "krb5_locl.h" /* this is an attempt at one of the most horrible `compression' schemes that has ever been invented; it's so amazingly brain-dead that words can not describe it, and all this just to save a few silly bytes */ struct tr_realm { char *realm; unsigned leading_space:1; unsigned leading_slash:1; unsigned trailing_dot:1; struct tr_realm *next; }; static void free_realms(struct tr_realm *r) { struct tr_realm *p; while(r){ p = r; r = r->next; free(p->realm); free(p); } } static int make_path(krb5_context context, struct tr_realm *r, const char *from, const char *to) { struct tr_realm *tmp; const char *p; if(strlen(from) < strlen(to)){ const char *str; str = from; from = to; to = str; } if(strcmp(from + strlen(from) - strlen(to), to) == 0){ p = from; while(1){ p = strchr(p, '.'); if(p == NULL) { krb5_clear_error_message (context); return KRB5KDC_ERR_POLICY; } p++; if(strcmp(p, to) == 0) break; tmp = calloc(1, sizeof(*tmp)); if(tmp == NULL) return krb5_enomem(context); tmp->next = r->next; r->next = tmp; tmp->realm = strdup(p); if(tmp->realm == NULL){ r->next = tmp->next; free(tmp); return krb5_enomem(context); } } }else if(strncmp(from, to, strlen(to)) == 0){ p = from + strlen(from); while(1){ while(p >= from && *p != '/') p--; if(p == from) return KRB5KDC_ERR_POLICY; if(strncmp(to, from, p - from) == 0) break; tmp = calloc(1, sizeof(*tmp)); if(tmp == NULL) return krb5_enomem(context); tmp->next = r->next; r->next = tmp; tmp->realm = malloc(p - from + 1); if(tmp->realm == NULL){ r->next = tmp->next; free(tmp); return krb5_enomem(context); } memcpy(tmp->realm, from, p - from); tmp->realm[p - from] = '\0'; p--; } } else { krb5_clear_error_message (context); return KRB5KDC_ERR_POLICY; } return 0; } static int make_paths(krb5_context context, struct tr_realm *realms, const char *client_realm, const char *server_realm) { struct tr_realm *r; int ret; const char *prev_realm = client_realm; const char *next_realm = NULL; for(r = realms; r; r = r->next){ /* it *might* be that you can have more than one empty component in a row, at least that's how I interpret the "," exception in 1510 */ if(r->realm[0] == '\0'){ while(r->next && r->next->realm[0] == '\0') r = r->next; if(r->next) next_realm = r->next->realm; else next_realm = server_realm; ret = make_path(context, r, prev_realm, next_realm); if(ret){ free_realms(realms); return ret; } } prev_realm = r->realm; } return 0; } static int expand_realms(krb5_context context, struct tr_realm *realms, const char *client_realm) { struct tr_realm *r; const char *prev_realm = NULL; for(r = realms; r; r = r->next){ if(r->trailing_dot){ char *tmp; size_t len; if(prev_realm == NULL) prev_realm = client_realm; len = strlen(r->realm) + strlen(prev_realm) + 1; tmp = realloc(r->realm, len); if(tmp == NULL){ free_realms(realms); return krb5_enomem(context); } r->realm = tmp; strlcat(r->realm, prev_realm, len); }else if(r->leading_slash && !r->leading_space && prev_realm){ /* yet another exception: if you use x500-names, the leading realm doesn't have to be "quoted" with a space */ char *tmp; size_t len = strlen(r->realm) + strlen(prev_realm) + 1; tmp = malloc(len); if(tmp == NULL){ free_realms(realms); return krb5_enomem(context); } strlcpy(tmp, prev_realm, len); strlcat(tmp, r->realm, len); free(r->realm); r->realm = tmp; } prev_realm = r->realm; } return 0; } static struct tr_realm * make_realm(char *realm) { struct tr_realm *r; char *p, *q; int quote = 0; r = calloc(1, sizeof(*r)); if(r == NULL){ free(realm); return NULL; } r->realm = realm; for(p = q = r->realm; *p; p++){ if(p == r->realm && *p == ' '){ r->leading_space = 1; continue; } if(q == r->realm && *p == '/') r->leading_slash = 1; if(quote){ *q++ = *p; quote = 0; continue; } if(*p == '\\'){ quote = 1; continue; } if(p[0] == '.' && p[1] == '\0') r->trailing_dot = 1; *q++ = *p; } *q = '\0'; return r; } static struct tr_realm* append_realm(struct tr_realm *head, struct tr_realm *r) { struct tr_realm *p; if(head == NULL){ r->next = NULL; return r; } p = head; while(p->next) p = p->next; p->next = r; return head; } static int decode_realms(krb5_context context, const char *tr, int length, struct tr_realm **realms) { struct tr_realm *r = NULL; char *tmp; int quote = 0; const char *start = tr; int i; for(i = 0; i < length; i++){ if(quote){ quote = 0; continue; } if(tr[i] == '\\'){ quote = 1; continue; } if(tr[i] == ','){ tmp = malloc(tr + i - start + 1); if(tmp == NULL) return krb5_enomem(context); memcpy(tmp, start, tr + i - start); tmp[tr + i - start] = '\0'; r = make_realm(tmp); if(r == NULL){ free_realms(*realms); + *realms = NULL; return krb5_enomem(context); } *realms = append_realm(*realms, r); start = tr + i + 1; } } tmp = malloc(tr + i - start + 1); if(tmp == NULL){ - free(*realms); + free_realms(*realms); + *realms = NULL; return krb5_enomem(context); } memcpy(tmp, start, tr + i - start); tmp[tr + i - start] = '\0'; r = make_realm(tmp); if(r == NULL){ free_realms(*realms); + *realms = NULL; return krb5_enomem(context); } *realms = append_realm(*realms, r); return 0; } KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL krb5_domain_x500_decode(krb5_context context, krb5_data tr, char ***realms, unsigned int *num_realms, const char *client_realm, const char *server_realm) { struct tr_realm *r = NULL; struct tr_realm *p, **q; int ret; if(tr.length == 0) { *realms = NULL; *num_realms = 0; return 0; } /* split string in components */ ret = decode_realms(context, tr.data, tr.length, &r); if(ret) return ret; /* apply prefix rule */ ret = expand_realms(context, r, client_realm); if(ret) return ret; ret = make_paths(context, r, client_realm, server_realm); if(ret) return ret; /* remove empty components and count realms */ *num_realms = 0; for(q = &r; *q; ){ if((*q)->realm[0] == '\0'){ p = *q; *q = (*q)->next; free(p->realm); free(p); }else{ q = &(*q)->next; (*num_realms)++; } } if (*num_realms + 1 > UINT_MAX/sizeof(**realms)) return ERANGE; { char **R; R = malloc((*num_realms + 1) * sizeof(*R)); if (R == NULL) return krb5_enomem(context); *realms = R; while(r){ *R++ = r->realm; p = r->next; free(r); r = p; } } return 0; } KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL krb5_domain_x500_encode(char **realms, unsigned int num_realms, krb5_data *encoding) { char *s = NULL; int len = 0; unsigned int i; krb5_data_zero(encoding); if (num_realms == 0) return 0; for(i = 0; i < num_realms; i++){ len += strlen(realms[i]); if(realms[i][0] == '/') len++; } len += num_realms - 1; s = malloc(len + 1); if (s == NULL) return ENOMEM; *s = '\0'; for(i = 0; i < num_realms; i++){ if(i) strlcat(s, ",", len + 1); if(realms[i][0] == '/') strlcat(s, " ", len + 1); strlcat(s, realms[i], len + 1); } encoding->data = s; encoding->length = strlen(s); return 0; } KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL krb5_check_transited(krb5_context context, krb5_const_realm client_realm, krb5_const_realm server_realm, krb5_realm *realms, unsigned int num_realms, int *bad_realm) { char **tr_realms; char **p; size_t i; if(num_realms == 0) return 0; tr_realms = krb5_config_get_strings(context, NULL, "capaths", client_realm, server_realm, NULL); for(i = 0; i < num_realms; i++) { for(p = tr_realms; p && *p; p++) { if(strcmp(*p, realms[i]) == 0) break; } if(p == NULL || *p == NULL) { krb5_config_free_strings(tr_realms); krb5_set_error_message (context, KRB5KRB_AP_ERR_ILL_CR_TKT, N_("no transit allowed " "through realm %s", ""), realms[i]); if(bad_realm) *bad_realm = i; return KRB5KRB_AP_ERR_ILL_CR_TKT; } } krb5_config_free_strings(tr_realms); return 0; } KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL krb5_check_transited_realms(krb5_context context, const char *const *realms, unsigned int num_realms, int *bad_realm) { size_t i; int ret = 0; char **bad_realms = krb5_config_get_strings(context, NULL, "libdefaults", "transited_realms_reject", NULL); if(bad_realms == NULL) return 0; for(i = 0; i < num_realms; i++) { char **p; for(p = bad_realms; *p; p++) if(strcmp(*p, realms[i]) == 0) { ret = KRB5KRB_AP_ERR_ILL_CR_TKT; krb5_set_error_message (context, ret, N_("no transit allowed " "through realm %s", ""), *p); if(bad_realm) *bad_realm = i; break; } } krb5_config_free_strings(bad_realms); return ret; } #if 0 int main(int argc, char **argv) { krb5_data x; char **r; int num, i; x.data = argv[1]; x.length = strlen(x.data); if(domain_expand(x, &r, &num, argv[2], argv[3])) exit(1); for(i = 0; i < num; i++) printf("%s\n", r[i]); return 0; } #endif diff --git a/crypto/heimdal/lib/roken/getaddrinfo.c b/crypto/heimdal/lib/roken/getaddrinfo.c index c8ed95413fe3..ae21bf11090c 100644 --- a/crypto/heimdal/lib/roken/getaddrinfo.c +++ b/crypto/heimdal/lib/roken/getaddrinfo.c @@ -1,414 +1,416 @@ /* * Copyright (c) 1999 - 2001 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 "roken.h" /* * uses hints->ai_socktype and hints->ai_protocol */ static int get_port_protocol_socktype (const char *servname, const struct addrinfo *hints, int *port, int *protocol, int *socktype) { struct servent *se; const char *proto_str = NULL; *socktype = 0; if (hints != NULL && hints->ai_protocol != 0) { struct protoent *protoent = getprotobynumber (hints->ai_protocol); if (protoent == NULL) return EAI_SOCKTYPE; /* XXX */ proto_str = protoent->p_name; *protocol = protoent->p_proto; } if (hints != NULL) *socktype = hints->ai_socktype; if (*socktype == SOCK_STREAM) { se = getservbyname (servname, proto_str ? proto_str : "tcp"); if (proto_str == NULL) *protocol = IPPROTO_TCP; } else if (*socktype == SOCK_DGRAM) { se = getservbyname (servname, proto_str ? proto_str : "udp"); if (proto_str == NULL) *protocol = IPPROTO_UDP; } else if (*socktype == 0) { if (proto_str != NULL) { se = getservbyname (servname, proto_str); } else { se = getservbyname (servname, "tcp"); *protocol = IPPROTO_TCP; *socktype = SOCK_STREAM; if (se == NULL) { se = getservbyname (servname, "udp"); *protocol = IPPROTO_UDP; *socktype = SOCK_DGRAM; } } } else return EAI_SOCKTYPE; if (se == NULL) { char *endstr; *port = htons(strtol (servname, &endstr, 10)); if (servname == endstr) return EAI_NONAME; } else { *port = se->s_port; } return 0; } static int add_one (int port, int protocol, int socktype, struct addrinfo ***ptr, int (*func)(struct addrinfo *, void *data, int port), void *data, char *canonname) { struct addrinfo *a; int ret; a = malloc (sizeof (*a)); if (a == NULL) return EAI_MEMORY; memset (a, 0, sizeof(*a)); a->ai_flags = 0; a->ai_next = NULL; a->ai_protocol = protocol; a->ai_socktype = socktype; a->ai_canonname = canonname; ret = (*func)(a, data, port); if (ret) { free (a); return ret; } **ptr = a; *ptr = &a->ai_next; return 0; } static int const_v4 (struct addrinfo *a, void *data, int port) { struct sockaddr_in *sin4; struct in_addr *addr = (struct in_addr *)data; a->ai_family = PF_INET; a->ai_addrlen = sizeof(*sin4); a->ai_addr = malloc (sizeof(*sin4)); if (a->ai_addr == NULL) return EAI_MEMORY; sin4 = (struct sockaddr_in *)a->ai_addr; memset (sin4, 0, sizeof(*sin4)); sin4->sin_family = AF_INET; sin4->sin_port = port; sin4->sin_addr = *addr; return 0; } #ifdef HAVE_IPV6 static int const_v6 (struct addrinfo *a, void *data, int port) { struct sockaddr_in6 *sin6; struct in6_addr *addr = (struct in6_addr *)data; a->ai_family = PF_INET6; a->ai_addrlen = sizeof(*sin6); a->ai_addr = malloc (sizeof(*sin6)); if (a->ai_addr == NULL) return EAI_MEMORY; sin6 = (struct sockaddr_in6 *)a->ai_addr; memset (sin6, 0, sizeof(*sin6)); sin6->sin6_family = AF_INET6; sin6->sin6_port = port; sin6->sin6_addr = *addr; return 0; } #endif /* this is mostly a hack for some versions of AIX that has a prototype for in6addr_loopback but no actual symbol in libc */ #if defined(HAVE_IPV6) && !defined(HAVE_IN6ADDR_LOOPBACK) && defined(IN6ADDR_LOOPBACK_INIT) #define in6addr_loopback _roken_in6addr_loopback struct in6_addr in6addr_loopback = IN6ADDR_LOOPBACK_INIT; #endif static int get_null (const struct addrinfo *hints, int port, int protocol, int socktype, struct addrinfo **res) { struct in_addr v4_addr; #ifdef HAVE_IPV6 struct in6_addr v6_addr; #endif struct addrinfo *first = NULL; struct addrinfo **current = &first; int family = PF_UNSPEC; - int ret; + int ret = 0; if (hints != NULL) family = hints->ai_family; if (hints && hints->ai_flags & AI_PASSIVE) { v4_addr.s_addr = INADDR_ANY; #ifdef HAVE_IPV6 v6_addr = in6addr_any; #endif } else { v4_addr.s_addr = htonl(INADDR_LOOPBACK); #ifdef HAVE_IPV6 v6_addr = in6addr_loopback; #endif } #ifdef HAVE_IPV6 if (family == PF_INET6 || family == PF_UNSPEC) { ret = add_one (port, protocol, socktype, ¤t, const_v6, &v6_addr, NULL); + if (ret) + return ret; } #endif if (family == PF_INET || family == PF_UNSPEC) { ret = add_one (port, protocol, socktype, ¤t, const_v4, &v4_addr, NULL); } *res = first; - return 0; + return ret; } static int add_hostent (int port, int protocol, int socktype, struct addrinfo ***current, int (*func)(struct addrinfo *, void *data, int port), struct hostent *he, int *flags) { int ret; char *canonname = NULL; char **h; if (*flags & AI_CANONNAME) { struct hostent *he2 = NULL; const char *tmp_canon; tmp_canon = hostent_find_fqdn (he); if (strchr (tmp_canon, '.') == NULL) { int error; he2 = getipnodebyaddr (he->h_addr_list[0], he->h_length, he->h_addrtype, &error); if (he2 != NULL) { const char *tmp = hostent_find_fqdn (he2); if (strchr (tmp, '.') != NULL) tmp_canon = tmp; } } canonname = strdup (tmp_canon); if (he2 != NULL) freehostent (he2); if (canonname == NULL) return EAI_MEMORY; } for (h = he->h_addr_list; *h != NULL; ++h) { ret = add_one (port, protocol, socktype, current, func, *h, canonname); if (ret) return ret; if (*flags & AI_CANONNAME) { *flags &= ~AI_CANONNAME; canonname = NULL; } } return 0; } static int get_number (const char *nodename, const struct addrinfo *hints, int port, int protocol, int socktype, struct addrinfo **res) { struct addrinfo *first = NULL; struct addrinfo **current = &first; int family = PF_UNSPEC; int ret; if (hints != NULL) { family = hints->ai_family; } #ifdef HAVE_IPV6 if (family == PF_INET6 || family == PF_UNSPEC) { struct in6_addr v6_addr; if (inet_pton (PF_INET6, nodename, &v6_addr) == 1) { ret = add_one (port, protocol, socktype, ¤t, const_v6, &v6_addr, NULL); *res = first; return ret; } } #endif if (family == PF_INET || family == PF_UNSPEC) { struct in_addr v4_addr; if (inet_pton (PF_INET, nodename, &v4_addr) == 1) { ret = add_one (port, protocol, socktype, ¤t, const_v4, &v4_addr, NULL); *res = first; return ret; } } return EAI_NONAME; } static int get_nodes (const char *nodename, const struct addrinfo *hints, int port, int protocol, int socktype, struct addrinfo **res) { struct addrinfo *first = NULL; struct addrinfo **current = &first; int family = PF_UNSPEC; int flags = 0; int ret = EAI_NONAME; int error; if (hints != NULL) { family = hints->ai_family; flags = hints->ai_flags; } #ifdef HAVE_IPV6 if (family == PF_INET6 || family == PF_UNSPEC) { struct hostent *he; he = getipnodebyname (nodename, PF_INET6, 0, &error); if (he != NULL) { ret = add_hostent (port, protocol, socktype, ¤t, const_v6, he, &flags); freehostent (he); } } #endif if (family == PF_INET || family == PF_UNSPEC) { struct hostent *he; he = getipnodebyname (nodename, PF_INET, 0, &error); if (he != NULL) { ret = add_hostent (port, protocol, socktype, ¤t, const_v4, he, &flags); freehostent (he); } } *res = first; return ret; } /* * hints: * * struct addrinfo { * int ai_flags; * int ai_family; * int ai_socktype; * int ai_protocol; * ... * }; */ ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL getaddrinfo(const char *nodename, const char *servname, const struct addrinfo *hints, struct addrinfo **res) { int ret; int port = 0; int protocol = 0; int socktype = 0; *res = NULL; if (servname == NULL && nodename == NULL) return EAI_NONAME; if (hints != NULL && hints->ai_family != PF_UNSPEC && hints->ai_family != PF_INET #ifdef HAVE_IPV6 && hints->ai_family != PF_INET6 #endif ) return EAI_FAMILY; if (servname != NULL) { ret = get_port_protocol_socktype (servname, hints, &port, &protocol, &socktype); if (ret) return ret; } if (nodename != NULL) { ret = get_number (nodename, hints, port, protocol, socktype, res); if (ret) { if(hints && hints->ai_flags & AI_NUMERICHOST) ret = EAI_NONAME; else ret = get_nodes (nodename, hints, port, protocol, socktype, res); } } else { ret = get_null (hints, port, protocol, socktype, res); } if (ret) freeaddrinfo (*res); return ret; } diff --git a/crypto/heimdal/lib/wind/idn-lookup.c b/crypto/heimdal/lib/wind/idn-lookup.c index 1bc63a33dd8a..378c912a392d 100644 --- a/crypto/heimdal/lib/wind/idn-lookup.c +++ b/crypto/heimdal/lib/wind/idn-lookup.c @@ -1,162 +1,164 @@ /* * Copyright (c) 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. */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include #include #include #include #include #include #include #include "windlocl.h" static int version_flag = 0; static int help_flag = 0; static int is_separator(uint32_t u) { return u == 0x002E || u == 0x3002; } static void lookup(const char *name) { unsigned i; char encoded[1024]; char *ep; int ret; struct addrinfo hints; struct addrinfo *ai; size_t u_len = strlen(name); uint32_t *u = malloc(u_len * sizeof(uint32_t)); size_t norm_len = u_len * 2; uint32_t *norm = malloc(norm_len * sizeof(uint32_t)); if (u == NULL && u_len != 0) errx(1, "malloc failed"); if (norm == NULL && norm_len != 0) errx(1, "malloc failed"); ret = wind_utf8ucs4(name, u, &u_len); if (ret) errx(1, "utf8 conversion failed"); ret = wind_stringprep(u, u_len, norm, &norm_len, WIND_PROFILE_NAME); if (ret) errx(1, "stringprep failed"); free(u); ep = encoded; for (i = 0; i < norm_len; ++i) { unsigned j; size_t len; for (j = i; j < norm_len && !is_separator(norm[j]); ++j) ; len = sizeof(encoded) - (ep - encoded); ret = wind_punycode_label_toascii(norm + i, j - i, ep, &len); if (ret) errx(1, "punycode failed"); ep += len; *ep++ = '.'; i = j; } *ep = '\0'; free(norm); printf("Converted \"%s\" into \"%s\"\n", name, encoded); memset(&hints, 0, sizeof(hints)); hints.ai_flags = AI_CANONNAME; ret = getaddrinfo(encoded, NULL, &hints, &ai); if(ret) errx(1, "getaddrinfo failed: %s", gai_strerror(ret)); printf("canonical-name: %s\n", ai->ai_canonname); freeaddrinfo(ai); } static struct getargs args[] = { {"version", 0, arg_flag, &version_flag, "print version", NULL }, {"help", 0, arg_flag, &help_flag, NULL, NULL } }; static void usage (int ret) { arg_printusage(args, sizeof(args)/sizeof(args[0]), NULL, "dns-names ..."); exit (ret); } int main(int argc, char **argv) { int optidx = 0; unsigned i; setprogname (argv[0]); if(getarg(args, sizeof(args) / sizeof(args[0]), argc, argv, &optidx)) usage(1); if (help_flag) usage (0); if(version_flag){ print_version(NULL); exit(0); } argc -= optidx; argv += optidx; if (argc == 0) usage(1); - for (i = 0; i < argc; ++i) - lookup(argv[i]); + for (i = 0; i < argc; ++i) { + if (argv[i][0]) /* Quiet lint */ + lookup(argv[i]); + } return 0; } diff --git a/crypto/heimdal/lib/wind/normalize.c b/crypto/heimdal/lib/wind/normalize.c index 15274f6855e1..67fc1de92181 100644 --- a/crypto/heimdal/lib/wind/normalize.c +++ b/crypto/heimdal/lib/wind/normalize.c @@ -1,325 +1,325 @@ /* * Copyright (c) 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. */ #ifdef HAVE_CONFIG_H #include #endif #include "windlocl.h" #include #include #include #include #include "roken.h" #include "normalize_table.h" static int translation_cmp(const void *key, const void *data) { const struct translation *t1 = (const struct translation *)key; const struct translation *t2 = (const struct translation *)data; return t1->key - t2->key; } enum { s_base = 0xAC00}; enum { s_count = 11172}; enum { l_base = 0x1100}; enum { l_count = 19}; enum { v_base = 0x1161}; enum { v_count = 21}; enum { t_base = 0x11A7}; enum { t_count = 28}; enum { n_count = v_count * t_count}; static int hangul_decomp(const uint32_t *in, size_t in_len, uint32_t *out, size_t *out_len) { uint32_t u = *in; unsigned s_index; unsigned l, v, t; unsigned o; if (u < s_base || u >= s_base + s_count) return 0; s_index = u - s_base; l = l_base + s_index / n_count; v = v_base + (s_index % n_count) / t_count; t = t_base + s_index % t_count; o = 2; if (t != t_base) ++o; if (*out_len < o) return WIND_ERR_OVERRUN; out[0] = l; out[1] = v; if (t != t_base) out[2] = t; *out_len = o; return 1; } static uint32_t hangul_composition(const uint32_t *in, size_t in_len) { if (in_len < 2) return 0; if (in[0] >= l_base && in[0] < l_base + l_count) { unsigned l_index = in[0] - l_base; unsigned v_index; if (in[1] < v_base || in[1] >= v_base + v_count) return 0; v_index = in[1] - v_base; return (l_index * v_count + v_index) * t_count + s_base; } else if (in[0] >= s_base && in[0] < s_base + s_count) { unsigned s_index = in[0] - s_base; unsigned t_index; if (s_index % t_count != 0) return 0; if (in[1] < t_base || in[1] >= t_base + t_count) return 0; t_index = in[1] - t_base; return in[0] + t_index; } return 0; } static int compat_decomp(const uint32_t *in, size_t in_len, uint32_t *out, size_t *out_len) { unsigned i; unsigned o = 0; for (i = 0; i < in_len; ++i) { struct translation ts = {in[i]}; size_t sub_len = *out_len - o; int ret; ret = hangul_decomp(in + i, in_len - i, out + o, &sub_len); if (ret) { if (ret == WIND_ERR_OVERRUN) return ret; o += sub_len; } else { void *s = bsearch(&ts, _wind_normalize_table, _wind_normalize_table_size, sizeof(_wind_normalize_table[0]), translation_cmp); if (s != NULL) { const struct translation *t = (const struct translation *)s; ret = compat_decomp(_wind_normalize_val_table + t->val_offset, t->val_len, out + o, &sub_len); if (ret) return ret; o += sub_len; } else { if (o >= *out_len) return WIND_ERR_OVERRUN; out[o++] = in[i]; } } } *out_len = o; return 0; } static void swap_char(uint32_t * a, uint32_t * b) { uint32_t t; t = *a; *a = *b; *b = t; } /* Unicode 5.2.0 D109 Canonical Ordering for a sequence of code points * that all have Canonical_Combining_Class > 0 */ static void canonical_reorder_sequence(uint32_t * a, size_t len) { size_t i, j; if (len <= 1) return; for (i = 1; i < len; i++) { for (j = i; j > 0 && _wind_combining_class(a[j]) < _wind_combining_class(a[j-1]); j--) swap_char(&a[j], &a[j-1]); } } static void canonical_reorder(uint32_t *tmp, size_t tmp_len) { size_t i; for (i = 0; i < tmp_len; ++i) { int cc = _wind_combining_class(tmp[i]); if (cc) { size_t j; for (j = i + 1; j < tmp_len && _wind_combining_class(tmp[j]); ++j) ; canonical_reorder_sequence(&tmp[i], j - i); i = j; } } } static uint32_t find_composition(const uint32_t *in, unsigned in_len) { unsigned short canon_index = 0; uint32_t cur; unsigned n = 0; cur = hangul_composition(in, in_len); if (cur) return cur; do { const struct canon_node *c = &_wind_canon_table[canon_index]; unsigned i; if (n % 5 == 0) { - cur = *in++; if (in_len-- == 0) return c->val; + cur = *in++; } i = cur >> 16; if (i < c->next_start || i >= c->next_end) canon_index = 0; else canon_index = _wind_canon_next_table[c->next_offset + i - c->next_start]; if (canon_index != 0) { cur = (cur << 4) & 0xFFFFF; ++n; } } while (canon_index != 0); return 0; } static int combine(const uint32_t *in, size_t in_len, uint32_t *out, size_t *out_len) { unsigned i; int ostarter; unsigned o = 0; int old_cc; for (i = 0; i < in_len;) { while (i < in_len && _wind_combining_class(in[i]) != 0) { out[o++] = in[i++]; } if (i < in_len) { if (o >= *out_len) return WIND_ERR_OVERRUN; ostarter = o; out[o++] = in[i++]; old_cc = -1; while (i < in_len) { uint32_t comb; uint32_t v[2]; int cc; v[0] = out[ostarter]; v[1] = in[i]; cc = _wind_combining_class(in[i]); if (old_cc != cc && (comb = find_composition(v, 2))) { out[ostarter] = comb; } else if (cc == 0) { break; } else { if (o >= *out_len) return WIND_ERR_OVERRUN; out[o++] = in[i]; old_cc = cc; } ++i; } } } *out_len = o; return 0; } int _wind_stringprep_normalize(const uint32_t *in, size_t in_len, uint32_t *out, size_t *out_len) { size_t tmp_len; uint32_t *tmp; int ret; if (in_len == 0) { *out_len = 0; return 0; } tmp_len = in_len * 4; if (tmp_len < MAX_LENGTH_CANON) tmp_len = MAX_LENGTH_CANON; tmp = malloc(tmp_len * sizeof(uint32_t)); if (tmp == NULL) return ENOMEM; ret = compat_decomp(in, in_len, tmp, &tmp_len); if (ret) { free(tmp); return ret; } canonical_reorder(tmp, tmp_len); ret = combine(tmp, tmp_len, out, out_len); free(tmp); return ret; }