Exemplo n.º 1
0
void
DecryptionManager::PacketHandler::decryptApplicationMessage(bool fromClient) {
  // Split and coalesce the cipher data into a continuous buffer
  unique_ptr<IOBuf> msg = cipher_->split(cipher_->chainLength());
  msg->coalesce();

  // Start decryption. This is mostly copied from k5sealv3.c.
  krb5_error_code code;
  unsigned char *ptr;
  unsigned int bodysize;

  ptr = (unsigned char *)msg->data() + 4;
  code = gssint_g_verify_token_header(gss_mech_krb5, // always use krb5
                                      &bodysize,
                                      &ptr,
                                      -1,
                                      msg->length() - 4,
                                      0);

  if (code) {
    onError("Failed to verify application message header");
    return;
  }

  size_t ec = load_16_be(ptr+4);
  size_t rrc = load_16_be(ptr+6);
  if (!gss_krb5int_rotate_left(ptr+16, bodysize-16, rrc)) {
    throw std::runtime_error("Cannot allocate memory");
  }

  krb5_enc_data cipher;
  krb5_data plain;
  int keyUsage;
  cipher.enctype = subkey_->enctype;
  cipher.ciphertext.length = bodysize - 16;
  cipher.ciphertext.data = (char*)ptr + 16;
  plain.length = bodysize - 16;
  plain.data = (char*)malloc(plain.length);
  if (!plain.data) {
    throw std::runtime_error("Cannot allocate memory");
  }

  SCOPE_EXIT {
    free(plain.data);
  };

  keyUsage = (fromClient ? KG_USAGE_INITIATOR_SEAL : KG_USAGE_ACCEPTOR_SEAL);
  code = krb5_c_decrypt(ctx_->get(), subkey_, keyUsage, 0, &cipher, &plain);
  if (code) {
    onError("Cannot decrypt application message");
    return;
  }

  decryptedMessages_.push_back(IOBuf::copyBuffer(plain.data,
                                                 plain.length - ec - 16));
}
Exemplo n.º 2
0
/* Load two bytes from the cache file and return their value as a 16-bit
 * unsigned integer according to the file format. */
