static int krb_enc(krb5_context context, krb5_crypto crypto, unsigned usage, krb5_data *cipher, krb5_data *clear) { krb5_data decrypt; krb5_error_code ret; krb5_data_zero(&decrypt); ret = krb5_decrypt(context, crypto, usage, cipher->data, cipher->length, &decrypt); if (ret) { krb5_warn(context, ret, "krb5_decrypt"); return ret; } if (decrypt.length != clear->length || memcmp(decrypt.data, clear->data, decrypt.length) != 0) { krb5_warnx(context, "clear text not same"); return EINVAL; } krb5_data_free(&decrypt); return 0; }
static krb5_error_code decrypt_etext(krb5_context context, const krb5_keyblock *key, const krb5_data *cdata, krb5_data *data) { krb5_error_code ret; krb5_crypto crypto; ret = krb5_crypto_init(context, key, ETYPE_DES_PCBC_NONE, &crypto); if (ret) return ret; ret = krb5_decrypt(context, crypto, 0, cdata->data, cdata->length, data); krb5_crypto_destroy(context, crypto); return ret; }
static int krb_enc_iov2(krb5_context context, krb5_crypto crypto, unsigned usage, size_t cipher_len, krb5_data *clear) { krb5_crypto_iov iov[4]; krb5_data decrypt; int ret; char *p, *q; size_t len, i; p = clear->data; len = clear->length; iov[0].flags = KRB5_CRYPTO_TYPE_HEADER; krb5_crypto_length(context, crypto, iov[0].flags, &iov[0].data.length); iov[0].data.data = emalloc(iov[0].data.length); iov[1].flags = KRB5_CRYPTO_TYPE_DATA; iov[1].data.length = len; iov[1].data.data = emalloc(iov[1].data.length); memcpy(iov[1].data.data, p, iov[1].data.length); /* padding buffer */ iov[2].flags = KRB5_CRYPTO_TYPE_PADDING; krb5_crypto_length(context, crypto, KRB5_CRYPTO_TYPE_PADDING, &iov[2].data.length); iov[2].data.data = emalloc(iov[2].data.length); iov[3].flags = KRB5_CRYPTO_TYPE_TRAILER; krb5_crypto_length(context, crypto, iov[3].flags, &iov[3].data.length); iov[3].data.data = emalloc(iov[3].data.length); ret = krb5_encrypt_iov_ivec(context, crypto, usage, iov, sizeof(iov)/sizeof(iov[0]), NULL); if (ret) errx(1, "encrypt iov failed: %d", ret); /* check len */ for (i = 0, len = 0; i < sizeof(iov)/sizeof(iov[0]); i++) len += iov[i].data.length; if (len != cipher_len) errx(1, "cipher len wrong"); /* * Plain decrypt */ p = q = emalloc(len); for (i = 0; i < sizeof(iov)/sizeof(iov[0]); i++) { memcpy(q, iov[i].data.data, iov[i].data.length); q += iov[i].data.length; } ret = krb5_decrypt(context, crypto, usage, p, len, &decrypt); if (ret) krb5_err(context, 1, ret, "krb5_decrypt"); else krb5_data_free(&decrypt); free(p); /* * Now decrypt use iov */ /* padding turn into data */ p = q = emalloc(iov[1].data.length + iov[2].data.length); memcpy(q, iov[1].data.data, iov[1].data.length); q += iov[1].data.length; memcpy(q, iov[2].data.data, iov[2].data.length); free(iov[1].data.data); free(iov[2].data.data); iov[1].data.data = p; iov[1].data.length += iov[2].data.length; iov[2].flags = KRB5_CRYPTO_TYPE_EMPTY; iov[2].data.length = 0; ret = krb5_decrypt_iov_ivec(context, crypto, usage, iov, sizeof(iov)/sizeof(iov[0]), NULL); free(iov[0].data.data); free(iov[3].data.data); if (ret) krb5_err(context, 1, ret, "decrypt iov failed: %d", ret); if (clear->length != iov[1].data.length) errx(1, "length incorrect"); p = clear->data; if (memcmp(iov[1].data.data, p, iov[1].data.length) != 0) errx(1, "iov[1] incorrect"); free(iov[1].data.data); return 0; }
krb5_error_code kcm_store_io(krb5_context context, krb5_uuid uuid, void *ptr, size_t length, krb5_data *data, bool encrypt) { xtsEncrypt_InStruct_t xtsEncrypt_InStruct; size_t inseed_size = 64; io_connect_t conn; kern_return_t kr; uint8_t *inseed; krb5_crypto crypto = NULL; krb5_error_code ret; krb5_data_zero(data); inseed = malloc(inseed_size); if (inseed == NULL) err(1, "malloc"); memset(inseed, 0, inseed_size); conn = openiodev(); if (conn == IO_OBJECT_NULL) { free(inseed); return EINVAL; } uuid_copy(xtsEncrypt_InStruct.key_uuid, uuid); xtsEncrypt_InStruct.bufferAddress = (uint64_t) (intptr_t) inseed; xtsEncrypt_InStruct.bufferLength = (uint64_t) inseed_size; memset(xtsEncrypt_InStruct.tweak, 0, XTS_TWEAK_BYTES); kr = IOConnectCallMethod(conn, kAppleFDEKeyStore_xtsEncrypt, NULL, 0, & xtsEncrypt_InStruct, sizeof(xtsEncrypt_InStruct), NULL, 0, NULL, 0); closeiodev(conn); if (kr != KERN_SUCCESS) { free(inseed); return EINVAL; } CC_SHA256(inseed, (CC_LONG)inseed_size, inseed); krb5_keyblock keyblock; keyblock.keytype = ETYPE_AES128_CTS_HMAC_SHA1_96; keyblock.keyvalue.data = inseed; keyblock.keyvalue.length = 16; ret = krb5_crypto_init(context, &keyblock, 0, &crypto); free(inseed); if (ret) return ret; if (encrypt) ret = krb5_encrypt(context, crypto, 1, ptr, length, data); else ret = krb5_decrypt(context, crypto, 1, ptr, length, data); krb5_crypto_destroy(context, crypto); return ret; }
static OM_uint32 unwrap_des3 (OM_uint32 * minor_status, const gsskrb5_ctx context_handle, krb5_context context, const gss_buffer_t input_message_buffer, gss_buffer_t output_message_buffer, int * conf_state, gss_qop_t * qop_state, krb5_keyblock *key ) { u_char *p; size_t len; u_char *seq; krb5_data seq_data; u_char cksum[20]; uint32_t seq_number; size_t padlength; OM_uint32 ret; int cstate; krb5_crypto crypto; Checksum csum; int cmp; p = input_message_buffer->value; ret = _gsskrb5_verify_header (&p, input_message_buffer->length, "\x02\x01", GSS_KRB5_MECHANISM); if (ret) return ret; if (memcmp (p, "\x04\x00", 2) != 0) /* HMAC SHA1 DES3_KD */ return GSS_S_BAD_SIG; p += 2; if (memcmp (p, "\x02\x00", 2) == 0) { cstate = 1; } else if (memcmp (p, "\xff\xff", 2) == 0) { cstate = 0; } else return GSS_S_BAD_MIC; p += 2; if(conf_state != NULL) *conf_state = cstate; if (memcmp (p, "\xff\xff", 2) != 0) return GSS_S_DEFECTIVE_TOKEN; p += 2; p += 28; len = p - (u_char *)input_message_buffer->value; if(cstate) { /* decrypt data */ krb5_data tmp; ret = krb5_crypto_init(context, key, ETYPE_DES3_CBC_NONE, &crypto); if (ret) { *minor_status = ret; return GSS_S_FAILURE; } ret = krb5_decrypt(context, crypto, KRB5_KU_USAGE_SEAL, p, input_message_buffer->length - len, &tmp); krb5_crypto_destroy(context, crypto); if (ret) { *minor_status = ret; return GSS_S_FAILURE; } assert (tmp.length == input_message_buffer->length - len); memcpy (p, tmp.data, tmp.length); krb5_data_free(&tmp); } /* check pad */ ret = _gssapi_verify_pad(input_message_buffer, input_message_buffer->length - len, &padlength); if (ret) return ret; /* verify sequence number */ HEIMDAL_MUTEX_lock(&context_handle->ctx_id_mutex); p -= 28; ret = krb5_crypto_init(context, key, ETYPE_DES3_CBC_NONE, &crypto); if (ret) { *minor_status = ret; HEIMDAL_MUTEX_unlock(&context_handle->ctx_id_mutex); return GSS_S_FAILURE; } { DES_cblock ivec; memcpy(&ivec, p + 8, 8); ret = krb5_decrypt_ivec (context, crypto, KRB5_KU_USAGE_SEQ, p, 8, &seq_data, &ivec); } krb5_crypto_destroy (context, crypto); if (ret) { *minor_status = ret; HEIMDAL_MUTEX_unlock(&context_handle->ctx_id_mutex); return GSS_S_FAILURE; } if (seq_data.length != 8) { krb5_data_free (&seq_data); *minor_status = 0; HEIMDAL_MUTEX_unlock(&context_handle->ctx_id_mutex); return GSS_S_BAD_MIC; } seq = seq_data.data; _gsskrb5_decode_om_uint32(seq, &seq_number); if (context_handle->more_flags & LOCAL) cmp = memcmp(&seq[4], "\xff\xff\xff\xff", 4); else cmp = memcmp(&seq[4], "\x00\x00\x00\x00", 4); krb5_data_free (&seq_data); if (cmp != 0) { *minor_status = 0; HEIMDAL_MUTEX_unlock(&context_handle->ctx_id_mutex); return GSS_S_BAD_MIC; } ret = _gssapi_msg_order_check(context_handle->order, seq_number); if (ret) { *minor_status = 0; HEIMDAL_MUTEX_unlock(&context_handle->ctx_id_mutex); return ret; } HEIMDAL_MUTEX_unlock(&context_handle->ctx_id_mutex); /* verify checksum */ memcpy (cksum, p + 8, 20); memcpy (p + 20, p - 8, 8); csum.cksumtype = CKSUMTYPE_HMAC_SHA1_DES3; csum.checksum.length = 20; csum.checksum.data = cksum; ret = krb5_crypto_init(context, key, 0, &crypto); if (ret) { *minor_status = ret; return GSS_S_FAILURE; } ret = krb5_verify_checksum (context, crypto, KRB5_KU_USAGE_SIGN, p + 20, input_message_buffer->length - len + 8, &csum); krb5_crypto_destroy (context, crypto); if (ret) { *minor_status = ret; return GSS_S_FAILURE; } /* copy out data */ output_message_buffer->length = input_message_buffer->length - len - padlength - 8; output_message_buffer->value = malloc(output_message_buffer->length); if(output_message_buffer->length != 0 && output_message_buffer->value == NULL) return GSS_S_FAILURE; memcpy (output_message_buffer->value, p + 36, output_message_buffer->length); return GSS_S_COMPLETE; }
static int rxkad_keytab_decrypt(int kvno, int et, void *in, size_t inlen, void *out, size_t *outlen) { krb5_error_code code; /* use heimdal api if available, since heimdal's interface to krb5_c_decrypt is non-standard and annoying to use */ #ifdef HAVE_KRB5_CRYPTO_INIT krb5_crypto kcrypto; #else krb5_enc_data ind; #endif krb5_data outd; int i, foundkey; MUTEX_ENTER(&krb5_lock); reload_keys(); if (have_keytab_keys == 0) { MUTEX_EXIT(&krb5_lock); return RXKADUNKNOWNKEY; } foundkey = 0; code = -1; for (i = 0; i < nkeys; i++) { /* foundkey determines what error code we return for failure */ if (ktent[i].vno == kvno) foundkey = 1; /* but check against all keys if the enctype matches, for robustness */ if (kb_enctype(kte_keyblock(ktent[i])) == et) { #ifdef HAVE_KRB5_CRYPTO_INIT code = krb5_crypto_init(k5ctx, kte_keyblock(ktent[i]), et, &kcrypto); if (code == 0) { code = krb5_decrypt(k5ctx, kcrypto, KRB5_KEYUSAGE_KDC_REP_TICKET, in, inlen, &outd); krb5_crypto_destroy(k5ctx, kcrypto); } if (code == 0) { if (outd.length > *outlen) { /* This should never happen, but don't assert since we may * be dealing with untrusted user data. */ code = EINVAL; krb5_data_free(&outd); outd.data = NULL; } } if (code == 0) { /* heimdal allocates new memory for the decrypted data; put * the data back into the requested 'out' buffer */ *outlen = outd.length; memcpy(out, outd.data, outd.length); krb5_data_free(&outd); break; } #else outd.length = *outlen; outd.data = out; ind.ciphertext.length = inlen; ind.ciphertext.data = in; ind.enctype = et; ind.kvno = kvno; code = krb5_c_decrypt(k5ctx, kte_keyblock(ktent[i]), KRB5_KEYUSAGE_KDC_REP_TICKET, NULL, &ind, &outd); if (code == 0) { *outlen = outd.length; break; } #endif } } MUTEX_EXIT(&krb5_lock); if (code == 0) return 0; if (foundkey != 0) return RXKADBADTICKET; return RXKADUNKNOWNKEY; }
u32 krb5_read_token(struct krb5_ctx *ctx, struct xdr_netobj *read_token, struct xdr_netobj *message_buffer, int *qop_state, int toktype) { s32 code; int tmsglen = 0; int conflen = 0; int signalg; int sealalg; struct xdr_netobj token = {.len = 0, .data = NULL}; s32 checksum_type; struct xdr_netobj cksum; struct xdr_netobj md5cksum = {.len = 0, .data = NULL}; struct xdr_netobj plaind; char *data_ptr; s32 now; unsigned char *plain = NULL; int cksum_len = 0; int plainlen = 0; int direction; s32 seqnum; unsigned char *ptr = (unsigned char *)read_token->data; int bodysize; u32 ret = GSS_S_DEFECTIVE_TOKEN; dprintk("RPC: krb5_read_token\n"); if (g_verify_token_header((struct xdr_netobj *) &ctx->mech_used, &bodysize, &ptr, toktype, read_token->len)) goto out; if (toktype == KG_TOK_WRAP_MSG) { message_buffer->len = 0; message_buffer->data = NULL; } /* get the sign and seal algorithms */ signalg = ptr[0] + (ptr[1] << 8); sealalg = ptr[2] + (ptr[3] << 8); /* Sanity checks */ if ((ptr[4] != 0xff) || (ptr[5] != 0xff)) goto out; if (((toktype != KG_TOK_WRAP_MSG) && (sealalg != 0xffff)) || ((toktype == KG_TOK_WRAP_MSG) && (sealalg == 0xffff))) goto out; /* in the current spec, there is only one valid seal algorithm per key type, so a simple comparison is ok */ if ((toktype == KG_TOK_WRAP_MSG) && !(sealalg == ctx->sealalg)) goto out; /* there are several mappings of seal algorithms to sign algorithms, but few enough that we can try them all. */ if ((ctx->sealalg == SEAL_ALG_NONE && signalg > 1) || (ctx->sealalg == SEAL_ALG_1 && signalg != SGN_ALG_3) || (ctx->sealalg == SEAL_ALG_DES3KD && signalg != SGN_ALG_HMAC_SHA1_DES3_KD)) goto out; /* starting with a single alg */ switch (signalg) { case SGN_ALG_DES_MAC_MD5: cksum_len = 8; break; default: goto out; } if (toktype == KG_TOK_WRAP_MSG) tmsglen = bodysize - (14 + cksum_len); /* get the token parameters */ /* decode the message, if WRAP */ if (toktype == KG_TOK_WRAP_MSG) { dprintk("RPC: krb5_read_token KG_TOK_WRAP_MSG\n"); plain = kmalloc(tmsglen, GFP_KERNEL); ret = GSS_S_FAILURE; if (plain == NULL) goto out; code = krb5_decrypt(ctx->enc, NULL, ptr + 14 + cksum_len, plain, tmsglen); if (code) goto out; plainlen = tmsglen; conflen = crypto_tfm_alg_blocksize(ctx->enc); token.len = tmsglen - conflen - plain[tmsglen - 1]; if (token.len) { token.data = kmalloc(token.len, GFP_KERNEL); if (token.data == NULL) goto out; memcpy(token.data, plain + conflen, token.len); } } else if (toktype == KG_TOK_MIC_MSG) { dprintk("RPC: krb5_read_token KG_TOK_MIC_MSG\n"); token = *message_buffer; plain = token.data; plainlen = token.len; } else { token.len = 0; token.data = NULL; plain = token.data; plainlen = token.len; } dprintk("RPC krb5_read_token: token.len %d plainlen %d\n", token.len, plainlen); /* compute the checksum of the message */ /* initialize the the cksum */ switch (signalg) { case SGN_ALG_DES_MAC_MD5: checksum_type = CKSUMTYPE_RSA_MD5; break; default: ret = GSS_S_DEFECTIVE_TOKEN; goto out; } switch (signalg) { case SGN_ALG_DES_MAC_MD5: dprintk("RPC krb5_read_token SGN_ALG_DES_MAC_MD5\n"); /* compute the checksum of the message. * 8 = bytes of token body to be checksummed according to spec */ data_ptr = kmalloc(8 + plainlen, GFP_KERNEL); ret = GSS_S_FAILURE; if (!data_ptr) goto out; memcpy(data_ptr, ptr - 2, 8); memcpy(data_ptr + 8, plain, plainlen); plaind.len = 8 + plainlen; plaind.data = data_ptr; code = krb5_make_checksum(checksum_type, &plaind, &md5cksum); kfree(data_ptr); if (code) goto out; code = krb5_encrypt(ctx->seq, NULL, md5cksum.data, md5cksum.data, 16); if (code) goto out; if (signalg == 0) cksum.len = 8; else cksum.len = 16; cksum.data = md5cksum.data + 16 - cksum.len; dprintk ("RPC: krb5_read_token: memcmp digest cksum.len %d:\n", cksum.len); dprintk(" md5cksum.data\n"); print_hexl((u32 *) md5cksum.data, 16, 0); dprintk(" cksum.data:\n"); print_hexl((u32 *) cksum.data, cksum.len, 0); { u32 *p; (u8 *) p = ptr + 14; dprintk(" ptr+14:\n"); print_hexl(p, cksum.len, 0); } code = memcmp(cksum.data, ptr + 14, cksum.len); break; default: ret = GSS_S_DEFECTIVE_TOKEN; goto out; } ret = GSS_S_BAD_SIG; if (code) goto out; /* it got through unscathed. Make sure the context is unexpired */ if (toktype == KG_TOK_WRAP_MSG) *message_buffer = token; if (qop_state) *qop_state = GSS_C_QOP_DEFAULT; now = jiffies; ret = GSS_S_CONTEXT_EXPIRED; if (now > ctx->endtime) goto out; /* do sequencing checks */ ret = GSS_S_BAD_SIG; if ((code = krb5_get_seq_num(ctx->seq, ptr + 14, ptr + 6, &direction, &seqnum))) goto out; if ((ctx->initiate && direction != 0xff) || (!ctx->initiate && direction != 0)) goto out; ret = GSS_S_COMPLETE; out: if (md5cksum.data) kfree(md5cksum.data); if (toktype == KG_TOK_WRAP_MSG) { if (plain) kfree(plain); if (ret && token.data) kfree(token.data); } return ret; }
int ksm_process_in_msg(struct snmp_secmod_incoming_params *parms) { long temp; krb5_cksumtype cksumtype; krb5_auth_context auth_context = NULL; krb5_error_code retcode; krb5_checksum checksum; krb5_data ap_req, ivector; krb5_flags flags; krb5_keyblock *subkey = NULL; #ifdef MIT_NEW_CRYPTO krb5_data input, output; krb5_boolean valid; krb5_enc_data in_crypt; #else /* MIT_NEW_CRYPTO */ krb5_encrypt_block eblock; #endif /* MIT_NEW_CRYPTO */ krb5_ticket *ticket = NULL; int retval = SNMPERR_SUCCESS, response = 0; size_t length = parms->wholeMsgLen - (u_int) (parms->secParams - parms->wholeMsg); u_char *current = parms->secParams, type; size_t cksumlength, blocksize; long hint; char *cname; struct ksm_secStateRef *ksm_state; struct ksm_cache_entry *entry; DEBUGMSGTL(("ksm", "Processing has begun\n")); checksum.contents = NULL; ap_req.data = NULL; ivector.length = 0; ivector.data = NULL; /* * First, parse the security parameters (because we need the subkey inside * of the ticket to do anything */ if ((current = asn_parse_sequence(current, &length, &type, (ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_OCTET_STR), "ksm first octet")) == NULL) { DEBUGMSGTL(("ksm", "Initial security paramter parsing failed\n")); retval = SNMPERR_ASN_PARSE_ERR; goto error; } if ((current = asn_parse_sequence(current, &length, &type, (ASN_SEQUENCE | ASN_CONSTRUCTOR), "ksm sequence")) == NULL) { DEBUGMSGTL(("ksm", "Security parameter sequence parsing failed\n")); retval = SNMPERR_ASN_PARSE_ERR; goto error; } if ((current = asn_parse_int(current, &length, &type, &temp, sizeof(temp))) == NULL) { DEBUGMSGTL(("ksm", "Security parameter checksum type parsing" "failed\n")); retval = SNMPERR_ASN_PARSE_ERR; goto error; } cksumtype = temp; #ifdef MIT_NEW_CRYPTO if (!krb5_c_valid_cksumtype(cksumtype)) { DEBUGMSGTL(("ksm", "Invalid checksum type (%d)\n", cksumtype)); retval = SNMPERR_KRB5; snmp_set_detail("Invalid checksum type"); goto error; } if (!krb5_c_is_keyed_cksum(cksumtype)) { DEBUGMSGTL(("ksm", "Checksum type %d is not a keyed checksum\n", cksumtype)); snmp_set_detail("Checksum is not a keyed checksum"); retval = SNMPERR_KRB5; goto error; } if (!krb5_c_is_coll_proof_cksum(cksumtype)) { DEBUGMSGTL(("ksm", "Checksum type %d is not a collision-proof " "checksum\n", cksumtype)); snmp_set_detail("Checksum is not a collision-proof checksum"); retval = SNMPERR_KRB5; goto error; } #else /* ! MIT_NEW_CRYPTO */ if (!valid_cksumtype(cksumtype)) { DEBUGMSGTL(("ksm", "Invalid checksum type (%d)\n", cksumtype)); retval = SNMPERR_KRB5; snmp_set_detail("Invalid checksum type"); goto error; } if (!is_keyed_cksum(cksumtype)) { DEBUGMSGTL(("ksm", "Checksum type %d is not a keyed checksum\n", cksumtype)); snmp_set_detail("Checksum is not a keyed checksum"); retval = SNMPERR_KRB5; goto error; } if (!is_coll_proof_cksum(cksumtype)) { DEBUGMSGTL(("ksm", "Checksum type %d is not a collision-proof " "checksum\n", cksumtype)); snmp_set_detail("Checksum is not a collision-proof checksum"); retval = SNMPERR_KRB5; goto error; } #endif /* MIT_NEW_CRYPTO */ checksum.checksum_type = cksumtype; cksumlength = length; if ((current = asn_parse_sequence(current, &cksumlength, &type, (ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_OCTET_STR), "ksm checksum")) == NULL) { DEBUGMSGTL(("ksm", "Security parameter checksum parsing failed\n")); retval = SNMPERR_ASN_PARSE_ERR; goto error; } checksum.contents = malloc(cksumlength); if (!checksum.contents) { DEBUGMSGTL(("ksm", "Unable to malloc %d bytes for checksum.\n", cksumlength)); retval = SNMPERR_MALLOC; goto error; } memcpy(checksum.contents, current, cksumlength); checksum.length = cksumlength; checksum.checksum_type = cksumtype; /* * Zero out the checksum so the validation works correctly */ memset(current, 0, cksumlength); current += cksumlength; length = parms->wholeMsgLen - (u_int) (current - parms->wholeMsg); if ((current = asn_parse_sequence(current, &length, &type, (ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_OCTET_STR), "ksm ap_req")) == NULL) { DEBUGMSGTL(("ksm", "KSM security parameter AP_REQ/REP parsing " "failed\n")); retval = SNMPERR_ASN_PARSE_ERR; goto error; } ap_req.length = length; ap_req.data = malloc(length); if (!ap_req.data) { DEBUGMSGTL(("ksm", "KSM unable to malloc %d bytes for AP_REQ/REP.\n", length)); retval = SNMPERR_MALLOC; goto error; } memcpy(ap_req.data, current, length); current += length; length = parms->wholeMsgLen - (u_int) (current - parms->wholeMsg); if ((current = asn_parse_int(current, &length, &type, &hint, sizeof(hint))) == NULL) { DEBUGMSGTL(("ksm", "KSM security parameter hint parsing failed\n")); retval = SNMPERR_ASN_PARSE_ERR; goto error; } /* * Okay! We've got it all! Now try decoding the damn ticket. * * But of course there's a WRINKLE! We need to figure out if we're * processing a AP_REQ or an AP_REP. How do we do that? We're going * to cheat, and look at the first couple of bytes (which is what * the Kerberos library routines do anyway). * * If there are ever new Kerberos message formats, we'll need to fix * this here. * * If it's a _response_, then we need to get the auth_context * from our cache. */ if (ap_req.length && (ap_req.data[0] == 0x6e || ap_req.data[0] == 0x4e)) { /* * We need to initalize the authorization context, and set the * replay cache in it (and initialize the replay cache if we * haven't already */ retcode = krb5_auth_con_init(kcontext, &auth_context); if (retcode) { DEBUGMSGTL(("ksm", "krb5_auth_con_init failed: %s\n", error_message(retcode))); retval = SNMPERR_KRB5; snmp_set_detail(error_message(retcode)); goto error; } if (!rcache) { krb5_data server; server.data = "host"; server.length = strlen(server.data); retcode = krb5_get_server_rcache(kcontext, &server, &rcache); if (retcode) { DEBUGMSGTL(("ksm", "krb5_get_server_rcache failed: %s\n", error_message(retcode))); retval = SNMPERR_KRB5; snmp_set_detail(error_message(retcode)); goto error; } } retcode = krb5_auth_con_setrcache(kcontext, auth_context, rcache); if (retcode) { DEBUGMSGTL(("ksm", "krb5_auth_con_setrcache failed: %s\n", error_message(retcode))); retval = SNMPERR_KRB5; snmp_set_detail(error_message(retcode)); goto error; } retcode = krb5_rd_req(kcontext, &auth_context, &ap_req, NULL, keytab, &flags, &ticket); krb5_auth_con_setrcache(kcontext, auth_context, NULL); if (retcode) { DEBUGMSGTL(("ksm", "krb5_rd_req() failed: %s\n", error_message(retcode))); retval = SNMPERR_KRB5; snmp_set_detail(error_message(retcode)); goto error; } retcode = krb5_unparse_name(kcontext, ticket->enc_part2->client, &cname); if (retcode == 0) { DEBUGMSGTL(("ksm", "KSM authenticated principal name: %s\n", cname)); free(cname); } /* * Check to make sure AP_OPTS_MUTUAL_REQUIRED was set */ if (!(flags & AP_OPTS_MUTUAL_REQUIRED)) { DEBUGMSGTL(("ksm", "KSM MUTUAL_REQUIRED not set in request!\n")); retval = SNMPERR_KRB5; snmp_set_detail("MUTUAL_REQUIRED not set in message"); goto error; } retcode = krb5_auth_con_getremotesubkey(kcontext, auth_context, &subkey); if (retcode) { DEBUGMSGTL(("ksm", "KSM remote subkey retrieval failed: %s\n", error_message(retcode))); retval = SNMPERR_KRB5; snmp_set_detail(error_message(retcode)); goto error; } } else if (ap_req.length && (ap_req.data[0] == 0x6f || ap_req.data[0] == 0x4f)) { /* * Looks like a response; let's see if we've got that auth_context * in our cache. */ krb5_ap_rep_enc_part *repl = NULL; response = 1; entry = ksm_get_cache(parms->pdu->msgid); if (!entry) { DEBUGMSGTL(("ksm", "KSM: Unable to find auth_context for PDU with " "message ID of %ld\n", parms->pdu->msgid)); retval = SNMPERR_KRB5; goto error; } auth_context = entry->auth_context; /* * In that case, let's call the rd_rep function */ retcode = krb5_rd_rep(kcontext, auth_context, &ap_req, &repl); if (repl) krb5_free_ap_rep_enc_part(kcontext, repl); if (retcode) { DEBUGMSGTL(("ksm", "KSM: krb5_rd_rep() failed: %s\n", error_message(retcode))); retval = SNMPERR_KRB5; goto error; } DEBUGMSGTL(("ksm", "KSM: krb5_rd_rep() decoded successfully.\n")); retcode = krb5_auth_con_getlocalsubkey(kcontext, auth_context, &subkey); if (retcode) { DEBUGMSGTL(("ksm", "Unable to retrieve local subkey: %s\n", error_message(retcode))); retval = SNMPERR_KRB5; snmp_set_detail("Unable to retrieve local subkey"); goto error; } } else { DEBUGMSGTL(("ksm", "Unknown Kerberos message type (%02x)\n", ap_req.data[0])); retval = SNMPERR_KRB5; snmp_set_detail("Unknown Kerberos message type"); goto error; } #ifdef MIT_NEW_CRYPTO input.data = (char *) parms->wholeMsg; input.length = parms->wholeMsgLen; retcode = krb5_c_verify_checksum(kcontext, subkey, KSM_KEY_USAGE_CHECKSUM, &input, &checksum, &valid); #else /* MIT_NEW_CRYPTO */ retcode = krb5_verify_checksum(kcontext, cksumtype, &checksum, parms->wholeMsg, parms->wholeMsgLen, (krb5_pointer) subkey->contents, subkey->length); #endif /* MIT_NEW_CRYPTO */ if (retcode) { DEBUGMSGTL(("ksm", "KSM checksum verification failed: %s\n", error_message(retcode))); retval = SNMPERR_KRB5; snmp_set_detail(error_message(retcode)); goto error; } /* * Don't ask me why they didn't simply return an error, but we have * to check to see if "valid" is false. */ #ifdef MIT_NEW_CRYPTO if (!valid) { DEBUGMSGTL(("ksm", "Computed checksum did not match supplied " "checksum!\n")); retval = SNMPERR_KRB5; snmp_set_detail ("Computed checksum did not match supplied checksum"); goto error; } #endif /* MIT_NEW_CRYPTO */ /* * Handle an encrypted PDU. Note that it's an OCTET_STRING of the * output of whatever Kerberos cryptosystem you're using (defined by * the encryption type). Note that this is NOT the EncryptedData * sequence - it's what goes in the "cipher" field of EncryptedData. */ if (parms->secLevel == SNMP_SEC_LEVEL_AUTHPRIV) { if ((current = asn_parse_sequence(current, &length, &type, (ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_OCTET_STR), "ksm pdu")) == NULL) { DEBUGMSGTL(("ksm", "KSM sPDU octet decoding failed\n")); retval = SNMPERR_ASN_PARSE_ERR; goto error; } /* * The PDU is now pointed at by "current", and the length is in * "length". */ DEBUGMSGTL(("ksm", "KSM starting sPDU decode\n")); /* * We need to set up a blank initialization vector for the decryption. * Use a block of all zero's (which is dependent on the block size * of the encryption method). */ #ifdef MIT_NEW_CRYPTO retcode = krb5_c_block_size(kcontext, subkey->enctype, &blocksize); if (retcode) { DEBUGMSGTL(("ksm", "Unable to determine crypto block size: %s\n", error_message(retcode))); snmp_set_detail(error_message(retcode)); retval = SNMPERR_KRB5; goto error; } #else /* MIT_NEW_CRYPTO */ blocksize = krb5_enctype_array[subkey->enctype]->system->block_length; #endif /* MIT_NEW_CRYPTO */ ivector.data = malloc(blocksize); if (!ivector.data) { DEBUGMSGTL(("ksm", "Unable to allocate %d bytes for ivector\n", blocksize)); retval = SNMPERR_MALLOC; goto error; } ivector.length = blocksize; memset(ivector.data, 0, blocksize); #ifndef MIT_NEW_CRYPTO krb5_use_enctype(kcontext, &eblock, subkey->enctype); retcode = krb5_process_key(kcontext, &eblock, subkey); if (retcode) { DEBUGMSGTL(("ksm", "KSM key post-processing failed: %s\n", error_message(retcode))); snmp_set_detail(error_message(retcode)); retval = SNMPERR_KRB5; goto error; } #endif /* !MIT_NEW_CRYPTO */ if (length > *parms->scopedPduLen) { DEBUGMSGTL(("ksm", "KSM not enough room - have %d bytes to " "decrypt but only %d bytes available\n", length, *parms->scopedPduLen)); retval = SNMPERR_TOO_LONG; #ifndef MIT_NEW_CRYPTO krb5_finish_key(kcontext, &eblock); #endif /* ! MIT_NEW_CRYPTO */ goto error; } #ifdef MIT_NEW_CRYPTO in_crypt.ciphertext.data = (char *) current; in_crypt.ciphertext.length = length; in_crypt.enctype = subkey->enctype; output.data = (char *) *parms->scopedPdu; output.length = *parms->scopedPduLen; retcode = krb5_c_decrypt(kcontext, subkey, KSM_KEY_USAGE_ENCRYPTION, &ivector, &in_crypt, &output); #else /* MIT_NEW_CRYPTO */ retcode = krb5_decrypt(kcontext, (krb5_pointer) current, *parms->scopedPdu, length, &eblock, ivector.data); krb5_finish_key(kcontext, &eblock); #endif /* MIT_NEW_CRYPTO */ if (retcode) { DEBUGMSGTL(("ksm", "Decryption failed: %s\n", error_message(retcode))); snmp_set_detail(error_message(retcode)); retval = SNMPERR_KRB5; goto error; } *parms->scopedPduLen = length; } else { /* * Clear PDU */ *parms->scopedPdu = current; *parms->scopedPduLen = parms->wholeMsgLen - (current - parms->wholeMsg); } /* * A HUGE GROSS HACK */ *parms->maxSizeResponse = parms->maxMsgSize - 200; DEBUGMSGTL(("ksm", "KSM processing complete\n")); /* * Set the secName to the right value (a hack for now). But that's * only used for when we're processing a request, not a response. */ if (!response) { retcode = krb5_unparse_name(kcontext, ticket->enc_part2->client, &cname); if (retcode) { DEBUGMSGTL(("ksm", "KSM krb5_unparse_name failed: %s\n", error_message(retcode))); snmp_set_detail(error_message(retcode)); retval = SNMPERR_KRB5; goto error; } if (strlen(cname) > *parms->secNameLen + 1) { DEBUGMSGTL(("ksm", "KSM: Principal length (%d) is too long (%d)\n", strlen(cname), parms->secNameLen)); retval = SNMPERR_TOO_LONG; free(cname); goto error; } strcpy(parms->secName, cname); *parms->secNameLen = strlen(cname); free(cname); /* * Also, if we're not a response, keep around our auth_context so we * can encode the reply message correctly */ ksm_state = SNMP_MALLOC_STRUCT(ksm_secStateRef); if (!ksm_state) { DEBUGMSGTL(("ksm", "KSM unable to malloc memory for " "ksm_secStateRef\n")); retval = SNMPERR_MALLOC; goto error; } ksm_state->auth_context = auth_context; auth_context = NULL; ksm_state->cksumtype = cksumtype; *parms->secStateRef = ksm_state; } else { /* * We _still_ have to set the secName in process_in_msg(). Do * that now with what we were passed in before (we cached it, * remember?) */ memcpy(parms->secName, entry->secName, entry->secNameLen); *parms->secNameLen = entry->secNameLen; } /* * Just in case */ parms->secEngineID = (u_char *) ""; *parms->secEngineIDLen = 0; auth_context = NULL; /* So we don't try to free it on success */ error: if (retval == SNMPERR_ASN_PARSE_ERR && snmp_increment_statistic(STAT_SNMPINASNPARSEERRS) == 0) DEBUGMSGTL(("ksm", "Failed to increment statistics.\n")); if (subkey) krb5_free_keyblock(kcontext, subkey); if (checksum.contents) free(checksum.contents); if (ivector.data) free(ivector.data); if (ticket) krb5_free_ticket(kcontext, ticket); if (!response && auth_context) krb5_auth_con_free(kcontext, auth_context); if (ap_req.data) free(ap_req.data); return retval; }