Пример #1
0
static krb5_error_code
k5_pac_verify_kdc_checksum(krb5_context context,
                           const krb5_pac pac,
                           const krb5_keyblock *privsvr)
{
    krb5_error_code ret;
    krb5_data server_checksum, privsvr_checksum;
    krb5_checksum checksum;
    krb5_boolean valid;
    krb5_octet *p;

    ret = k5_pac_locate_buffer(context, pac,
                               PAC_PRIVSVR_CHECKSUM, &privsvr_checksum);
    if (ret != 0)
        return ret;

    if (privsvr_checksum.length < PAC_SIGNATURE_DATA_LENGTH)
        return KRB5_BAD_MSIZE;

    ret = k5_pac_locate_buffer(context, pac,
                               PAC_SERVER_CHECKSUM, &server_checksum);
    if (ret != 0)
        return ret;

    if (server_checksum.length < PAC_SIGNATURE_DATA_LENGTH)
        return KRB5_BAD_MSIZE;

    p = (krb5_octet *)privsvr_checksum.data;
    checksum.checksum_type = load_32_le(p);
    checksum.length = privsvr_checksum.length - PAC_SIGNATURE_DATA_LENGTH;
    checksum.contents = p + PAC_SIGNATURE_DATA_LENGTH;
    if (!krb5_c_is_keyed_cksum(checksum.checksum_type))
        return KRB5KRB_AP_ERR_INAPP_CKSUM;

    server_checksum.data += PAC_SIGNATURE_DATA_LENGTH;
    server_checksum.length -= PAC_SIGNATURE_DATA_LENGTH;

    ret = krb5_c_verify_checksum(context, privsvr,
                                 KRB5_KEYUSAGE_APP_DATA_CKSUM,
                                 &server_checksum, &checksum, &valid);
    if (ret != 0)
        return ret;

    if (valid == FALSE)
        ret = KRB5KRB_AP_ERR_BAD_INTEGRITY;

    return ret;
}
Пример #2
0
static krb5_error_code
k5_insert_client_info(krb5_context context,
                      krb5_pac pac,
                      krb5_timestamp authtime,
                      krb5_const_principal principal)
{
    krb5_error_code ret;
    krb5_data client_info;
    char *princ_name_utf8 = NULL;
    unsigned char *princ_name_ucs2 = NULL, *p;
    size_t princ_name_ucs2_len = 0;
    krb5_ui_8 nt_authtime;

    /* If we already have a CLIENT_INFO buffer, then just validate it */
    if (k5_pac_locate_buffer(context, pac, KRB5_PAC_CLIENT_INFO,
                             &client_info) == 0) {
        return k5_pac_validate_client(context, pac, authtime, principal);
    }

    ret = krb5_unparse_name_flags(context, principal,
                                  KRB5_PRINCIPAL_UNPARSE_NO_REALM,
                                  &princ_name_utf8);
    if (ret != 0)
        goto cleanup;

    ret = krb5int_utf8s_to_ucs2les(princ_name_utf8,
                                   &princ_name_ucs2,
                                   &princ_name_ucs2_len);
    if (ret != 0)
        goto cleanup;

    client_info.length = PAC_CLIENT_INFO_LENGTH + princ_name_ucs2_len;
    client_info.data = NULL;

    ret = k5_pac_add_buffer(context, pac, KRB5_PAC_CLIENT_INFO,
                            &client_info, TRUE, &client_info);
    if (ret != 0)
        goto cleanup;

    p = (unsigned char *)client_info.data;

    /* copy in authtime converted to a 64-bit NT time */
    k5_seconds_since_1970_to_time(authtime, &nt_authtime);
    store_64_le(nt_authtime, p);
    p += 8;

    /* copy in number of UCS-2 characters in principal name */
    store_16_le(princ_name_ucs2_len, p);
    p += 2;

    /* copy in principal name */
    memcpy(p, princ_name_ucs2, princ_name_ucs2_len);

cleanup:
    if (princ_name_ucs2 != NULL)
        free(princ_name_ucs2);
    krb5_free_unparsed_name(context, princ_name_utf8);

    return ret;
}
Пример #3
0
krb5_error_code
k5_pac_validate_client(krb5_context context,
                       const krb5_pac pac,
                       krb5_timestamp authtime,
                       krb5_const_principal principal)
{
    krb5_error_code ret;
    krb5_data client_info;
    char *pac_princname;
    unsigned char *p;
    krb5_timestamp pac_authtime;
    krb5_ui_2 pac_princname_length;
    krb5_int64 pac_nt_authtime;
    krb5_principal pac_principal;

    ret = k5_pac_locate_buffer(context, pac, PAC_CLIENT_INFO, &client_info);
    if (ret != 0)
        return ret;

    if (client_info.length < PAC_CLIENT_INFO_LENGTH)
        return ERANGE;

    p = (unsigned char *)client_info.data;
    pac_nt_authtime = load_64_le(p);
    p += 8;
    pac_princname_length = load_16_le(p);
    p += 2;

    ret = k5_time_to_seconds_since_1970(pac_nt_authtime, &pac_authtime);
    if (ret != 0)
        return ret;

    if (client_info.length < PAC_CLIENT_INFO_LENGTH + pac_princname_length ||
        pac_princname_length % 2)
        return ERANGE;

    ret = krb5int_ucs2lecs_to_utf8s(p, (size_t)pac_princname_length / 2,
                                    &pac_princname, NULL);
    if (ret != 0)
        return ret;

    ret = krb5_parse_name_flags(context, pac_princname, 0, &pac_principal);
    if (ret != 0) {
        free(pac_princname);
        return ret;
    }

    free(pac_princname);

    if (pac_authtime != authtime ||
        !krb5_principal_compare_flags(context,
                                      pac_principal,
                                      principal,
                                      KRB5_PRINCIPAL_COMPARE_IGNORE_REALM))
        ret = KRB5KRB_AP_WRONG_PRINC;

    krb5_free_principal(context, pac_principal);

    return ret;
}
Пример #4
0
static krb5_error_code
mspac_get_attribute(krb5_context kcontext,
                    krb5_authdata_context context,
                    void *plugin_context,
                    void *request_context,
                    const krb5_data *attribute,
                    krb5_boolean *authenticated,
                    krb5_boolean *complete,
                    krb5_data *value,
                    krb5_data *display_value,
                    int *more)
{
    struct mspac_context *pacctx = (struct mspac_context *)request_context;
    krb5_error_code code;
    krb5_ui_4 type;

    if (display_value != NULL) {
        display_value->data = NULL;
        display_value->length = 0;
    }

    if (*more != -1 || pacctx->pac == NULL)
        return ENOENT;

    /* If it didn't verify, pretend it didn't exist. */
    if (!pacctx->pac->verified) {
        TRACE_MSPAC_DISCARD_UNVERF(kcontext);
        return ENOENT;
    }

    code = mspac_attr2type(attribute, &type);
    if (code != 0)
        return code;

    /* -1 is a magic type that refers to the entire PAC */
    if (type == (krb5_ui_4)-1) {
        if (value != NULL)
            code = krb5int_copy_data_contents(kcontext,
                                              &pacctx->pac->data,
                                              value);
        else
            code = 0;
    } else {
        if (value != NULL)
            code = krb5_pac_get_buffer(kcontext, pacctx->pac, type, value);
        else
            code = k5_pac_locate_buffer(kcontext, pacctx->pac, type, NULL);
    }
    if (code == 0) {
        *authenticated = pacctx->pac->verified;
        *complete = TRUE;
    }

    *more = 0;

    return code;
}
Пример #5
0
static krb5_error_code
k5_insert_checksum(krb5_context context,
                   krb5_pac pac,
                   krb5_ui_4 type,
                   const krb5_keyblock *key,
                   krb5_cksumtype *cksumtype)
{
    krb5_error_code ret;
    size_t len;
    krb5_data cksumdata;

    ret = krb5int_c_mandatory_cksumtype(context, key->enctype, cksumtype);
    if (ret != 0)
        return ret;

    ret = krb5_c_checksum_length(context, *cksumtype, &len);
    if (ret != 0)
        return ret;

    ret = k5_pac_locate_buffer(context, pac, type, &cksumdata);
    if (ret == 0) {
        /*
         * If we're resigning PAC, make sure we can fit checksum
         * into existing buffer
         */
        if (cksumdata.length != PAC_SIGNATURE_DATA_LENGTH + len)
            return ERANGE;

        memset(cksumdata.data, 0, cksumdata.length);
    } else {
        /* Add a zero filled buffer */
        cksumdata.length = PAC_SIGNATURE_DATA_LENGTH + len;
        cksumdata.data = NULL;

        ret = k5_pac_add_buffer(context, pac,
                                type, &cksumdata,
                                TRUE, &cksumdata);
        if (ret != 0)
            return ret;
    }

    /* Encode checksum type into buffer */
    store_32_le((krb5_ui_4)*cksumtype, cksumdata.data);

    return 0;
}
Пример #6
0
/*
 * Find a buffer and copy data into output
 */
