Blame src/tests/adata.c

Packit fd8b60
/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
Packit fd8b60
/* tests/adata.c - Test harness for KDC authorization data */
Packit fd8b60
/*
Packit fd8b60
 * Copyright (C) 2014 by the Massachusetts Institute of Technology.
Packit fd8b60
 * All rights reserved.
Packit fd8b60
 *
Packit fd8b60
 * Redistribution and use in source and binary forms, with or without
Packit fd8b60
 * modification, are permitted provided that the following conditions
Packit fd8b60
 * are met:
Packit fd8b60
 *
Packit fd8b60
 * * Redistributions of source code must retain the above copyright
Packit fd8b60
 *   notice, this list of conditions and the following disclaimer.
Packit fd8b60
 *
Packit fd8b60
 * * Redistributions in binary form must reproduce the above copyright
Packit fd8b60
 *   notice, this list of conditions and the following disclaimer in
Packit fd8b60
 *   the documentation and/or other materials provided with the
Packit fd8b60
 *   distribution.
Packit fd8b60
 *
Packit fd8b60
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
Packit fd8b60
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
Packit fd8b60
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
Packit fd8b60
 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
Packit fd8b60
 * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
Packit fd8b60
 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
Packit fd8b60
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
Packit fd8b60
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
Packit fd8b60
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
Packit fd8b60
 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
Packit fd8b60
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
Packit fd8b60
 * OF THE POSSIBILITY OF SUCH DAMAGE.
Packit fd8b60
 */
Packit fd8b60
Packit fd8b60
/*
Packit fd8b60
 * Usage: ./adata [-c ccname] [-p clientprinc] serviceprinc
Packit fd8b60
 *            [ad-type ad-contents ...]
Packit fd8b60
 *
Packit fd8b60
 * This program acquires credentials for the specified service principal, using
Packit fd8b60
 * the specified or default ccache, possibly including requested authdata.  The
Packit fd8b60
 * resulting ticket is decrypted using the default keytab, and the authdata in
Packit fd8b60
 * the ticket are displayed to stdout.
Packit fd8b60
 *
Packit fd8b60
 * In the requested authdata types, the type may be prefixed with '?' for an
Packit fd8b60
 * AD-IF-RELEVANT container, '!' for an AD-MANDATORY-FOR-KDC container, or '^'
Packit fd8b60
 * for an AD-KDC-ISSUED container checksummed with a random AES256 key.
Packit fd8b60
 * Multiple prefixes may be specified for nested container.
Packit fd8b60
 *
Packit fd8b60
 * In the output, authdata containers will be flattened and displayed with the
Packit fd8b60
 * above prefixes or '+' for an AD-CAMMAC container.  AD-KDC-ISSUED and
Packit fd8b60
 * AD-CAMMAC containers will be verified with the appropriate key.  Nested
Packit fd8b60
 * containers only display the prefix for the innermost container.
Packit fd8b60
 */
Packit fd8b60
Packit fd8b60
#include <k5-int.h>
Packit fd8b60
#include <ctype.h>
Packit fd8b60
Packit fd8b60
static krb5_context ctx;
Packit fd8b60
Packit fd8b60
static void display_authdata_list(krb5_authdata **list, krb5_keyblock *skey,
Packit fd8b60
                                  krb5_keyblock *tktkey, char prefix_byte,
Packit fd8b60
                                  krb5_boolean pac_expected);