static krb5_error_code
read16(krb5_context context, krb5_ccache id, uint16_t *out)
{
    krb5_error_code ret;
    char bytes[2];

    k5_cc_mutex_assert_locked(context, &((fcc_data *)id->data)->lock);

    ret = read_bytes(context, id, bytes, 2);
    if (ret)
        return ret;
    *out = (version(id) < 3) ? load_16_n(bytes) : load_16_be(bytes);
    return 0;
}
Exemplo n.º 3
0
ssize_t
krad_packet_bytes_needed(const krb5_data *buffer)
{
    size_t len;

    if (buffer->length < OFFSET_AUTH)
        return OFFSET_AUTH - buffer->length;

    len = load_16_be(offset(buffer, OFFSET_LENGTH));
    if (len > KRAD_PACKET_SIZE_MAX)
        return -1;

    return (buffer->length > len) ? 0 : len - buffer->length;
}
Exemplo n.º 4
0
Arquivo: t_unal.c Projeto: aacero/krb5
int main ()
{
    /* Test some low-level assumptions the Kerberos code depends
       on.  */

    union {
        uint64_t n64;
        uint32_t n32;
        uint16_t n16;
        unsigned char b[9];
    } u;
    static unsigned char buf[9] = { 0, 1, 2, 3, 4, 5, 6, 7, 8 };

    assert(load_64_be(buf+1) == 0x0102030405060708LL);
    assert(load_64_le(buf+1) == 0x0807060504030201LL);
    assert(load_32_le(buf+2) == 0x05040302);
    assert(load_32_be(buf+2) == 0x02030405);
    assert(load_16_be(buf+3) == 0x0304);
    assert(load_16_le(buf+3) == 0x0403);
    u.b[0] = 0;
    assert((store_64_be(0x0102030405060708LL, u.b+1), !memcmp(buf, u.b, 9)));
    u.b[1] = 9;
    assert((store_64_le(0x0807060504030201LL, u.b+1), !memcmp(buf, u.b, 9)));
    u.b[2] = 10;
    assert((store_32_be(0x02030405, u.b+2), !memcmp(buf, u.b, 9)));
    u.b[3] = 11;
    assert((store_32_le(0x05040302, u.b+2), !memcmp(buf, u.b, 9)));
    u.b[4] = 12;
    assert((store_16_be(0x0304, u.b+3), !memcmp(buf, u.b, 9)));
    u.b[4] = 13;
    assert((store_16_le(0x0403, u.b+3), !memcmp(buf, u.b, 9)));
    /* Verify that load_*_n properly does native format.  Assume
       the unaligned thing is okay.  */
    u.n64 = 0x090a0b0c0d0e0f00LL;
    assert(load_64_n((unsigned char *) &u.n64) == 0x090a0b0c0d0e0f00LL);
    u.n32 = 0x06070809;
    assert(load_32_n((unsigned char *) &u.n32) == 0x06070809);
    u.n16 = 0x0a0b;
    assert(load_16_n((unsigned char *) &u.n16) == 0x0a0b);

    return 0;
}
Exemplo n.º 5
0
/* Decode a packet. */
static krb5_error_code
decode_packet(krb5_context ctx, const char *secret, const krb5_data *buffer,
              krad_packet **pkt)
{
    krb5_error_code retval;
    krad_packet *tmp;
    krb5_ui_2 len;

    tmp = packet_new();
    if (tmp == NULL) {
        retval = ENOMEM;
        goto error;
    }

    /* Ensure a proper message length. */
    retval = (buffer->length < OFFSET_ATTR) ? EMSGSIZE : 0;
    if (retval != 0)
        goto error;
    len = load_16_be(offset(buffer, OFFSET_LENGTH));
    retval = (len < OFFSET_ATTR) ? EBADMSG : 0;
    if (retval != 0)
        goto error;
    retval = (len > buffer->length || len > tmp->pkt.length) ? EBADMSG : 0;
    if (retval != 0)
        goto error;

    /* Copy over the buffer. */
    tmp->pkt.length = len;
    memcpy(tmp->pkt.data, buffer->data, len);

    /* Parse the packet to ensure it is well-formed. */
    retval = packet_set_attrset(ctx, secret, tmp);
    if (retval != 0)
        goto error;

    *pkt = tmp;
    return 0;

error:
    krad_packet_free(tmp);
    return retval;
}
Exemplo n.º 6
0
OM_uint32
gss_krb5int_unseal_token_v3(krb5_context *contextptr,
			    OM_uint32 *minor_status,
			    krb5_gss_ctx_id_rec *ctx,
			    unsigned char *ptr, int bodysize,
			    gss_buffer_t message_buffer,
			    int *conf_state, int *qop_state, int toktype)
{
    krb5_context context = *contextptr;
    krb5_data plain;
    gssint_uint64 seqnum;
    size_t ec, rrc;
    int key_usage;
    unsigned char acceptor_flag;
    krb5_checksum sum;
    krb5_error_code err;
    krb5_boolean valid;
    krb5_keyblock *key;

    ASSERT(toktype != KG_TOK_SEAL_MSG || ctx->enc != 0);
    ASSERT(ctx->big_endian == 0);
    ASSERT(ctx->proto == 1);

    if (qop_state)
	*qop_state = GSS_C_QOP_DEFAULT;

    acceptor_flag = ctx->initiate ? FLAG_SENDER_IS_ACCEPTOR : 0;
    key_usage = (toktype == KG_TOK_WRAP_MSG
		 ? (!ctx->initiate
		    ? KG_USAGE_INITIATOR_SEAL
		    : KG_USAGE_ACCEPTOR_SEAL)
		 : (!ctx->initiate
		    ? KG_USAGE_INITIATOR_SIGN
		    : KG_USAGE_ACCEPTOR_SIGN));

    /* Oops.  I wrote this code assuming ptr would be at the start of
       the token header.  */
    ptr -= 2;
    bodysize += 2;

    if (bodysize < 16) {
    defective:
	*minor_status = 0;
	return GSS_S_DEFECTIVE_TOKEN;
    }
    if ((ptr[2] & FLAG_SENDER_IS_ACCEPTOR) != acceptor_flag) {
	*minor_status = (OM_uint32)G_BAD_DIRECTION;
	return GSS_S_BAD_SIG;
    }

    /* Two things to note here.

       First, we can't really enforce the use of the acceptor's subkey,
       if we're the acceptor; the initiator may have sent messages
       before getting the subkey.  We could probably enforce it if
       we're the initiator.

       Second, if someone tweaks the code to not set the flag telling
       the krb5 library to generate a new subkey in the AP-REP
       message, the MIT library may include a subkey anyways --
       namely, a copy of the AP-REQ subkey, if it was provided.  So
       the initiator may think we wanted a subkey, and set the flag,
       even though we weren't trying to set the subkey.  The "other"
       key, the one not asserted by the acceptor, will have the same
       value in that case, though, so we can just ignore the flag.  */
    if (ctx->have_acceptor_subkey && (ptr[2] & FLAG_ACCEPTOR_SUBKEY)) {
	key = ctx->acceptor_subkey;
    } else {
	key = ctx->enc;
    }

#ifdef _KERNEL
    context->kef_cipher_mt = get_cipher_mech_type(context, key);
    context->kef_hash_mt = get_hash_mech_type(context, key);

    if ((err = init_key_kef(context->kef_cipher_mt, key))) {
	return (GSS_S_FAILURE);
    }
#endif /* _KERNEL */

    if (toktype == KG_TOK_WRAP_MSG) {
	if (load_16_be(ptr) != 0x0504)
	    goto defective;
	if (ptr[3] != 0xff)
	    goto defective;
	ec = load_16_be(ptr+4);
	rrc = load_16_be(ptr+6);
	seqnum = load_64_be(ptr+8);
	if (!rotate_left(ptr+16, bodysize-16, rrc)) {
	no_mem:
	    *minor_status = ENOMEM;
	    return GSS_S_FAILURE;
	}
	if (ptr[2] & FLAG_WRAP_CONFIDENTIAL) {
	    /* confidentiality */
	    krb5_enc_data cipher;
	    unsigned char *althdr;
            size_t plainlen;

	    if (conf_state)
		*conf_state = 1;
	    /* Do we have no decrypt_size function?

	       For all current cryptosystems, the ciphertext size will
	       be larger than the plaintext size.  */
	    cipher.enctype = key->enctype;
	    cipher.ciphertext.length = bodysize - 16;
	    cipher.ciphertext.data = (char *)ptr + 16;
	    plain.length = plainlen = bodysize - 16;
	    plain.data = MALLOC(plain.length);
	    if (plain.data == NULL)
		goto no_mem;
	    err = krb5_c_decrypt(context, key, key_usage, 0,
				 &cipher, &plain);
	    if (err) {
		goto error;
	    }
	    /* Don't use bodysize here!  Use the fact that
	       plain.length has been adjusted to the
	       correct length.  */
	    althdr = (uchar_t *)plain.data + plain.length - 16;
	    if (load_16_be(althdr) != 0x0504
		|| althdr[2] != ptr[2]
		|| althdr[3] != ptr[3]
		|| memcmp(althdr+8, ptr+8, 8)) {
		FREE(plain.data, plainlen);
		goto defective;
	    }
	    message_buffer->length = plain.length - ec - 16;
	    message_buffer->value = MALLOC(message_buffer->length);
	    if (message_buffer->value == NULL) {
		FREE(plain.data, plainlen);
		goto no_mem;
	    }
	    (void) memcpy(message_buffer->value, plain.data,
			message_buffer->length);
	    FREE(plain.data, plainlen);
	} else {
	    /* no confidentiality */
	    if (conf_state)
		*conf_state = 0;
	    if (ec + 16 < ec)
		/* overflow check */
		goto defective;
	    if (ec + 16 > bodysize)
		goto defective;
	    /* We have: header | msg | cksum.
	       We need cksum(msg | header).
	       Rotate the first two.  */
	    store_16_be(0, ptr+4);
	    store_16_be(0, ptr+6);
	    plain.length = bodysize-ec;
	    plain.data = (char *)ptr;
	    if (!rotate_left(ptr, bodysize-ec, 16))
		goto no_mem;
	    sum.length = ec;
	    if (sum.length != ctx->cksum_size) {
		*minor_status = 0;
		return GSS_S_BAD_SIG;
	    }
	    sum.contents = ptr+bodysize-ec;
	    sum.checksum_type = ctx->cksumtype;
	    err = krb5_c_verify_checksum(context, key, key_usage,
					 &plain, &sum, &valid);
	    if (err) {
		*minor_status = err;
		return GSS_S_BAD_SIG;
	    }
	    if (!valid) {
		*minor_status = 0;
		return GSS_S_BAD_SIG;
	    }
	    message_buffer->length = plain.length - 16;
	    message_buffer->value = MALLOC(message_buffer->length);
	    if (message_buffer->value == NULL)
		goto no_mem;
	    (void) memcpy(message_buffer->value,
		plain.data, message_buffer->length);

		/*
		 * Solaris Kerberos: Restore the original token.
		 * This allows the token to be detected as a duplicate if it
		 * is passed in to gss_unwrap() again.
		 */
		if (!rotate_left(ptr, bodysize-ec, bodysize - ec - 16))
			goto no_mem;
		store_16_be(ec, ptr+4);
		store_16_be(rrc, ptr+6);
	}
	err = g_order_check(&ctx->seqstate, seqnum);
	*minor_status = 0;
	return err;
    } else if (toktype == KG_TOK_MIC_MSG) {
	/* wrap token, no confidentiality */
	if (load_16_be(ptr) != 0x0404)
	    goto defective;
    verify_mic_1:
	if (ptr[3] != 0xff)
	    goto defective;
	if (load_32_be(ptr+4) != (ulong_t)0xffffffffU)
	    goto defective;
	seqnum = load_64_be(ptr+8);
	plain.length = message_buffer->length + 16;
	plain.data = MALLOC(plain.length);
	if (plain.data == NULL)
	    goto no_mem;
	if (message_buffer->length)
	    (void) memcpy(plain.data,
		message_buffer->value, message_buffer->length);
	(void) memcpy(plain.data + message_buffer->length, ptr, 16);
	sum.length = bodysize - 16;
	sum.contents = ptr + 16;
	sum.checksum_type = ctx->cksumtype;
	err = krb5_c_verify_checksum(context, key, key_usage,
				     &plain, &sum, &valid);
	if (err) {
	error:
	    FREE(plain.data, plain.length);
	    *minor_status = err;
	    save_error_info(*minor_status, context);
	    return GSS_S_BAD_SIG; /* XXX */
	}
	FREE(plain.data, plain.length);
	if (!valid) {
	    *minor_status = 0;
	    return GSS_S_BAD_SIG;
	}
	err = g_order_check(&ctx->seqstate, seqnum);
	*minor_status = 0;
	return err;
    } else if (toktype == KG_TOK_DEL_CTX) {
	if (load_16_be(ptr) != 0x0405)
	    goto defective;
	message_buffer = (gss_buffer_t)&empty_message;
	goto verify_mic_1;
    } else {
	goto defective;
    }
}
Exemplo n.º 7
0
/* Scan tl for a value of the given type and return it in allocated memory.
 * For KDB_TL_LINKDN, return a list of all values found. */
