summaryrefslogtreecommitdiff
path: root/src/lib/krb5/krb/mk_req_ext.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib/krb5/krb/mk_req_ext.c')
-rw-r--r--src/lib/krb5/krb/mk_req_ext.c430
1 files changed, 430 insertions, 0 deletions
diff --git a/src/lib/krb5/krb/mk_req_ext.c b/src/lib/krb5/krb/mk_req_ext.c
new file mode 100644
index 000000000000..dce0927814df
--- /dev/null
+++ b/src/lib/krb5/krb/mk_req_ext.c
@@ -0,0 +1,430 @@
+/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/* lib/krb5/krb/mk_req_ext.c */
+/*
+ * Copyright 1990,1991 by the Massachusetts Institute of Technology.
+ * All Rights Reserved.
+ *
+ * Export of this software from the United States of America may
+ * require a specific license from the United States Government.
+ * It is the responsibility of any person or organization contemplating
+ * export to obtain such a license before exporting.
+ *
+ * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
+ * distribute this software and its documentation for any purpose and
+ * without fee is hereby granted, provided that the above copyright
+ * notice appear in all copies and that both that copyright notice and
+ * this permission notice appear in supporting documentation, and that
+ * the name of M.I.T. not be used in advertising or publicity pertaining
+ * to distribution of the software without specific, written prior
+ * permission. Furthermore if you modify this software you must label
+ * your software as modified software and not distribute it in such a
+ * fashion that it might be confused with the original M.I.T. software.
+ * M.I.T. makes no representations about the suitability of
+ * this software for any purpose. It is provided "as is" without express
+ * or implied warranty.
+ */
+
+/*
+ *
+ * krb5_mk_req_extended()
+ */
+
+
+#include "k5-int.h"
+#include "int-proto.h"
+#include "auth_con.h"
+
+/*
+ Formats a KRB_AP_REQ message into outbuf, with more complete options than
+ krb_mk_req.
+
+ outbuf, ap_req_options, checksum, and ccache are used in the
+ same fashion as for krb5_mk_req.
+
+ creds is used to supply the credentials (ticket and session key) needed
+ to form the request.
+
+ if creds->ticket has no data (length == 0), then a ticket is obtained
+ from either the cache or the TGS, passing creds to krb5_get_credentials().
+ kdc_options specifies the options requested for the ticket to be used.
+ If a ticket with appropriate flags is not found in the cache, then these
+ options are passed on in a request to an appropriate KDC.
+
+ ap_req_options specifies the KRB_AP_REQ options desired.
+
+ if ap_req_options specifies AP_OPTS_USE_SESSION_KEY, then creds->ticket
+ must contain the appropriate ENC-TKT-IN-SKEY ticket.
+
+ checksum specifies the checksum to be used in the authenticator.
+
+ The outbuf buffer storage is allocated, and should be freed by the
+ caller when finished.
+
+ On an error return, the credentials pointed to by creds might have been
+ augmented with additional fields from the obtained credentials; the entire
+ credentials should be released by calling krb5_free_creds().
+
+ returns system errors
+*/
+
+static krb5_error_code
+make_etype_list(krb5_context context,
+ krb5_enctype *desired_etypes,
+ krb5_enctype tkt_enctype,
+ krb5_authdata ***authdata);
+
+static krb5_error_code
+generate_authenticator(krb5_context,
+ krb5_authenticator *, krb5_principal,
+ krb5_checksum *, krb5_key,
+ krb5_ui_4, krb5_authdata **,
+ krb5_authdata_context ad_context,
+ krb5_enctype *desired_etypes,
+ krb5_enctype tkt_enctype);
+
+/* Return the checksum type for the AP request, or 0 to use the enctype's
+ * mandatory checksum. */
+static krb5_cksumtype
+ap_req_cksum(krb5_context context, krb5_auth_context auth_context,
+ krb5_enctype enctype)
+{
+ /* Use the configured checksum type if one was set. */
+ if (auth_context->req_cksumtype)
+ return auth_context->req_cksumtype;
+
+ /*
+ * Otherwise choose based on the enctype. For interoperability with very
+ * old implementations, use unkeyed MD4 or MD5 checkums for DES enctypes.
+ * (The authenticator checksum does not have to be keyed since it is
+ * contained within an encrypted blob.)
+ */
+ switch (enctype) {
+ case ENCTYPE_DES_CBC_CRC:
+ case ENCTYPE_DES_CBC_MD5:
+ return CKSUMTYPE_RSA_MD5;
+ break;
+ case ENCTYPE_DES_CBC_MD4:
+ return CKSUMTYPE_RSA_MD4;
+ break;
+ default:
+ /* Use the mandatory checksum type for the enctype. */
+ return 0;
+ }
+}
+
+krb5_error_code KRB5_CALLCONV
+krb5_mk_req_extended(krb5_context context, krb5_auth_context *auth_context,
+ krb5_flags ap_req_options, krb5_data *in_data,
+ krb5_creds *in_creds, krb5_data *outbuf)
+{
+ krb5_error_code retval;
+ krb5_checksum checksum;
+ krb5_checksum *checksump = 0;
+ krb5_auth_context new_auth_context;
+ krb5_enctype *desired_etypes = NULL;
+
+ krb5_ap_req request;
+ krb5_data *scratch = 0;
+ krb5_data *toutbuf;
+
+ request.ap_options = ap_req_options & AP_OPTS_WIRE_MASK;
+ request.authenticator.ciphertext.data = NULL;
+ request.ticket = 0;
+
+ if (!in_creds->ticket.length)
+ return(KRB5_NO_TKT_SUPPLIED);
+
+ if ((ap_req_options & AP_OPTS_ETYPE_NEGOTIATION) &&
+ !(ap_req_options & AP_OPTS_MUTUAL_REQUIRED))
+ return(EINVAL);
+
+ /* we need a native ticket */
+ if ((retval = decode_krb5_ticket(&(in_creds)->ticket, &request.ticket)))
+ return(retval);
+
+ /* verify that the ticket is not expired */
+ if ((retval = krb5int_validate_times(context, &in_creds->times)) != 0)
+ goto cleanup;
+
+ /* generate auth_context if needed */
+ if (*auth_context == NULL) {
+ if ((retval = krb5_auth_con_init(context, &new_auth_context)))
+ goto cleanup;
+ *auth_context = new_auth_context;
+ }
+
+ if ((*auth_context)->key != NULL) {
+ krb5_k_free_key(context, (*auth_context)->key);
+ (*auth_context)->key = NULL;
+ }
+
+ /* set auth context keyblock */
+ if ((retval = krb5_k_create_key(context, &in_creds->keyblock,
+ &((*auth_context)->key))))
+ goto cleanup;
+
+ /* generate seq number if needed */
+ if ((((*auth_context)->auth_context_flags & KRB5_AUTH_CONTEXT_DO_SEQUENCE)
+ || ((*auth_context)->auth_context_flags & KRB5_AUTH_CONTEXT_RET_SEQUENCE))
+ && ((*auth_context)->local_seq_number == 0)) {
+ if ((retval = krb5_generate_seq_number(context, &in_creds->keyblock,
+ &(*auth_context)->local_seq_number)))
+ goto cleanup;
+ }
+
+ /* generate subkey if needed */
+ if ((ap_req_options & AP_OPTS_USE_SUBKEY)&&(!(*auth_context)->send_subkey)) {
+ retval = k5_generate_and_save_subkey(context, *auth_context,
+ &in_creds->keyblock,
+ in_creds->keyblock.enctype);
+ if (retval)
+ goto cleanup;
+ }
+
+
+ if (!in_data && (*auth_context)->checksum_func) {
+ retval = (*auth_context)->checksum_func( context,
+ *auth_context,
+ (*auth_context)->checksum_func_data,
+ &in_data);
+ if (retval)
+ goto cleanup;
+ }
+
+ if (in_data) {
+ if ((*auth_context)->req_cksumtype == 0x8003) {
+ /* XXX Special hack for GSSAPI */
+ checksum.checksum_type = 0x8003;
+ checksum.length = in_data->length;
+ checksum.contents = (krb5_octet *) in_data->data;
+ } else {
+ krb5_enctype enctype = krb5_k_key_enctype(context,
+ (*auth_context)->key);
+ krb5_cksumtype cksumtype = ap_req_cksum(context, *auth_context,
+ enctype);
+ if ((retval = krb5_k_make_checksum(context,
+ cksumtype,
+ (*auth_context)->key,
+ KRB5_KEYUSAGE_AP_REQ_AUTH_CKSUM,
+ in_data, &checksum)))
+ goto cleanup_cksum;
+ }
+ checksump = &checksum;
+ }
+
+ /* Generate authenticator */
+ if (((*auth_context)->authentp = (krb5_authenticator *)malloc(sizeof(
+ krb5_authenticator))) == NULL) {
+ retval = ENOMEM;
+ goto cleanup_cksum;
+ }
+
+ if (ap_req_options & AP_OPTS_ETYPE_NEGOTIATION) {
+ if ((*auth_context)->permitted_etypes == NULL) {
+ retval = krb5_get_tgs_ktypes(context, in_creds->server, &desired_etypes);
+ if (retval)
+ goto cleanup_cksum;
+ } else
+ desired_etypes = (*auth_context)->permitted_etypes;
+ }
+
+ TRACE_MK_REQ(context, in_creds, (*auth_context)->local_seq_number,
+ (*auth_context)->send_subkey, &in_creds->keyblock);
+ if ((retval = generate_authenticator(context,
+ (*auth_context)->authentp,
+ in_creds->client, checksump,
+ (*auth_context)->send_subkey,
+ (*auth_context)->local_seq_number,
+ in_creds->authdata,
+ (*auth_context)->ad_context,
+ desired_etypes,
+ in_creds->keyblock.enctype)))
+ goto cleanup_cksum;
+
+ /* encode the authenticator */
+ if ((retval = encode_krb5_authenticator((*auth_context)->authentp,
+ &scratch)))
+ goto cleanup_cksum;
+
+ /* call the encryption routine */
+ if ((retval = krb5_encrypt_helper(context, &in_creds->keyblock,
+ KRB5_KEYUSAGE_AP_REQ_AUTH,
+ scratch, &request.authenticator)))
+ goto cleanup_cksum;
+
+ if ((retval = encode_krb5_ap_req(&request, &toutbuf)))
+ goto cleanup_cksum;
+ *outbuf = *toutbuf;
+
+ free(toutbuf);
+
+cleanup_cksum:
+ /* Null out these fields, to prevent pointer sharing problems;
+ * they were supplied by the caller
+ */
+ if ((*auth_context)->authentp != NULL) {
+ (*auth_context)->authentp->client = NULL;
+ (*auth_context)->authentp->checksum = NULL;
+ }
+ if (checksump && checksump->checksum_type != 0x8003)
+ free(checksump->contents);
+
+cleanup:
+ if (desired_etypes &&
+ desired_etypes != (*auth_context)->permitted_etypes)
+ free(desired_etypes);
+ if (request.ticket)
+ krb5_free_ticket(context, request.ticket);
+ if (request.authenticator.ciphertext.data) {
+ (void) memset(request.authenticator.ciphertext.data, 0,
+ request.authenticator.ciphertext.length);
+ free(request.authenticator.ciphertext.data);
+ }
+ if (scratch) {
+ memset(scratch->data, 0, scratch->length);
+ free(scratch->data);
+ free(scratch);
+ }
+ return retval;
+}
+
+static krb5_error_code
+generate_authenticator(krb5_context context, krb5_authenticator *authent,
+ krb5_principal client, krb5_checksum *cksum,
+ krb5_key key, krb5_ui_4 seq_number,
+ krb5_authdata **authorization,
+ krb5_authdata_context ad_context,
+ krb5_enctype *desired_etypes,
+ krb5_enctype tkt_enctype)
+{
+ krb5_error_code retval;
+ krb5_authdata **ext_authdata = NULL;
+
+ authent->client = client;
+ authent->checksum = cksum;
+ if (key) {
+ retval = krb5_k_key_keyblock(context, key, &authent->subkey);
+ if (retval)
+ return retval;
+ } else
+ authent->subkey = 0;
+ authent->seq_number = seq_number;
+ authent->authorization_data = NULL;
+
+ if (ad_context != NULL) {
+ retval = krb5_authdata_export_authdata(context,
+ ad_context,
+ AD_USAGE_AP_REQ,
+ &ext_authdata);
+ if (retval)
+ return retval;
+ }
+
+ if (authorization != NULL || ext_authdata != NULL) {
+ retval = krb5_merge_authdata(context,
+ authorization,
+ ext_authdata,
+ &authent->authorization_data);
+ if (retval) {
+ krb5_free_authdata(context, ext_authdata);
+ return retval;
+ }
+ krb5_free_authdata(context, ext_authdata);
+ }
+
+ /* Only send EtypeList if we prefer another enctype to tkt_enctype */
+ if (desired_etypes != NULL && desired_etypes[0] != tkt_enctype) {
+ TRACE_MK_REQ_ETYPES(context, desired_etypes);
+ retval = make_etype_list(context, desired_etypes, tkt_enctype,
+ &authent->authorization_data);
+ if (retval)
+ return retval;
+ }
+
+ return(krb5_us_timeofday(context, &authent->ctime, &authent->cusec));
+}
+
+/* RFC 4537 */
+static krb5_error_code
+make_etype_list(krb5_context context,
+ krb5_enctype *desired_etypes,
+ krb5_enctype tkt_enctype,
+ krb5_authdata ***authdata)
+{
+ krb5_error_code code;
+ krb5_etype_list etypes;
+ krb5_data *enc_etype_list;
+ krb5_data *ad_if_relevant;
+ krb5_authdata *etype_adata[2], etype_adatum, **adata;
+ int i;
+
+ etypes.etypes = desired_etypes;
+
+ for (etypes.length = 0;
+ etypes.etypes[etypes.length] != ENCTYPE_NULL;
+ etypes.length++)
+ {
+ /*
+ * RFC 4537:
+ *
+ * If the enctype of the ticket session key is included in the enctype
+ * list sent by the client, it SHOULD be the last on the list;
+ */
+ if (etypes.length && etypes.etypes[etypes.length - 1] == tkt_enctype)
+ break;
+ }
+
+ code = encode_krb5_etype_list(&etypes, &enc_etype_list);
+ if (code) {
+ return code;
+ }
+
+ etype_adatum.magic = KV5M_AUTHDATA;
+ etype_adatum.ad_type = KRB5_AUTHDATA_ETYPE_NEGOTIATION;
+ etype_adatum.length = enc_etype_list->length;
+ etype_adatum.contents = (krb5_octet *)enc_etype_list->data;
+
+ etype_adata[0] = &etype_adatum;
+ etype_adata[1] = NULL;
+
+ /* Wrap in AD-IF-RELEVANT container */
+ code = encode_krb5_authdata(etype_adata, &ad_if_relevant);
+ if (code) {
+ krb5_free_data(context, enc_etype_list);
+ return code;
+ }
+
+ krb5_free_data(context, enc_etype_list);
+
+ adata = *authdata;
+ if (adata == NULL) {
+ adata = (krb5_authdata **)calloc(2, sizeof(krb5_authdata *));
+ i = 0;
+ } else {
+ for (i = 0; adata[i] != NULL; i++)
+ ;
+
+ adata = (krb5_authdata **)realloc(*authdata,
+ (i + 2) * sizeof(krb5_authdata *));
+ }
+ if (adata == NULL) {
+ krb5_free_data(context, ad_if_relevant);
+ return ENOMEM;
+ }
+ *authdata = adata;
+
+ adata[i] = (krb5_authdata *)malloc(sizeof(krb5_authdata));
+ if (adata[i] == NULL) {
+ krb5_free_data(context, ad_if_relevant);
+ return ENOMEM;
+ }
+ adata[i]->magic = KV5M_AUTHDATA;
+ adata[i]->ad_type = KRB5_AUTHDATA_IF_RELEVANT;
+ adata[i]->length = ad_if_relevant->length;
+ adata[i]->contents = (krb5_octet *)ad_if_relevant->data;
+ free(ad_if_relevant); /* contents owned by adata[i] */
+
+ adata[i + 1] = NULL;
+
+ return 0;
+}