summaryrefslogtreecommitdiff
path: root/src/lib/krb5/krb/send_tgs.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib/krb5/krb/send_tgs.c')
-rw-r--r--src/lib/krb5/krb/send_tgs.c293
1 files changed, 293 insertions, 0 deletions
diff --git a/src/lib/krb5/krb/send_tgs.c b/src/lib/krb5/krb/send_tgs.c
new file mode 100644
index 000000000000..f6fdf68d4725
--- /dev/null
+++ b/src/lib/krb5/krb/send_tgs.c
@@ -0,0 +1,293 @@
+/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/* lib/krb5/krb/send_tgs.c - Construct a TGS request */
+/*
+ * Copyright 1990,1991,2009,2013 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.
+ */
+
+#include "k5-int.h"
+#include "int-proto.h"
+#include "fast.h"
+
+/* Construct an AP-REQ message for a TGS request. */
+static krb5_error_code
+tgs_construct_ap_req(krb5_context context, krb5_data *checksum_data,
+ krb5_creds *tgt, krb5_keyblock *subkey,
+ krb5_data **ap_req_asn1_out)
+{
+ krb5_cksumtype cksumtype;
+ krb5_error_code ret;
+ krb5_checksum checksum;
+ krb5_authenticator authent;
+ krb5_ap_req ap_req;
+ krb5_data *authent_asn1 = NULL;
+ krb5_ticket *ticket = NULL;
+ krb5_enc_data authent_enc;
+
+ *ap_req_asn1_out = NULL;
+ memset(&checksum, 0, sizeof(checksum));
+ memset(&ap_req, 0, sizeof(ap_req));
+ memset(&authent_enc, 0, sizeof(authent_enc));
+
+ /* Determine the authenticator checksum type. */
+ switch (tgt->keyblock.enctype) {
+ case ENCTYPE_DES_CBC_CRC:
+ case ENCTYPE_DES_CBC_MD4:
+ case ENCTYPE_DES_CBC_MD5:
+ case ENCTYPE_ARCFOUR_HMAC:
+ case ENCTYPE_ARCFOUR_HMAC_EXP:
+ cksumtype = context->kdc_req_sumtype;
+ break;
+ default:
+ ret = krb5int_c_mandatory_cksumtype(context, tgt->keyblock.enctype,
+ &cksumtype);
+ if (ret)
+ goto cleanup;
+ }
+
+ /* Generate checksum. */
+ ret = krb5_c_make_checksum(context, cksumtype, &tgt->keyblock,
+ KRB5_KEYUSAGE_TGS_REQ_AUTH_CKSUM, checksum_data,
+ &checksum);
+ if (ret)
+ goto cleanup;
+
+ /* Construct, encode, and encrypt an authenticator. */
+ authent.subkey = subkey;
+ authent.seq_number = 0;
+ authent.checksum = &checksum;
+ authent.client = tgt->client;
+ authent.authorization_data = tgt->authdata;
+ ret = krb5_us_timeofday(context, &authent.ctime, &authent.cusec);
+ if (ret)
+ goto cleanup;
+ ret = encode_krb5_authenticator(&authent, &authent_asn1);
+ if (ret)
+ goto cleanup;
+ ret = krb5_encrypt_helper(context, &tgt->keyblock,
+ KRB5_KEYUSAGE_TGS_REQ_AUTH, authent_asn1,
+ &authent_enc);
+ if (ret)
+ goto cleanup;
+
+ ret = decode_krb5_ticket(&tgt->ticket, &ticket);
+ if (ret)
+ goto cleanup;
+
+ /* Encode the AP-REQ. */
+ ap_req.authenticator = authent_enc;
+ ap_req.ticket = ticket;
+ ret = encode_krb5_ap_req(&ap_req, ap_req_asn1_out);
+
+cleanup:
+ free(checksum.contents);
+ krb5_free_ticket(context, ticket);
+ krb5_free_data_contents(context, &authent_enc.ciphertext);
+ if (authent_asn1 != NULL)
+ zapfree(authent_asn1->data, authent_asn1->length);
+ free(authent_asn1);
+ return ret;
+}
+
+/*
+ * Construct a TGS request and return its ASN.1 encoding as well as the
+ * timestamp, nonce, and subkey used. The pacb_fn callback allows the caller
+ * to amend the request padata after the nonce and subkey are determined.
+ */
+krb5_error_code
+k5_make_tgs_req(krb5_context context,
+ struct krb5int_fast_request_state *fast_state,
+ krb5_creds *tgt, krb5_flags kdcoptions,
+ krb5_address *const *addrs, krb5_pa_data **in_padata,
+ krb5_creds *desired, k5_pacb_fn pacb_fn, void *pacb_data,
+ krb5_data *req_asn1_out, krb5_timestamp *timestamp_out,
+ krb5_int32 *nonce_out, krb5_keyblock **subkey_out)
+{
+ krb5_error_code ret;
+ krb5_kdc_req req;
+ krb5_data *authdata_asn1 = NULL, *req_body_asn1 = NULL;
+ krb5_data *ap_req_asn1 = NULL, *tgs_req_asn1 = NULL;
+ krb5_ticket *sec_ticket = NULL;
+ krb5_ticket *sec_ticket_arr[2];
+ krb5_timestamp time_now;
+ krb5_pa_data **padata = NULL, *pa;
+ krb5_keyblock *subkey = NULL;
+ krb5_enc_data authdata_enc;
+ krb5_enctype enctypes[2], *defenctypes = NULL;
+ size_t count, i;
+
+ *req_asn1_out = empty_data();
+ *timestamp_out = 0;
+ *nonce_out = 0;
+ *subkey_out = NULL;
+ memset(&req, 0, sizeof(req));
+ memset(&authdata_enc, 0, sizeof(authdata_enc));
+
+ /* tgt's client principal must match the desired client principal. */
+ if (!krb5_principal_compare(context, tgt->client, desired->client))
+ return KRB5_PRINC_NOMATCH;
+
+ /* tgt must be an actual credential, not a template. */
+ if (!tgt->ticket.length)
+ return KRB5_NO_TKT_SUPPLIED;
+
+ req.kdc_options = kdcoptions;
+ req.server = desired->server;
+ req.from = desired->times.starttime;
+ req.till = desired->times.endtime ? desired->times.endtime :
+ tgt->times.endtime;
+ req.rtime = desired->times.renew_till;
+ ret = krb5_timeofday(context, &time_now);
+ if (ret)
+ return ret;
+ *nonce_out = req.nonce = (krb5_int32)time_now;
+ *timestamp_out = time_now;
+
+ req.addresses = (krb5_address **)addrs;
+
+ /* Generate subkey. */
+ ret = krb5_generate_subkey(context, &tgt->keyblock, &subkey);
+ if (ret)
+ return ret;
+ TRACE_SEND_TGS_SUBKEY(context, subkey);
+
+ ret = krb5int_fast_tgs_armor(context, fast_state, subkey, &tgt->keyblock,
+ NULL, NULL);
+ if (ret)
+ goto cleanup;
+
+ if (desired->authdata != NULL) {
+ ret = encode_krb5_authdata(desired->authdata, &authdata_asn1);
+ if (ret)
+ goto cleanup;
+ ret = krb5_encrypt_helper(context, subkey,
+ KRB5_KEYUSAGE_TGS_REQ_AD_SUBKEY,
+ authdata_asn1, &authdata_enc);
+ if (ret)
+ goto cleanup;
+ req.authorization_data = authdata_enc;
+ }
+
+ if (desired->keyblock.enctype != ENCTYPE_NULL) {
+ if (!krb5_c_valid_enctype(desired->keyblock.enctype)) {
+ ret = KRB5_PROG_ETYPE_NOSUPP;
+ goto cleanup;
+ }
+ enctypes[0] = desired->keyblock.enctype;
+ enctypes[1] = ENCTYPE_NULL;
+ req.ktype = enctypes;
+ req.nktypes = 1;
+ } else {
+ /* Get the default TGS enctypes. */
+ ret = krb5_get_tgs_ktypes(context, desired->server, &defenctypes);
+ if (ret)
+ goto cleanup;
+ for (count = 0; defenctypes[count]; count++);
+ req.ktype = defenctypes;
+ req.nktypes = count;
+ }
+ TRACE_SEND_TGS_ETYPES(context, req.ktype);
+
+ if (kdcoptions & (KDC_OPT_ENC_TKT_IN_SKEY | KDC_OPT_CNAME_IN_ADDL_TKT)) {
+ if (desired->second_ticket.length == 0) {
+ ret = KRB5_NO_2ND_TKT;
+ goto cleanup;
+ }
+ ret = decode_krb5_ticket(&desired->second_ticket, &sec_ticket);
+ if (ret)
+ goto cleanup;
+ sec_ticket_arr[0] = sec_ticket;
+ sec_ticket_arr[1] = NULL;
+ req.second_ticket = sec_ticket_arr;
+ }
+
+ /* Encode the request body. */
+ ret = krb5int_fast_prep_req_body(context, fast_state, &req,
+ &req_body_asn1);
+ if (ret)
+ goto cleanup;
+
+ ret = tgs_construct_ap_req(context, req_body_asn1, tgt, subkey,
+ &ap_req_asn1);
+ if (ret)
+ goto cleanup;
+
+ for (count = 0; in_padata != NULL && in_padata[count] != NULL; count++);
+
+ /* Construct a padata array for the request, beginning with the ap-req. */
+ padata = k5calloc(count + 2, sizeof(krb5_pa_data *), &ret);
+ if (padata == NULL)
+ goto cleanup;
+ padata[0] = k5alloc(sizeof(krb5_pa_data), &ret);
+ if (padata[0] == NULL)
+ goto cleanup;
+ padata[0]->pa_type = KRB5_PADATA_AP_REQ;
+ padata[0]->contents = k5memdup(ap_req_asn1->data, ap_req_asn1->length,
+ &ret);
+ if (padata[0] == NULL)
+ goto cleanup;
+ padata[0]->length = ap_req_asn1->length;
+
+ /* Append copies of any other supplied padata. */
+ for (i = 0; in_padata != NULL && in_padata[i] != NULL; i++) {
+ pa = k5alloc(sizeof(krb5_pa_data), &ret);
+ if (pa == NULL)
+ goto cleanup;
+ pa->pa_type = in_padata[i]->pa_type;
+ pa->length = in_padata[i]->length;
+ pa->contents = k5memdup(in_padata[i]->contents, in_padata[i]->length,
+ &ret);
+ if (pa->contents == NULL)
+ goto cleanup;
+ padata[i + 1] = pa;
+ }
+ req.padata = padata;
+
+ if (pacb_fn != NULL) {
+ ret = (*pacb_fn)(context, subkey, &req, pacb_data);
+ if (ret)
+ goto cleanup;
+ }
+
+ /* Encode the TGS-REQ. Discard the krb5_data container. */
+ ret = krb5int_fast_prep_req(context, fast_state, &req, ap_req_asn1,
+ encode_krb5_tgs_req, &tgs_req_asn1);
+ if (ret)
+ goto cleanup;
+ *req_asn1_out = *tgs_req_asn1;
+ free(tgs_req_asn1);
+ tgs_req_asn1 = NULL;
+
+ *subkey_out = subkey;
+ subkey = NULL;
+
+cleanup:
+ krb5_free_data(context, authdata_asn1);
+ krb5_free_data(context, req_body_asn1);
+ krb5_free_data(context, ap_req_asn1);
+ krb5_free_pa_data(context, req.padata);
+ krb5_free_ticket(context, sec_ticket);
+ krb5_free_data_contents(context, &authdata_enc.ciphertext);
+ krb5_free_keyblock(context, subkey);
+ free(defenctypes);
+ return ret;
+}