static krb5_error_code
decode_tl_data(krb5_tl_data *tl, int type, void **data_out)
{
    krb5_error_code ret;
    const unsigned char *ptr, *end;
    uint16_t len;
    size_t linkcount = 0, i;
    char **dnlist = NULL, **newlist;
    int *intptr;

    *data_out = NULL;

    /* Find the first matching subfield or return ENOENT.  For KDB_TL_LINKDN,
     * keep iterating after finding a match as it may be repeated. */
    ptr = tl->tl_data_contents;
    end = ptr + tl->tl_data_length;
    for (;;) {
        if (end - ptr < 3)
            break;
        len = load_16_be(ptr + 1);
        if (len > (end - ptr) - 3)
            break;
        if (*ptr != type) {
            ptr += 3 + len;
            continue;
        }
        ptr += 3;

        switch (type) {
        case KDB_TL_PRINCCOUNT:
        case KDB_TL_PRINCTYPE:
        case KDB_TL_MASK:
            if (len != 2)
                return EINVAL;
            intptr = malloc(sizeof(int));
            if (intptr == NULL)
                return ENOMEM;
            *intptr = load_16_be(ptr);
            *data_out = intptr;
            return 0;

        case KDB_TL_USERDN:
            *data_out = k5memdup0(ptr, len, &ret);
            return ret;

        case KDB_TL_LINKDN:
            newlist = realloc(dnlist, (linkcount + 2) * sizeof(char *));
            if (newlist == NULL)
                goto oom;
            dnlist = newlist;
            dnlist[linkcount] = k5memdup0(ptr, len, &ret);
            if (dnlist[linkcount] == NULL)
                goto oom;
            dnlist[linkcount + 1] = NULL;
            linkcount++;
            break;
        }

        ptr += len;
    }

    if (type != KDB_TL_LINKDN || dnlist == NULL)
        return ENOENT;
    *data_out = dnlist;
    return 0;

oom:
    for (i = 0; i < linkcount; i++)
        free(dnlist[i]);
    free(dnlist);
    return ENOMEM;
}
Exemplo n.º 8
0
/*
 * Split a STREAM | SIGN_DATA | DATA into
 *         HEADER | SIGN_DATA | DATA | PADDING | TRAILER
 */