Packit fd8b60
Packit fd8b60
static void
Packit fd8b60
check(krb5_error_code code)
Packit fd8b60
{
Packit fd8b60
    const char *errmsg;
Packit fd8b60
Packit fd8b60
    if (code) {
Packit fd8b60
        errmsg = krb5_get_error_message(ctx, code);
Packit fd8b60
        fprintf(stderr, "%s\n", errmsg);
Packit fd8b60
        krb5_free_error_message(ctx, errmsg);
Packit fd8b60
        exit(1);
Packit fd8b60
    }
Packit fd8b60
}
Packit fd8b60
Packit fd8b60
static krb5_authdatatype
Packit fd8b60
get_type_for_prefix(int prefix_byte)
Packit fd8b60
{
Packit fd8b60
    if (prefix_byte == '?')
Packit fd8b60
        return KRB5_AUTHDATA_IF_RELEVANT;
Packit fd8b60
    if (prefix_byte == '!')
Packit fd8b60
        return KRB5_AUTHDATA_MANDATORY_FOR_KDC;
Packit fd8b60
    if (prefix_byte == '^')
Packit fd8b60
        return KRB5_AUTHDATA_KDC_ISSUED;
Packit fd8b60
    if (prefix_byte == '+')
Packit fd8b60
        return KRB5_AUTHDATA_CAMMAC;
Packit fd8b60
    abort();
Packit fd8b60
}
Packit fd8b60
Packit fd8b60
static int
Packit fd8b60
get_prefix_byte(krb5_authdata *ad)
Packit fd8b60
{
Packit fd8b60
    if (ad->ad_type == KRB5_AUTHDATA_IF_RELEVANT)
Packit fd8b60
        return '?';
Packit fd8b60
    if (ad->ad_type == KRB5_AUTHDATA_MANDATORY_FOR_KDC)
Packit fd8b60
        return '!';
Packit fd8b60
    if (ad->ad_type == KRB5_AUTHDATA_KDC_ISSUED)
Packit fd8b60
        return '^';
Packit fd8b60
    if (ad->ad_type == KRB5_AUTHDATA_CAMMAC)
Packit fd8b60
        return '+';
Packit fd8b60
    abort();
Packit fd8b60
}
Packit fd8b60
Packit fd8b60
/* Construct a container of type ad_type for the single authdata element
Packit fd8b60
 * content.  For KDC-ISSUED containers, use a random checksum key. */