krb5_error_code KRB5_CALLCONV
krb5_pac_get_buffer(krb5_context context,
                    krb5_pac pac,
                    krb5_ui_4 type,
                    krb5_data *data)
{
    krb5_data d;
    krb5_error_code ret;

    ret = k5_pac_locate_buffer(context, pac, type, &d);
    if (ret != 0)
        return ret;

    data->data = malloc(d.length);
    if (data->data == NULL)
        return ENOMEM;

    data->length = d.length;
    memcpy(data->data, d.data, d.length);

    return 0;
}
Пример #7
0
krb5_error_code KRB5_CALLCONV
krb5_pac_sign(krb5_context context, krb5_pac pac, krb5_timestamp authtime,
              krb5_const_principal principal, const krb5_keyblock *server_key,
              const krb5_keyblock *privsvr_key, krb5_data *data)
{
    krb5_error_code ret;
    krb5_data server_cksum, privsvr_cksum;
    krb5_cksumtype server_cksumtype, privsvr_cksumtype;
    krb5_crypto_iov iov[2];

    data->length = 0;
    data->data = NULL;

    if (principal != NULL) {
        ret = k5_insert_client_info(context, pac, authtime, principal);
        if (ret != 0)
            return ret;
    }

    /* Create zeroed buffers for both checksums */
    ret = k5_insert_checksum(context, pac, KRB5_PAC_SERVER_CHECKSUM,
                             server_key, &server_cksumtype);
    if (ret != 0)
        return ret;

    ret = k5_insert_checksum(context, pac, KRB5_PAC_PRIVSVR_CHECKSUM,
                             privsvr_key, &privsvr_cksumtype);
    if (ret != 0)
        return ret;

    /* Now, encode the PAC header so that the checksums will include it */
    ret = k5_pac_encode_header(context, pac);
    if (ret != 0)
        return ret;

    /* Generate the server checksum over the entire PAC */
    ret = k5_pac_locate_buffer(context, pac, KRB5_PAC_SERVER_CHECKSUM,
                               &server_cksum);
    if (ret != 0)
        return ret;

    assert(server_cksum.length > PAC_SIGNATURE_DATA_LENGTH);

    iov[0].flags = KRB5_CRYPTO_TYPE_DATA;
    iov[0].data = pac->data;

    iov[1].flags = KRB5_CRYPTO_TYPE_CHECKSUM;
    iov[1].data.data = server_cksum.data + PAC_SIGNATURE_DATA_LENGTH;
    iov[1].data.length = server_cksum.length - PAC_SIGNATURE_DATA_LENGTH;

    ret = krb5_c_make_checksum_iov(context, server_cksumtype,
                                   server_key, KRB5_KEYUSAGE_APP_DATA_CKSUM,
                                   iov, sizeof(iov)/sizeof(iov[0]));
    if (ret != 0)
        return ret;

    /* Generate the privsvr checksum over the server checksum buffer */
    ret = k5_pac_locate_buffer(context, pac, KRB5_PAC_PRIVSVR_CHECKSUM,
                               &privsvr_cksum);
    if (ret != 0)
        return ret;

    assert(privsvr_cksum.length > PAC_SIGNATURE_DATA_LENGTH);

    iov[0].flags = KRB5_CRYPTO_TYPE_DATA;
    iov[0].data.data = server_cksum.data + PAC_SIGNATURE_DATA_LENGTH;
    iov[0].data.length = server_cksum.length - PAC_SIGNATURE_DATA_LENGTH;

    iov[1].flags = KRB5_CRYPTO_TYPE_CHECKSUM;
    iov[1].data.data = privsvr_cksum.data + PAC_SIGNATURE_DATA_LENGTH;
    iov[1].data.length = privsvr_cksum.length - PAC_SIGNATURE_DATA_LENGTH;

    ret = krb5_c_make_checksum_iov(context, privsvr_cksumtype,
                                   privsvr_key, KRB5_KEYUSAGE_APP_DATA_CKSUM,
                                   iov, sizeof(iov)/sizeof(iov[0]));
    if (ret != 0)
        return ret;

    data->data = k5memdup(pac->data.data, pac->data.length, &ret);
    if (data->data == NULL)
        return ret;
    data->length = pac->data.length;

    memset(pac->data.data, 0,
           PACTYPE_LENGTH + (pac->pac->cBuffers * PAC_INFO_BUFFER_LENGTH));

    return 0;
}
Пример #8
0
static krb5_error_code
k5_pac_verify_server_checksum(krb5_context context,
                              const krb5_pac pac,
                              const krb5_keyblock *server)
{
    krb5_error_code ret;
    krb5_data pac_data; /* PAC with zeroed checksums */
    krb5_checksum checksum;
    krb5_data checksum_data;
    krb5_boolean valid;
    krb5_octet *p;

    ret = k5_pac_locate_buffer(context, pac,
                               PAC_SERVER_CHECKSUM, &checksum_data);
    if (ret != 0)
        return ret;

    if (checksum_data.length < PAC_SIGNATURE_DATA_LENGTH)
        return KRB5_BAD_MSIZE;

    p = (krb5_octet *)checksum_data.data;
    checksum.checksum_type = load_32_le(p);
    checksum.length = checksum_data.length - PAC_SIGNATURE_DATA_LENGTH;
    checksum.contents = p + PAC_SIGNATURE_DATA_LENGTH;
    if (!krb5_c_is_keyed_cksum(checksum.checksum_type))
        return KRB5KRB_AP_ERR_INAPP_CKSUM;

    pac_data.length = pac->data.length;
    pac_data.data = malloc(pac->data.length);
    if (pac_data.data == NULL)
        return ENOMEM;

    memcpy(pac_data.data, pac->data.data, pac->data.length);

    /* Zero out both checksum buffers */
    ret = k5_pac_zero_signature(context, pac,
                                PAC_SERVER_CHECKSUM, &pac_data);
    if (ret != 0) {
        free(pac_data.data);
        return ret;
    }

    ret = k5_pac_zero_signature(context, pac,
                                PAC_PRIVSVR_CHECKSUM, &pac_data);
    if (ret != 0) {
        free(pac_data.data);
        return ret;
    }

    ret = krb5_c_verify_checksum(context, server,
                                 KRB5_KEYUSAGE_APP_DATA_CKSUM,
                                 &pac_data, &checksum, &valid);

    free(pac_data.data);

    if (ret != 0) {
        return ret;
    }

    if (valid == FALSE)
        ret = KRB5KRB_AP_ERR_BAD_INTEGRITY;

    return ret;
}
Пример #9
0
/*
 * Add a buffer to the provided PAC and update header.
 */
