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