Packit fd8b60
static krb5_authdata *
Packit fd8b60
make_container(krb5_authdatatype ad_type, krb5_authdata *content)
Packit fd8b60
{
Packit fd8b60
    krb5_authdata *list[2], **enclist, *ad;
Packit fd8b60
    krb5_keyblock kb;
Packit fd8b60
Packit fd8b60
    list[0] = content;
Packit fd8b60
    list[1] = NULL;
Packit fd8b60
Packit fd8b60
    if (ad_type == KRB5_AUTHDATA_KDC_ISSUED) {
Packit fd8b60
        check(krb5_c_make_random_key(ctx, ENCTYPE_AES256_CTS_HMAC_SHA1_96,
Packit fd8b60
                                     &kb));
Packit fd8b60
        check(krb5_make_authdata_kdc_issued(ctx, &kb, NULL, list, &enclist));
Packit fd8b60
        krb5_free_keyblock_contents(ctx, &kb;;
Packit fd8b60
    } else {
Packit fd8b60
        check(krb5_encode_authdata_container(ctx, ad_type, list, &enclist));
Packit fd8b60
    }
Packit fd8b60
Packit fd8b60
    /* Grab the first element from the encoded list and free the array. */
Packit fd8b60
    ad = enclist[0];
Packit fd8b60
    free(enclist);
Packit fd8b60
    return ad;
Packit fd8b60
}
Packit fd8b60
Packit fd8b60
/* Parse typestr and contents into an authdata element. */
Packit fd8b60
static krb5_authdata *
Packit fd8b60
make_authdata(const char *typestr, const char *contents)
Packit fd8b60
{
Packit fd8b60
    krb5_authdata *inner_ad, *ad;
Packit fd8b60
Packit fd8b60
    if (*typestr == '?' || *typestr == '!' || *typestr == '^') {
Packit fd8b60
        inner_ad = make_authdata(typestr + 1, contents);
Packit fd8b60
        ad = make_container(get_type_for_prefix(*typestr), inner_ad);
Packit fd8b60
        free(inner_ad->contents);
Packit fd8b60
        free(inner_ad);
Packit fd8b60
        return ad;
Packit fd8b60
    }
Packit fd8b60
Packit fd8b60
    ad = malloc(sizeof(*ad));
Packit fd8b60
    assert(ad != NULL);
Packit fd8b60
    ad->magic = KV5M_AUTHDATA;
Packit fd8b60
    ad->ad_type = atoi(typestr);
Packit fd8b60
    ad->length = strlen(contents);
Packit fd8b60
    ad->contents = (unsigned char *)strdup(contents);
Packit fd8b60
    assert(ad->contents != NULL);
Packit fd8b60
    return ad;
Packit fd8b60
}
Packit fd8b60
Packit fd8b60
static krb5_authdata **
Packit fd8b60
get_container_contents(krb5_authdata *ad, krb5_keyblock *skey,
Packit fd8b60
                       krb5_keyblock *tktkey)
Packit fd8b60
{
Packit fd8b60
    krb5_authdata **inner_ad;
Packit fd8b60
Packit fd8b60
    if (ad->ad_type == KRB5_AUTHDATA_KDC_ISSUED)
Packit fd8b60
        check(krb5_verify_authdata_kdc_issued(ctx, skey, ad, NULL, &inner_ad));
Packit fd8b60
    else if (ad->ad_type == KRB5_AUTHDATA_CAMMAC)
Packit fd8b60
        check(k5_unwrap_cammac_svc(ctx, ad, tktkey, &inner_ad));
Packit fd8b60
    else
Packit fd8b60
        check(krb5_decode_authdata_container(ctx, ad->ad_type, ad, &inner_ad));
Packit fd8b60
    return inner_ad;
Packit fd8b60
}
Packit fd8b60
Packit fd8b60
/* Decode and display authentication indicator authdata. */
Packit fd8b60
static void
Packit fd8b60
display_auth_indicator(krb5_authdata *ad)
Packit fd8b60
{
Packit fd8b60
    krb5_data **strs = NULL, **p;
Packit fd8b60
Packit fd8b60
    check(k5_authind_decode(ad, &strs));
Packit fd8b60
    assert(strs != NULL);
Packit fd8b60
Packit fd8b60
    printf("[");
Packit fd8b60
    for (p = strs; *p != NULL; p++) {
Packit fd8b60
        printf("%.*s", (int)(*p)->length, (*p)->data);
Packit fd8b60
        if (*(p + 1) != NULL)
Packit fd8b60
            printf(", ");
Packit fd8b60
    }
Packit fd8b60
    printf("]");
Packit fd8b60
    k5_free_data_ptr_list(strs);
Packit fd8b60
}
Packit fd8b60
Packit fd8b60
/* Display ad as either a hex dump or ASCII text. */
Packit fd8b60
static void
Packit fd8b60
display_binary_or_ascii(krb5_authdata *ad)
Packit fd8b60
{
Packit fd8b60
    krb5_boolean binary = FALSE;
Packit fd8b60
    unsigned char *p;
Packit fd8b60
Packit fd8b60
    for (p = ad->contents; p < ad->contents + ad->length; p++) {
Packit fd8b60
        if (!isascii(*p) || !isprint(*p))
Packit fd8b60
            binary = TRUE;
Packit fd8b60
    }
Packit fd8b60
    if (binary) {
Packit fd8b60
        for (p = ad->contents; p < ad->contents + ad->length; p++)
Packit fd8b60
            printf("%02X", *p);
Packit fd8b60
    } else {
Packit fd8b60
        printf("%.*s", (int)ad->length, ad->contents);
Packit fd8b60
    }
Packit fd8b60
}
Packit fd8b60
Packit fd8b60
/* Display the contents of an authdata element, prefixed by prefix_byte.  skey
Packit fd8b60
 * must be the ticket session key. */
Packit fd8b60
static void
Packit fd8b60
display_authdata(krb5_authdata *ad, krb5_keyblock *skey, krb5_keyblock *tktkey,
Packit fd8b60
                 int prefix_byte, krb5_boolean pac_expected)
Packit fd8b60
{
Packit fd8b60
    krb5_authdata **inner_ad;
Packit fd8b60
Packit fd8b60
    if (ad->ad_type == KRB5_AUTHDATA_IF_RELEVANT ||
Packit fd8b60
        ad->ad_type == KRB5_AUTHDATA_MANDATORY_FOR_KDC ||
Packit fd8b60
        ad->ad_type == KRB5_AUTHDATA_KDC_ISSUED ||
Packit fd8b60
        ad->ad_type == KRB5_AUTHDATA_CAMMAC) {
Packit fd8b60
        if (ad->ad_type != KRB5_AUTHDATA_IF_RELEVANT)
Packit fd8b60
            pac_expected = FALSE;
Packit fd8b60
        /* Decode and display the contents. */
Packit fd8b60
        inner_ad = get_container_contents(ad, skey, tktkey);
Packit fd8b60
        display_authdata_list(inner_ad, skey, tktkey, get_prefix_byte(ad),
Packit fd8b60
                              pac_expected);
Packit fd8b60
        krb5_free_authdata(ctx, inner_ad);
Packit fd8b60
        return;
Packit fd8b60
    }
Packit fd8b60
Packit fd8b60
    assert(!pac_expected || ad->ad_type == KRB5_AUTHDATA_WIN2K_PAC);
Packit fd8b60
Packit fd8b60
    printf("%c", prefix_byte);
Packit fd8b60
    printf("%d: ", (int)ad->ad_type);
Packit fd8b60
Packit fd8b60
    if (ad->ad_type == KRB5_AUTHDATA_AUTH_INDICATOR)
Packit fd8b60
        display_auth_indicator(ad);
Packit fd8b60
    else
Packit fd8b60
        display_binary_or_ascii(ad);
Packit fd8b60
    printf("\n");
Packit fd8b60
}
Packit fd8b60
Packit fd8b60
static void
Packit fd8b60
display_authdata_list(krb5_authdata **list, krb5_keyblock *skey,
Packit fd8b60
                      krb5_keyblock *tktkey, char prefix_byte,
Packit fd8b60
                      krb5_boolean pac_expected)
Packit fd8b60
{
Packit fd8b60
    if (list == NULL)
Packit fd8b60
        return;
Packit fd8b60
    /* Only expect a PAC in the first element, if at all. */
Packit fd8b60
    for (; *list != NULL; list++) {
Packit fd8b60
        display_authdata(*list, skey, tktkey, prefix_byte, pac_expected);
Packit fd8b60
        pac_expected = FALSE;
Packit fd8b60
    }
Packit fd8b60
}
Packit fd8b60
Packit fd8b60
/* If a PAC is present in enc_part2, verify its service signature with key and
Packit fd8b60
 * set *has_pac to true. */
Packit fd8b60
static void
Packit fd8b60
check_pac(krb5_context context, krb5_enc_tkt_part *enc_part2,
Packit fd8b60
          const krb5_keyblock *key, krb5_boolean *has_pac)
Packit fd8b60
{
Packit fd8b60
    krb5_authdata **authdata;
Packit fd8b60
    krb5_pac pac;
Packit fd8b60
Packit fd8b60
    *has_pac = FALSE;
Packit fd8b60
Packit fd8b60
    check(krb5_find_authdata(context, enc_part2->authorization_data, NULL,
Packit fd8b60
                             KRB5_AUTHDATA_WIN2K_PAC, &authdata));
Packit fd8b60
    if (authdata == NULL)
Packit fd8b60
        return;
Packit fd8b60
Packit fd8b60
    assert(authdata[1] == NULL);
Packit fd8b60
    check(krb5_pac_parse(context, authdata[0]->contents, authdata[0]->length,
Packit fd8b60
                         &pac));
Packit fd8b60
    krb5_free_authdata(context, authdata);
Packit fd8b60
Packit fd8b60
    check(krb5_pac_verify(context, pac, enc_part2->times.authtime,
Packit fd8b60
                          enc_part2->client, key, NULL));
Packit fd8b60
    krb5_pac_free(context, pac);
Packit fd8b60
    *has_pac = TRUE;
Packit fd8b60
}
Packit fd8b60
Packit fd8b60
int
Packit fd8b60
main(int argc, char **argv)
Packit fd8b60
{
Packit fd8b60
    const char *ccname = NULL, *clientname = NULL;
Packit fd8b60
    krb5_principal client, server;
Packit fd8b60
    krb5_ccache ccache;
Packit fd8b60
    krb5_keytab keytab;
Packit fd8b60
    krb5_creds in_creds, *creds;
Packit fd8b60
    krb5_ticket *ticket;
Packit fd8b60
    krb5_authdata **req_authdata = NULL, *ad;
Packit fd8b60
    krb5_keytab_entry ktent;
Packit fd8b60
    krb5_boolean with_pac;
Packit fd8b60
    size_t count;
Packit fd8b60
    int c;
Packit fd8b60
Packit fd8b60
    check(krb5_init_context(&ctx));
Packit fd8b60
Packit fd8b60
    while ((c = getopt(argc, argv, "+c:p:")) != -1) {
Packit fd8b60
        switch (c) {
Packit fd8b60
        case 'c':
Packit fd8b60
            ccname = optarg;
Packit fd8b60
            break;
Packit fd8b60
        case 'p':
Packit fd8b60
            clientname = optarg;
Packit fd8b60
            break;
Packit fd8b60
        default:
Packit fd8b60
            abort();
Packit fd8b60
        }
Packit fd8b60
    }
Packit fd8b60
    argv += optind;
Packit fd8b60
    /* Parse arguments. */
Packit fd8b60
    assert(*argv != NULL);
Packit fd8b60
    check(krb5_parse_name(ctx, *argv++, &server));
Packit fd8b60
Packit fd8b60
    count = 0;
Packit fd8b60
    for (; argv[0] != NULL && argv[1] != NULL; argv += 2) {
Packit fd8b60
        ad = make_authdata(argv[0], argv[1]);
Packit fd8b60
        req_authdata = realloc(req_authdata,
Packit fd8b60
                               (count + 2) * sizeof(*req_authdata));
Packit fd8b60
        assert(req_authdata != NULL);
Packit fd8b60
        req_authdata[count++] = ad;
Packit fd8b60
        req_authdata[count] = NULL;
Packit fd8b60
    }
Packit fd8b60
    assert(*argv == NULL);
Packit fd8b60
Packit fd8b60
    if (ccname != NULL)
Packit fd8b60
        check(krb5_cc_resolve(ctx, ccname, &ccache));
Packit fd8b60
    else
Packit fd8b60
        check(krb5_cc_default(ctx, &ccache));
Packit fd8b60
Packit fd8b60
    if (clientname != NULL)
Packit fd8b60
        check(krb5_parse_name(ctx, clientname, &client));
Packit fd8b60
    else
Packit fd8b60
        check(krb5_cc_get_principal(ctx, ccache, &client));
Packit fd8b60
Packit fd8b60
    memset(&in_creds, 0, sizeof(in_creds));
Packit fd8b60
    in_creds.client = client;
Packit fd8b60
    in_creds.server = server;
Packit fd8b60
    in_creds.authdata = req_authdata;
Packit fd8b60
Packit fd8b60
    check(krb5_get_credentials(ctx, KRB5_GC_NO_STORE, ccache, &in_creds,
Packit fd8b60
                               &creds));
Packit fd8b60
Packit fd8b60
    assert(in_creds.authdata == NULL || creds->authdata != NULL);
Packit fd8b60
Packit fd8b60
    check(krb5_decode_ticket(&creds->ticket, &ticket));
Packit fd8b60
    check(krb5_kt_default(ctx, &keytab));
Packit fd8b60
    check(krb5_kt_get_entry(ctx, keytab, ticket->server, ticket->enc_part.kvno,
Packit fd8b60
                            ticket->enc_part.enctype, &ktent));
Packit fd8b60
    check(krb5_decrypt_tkt_part(ctx, &ktent.key, ticket));
Packit fd8b60
Packit fd8b60
    check_pac(ctx, ticket->enc_part2, &ktent.key, &with_pac);
Packit fd8b60
    display_authdata_list(ticket->enc_part2->authorization_data,
Packit fd8b60
                          ticket->enc_part2->session, &ktent.key, ' ',
Packit fd8b60
                          with_pac);
Packit fd8b60
Packit fd8b60
    while (count > 0) {
Packit fd8b60
        free(req_authdata[--count]->contents);
Packit fd8b60
        free(req_authdata[count]);
Packit fd8b60
    }
Packit fd8b60
    free(req_authdata);
Packit fd8b60
    krb5_free_keytab_entry_contents(ctx, &ktent);
Packit fd8b60
    krb5_free_creds(ctx, creds);
Packit fd8b60
    krb5_free_ticket(ctx, ticket);
Packit fd8b60
    krb5_free_principal(ctx, client);
Packit fd8b60
    krb5_free_principal(ctx, server);
Packit fd8b60
    krb5_cc_close(ctx, ccache);
Packit fd8b60
    krb5_kt_close(ctx, keytab);
Packit fd8b60
    krb5_free_context(ctx);
Packit fd8b60
    return 0;
Packit fd8b60
}