krb5_error_code
k5_pac_add_buffer(krb5_context context,
                  krb5_pac pac,
                  krb5_ui_4 type,
                  const krb5_data *data,
                  krb5_boolean zerofill,
                  krb5_data *out_data)
{
    PACTYPE *header;
    size_t header_len, i, pad = 0;
    char *pac_data;

    assert((data->data == NULL) == zerofill);

    /* Check there isn't already a buffer of this type */
    if (k5_pac_locate_buffer(context, pac, type, NULL) == 0) {
        return EEXIST;
    }

    header = (PACTYPE *)realloc(pac->pac,
                                sizeof(PACTYPE) +
                                (pac->pac->cBuffers * sizeof(PAC_INFO_BUFFER)));
    if (header == NULL) {
        return ENOMEM;
    }
    pac->pac = header;

    header_len = PACTYPE_LENGTH + (pac->pac->cBuffers * PAC_INFO_BUFFER_LENGTH);

    if (data->length % PAC_ALIGNMENT)
        pad = PAC_ALIGNMENT - (data->length % PAC_ALIGNMENT);

    pac_data = realloc(pac->data.data,
                       pac->data.length + PAC_INFO_BUFFER_LENGTH + data->length + pad);
    if (pac_data == NULL) {
        return ENOMEM;
    }
    pac->data.data = pac_data;

    /* Update offsets of existing buffers */
    for (i = 0; i < pac->pac->cBuffers; i++)
        pac->pac->Buffers[i].Offset += PAC_INFO_BUFFER_LENGTH;

    /* Make room for new PAC_INFO_BUFFER */
    memmove(pac->data.data + header_len + PAC_INFO_BUFFER_LENGTH,
            pac->data.data + header_len,
            pac->data.length - header_len);
    memset(pac->data.data + header_len, 0, PAC_INFO_BUFFER_LENGTH);

    /* Initialise new PAC_INFO_BUFFER */
    pac->pac->Buffers[i].ulType = type;
    pac->pac->Buffers[i].cbBufferSize = data->length;
    pac->pac->Buffers[i].Offset = pac->data.length + PAC_INFO_BUFFER_LENGTH;
    assert((pac->pac->Buffers[i].Offset % PAC_ALIGNMENT) == 0);

    /* Copy in new PAC data and zero padding bytes */
    if (zerofill)
        memset(pac->data.data + pac->pac->Buffers[i].Offset, 0, data->length);
    else
        memcpy(pac->data.data + pac->pac->Buffers[i].Offset, data->data, data->length);

    memset(pac->data.data + pac->pac->Buffers[i].Offset + data->length, 0, pad);

    pac->pac->cBuffers++;
    pac->data.length += PAC_INFO_BUFFER_LENGTH + data->length + pad;

    if (out_data != NULL) {
        out_data->data = pac->data.data + pac->pac->Buffers[i].Offset;
        out_data->length = data->length;
    }

    pac->verified = FALSE;

    return 0;
}