static OM_uint32
kg_unseal_stream_iov(OM_uint32 *minor_status,
                     krb5_gss_ctx_id_rec *ctx,
                     int *conf_state,
                     gss_qop_t *qop_state,
                     gss_iov_buffer_desc *iov,
                     int iov_count,
                     int toktype)
{
    unsigned char *ptr;
    unsigned int bodysize;
    OM_uint32 code = 0, major_status = GSS_S_FAILURE;
    krb5_context context = ctx->k5_context;
    int conf_req_flag, toktype2;
    int i = 0, j;
    gss_iov_buffer_desc *tiov = NULL;
    gss_iov_buffer_t stream, data = NULL;
    gss_iov_buffer_t theader, tdata = NULL, tpadding, ttrailer;

    assert(toktype == KG_TOK_WRAP_MSG);

    if (toktype != KG_TOK_WRAP_MSG || (ctx->gss_flags & GSS_C_DCE_STYLE)) {
        code = EINVAL;
        goto cleanup;
    }

    stream = kg_locate_iov(iov, iov_count, GSS_IOV_BUFFER_TYPE_STREAM);
    assert(stream != NULL);

    ptr = (unsigned char *)stream->buffer.value;

    code = g_verify_token_header(ctx->mech_used,
                                 &bodysize, &ptr, -1,
                                 stream->buffer.length, 0);
    if (code != 0) {
        major_status = GSS_S_DEFECTIVE_TOKEN;
        goto cleanup;
    }

    if (bodysize < 2) {
        *minor_status = (OM_uint32)G_BAD_TOK_HEADER;
        return GSS_S_DEFECTIVE_TOKEN;
    }

    toktype2 = load_16_be(ptr);

    ptr += 2;
    bodysize -= 2;

    tiov = (gss_iov_buffer_desc *)calloc((size_t)iov_count + 2, sizeof(gss_iov_buffer_desc));
    if (tiov == NULL) {
        code = ENOMEM;
        goto cleanup;
    }

    /* HEADER */
    theader = &tiov[i++];
    theader->type = GSS_IOV_BUFFER_TYPE_HEADER;
    theader->buffer.value = stream->buffer.value;
    theader->buffer.length = ptr - (unsigned char *)stream->buffer.value;
    if (bodysize < 14 ||
        stream->buffer.length != theader->buffer.length + bodysize) {
        major_status = GSS_S_DEFECTIVE_TOKEN;
        goto cleanup;
    }
    theader->buffer.length += 14;

    /* n[SIGN_DATA] | DATA | m[SIGN_DATA] */
    for (j = 0; j < iov_count; j++) {
        OM_uint32 type = GSS_IOV_BUFFER_TYPE(iov[j].type);

        if (type == GSS_IOV_BUFFER_TYPE_DATA) {
            if (data != NULL) {
                /* only a single DATA buffer can appear */
                code = EINVAL;
                goto cleanup;
            }

            data = &iov[j];
            tdata = &tiov[i];
        }
        if (type == GSS_IOV_BUFFER_TYPE_DATA ||
            type == GSS_IOV_BUFFER_TYPE_SIGN_ONLY)
            tiov[i++] = iov[j];
    }

    if (data == NULL) {
        /* a single DATA buffer must be present */
        code = EINVAL;
        goto cleanup;
    }

    /* PADDING | TRAILER */
    tpadding = &tiov[i++];
    tpadding->type = GSS_IOV_BUFFER_TYPE_PADDING;
    tpadding->buffer.length = 0;
    tpadding->buffer.value = NULL;

    ttrailer = &tiov[i++];
    ttrailer->type = GSS_IOV_BUFFER_TYPE_TRAILER;

    switch (toktype2) {
    case KG2_TOK_MIC_MSG:
    case KG2_TOK_WRAP_MSG:
    case KG2_TOK_DEL_CTX: {
        size_t ec, rrc;
        krb5_enctype enctype;
        unsigned int k5_headerlen = 0;
        unsigned int k5_trailerlen = 0;

        if (ctx->have_acceptor_subkey)
            enctype = ctx->acceptor_subkey->keyblock.enctype;
        else
            enctype = ctx->subkey->keyblock.enctype;
        conf_req_flag = ((ptr[0] & FLAG_WRAP_CONFIDENTIAL) != 0);
        ec = conf_req_flag ? load_16_be(ptr + 2) : 0;
        rrc = load_16_be(ptr + 4);

        if (rrc != 0) {
            if (!gss_krb5int_rotate_left((unsigned char *)stream->buffer.value + 16,
                                         stream->buffer.length - 16, rrc)) {
                code = ENOMEM;
                goto cleanup;
            }
            store_16_be(0, ptr + 4); /* set RRC to zero */
        }

        if (conf_req_flag) {
            code = krb5_c_crypto_length(context, enctype, KRB5_CRYPTO_TYPE_HEADER, &k5_headerlen);
            if (code != 0)
                goto cleanup;
            theader->buffer.length += k5_headerlen; /* length validated later */
        }

        /* no PADDING for CFX, EC is used instead */
        code = krb5_c_crypto_length(context, enctype,
                                    conf_req_flag ? KRB5_CRYPTO_TYPE_TRAILER : KRB5_CRYPTO_TYPE_CHECKSUM,
                                    &k5_trailerlen);
        if (code != 0)
            goto cleanup;

        ttrailer->buffer.length = ec + (conf_req_flag ? 16 : 0 /* E(Header) */) + k5_trailerlen;
        ttrailer->buffer.value = (unsigned char *)stream->buffer.value +
            stream->buffer.length - ttrailer->buffer.length;
        break;
    }
    case KG_TOK_MIC_MSG:
    case KG_TOK_WRAP_MSG:
    case KG_TOK_DEL_CTX:
        theader->buffer.length += ctx->cksum_size +
            kg_confounder_size(context, ctx->enc->keyblock.enctype);

        /*
         * we can't set the padding accurately until decryption;
         * kg_fixup_padding_iov() will take care of this
         */
        tpadding->buffer.length = 1;
        tpadding->buffer.value = (unsigned char *)stream->buffer.value + stream->buffer.length - 1;

        /* no TRAILER for pre-CFX */
        ttrailer->buffer.length = 0;
        ttrailer->buffer.value = NULL;

        break;
    default:
        code = (OM_uint32)G_BAD_TOK_HEADER;
        major_status = GSS_S_DEFECTIVE_TOKEN;
        goto cleanup;
        break;
    }

    /* IOV: -----------0-------------+---1---+--2--+----------------3--------------*/
    /* Old: GSS-Header | Conf        | Data  | Pad |                               */
    /* CFX: GSS-Header | Kerb-Header | Data  |     | EC | E(Header) | Kerb-Trailer */
    /* GSS: -------GSS-HEADER--------+-DATA--+-PAD-+----------GSS-TRAILER----------*/

    /* validate lengths */
    if (stream->buffer.length < theader->buffer.length +
        tpadding->buffer.length +
        ttrailer->buffer.length)
    {
        code = (OM_uint32)KRB5_BAD_MSIZE;
        major_status = GSS_S_DEFECTIVE_TOKEN;
        goto cleanup;
    }

    /* setup data */
    tdata->buffer.length = stream->buffer.length - ttrailer->buffer.length -
        tpadding->buffer.length - theader->buffer.length;

    assert(data != NULL);

    if (data->type & GSS_IOV_BUFFER_FLAG_ALLOCATE) {
        code = kg_allocate_iov(tdata, tdata->buffer.length);
        if (code != 0)
            goto cleanup;
        memcpy(tdata->buffer.value,
               (unsigned char *)stream->buffer.value + theader->buffer.length, tdata->buffer.length);
    } else
        tdata->buffer.value = (unsigned char *)stream->buffer.value + theader->buffer.length;

    assert(i <= iov_count + 2);

    major_status = kg_unseal_iov_token(&code, ctx, conf_state, qop_state,
                                       tiov, i, toktype);
    if (major_status == GSS_S_COMPLETE)
        *data = *tdata;
    else
        kg_release_iov(tdata, 1);

cleanup:
    if (tiov != NULL)
        free(tiov);

    *minor_status = code;

    return major_status;
}
Exemplo n.º 9
0
/*
 * Caller must provide TOKEN | DATA | PADDING | TRAILER, except
 * for DCE in which case it can just provide TOKEN | DATA (must
 * guarantee that DATA is padded)
 */
static OM_uint32
kg_unseal_iov_token(OM_uint32 *minor_status,
                    krb5_gss_ctx_id_rec *ctx,
                    int *conf_state,
                    gss_qop_t *qop_state,
                    gss_iov_buffer_desc *iov,
                    int iov_count,
                    int toktype)
{
    krb5_error_code code;
    krb5_context context = ctx->k5_context;
    unsigned char *ptr;
    gss_iov_buffer_t header;
    gss_iov_buffer_t padding;
    gss_iov_buffer_t trailer;
    size_t input_length;
    unsigned int bodysize;
    int toktype2;

    header = kg_locate_header_iov(iov, iov_count, toktype);
    if (header == NULL) {
        *minor_status = EINVAL;
        return GSS_S_FAILURE;
    }

    padding = kg_locate_iov(iov, iov_count, GSS_IOV_BUFFER_TYPE_PADDING);
    trailer = kg_locate_iov(iov, iov_count, GSS_IOV_BUFFER_TYPE_TRAILER);

    ptr = (unsigned char *)header->buffer.value;
    input_length = header->buffer.length;

    if ((ctx->gss_flags & GSS_C_DCE_STYLE) == 0 &&
        toktype == KG_TOK_WRAP_MSG) {
        size_t data_length, assoc_data_length;

        kg_iov_msglen(iov, iov_count, &data_length, &assoc_data_length);

        input_length += data_length - assoc_data_length;

        if (padding != NULL)
            input_length += padding->buffer.length;

        if (trailer != NULL)
            input_length += trailer->buffer.length;
    }

    code = g_verify_token_header(ctx->mech_used,
                                 &bodysize, &ptr, -1,
                                 input_length, 0);
    if (code != 0) {
        *minor_status = code;
        return GSS_S_DEFECTIVE_TOKEN;
    }

    if (bodysize < 2) {
        *minor_status = (OM_uint32)G_BAD_TOK_HEADER;
        return GSS_S_DEFECTIVE_TOKEN;
    }

    toktype2 = load_16_be(ptr);

    ptr += 2;
    bodysize -= 2;

    switch (toktype2) {
    case KG2_TOK_MIC_MSG:
    case KG2_TOK_WRAP_MSG:
    case KG2_TOK_DEL_CTX:
        code = gss_krb5int_unseal_v3_iov(context, minor_status, ctx, iov, iov_count,
                                         conf_state, qop_state, toktype);
        break;
    case KG_TOK_MIC_MSG:
    case KG_TOK_WRAP_MSG:
    case KG_TOK_DEL_CTX:
        code = kg_unseal_v1_iov(context, minor_status, ctx, iov, iov_count,
                                (size_t)(ptr - (unsigned char *)header->buffer.value),
                                conf_state, qop_state, toktype);
        break;
    default:
        *minor_status = (OM_uint32)G_BAD_TOK_HEADER;
        code = GSS_S_DEFECTIVE_TOKEN;
        break;
    }

    if (code != 0)
        save_error_info(*minor_status, context);

    return code;
}
Exemplo n.º 10
0
OM_uint32
gss_krb5int_unseal_token_v3(krb5_context *contextptr,
                            OM_uint32 *minor_status,
                            krb5_gss_ctx_id_rec *ctx,
                            unsigned char *ptr, unsigned int bodysize,
                            gss_buffer_t message_buffer,
                            int *conf_state, gss_qop_t *qop_state, int toktype)
{
    krb5_context context = *contextptr;
    krb5_data plain;
    uint64_t seqnum;
    size_t ec, rrc;
    int key_usage;
    unsigned char acceptor_flag;
    krb5_checksum sum;
    krb5_error_code err;
    krb5_boolean valid;
    krb5_key key;
    krb5_cksumtype cksumtype;

    if (qop_state)
        *qop_state = GSS_C_QOP_DEFAULT;

    acceptor_flag = ctx->initiate ? FLAG_SENDER_IS_ACCEPTOR : 0;
    key_usage = (toktype == KG_TOK_WRAP_MSG
                 ? (!ctx->initiate
                    ? KG_USAGE_INITIATOR_SEAL
                    : KG_USAGE_ACCEPTOR_SEAL)
                 : (!ctx->initiate
                    ? KG_USAGE_INITIATOR_SIGN
                    : KG_USAGE_ACCEPTOR_SIGN));

    /* Oops.  I wrote this code assuming ptr would be at the start of
       the token header.  */
    ptr -= 2;
    bodysize += 2;

    if (bodysize < 16) {
    defective:
        *minor_status = 0;
        return GSS_S_DEFECTIVE_TOKEN;
    }
    if ((ptr[2] & FLAG_SENDER_IS_ACCEPTOR) != acceptor_flag) {
        *minor_status = (OM_uint32)G_BAD_DIRECTION;
        return GSS_S_BAD_SIG;
    }

    /* Two things to note here.

       First, we can't really enforce the use of the acceptor's subkey,
       if we're the acceptor; the initiator may have sent messages
       before getting the subkey.  We could probably enforce it if
       we're the initiator.

       Second, if someone tweaks the code to not set the flag telling
       the krb5 library to generate a new subkey in the AP-REP
       message, the MIT library may include a subkey anyways --
       namely, a copy of the AP-REQ subkey, if it was provided.  So
       the initiator may think we wanted a subkey, and set the flag,
       even though we weren't trying to set the subkey.  The "other"
       key, the one not asserted by the acceptor, will have the same
       value in that case, though, so we can just ignore the flag.  */
    if (ctx->have_acceptor_subkey && (ptr[2] & FLAG_ACCEPTOR_SUBKEY)) {
        key = ctx->acceptor_subkey;
        cksumtype = ctx->acceptor_subkey_cksumtype;
    } else {
        key = ctx->subkey;
        cksumtype = ctx->cksumtype;
    }
    assert(key != NULL);

    if (toktype == KG_TOK_WRAP_MSG) {
        if (load_16_be(ptr) != KG2_TOK_WRAP_MSG)
            goto defective;
        if (ptr[3] != 0xff)
            goto defective;
        ec = load_16_be(ptr+4);
        rrc = load_16_be(ptr+6);
        seqnum = load_64_be(ptr+8);
        if (!gss_krb5int_rotate_left(ptr+16, bodysize-16, rrc)) {
        no_mem:
            *minor_status = ENOMEM;
            return GSS_S_FAILURE;
        }
        if (ptr[2] & FLAG_WRAP_CONFIDENTIAL) {
            /* confidentiality */
            krb5_enc_data cipher;
            unsigned char *althdr;

            if (conf_state)
                *conf_state = 1;
            /* Do we have no decrypt_size function?

               For all current cryptosystems, the ciphertext size will
               be larger than the plaintext size.  */
            cipher.enctype = key->keyblock.enctype;
            cipher.ciphertext.length = bodysize - 16;
            cipher.ciphertext.data = (char *)ptr + 16;
            plain.length = bodysize - 16;
            plain.data = gssalloc_malloc(plain.length);
            if (plain.data == NULL)
                goto no_mem;
            err = krb5_k_decrypt(context, key, key_usage, 0,
                                 &cipher, &plain);
            if (err) {
                gssalloc_free(plain.data);
                goto error;
            }
            /* Don't use bodysize here!  Use the fact that
               cipher.ciphertext.length has been adjusted to the
               correct length.  */
            althdr = (unsigned char *)plain.data + plain.length - 16;
            if (load_16_be(althdr) != KG2_TOK_WRAP_MSG
                || althdr[2] != ptr[2]
                || althdr[3] != ptr[3]
                || memcmp(althdr+8, ptr+8, 8)) {
                free(plain.data);
                goto defective;
            }
            message_buffer->value = plain.data;
            message_buffer->length = plain.length - ec - 16;
            if(message_buffer->length == 0) {
                gssalloc_free(message_buffer->value);
                message_buffer->value = NULL;
            }
        } else {
            size_t cksumsize;

            err = krb5_c_checksum_length(context, cksumtype, &cksumsize);
            if (err)
                goto error;

            /* no confidentiality */
            if (conf_state)
                *conf_state = 0;
            if (ec + 16 < ec)
                /* overflow check */
                goto defective;
            if (ec + 16 > bodysize)
                goto defective;
            /* We have: header | msg | cksum.
               We need cksum(msg | header).
               Rotate the first two.  */
            store_16_be(0, ptr+4);
            store_16_be(0, ptr+6);
            plain = make_data(ptr, bodysize - ec);
            if (!gss_krb5int_rotate_left(ptr, bodysize-ec, 16))
                goto no_mem;
            sum.length = ec;
            if (sum.length != cksumsize) {
                *minor_status = 0;
                return GSS_S_BAD_SIG;
            }
            sum.contents = ptr+bodysize-ec;
            sum.checksum_type = cksumtype;
            err = krb5_k_verify_checksum(context, key, key_usage,
                                         &plain, &sum, &valid);
            if (err)
                goto error;
            if (!valid) {
                *minor_status = 0;
                return GSS_S_BAD_SIG;
            }
            message_buffer->length = plain.length - 16;
            message_buffer->value = gssalloc_malloc(message_buffer->length);
            if (message_buffer->value == NULL)
                goto no_mem;
            memcpy(message_buffer->value, plain.data, message_buffer->length);
        }
        err = g_seqstate_check(ctx->seqstate, seqnum);
        *minor_status = 0;
        return err;
    } else if (toktype == KG_TOK_MIC_MSG) {
        /* wrap token, no confidentiality */
        if (load_16_be(ptr) != KG2_TOK_MIC_MSG)
            goto defective;
    verify_mic_1:
        if (ptr[3] != 0xff)
            goto defective;
        if (load_32_be(ptr+4) != 0xffffffffL)
            goto defective;
        seqnum = load_64_be(ptr+8);
        plain.length = message_buffer->length + 16;
        plain.data = malloc(plain.length);
        if (plain.data == NULL)
            goto no_mem;
        if (message_buffer->length)
            memcpy(plain.data, message_buffer->value, message_buffer->length);
        memcpy(plain.data + message_buffer->length, ptr, 16);
        sum.length = bodysize - 16;
        sum.contents = ptr + 16;
        sum.checksum_type = cksumtype;
        err = krb5_k_verify_checksum(context, key, key_usage,
                                     &plain, &sum, &valid);
        free(plain.data);
        plain.data = NULL;
        if (err) {
        error:
            *minor_status = err;
            save_error_info(*minor_status, context);
            return GSS_S_BAD_SIG; /* XXX */
        }
        if (!valid) {
            *minor_status = 0;
            return GSS_S_BAD_SIG;
        }
        err = g_seqstate_check(ctx->seqstate, seqnum);
        *minor_status = 0;
        return err;
    } else if (toktype == KG_TOK_DEL_CTX) {
        if (load_16_be(ptr) != KG2_TOK_DEL_CTX)
            goto defective;
        message_buffer = (gss_buffer_t)&empty_message;
        goto verify_mic_1;
    } else {
        goto defective;
    }
}
Exemplo n.º 11
0
OM_uint32
gss_krb5int_unseal_v3_iov(krb5_context context,
                          OM_uint32 *minor_status,
                          krb5_gss_ctx_id_rec *ctx,
                          gss_iov_buffer_desc *iov,
                          int iov_count,
                          int *conf_state,
                          gss_qop_t *qop_state,
                          int toktype)
{
    OM_uint32 code;
    gss_iov_buffer_t header;
    gss_iov_buffer_t padding;
    gss_iov_buffer_t trailer;
    unsigned char acceptor_flag;
    unsigned char *ptr = NULL;
    int key_usage;
    size_t rrc, ec;
    size_t data_length, assoc_data_length;
    krb5_key key;
    gssint_uint64 seqnum;
    krb5_boolean valid;
    krb5_cksumtype cksumtype;
    int conf_flag = 0;

    if (ctx->big_endian != 0)
        return GSS_S_DEFECTIVE_TOKEN;

    if (qop_state != NULL)
        *qop_state = GSS_C_QOP_DEFAULT;

    header = kg_locate_iov(iov, iov_count, GSS_IOV_BUFFER_TYPE_HEADER);
    assert(header != NULL);

    padding = kg_locate_iov(iov, iov_count, GSS_IOV_BUFFER_TYPE_PADDING);
    if (padding != NULL && padding->buffer.length != 0)
        return GSS_S_DEFECTIVE_TOKEN;

    trailer = kg_locate_iov(iov, iov_count, GSS_IOV_BUFFER_TYPE_TRAILER);

    acceptor_flag = ctx->initiate ? FLAG_SENDER_IS_ACCEPTOR : 0;
    key_usage = (toktype == KG_TOK_WRAP_MSG
                 ? (!ctx->initiate
                    ? KG_USAGE_INITIATOR_SEAL
                    : KG_USAGE_ACCEPTOR_SEAL)
                 : (!ctx->initiate
                    ? KG_USAGE_INITIATOR_SIGN
                    : KG_USAGE_ACCEPTOR_SIGN));

    kg_iov_msglen(iov, iov_count, &data_length, &assoc_data_length);

    ptr = (unsigned char *)header->buffer.value;

    if (header->buffer.length < 16) {
        *minor_status = 0;
        return GSS_S_DEFECTIVE_TOKEN;
    }

    if ((ptr[2] & FLAG_SENDER_IS_ACCEPTOR) != acceptor_flag) {
        *minor_status = (OM_uint32)G_BAD_DIRECTION;
        return GSS_S_BAD_SIG;
    }

    if (ctx->have_acceptor_subkey && (ptr[2] & FLAG_ACCEPTOR_SUBKEY)) {
        key = ctx->acceptor_subkey;
        cksumtype = ctx->acceptor_subkey_cksumtype;
    } else {
        key = ctx->subkey;
        cksumtype = ctx->cksumtype;
    }
    assert(key != NULL);


    if (toktype == KG_TOK_WRAP_MSG) {
        unsigned int k5_trailerlen;

        if (load_16_be(ptr) != KG2_TOK_WRAP_MSG)
            goto defective;
        conf_flag = ((ptr[2] & FLAG_WRAP_CONFIDENTIAL) != 0);
        if (ptr[3] != 0xFF)
            goto defective;
        ec = load_16_be(ptr + 4);
        rrc = load_16_be(ptr + 6);
        seqnum = load_64_be(ptr + 8);

        code = krb5_c_crypto_length(context, key->keyblock.enctype,
                                    conf_flag ? KRB5_CRYPTO_TYPE_TRAILER :
                                    KRB5_CRYPTO_TYPE_CHECKSUM,
                                    &k5_trailerlen);
        if (code != 0) {
            *minor_status = code;
            return GSS_S_FAILURE;
        }

        /* Deal with RRC */
        if (trailer == NULL) {
            size_t desired_rrc = k5_trailerlen;

            if (conf_flag) {
                desired_rrc += 16; /* E(Header) */

                if ((ctx->gss_flags & GSS_C_DCE_STYLE) == 0)
                    desired_rrc += ec;
            }

            /* According to MS, we only need to deal with a fixed RRC for DCE */
            if (rrc != desired_rrc)
                goto defective;
        } else if (rrc != 0) {
            /* Should have been rotated by kg_unseal_stream_iov() */
            goto defective;
        }

        if (conf_flag) {
            unsigned char *althdr;

            /* Decrypt */
            code = kg_decrypt_iov(context, ctx->proto,
                                  ((ctx->gss_flags & GSS_C_DCE_STYLE) != 0),
                                  ec, rrc,
                                  key, key_usage, 0, iov, iov_count);
            if (code != 0) {
                *minor_status = code;
                return GSS_S_BAD_SIG;
            }

            /* Validate header integrity */
            if (trailer == NULL)
                althdr = (unsigned char *)header->buffer.value + 16 + ec;
            else
                althdr = (unsigned char *)trailer->buffer.value + ec;

            if (load_16_be(althdr) != KG2_TOK_WRAP_MSG
                    || althdr[2] != ptr[2]
                    || althdr[3] != ptr[3]
                    || memcmp(althdr + 8, ptr + 8, 8) != 0) {
                *minor_status = 0;
                return GSS_S_BAD_SIG;
            }
        } else {
            /* Verify checksum: note EC is checksum size here, not padding */
            if (ec != k5_trailerlen)
                goto defective;

            /* Zero EC, RRC before computing checksum */
            store_16_be(0, ptr + 4);
            store_16_be(0, ptr + 6);

            code = kg_verify_checksum_iov_v3(context, cksumtype, rrc,
                                             key, key_usage,
                                             iov, iov_count, &valid);
            if (code != 0 || valid == FALSE) {
                *minor_status = code;
                return GSS_S_BAD_SIG;
            }
        }

        code = g_order_check(&ctx->seqstate, seqnum);
    } else if (toktype == KG_TOK_MIC_MSG) {
        if (load_16_be(ptr) != KG2_TOK_MIC_MSG)
            goto defective;

verify_mic_1:
        if (ptr[3] != 0xFF)
            goto defective;
        seqnum = load_64_be(ptr + 8);

        code = kg_verify_checksum_iov_v3(context, cksumtype, 0,
                                         key, key_usage,
                                         iov, iov_count, &valid);
        if (code != 0 || valid == FALSE) {
            *minor_status = code;
            return GSS_S_BAD_SIG;
        }
        code = g_order_check(&ctx->seqstate, seqnum);
    } else if (toktype == KG_TOK_DEL_CTX) {
        if (load_16_be(ptr) != KG2_TOK_DEL_CTX)
            goto defective;
        goto verify_mic_1;
    } else {
        goto defective;
    }

    *minor_status = 0;

    if (conf_state != NULL)
        *conf_state = conf_flag;

    return code;

defective:
    *minor_status = 0;

    return GSS_S_DEFECTIVE_TOKEN;
}
Exemplo n.º 12
0
static krb5_error_code
decode_ad_policy_info(const krb5_data *data, char **msg_out)
{
    struct ad_policy_info policy;
    uint64_t password_days;
    const char *p;
    char *msg;
    struct k5buf buf;

    *msg_out = NULL;
    if (data->length != AD_POLICY_INFO_LENGTH)
        return 0;

    p = data->data;
    policy.zero_bytes = load_16_be(p);
    p += 2;

    /* first two bytes are zeros */
    if (policy.zero_bytes != 0)
        return 0;

    /* Read in the rest of structure */
    policy.min_length_password = load_32_be(p);
    p += 4;
    policy.password_history = load_32_be(p);
    p += 4;
    policy.password_properties = load_32_be(p);
    p += 4;
    policy.expire = load_64_be(p);
    p += 8;
    policy.min_passwordage = load_64_be(p);
    p += 8;

    /* Check that we processed exactly the expected number of bytes. */
    assert(p == data->data + AD_POLICY_INFO_LENGTH);

    krb5int_buf_init_dynamic(&buf);

    /*
     * Update src/tests/misc/test_chpw_message.c if changing these strings!
     */

    if (policy.password_properties & AD_POLICY_COMPLEX) {
        krb5int_buf_add(&buf,
                        _("The password must include numbers or symbols.  "
                          "Don't include any part of your name in the "
                          "password."));
    }
    if (policy.min_length_password > 0) {
        add_spaces(&buf);
        krb5int_buf_add_fmt(&buf,
                            ngettext("The password must contain at least %d "
                                     "character.",
                                     "The password must contain at least %d "
                                     "characters.",
                                     policy.min_length_password),
                            policy.min_length_password);
    }
    if (policy.password_history) {
        add_spaces(&buf);
        krb5int_buf_add_fmt(&buf,
                            ngettext("The password must be different from the "
                                     "previous password.",
                                     "The password must be different from the "
                                     "previous %d passwords.",
                                     policy.password_history),
                            policy.password_history);
    }
    if (policy.min_passwordage) {
        password_days = policy.min_passwordage / AD_POLICY_TIME_TO_DAYS;
        if (password_days == 0)
            password_days = 1;
        add_spaces(&buf);
        krb5int_buf_add_fmt(&buf,
                            ngettext("The password can only be changed once a "
                                     "day.",
                                     "The password can only be changed every "
                                     "%d days.", (int)password_days),
                            (int)password_days);
    }

    msg = krb5int_buf_data(&buf);
    if (msg == NULL)
        return ENOMEM;

    if (*msg != '\0')
        *msg_out = msg;
    else
        free(msg);
    return 0;
}
Exemplo n.º 13
0
/* Open and lock the cache file.  If mode is FCC_OPEN_AND_ERASE, initialize it
 * with a header.  Call with the mutex locked. */
static krb5_error_code
open_cache_file(krb5_context context, krb5_ccache id, int mode)
{
    krb5_os_context os_ctx = &context->os_context;
    krb5_error_code ret;
    fcc_data *data = id->data;
    char fcc_fvno[2];
    uint16_t fcc_flen, fcc_tag, fcc_taglen;
    uint32_t time_offset, usec_offset;
    int f, open_flag, lock_flag, cnt;
    char buf[1024];

    k5_cc_mutex_assert_locked(context, &data->lock);
    invalidate_cache(data);

    if (data->fd != NO_FILE) {
        /* Don't know what state it's in; shut down and start anew. */
        (void)krb5_unlock_file(context, data->fd);
        (void)close(data->fd);
        data->fd = NO_FILE;
    }

    switch (mode) {
    case FCC_OPEN_AND_ERASE:
        unlink(data->filename);
        open_flag = O_CREAT | O_EXCL | O_TRUNC | O_RDWR;
        break;
    case FCC_OPEN_RDWR:
        open_flag = O_RDWR;
        break;
    case FCC_OPEN_RDONLY:
    default:
        open_flag = O_RDONLY;
        break;
    }

    f = THREEPARAMOPEN(data->filename, open_flag | O_BINARY, 0600);
    if (f == NO_FILE) {
        if (errno == ENOENT) {
            ret = KRB5_FCC_NOFILE;
            k5_setmsg(context, ret, _("Credentials cache file '%s' not found"),
                      data->filename);
            return ret;
        } else {
            return interpret_errno(context, errno);
        }
    }
    set_cloexec_fd(f);

    data->mode = mode;

    if (data->mode == FCC_OPEN_RDONLY)
        lock_flag = KRB5_LOCKMODE_SHARED;
    else
        lock_flag = KRB5_LOCKMODE_EXCLUSIVE;
    ret = krb5_lock_file(context, f, lock_flag);
    if (ret) {
        (void)close(f);
        return ret;
    }

    if (mode == FCC_OPEN_AND_ERASE) {
        /* write the version number */
        store_16_be(context->fcc_default_format, fcc_fvno);
        data->version = context->fcc_default_format;
        cnt = write(f, fcc_fvno, 2);
        if (cnt != 2) {
            ret = (cnt == -1) ? interpret_errno(context, errno) : KRB5_CC_IO;
            goto done;
        }
        data->fd = f;

        if (data->version == FVNO_4) {
            /* V4 of the credentials cache format allows for header tags */
            fcc_flen = 0;

            if (os_ctx->os_flags & KRB5_OS_TOFFSET_VALID)
                fcc_flen += 2 + 2 + 4 + 4;

            /* Write header length. */
            ret = store16(context, id, fcc_flen);
            if (ret)
                goto done;

            if (os_ctx->os_flags & KRB5_OS_TOFFSET_VALID) {
                /* Write time offset tag. */
                fcc_tag = FCC_TAG_DELTATIME;
                fcc_taglen = 2 * 4;

                ret = store16(context, id, fcc_tag);
                if (ret)
                    goto done;
                ret = store16(context, id, fcc_taglen);
                if (ret)
                    goto done;
                ret = store32(context, id, os_ctx->time_offset);
                if (ret)
                    goto done;
                ret = store32(context, id, os_ctx->usec_offset);
                if (ret)
                    goto done;
            }
        }
        invalidate_cache(data);
        goto done;
    }

    /* Verify a valid version number is there. */
    invalidate_cache(data);
    if (read(f, fcc_fvno, 2) != 2) {
        ret = KRB5_CC_FORMAT;
        goto done;
    }
    data->version = load_16_be(fcc_fvno);
    if (data->version != FVNO_4 && data->version != FVNO_3 &&
        data->version != FVNO_2 && data->version != FVNO_1) {
        ret = KRB5_CCACHE_BADVNO;
        goto done;
    }

    data->fd = f;

    if (data->version == FVNO_4) {
        if (read16(context, id, &fcc_flen) || fcc_flen > sizeof(buf)) {
            ret = KRB5_CC_FORMAT;
            goto done;
        }

        while (fcc_flen) {
            if (fcc_flen < 2 * 2 || read16(context, id, &fcc_tag) ||
                read16(context, id, &fcc_taglen) ||
                fcc_taglen > fcc_flen - 2 * 2) {
                ret = KRB5_CC_FORMAT;
                goto done;
            }

            switch (fcc_tag) {
            case FCC_TAG_DELTATIME:
                if (fcc_taglen != 2 * 4) {
                    ret = KRB5_CC_FORMAT;
                    goto done;
                }
                if (!(context->library_options & KRB5_LIBOPT_SYNC_KDCTIME) ||
                    (os_ctx->os_flags & KRB5_OS_TOFFSET_VALID)) {
                    if (read_bytes(context, id, buf, fcc_taglen)) {
                        ret = KRB5_CC_FORMAT;
                        goto done;
                    }
                    break;
                }
                if (read32(context, id, NULL, &time_offset) ||
                    read32(context, id, NULL, &usec_offset)) {
                    ret = KRB5_CC_FORMAT;
                    goto done;
                }
                os_ctx->time_offset = time_offset;
                os_ctx->usec_offset = usec_offset;
                os_ctx->os_flags =
                    ((os_ctx->os_flags & ~KRB5_OS_TOFFSET_TIME) |
                     KRB5_OS_TOFFSET_VALID);
                break;
            default:
                if (fcc_taglen && read_bytes(context, id, buf, fcc_taglen)) {
                    ret = KRB5_CC_FORMAT;
                    goto done;
                }
                break;
            }
            fcc_flen -= (2 * 2 + fcc_taglen);
        }
    }

done:
    if (ret) {
        data->fd = -1;
        (void)krb5_unlock_file(context, f);
        (void)close(f);
    }
    return ret;
}