static krb5_error_code pkinit_server_return_padata(krb5_context context, krb5_pa_data * padata, struct _krb5_db_entry_new * client, krb5_data *req_pkt, krb5_kdc_req * request, krb5_kdc_rep * reply, struct _krb5_key_data * client_key, krb5_keyblock * encrypting_key, krb5_pa_data ** send_pa, preauth_get_entry_data_proc server_get_entry_data, void *pa_plugin_context, void **pa_request_context) { krb5_error_code retval = 0; krb5_data scratch = {0, 0, NULL}; krb5_pa_pk_as_req *reqp = NULL; krb5_pa_pk_as_req_draft9 *reqp9 = NULL; int i = 0; unsigned char *subjectPublicKey = NULL; unsigned char *dh_pubkey = NULL, *server_key = NULL; unsigned int subjectPublicKey_len = 0; unsigned int server_key_len = 0, dh_pubkey_len = 0; krb5_kdc_dh_key_info dhkey_info; krb5_data *encoded_dhkey_info = NULL; krb5_pa_pk_as_rep *rep = NULL; krb5_pa_pk_as_rep_draft9 *rep9 = NULL; krb5_data *out_data = NULL; krb5_enctype enctype = -1; krb5_reply_key_pack *key_pack = NULL; krb5_reply_key_pack_draft9 *key_pack9 = NULL; krb5_data *encoded_key_pack = NULL; unsigned int num_types; krb5_cksumtype *cksum_types = NULL; pkinit_kdc_context plgctx; pkinit_kdc_req_context reqctx; int fixed_keypack = 0; *send_pa = NULL; if (padata == NULL || padata->length <= 0 || padata->contents == NULL) return 0; if (pa_request_context == NULL || *pa_request_context == NULL) { pkiDebug("missing request context \n"); return EINVAL; } plgctx = pkinit_find_realm_context(context, pa_plugin_context, request->server); if (plgctx == NULL) { pkiDebug("Unable to locate correct realm context\n"); return ENOENT; } pkiDebug("pkinit_return_padata: entered!\n"); reqctx = (pkinit_kdc_req_context)*pa_request_context; if (encrypting_key->contents) { free(encrypting_key->contents); encrypting_key->length = 0; encrypting_key->contents = NULL; } for(i = 0; i < request->nktypes; i++) { enctype = request->ktype[i]; if (!krb5_c_valid_enctype(enctype)) continue; else { pkiDebug("KDC picked etype = %d\n", enctype); break; } } if (i == request->nktypes) { retval = KRB5KDC_ERR_ETYPE_NOSUPP; goto cleanup; } switch((int)reqctx->pa_type) { case KRB5_PADATA_PK_AS_REQ: init_krb5_pa_pk_as_rep(&rep); if (rep == NULL) { retval = ENOMEM; goto cleanup; } /* let's assume it's RSA. we'll reset it to DH if needed */ rep->choice = choice_pa_pk_as_rep_encKeyPack; break; case KRB5_PADATA_PK_AS_REP_OLD: case KRB5_PADATA_PK_AS_REQ_OLD: init_krb5_pa_pk_as_rep_draft9(&rep9); if (rep9 == NULL) { retval = ENOMEM; goto cleanup; } rep9->choice = choice_pa_pk_as_rep_draft9_encKeyPack; break; default: retval = KRB5KDC_ERR_PREAUTH_FAILED; goto cleanup; } if (reqctx->rcv_auth_pack != NULL && reqctx->rcv_auth_pack->clientPublicValue != NULL) { subjectPublicKey = reqctx->rcv_auth_pack->clientPublicValue->subjectPublicKey.data; subjectPublicKey_len = reqctx->rcv_auth_pack->clientPublicValue->subjectPublicKey.length; rep->choice = choice_pa_pk_as_rep_dhInfo; } else if (reqctx->rcv_auth_pack9 != NULL && reqctx->rcv_auth_pack9->clientPublicValue != NULL) { subjectPublicKey = reqctx->rcv_auth_pack9->clientPublicValue->subjectPublicKey.data; subjectPublicKey_len = reqctx->rcv_auth_pack9->clientPublicValue->subjectPublicKey.length; rep9->choice = choice_pa_pk_as_rep_draft9_dhSignedData; } /* if this DH, then process finish computing DH key */ if (rep != NULL && (rep->choice == choice_pa_pk_as_rep_dhInfo || rep->choice == choice_pa_pk_as_rep_draft9_dhSignedData)) { pkiDebug("received DH key delivery AS REQ\n"); retval = server_process_dh(context, plgctx->cryptoctx, reqctx->cryptoctx, plgctx->idctx, subjectPublicKey, subjectPublicKey_len, &dh_pubkey, &dh_pubkey_len, &server_key, &server_key_len); if (retval) { pkiDebug("failed to process/create dh paramters\n"); goto cleanup; } } if ((rep9 != NULL && rep9->choice == choice_pa_pk_as_rep_draft9_dhSignedData) || (rep != NULL && rep->choice == choice_pa_pk_as_rep_dhInfo)) { retval = pkinit_octetstring2key(context, enctype, server_key, server_key_len, encrypting_key); if (retval) { pkiDebug("pkinit_octetstring2key failed: %s\n", error_message(retval)); goto cleanup; } dhkey_info.subjectPublicKey.length = dh_pubkey_len; dhkey_info.subjectPublicKey.data = dh_pubkey; dhkey_info.nonce = request->nonce; dhkey_info.dhKeyExpiration = 0; retval = k5int_encode_krb5_kdc_dh_key_info(&dhkey_info, &encoded_dhkey_info); if (retval) { pkiDebug("encode_krb5_kdc_dh_key_info failed\n"); goto cleanup; } #ifdef DEBUG_ASN1 print_buffer_bin((unsigned char *)encoded_dhkey_info->data, encoded_dhkey_info->length, "/tmp/kdc_dh_key_info"); #endif switch ((int)padata->pa_type) { case KRB5_PADATA_PK_AS_REQ: retval = cms_signeddata_create(context, plgctx->cryptoctx, reqctx->cryptoctx, plgctx->idctx, CMS_SIGN_SERVER, 1, (unsigned char *)encoded_dhkey_info->data, encoded_dhkey_info->length, &rep->u.dh_Info.dhSignedData.data, &rep->u.dh_Info.dhSignedData.length); if (retval) { pkiDebug("failed to create pkcs7 signed data\n"); goto cleanup; } break; case KRB5_PADATA_PK_AS_REP_OLD: case KRB5_PADATA_PK_AS_REQ_OLD: retval = cms_signeddata_create(context, plgctx->cryptoctx, reqctx->cryptoctx, plgctx->idctx, CMS_SIGN_DRAFT9, 1, (unsigned char *)encoded_dhkey_info->data, encoded_dhkey_info->length, &rep9->u.dhSignedData.data, &rep9->u.dhSignedData.length); if (retval) { pkiDebug("failed to create pkcs7 signed data\n"); goto cleanup; } break; } } else { pkiDebug("received RSA key delivery AS REQ\n"); retval = krb5_c_make_random_key(context, enctype, encrypting_key); if (retval) { pkiDebug("unable to make a session key\n"); goto cleanup; } /* check if PA_TYPE of 132 is present which means the client is * requesting that a checksum is send back instead of the nonce */ for (i = 0; request->padata[i] != NULL; i++) { pkiDebug("%s: Checking pa_type 0x%08x\n", __FUNCTION__, request->padata[i]->pa_type); if (request->padata[i]->pa_type == 132) fixed_keypack = 1; } pkiDebug("%s: return checksum instead of nonce = %d\n", __FUNCTION__, fixed_keypack); /* if this is an RFC reply or draft9 client requested a checksum * in the reply instead of the nonce, create an RFC-style keypack */ if ((int)padata->pa_type == KRB5_PADATA_PK_AS_REQ || fixed_keypack) { init_krb5_reply_key_pack(&key_pack); if (key_pack == NULL) { retval = ENOMEM; goto cleanup; } /* retrieve checksums for a given enctype of the reply key */ retval = krb5_c_keyed_checksum_types(context, encrypting_key->enctype, &num_types, &cksum_types); if (retval) goto cleanup; /* pick the first of acceptable enctypes for the checksum */ retval = krb5_c_make_checksum(context, cksum_types[0], encrypting_key, KRB5_KEYUSAGE_TGS_REQ_AUTH_CKSUM, req_pkt, &key_pack->asChecksum); if (retval) { pkiDebug("unable to calculate AS REQ checksum\n"); goto cleanup; } #ifdef DEBUG_CKSUM pkiDebug("calculating checksum on buf size = %d\n", req_pkt->length); print_buffer(req_pkt->data, req_pkt->length); pkiDebug("checksum size = %d\n", key_pack->asChecksum.length); print_buffer(key_pack->asChecksum.contents, key_pack->asChecksum.length); pkiDebug("encrypting key (%d)\n", encrypting_key->length); print_buffer(encrypting_key->contents, encrypting_key->length); #endif krb5_copy_keyblock_contents(context, encrypting_key, &key_pack->replyKey); retval = k5int_encode_krb5_reply_key_pack(key_pack, &encoded_key_pack); if (retval) { pkiDebug("failed to encode reply_key_pack\n"); goto cleanup; } } switch ((int)padata->pa_type) { case KRB5_PADATA_PK_AS_REQ: rep->choice = choice_pa_pk_as_rep_encKeyPack; retval = cms_envelopeddata_create(context, plgctx->cryptoctx, reqctx->cryptoctx, plgctx->idctx, padata->pa_type, 1, (unsigned char *)encoded_key_pack->data, encoded_key_pack->length, &rep->u.encKeyPack.data, &rep->u.encKeyPack.length); break; case KRB5_PADATA_PK_AS_REP_OLD: case KRB5_PADATA_PK_AS_REQ_OLD: /* if the request is from the broken draft9 client that * expects back a nonce, create it now */ if (!fixed_keypack) { init_krb5_reply_key_pack_draft9(&key_pack9); if (key_pack9 == NULL) { retval = ENOMEM; goto cleanup; } key_pack9->nonce = reqctx->rcv_auth_pack9->pkAuthenticator.nonce; krb5_copy_keyblock_contents(context, encrypting_key, &key_pack9->replyKey); retval = k5int_encode_krb5_reply_key_pack_draft9(key_pack9, &encoded_key_pack); if (retval) { pkiDebug("failed to encode reply_key_pack\n"); goto cleanup; } } rep9->choice = choice_pa_pk_as_rep_draft9_encKeyPack; retval = cms_envelopeddata_create(context, plgctx->cryptoctx, reqctx->cryptoctx, plgctx->idctx, padata->pa_type, 1, (unsigned char *)encoded_key_pack->data, encoded_key_pack->length, &rep9->u.encKeyPack.data, &rep9->u.encKeyPack.length); break; } if (retval) { pkiDebug("failed to create pkcs7 enveloped data: %s\n", error_message(retval)); goto cleanup; } #ifdef DEBUG_ASN1 print_buffer_bin((unsigned char *)encoded_key_pack->data, encoded_key_pack->length, "/tmp/kdc_key_pack"); switch ((int)padata->pa_type) { case KRB5_PADATA_PK_AS_REQ: print_buffer_bin(rep->u.encKeyPack.data, rep->u.encKeyPack.length, "/tmp/kdc_enc_key_pack"); break; case KRB5_PADATA_PK_AS_REP_OLD: case KRB5_PADATA_PK_AS_REQ_OLD: print_buffer_bin(rep9->u.encKeyPack.data, rep9->u.encKeyPack.length, "/tmp/kdc_enc_key_pack"); break; } #endif } switch ((int)padata->pa_type) { case KRB5_PADATA_PK_AS_REQ: retval = k5int_encode_krb5_pa_pk_as_rep(rep, &out_data); break; case KRB5_PADATA_PK_AS_REP_OLD: case KRB5_PADATA_PK_AS_REQ_OLD: retval = k5int_encode_krb5_pa_pk_as_rep_draft9(rep9, &out_data); break; } if (retval) { pkiDebug("failed to encode AS_REP\n"); goto cleanup; } #ifdef DEBUG_ASN1 if (out_data != NULL) print_buffer_bin((unsigned char *)out_data->data, out_data->length, "/tmp/kdc_as_rep"); #endif *send_pa = (krb5_pa_data *) malloc(sizeof(krb5_pa_data)); if (*send_pa == NULL) { retval = ENOMEM; free(out_data->data); free(out_data); out_data = NULL; goto cleanup; } (*send_pa)->magic = KV5M_PA_DATA; switch ((int)padata->pa_type) { case KRB5_PADATA_PK_AS_REQ: (*send_pa)->pa_type = KRB5_PADATA_PK_AS_REP; break; case KRB5_PADATA_PK_AS_REQ_OLD: case KRB5_PADATA_PK_AS_REP_OLD: (*send_pa)->pa_type = KRB5_PADATA_PK_AS_REP_OLD; break; } (*send_pa)->length = out_data->length; (*send_pa)->contents = (krb5_octet *) out_data->data; cleanup: pkinit_fini_kdc_req_context(context, reqctx); if (scratch.data != NULL) free(scratch.data); if (out_data != NULL) free(out_data); if (encoded_dhkey_info != NULL) krb5_free_data(context, encoded_dhkey_info); if (encoded_key_pack != NULL) krb5_free_data(context, encoded_key_pack); if (dh_pubkey != NULL) free(dh_pubkey); if (server_key != NULL) free(server_key); if (cksum_types != NULL) free(cksum_types); switch ((int)padata->pa_type) { case KRB5_PADATA_PK_AS_REQ: free_krb5_pa_pk_as_req(&reqp); free_krb5_pa_pk_as_rep(&rep); free_krb5_reply_key_pack(&key_pack); break; case KRB5_PADATA_PK_AS_REP_OLD: case KRB5_PADATA_PK_AS_REQ_OLD: free_krb5_pa_pk_as_req_draft9(&reqp9); free_krb5_pa_pk_as_rep_draft9(&rep9); if (!fixed_keypack) free_krb5_reply_key_pack_draft9(&key_pack9); else free_krb5_reply_key_pack(&key_pack); break; } if (retval) pkiDebug("pkinit_verify_padata failure"); return retval; }
krb5_error_code KRB5_CALLCONV krb5_mk_safe(krb5_context context, krb5_auth_context auth_context, const krb5_data *userdata, krb5_data *outbuf, krb5_replay_data *outdata) { krb5_error_code retval; krb5_key key; krb5_replay_data replaydata; /* Clear replaydata block */ memset(&replaydata, 0, sizeof(krb5_replay_data)); /* Get key */ if ((key = auth_context->send_subkey) == NULL) key = auth_context->key; /* Get replay info */ if ((auth_context->auth_context_flags & KRB5_AUTH_CONTEXT_DO_TIME) && (auth_context->rcache == NULL)) return KRB5_RC_REQUIRED; if (((auth_context->auth_context_flags & KRB5_AUTH_CONTEXT_RET_TIME) || (auth_context->auth_context_flags & KRB5_AUTH_CONTEXT_RET_SEQUENCE)) && (outdata == NULL)) /* Need a better error */ return KRB5_RC_REQUIRED; if (!auth_context->local_addr) return KRB5_LOCAL_ADDR_REQUIRED; if ((auth_context->auth_context_flags & KRB5_AUTH_CONTEXT_DO_TIME) || (auth_context->auth_context_flags & KRB5_AUTH_CONTEXT_RET_TIME)) { if ((retval = krb5_us_timeofday(context, &replaydata.timestamp, &replaydata.usec))) return retval; if (auth_context->auth_context_flags & KRB5_AUTH_CONTEXT_RET_TIME) { outdata->timestamp = replaydata.timestamp; outdata->usec = replaydata.usec; } } if ((auth_context->auth_context_flags & KRB5_AUTH_CONTEXT_DO_SEQUENCE) || (auth_context->auth_context_flags & KRB5_AUTH_CONTEXT_RET_SEQUENCE)) { replaydata.seq = auth_context->local_seq_number++; if (auth_context->auth_context_flags & KRB5_AUTH_CONTEXT_RET_SEQUENCE) outdata->seq = replaydata.seq; } { krb5_address * premote_fulladdr = NULL; krb5_address * plocal_fulladdr; krb5_address remote_fulladdr; krb5_address local_fulladdr; krb5_cksumtype sumtype; CLEANUP_INIT(2); if (auth_context->local_port) { if (!(retval = krb5_make_fulladdr(context, auth_context->local_addr, auth_context->local_port, &local_fulladdr))){ CLEANUP_PUSH(local_fulladdr.contents, free); plocal_fulladdr = &local_fulladdr; } else { goto error; } } else { plocal_fulladdr = auth_context->local_addr; } if (auth_context->remote_addr) { if (auth_context->remote_port) { if (!(retval = krb5_make_fulladdr(context,auth_context->remote_addr, auth_context->remote_port, &remote_fulladdr))){ CLEANUP_PUSH(remote_fulladdr.contents, free); premote_fulladdr = &remote_fulladdr; } else { CLEANUP_DONE(); goto error; } } else { premote_fulladdr = auth_context->remote_addr; } } { krb5_enctype enctype = krb5_k_key_enctype(context, key); unsigned int nsumtypes; unsigned int i; krb5_cksumtype *sumtypes; retval = krb5_c_keyed_checksum_types (context, enctype, &nsumtypes, &sumtypes); if (retval) { CLEANUP_DONE (); goto error; } if (nsumtypes == 0) { retval = KRB5_BAD_ENCTYPE; krb5_free_cksumtypes (context, sumtypes); CLEANUP_DONE (); goto error; } for (i = 0; i < nsumtypes; i++) if (auth_context->safe_cksumtype == sumtypes[i]) break; krb5_free_cksumtypes (context, sumtypes); if (i < nsumtypes) sumtype = auth_context->safe_cksumtype; else { switch (enctype) { case ENCTYPE_DES_CBC_MD4: sumtype = CKSUMTYPE_RSA_MD4_DES; break; case ENCTYPE_DES_CBC_MD5: case ENCTYPE_DES_CBC_CRC: sumtype = CKSUMTYPE_RSA_MD5_DES; break; default: retval = krb5int_c_mandatory_cksumtype(context, enctype, &sumtype); if (retval) { CLEANUP_DONE(); goto error; } break; } } } if ((retval = krb5_mk_safe_basic(context, userdata, key, &replaydata, plocal_fulladdr, premote_fulladdr, sumtype, outbuf))) { CLEANUP_DONE(); goto error; } CLEANUP_DONE(); } if (auth_context->auth_context_flags & KRB5_AUTH_CONTEXT_DO_TIME) { krb5_donot_replay replay; if ((retval = krb5_gen_replay_name(context, auth_context->local_addr, "_safe", &replay.client))) { free(outbuf); goto error; } replay.server = ""; /* XXX */ replay.msghash = NULL; replay.cusec = replaydata.usec; replay.ctime = replaydata.timestamp; if ((retval = krb5_rc_store(context, auth_context->rcache, &replay))) { /* should we really error out here? XXX */ free(outbuf); goto error; } free(replay.client); } return 0; error: if ((auth_context->auth_context_flags & KRB5_AUTH_CONTEXT_DO_SEQUENCE) || (auth_context->auth_context_flags & KRB5_AUTH_CONTEXT_RET_SEQUENCE)) auth_context->local_seq_number--; return retval; }
static krb5_error_code client_process(krb5_context kcontext, void *client_plugin_context, void *client_request_context, krb5_get_init_creds_opt *opt, preauth_get_client_data_proc client_get_data_proc, struct _krb5_preauth_client_rock *rock, krb5_kdc_req *request, krb5_data *encoded_request_body, krb5_data *encoded_previous_request, krb5_pa_data *pa_data, krb5_prompter_fct prompter, void *prompter_data, preauth_get_as_key_proc gak_fct, void *gak_data, krb5_data *salt, krb5_data *s2kparams, krb5_keyblock *as_key, krb5_pa_data **out_pa_data) { krb5_pa_data *send_pa; krb5_checksum checksum; krb5_enctype enctype; krb5_cksumtype *cksumtypes; krb5_error_code status = 0; krb5_int32 cksumtype, *enctypes; unsigned int i, n_enctypes, cksumtype_count; int num_gic_info = 0; krb5_gic_opt_pa_data *gic_info; status = krb5_get_init_creds_opt_get_pa(kcontext, opt, &num_gic_info, &gic_info); if (status && status != ENOENT) { #ifdef DEBUG fprintf(stderr, "Error from krb5_get_init_creds_opt_get_pa: %s\n", error_message(status)); #endif return status; } #ifdef DEBUG fprintf(stderr, "(cksum_body) Got the following gic options:\n"); #endif for (i = 0; i < num_gic_info; i++) { #ifdef DEBUG fprintf(stderr, " '%s' = '%s'\n", gic_info[i].attr, gic_info[i].value); #endif } krb5_get_init_creds_opt_free_pa(kcontext, num_gic_info, gic_info); memset(&checksum, 0, sizeof(checksum)); /* Get the user's long-term key if we haven't asked for it yet. Try * all of the encryption types which the server supports. */ if (as_key->length == 0) { if ((pa_data != NULL) && (pa_data->length >= 4)) { #ifdef DEBUG fprintf(stderr, "%d bytes of preauth data.\n", pa_data->length); #endif n_enctypes = pa_data->length / 4; enctypes = (krb5_int32*) pa_data->contents; } else { n_enctypes = request->nktypes; } for (i = 0; i < n_enctypes; i++) { if ((pa_data != NULL) && (pa_data->length >= 4)) { memcpy(&enctype, pa_data->contents + 4 * i, 4); enctype = ntohl(enctype); } else { enctype = request->ktype[i]; } #ifdef DEBUG fprintf(stderr, "Asking for AS key (type = %d).\n", enctype); #endif status = (*gak_fct)(kcontext, request->client, enctype, prompter, prompter_data, salt, s2kparams, as_key, gak_data); if (status == 0) break; } if (status != 0) return status; } #ifdef DEBUG fprintf(stderr, "Got AS key (type = %d).\n", as_key->enctype); #endif /* Determine an appropriate checksum type for this key. */ cksumtype_count = 0; cksumtypes = NULL; status = krb5_c_keyed_checksum_types(kcontext, as_key->enctype, &cksumtype_count, &cksumtypes); if (status != 0) return status; /* Generate the checksum. */ for (i = 0; i < cksumtype_count; i++) { status = krb5_c_make_checksum(kcontext, cksumtypes[i], as_key, KRB5_KEYUSAGE_TGS_REQ_AUTH_CKSUM, encoded_request_body, &checksum); if (status == 0) { #ifdef DEBUG fprintf(stderr, "Made checksum (type = %d, %d bytes).\n", checksum.checksum_type, encoded_request_body->length); #endif break; } } cksumtype = htonl(cksumtypes[i]); krb5_free_cksumtypes(kcontext, cksumtypes); if (status != 0) { if (checksum.length > 0) krb5_free_checksum_contents(kcontext, &checksum); return status; } /* Allocate the preauth data structure. */ send_pa = malloc(sizeof(krb5_pa_data)); if (send_pa == NULL) { krb5_free_checksum_contents(kcontext, &checksum); return ENOMEM; } send_pa->pa_type = KRB5_PADATA_CKSUM_BODY_REQ; send_pa->length = 4 + checksum.length; send_pa->contents = malloc(4 + checksum.length); if (send_pa->contents == NULL) { krb5_free_checksum_contents(kcontext, &checksum); free(send_pa); return ENOMEM; } /* Store the checksum. */ memcpy(send_pa->contents, &cksumtype, 4); memcpy(send_pa->contents + 4, checksum.contents, checksum.length); *out_pa_data = send_pa; /* Clean up. */ krb5_free_checksum_contents(kcontext, &checksum); return 0; }
/* Verify a request from a client. */ static krb5_error_code server_verify(krb5_context kcontext, struct _krb5_db_entry_new *client, krb5_data *req_pkt, krb5_kdc_req *request, krb5_enc_tkt_part *enc_tkt_reply, krb5_pa_data *data, preauth_get_entry_data_proc server_get_entry_data, void *pa_module_context, void **pa_request_context, krb5_data **e_data) { krb5_int32 cksumtype; krb5_checksum checksum; krb5_boolean valid; krb5_data *key_data, *req_body; krb5_keyblock *keys, *key; size_t length; int i; unsigned int j, cksumtypes_count; krb5_cksumtype *cksumtypes; krb5_error_code status; struct server_stats *stats; krb5_data *test_edata; stats = pa_module_context; /* Verify the preauth data. Start with the checksum type. */ if (data->length < 4) { stats->failures++; return KRB5KDC_ERR_PREAUTH_FAILED; } memcpy(&cksumtype, data->contents, 4); memset(&checksum, 0, sizeof(checksum)); checksum.checksum_type = ntohl(cksumtype); /* Verify that the amount of data we have left is what we expect. */ if (krb5_c_checksum_length(kcontext, checksum.checksum_type, &length) != 0) { #ifdef DEBUG fprintf(stderr, "Error determining checksum size (type = %d). " "Is it supported?\n", checksum.checksum_type); #endif stats->failures++; return KRB5KDC_ERR_SUMTYPE_NOSUPP; } if (data->length - 4 != length) { #ifdef DEBUG fprintf(stderr, "Checksum size doesn't match client packet size.\n"); #endif stats->failures++; return KRB5KDC_ERR_PREAUTH_FAILED; } checksum.length = length; /* Pull up the client's keys. */ key_data = NULL; if ((*server_get_entry_data)(kcontext, request, client, krb5plugin_preauth_keys, &key_data) != 0) { #ifdef DEBUG fprintf(stderr, "Error retrieving client keys.\n"); #endif stats->failures++; return KRB5KDC_ERR_PREAUTH_FAILED; } /* Find the key which would have been used to generate the checksum. */ keys = (krb5_keyblock *) key_data->data; key = NULL; for (i = 0; keys[i].enctype != 0; i++) { key = &keys[i]; cksumtypes_count = 0; cksumtypes = NULL; if (krb5_c_keyed_checksum_types(kcontext, key->enctype, &cksumtypes_count, &cksumtypes) != 0) continue; for (j = 0; j < cksumtypes_count; j++) { if (cksumtypes[j] == checksum.checksum_type) break; } if (cksumtypes != NULL) krb5_free_cksumtypes(kcontext, cksumtypes); if (j < cksumtypes_count) { #ifdef DEBUG fprintf(stderr, "Found checksum key.\n"); #endif break; } } if ((key == NULL) || (key->enctype == 0)) { for (i = 0; keys[i].enctype != 0; i++) krb5_free_keyblock_contents(kcontext, &keys[i]); krb5_free_data(kcontext, key_data); stats->failures++; return KRB5KDC_ERR_SUMTYPE_NOSUPP; } /* Save a copy of the key. */ if (krb5_copy_keyblock(kcontext, &keys[i], &key) != 0) { for (i = 0; keys[i].enctype != 0; i++) krb5_free_keyblock_contents(kcontext, &keys[i]); krb5_free_data(kcontext, key_data); stats->failures++; return KRB5KDC_ERR_SUMTYPE_NOSUPP; } for (i = 0; keys[i].enctype != 0; i++) krb5_free_keyblock_contents(kcontext, &keys[i]); krb5_free_data(kcontext, key_data); /* Rebuild a copy of the client's request-body. If we were serious * about doing this with any chance of working interoperability, we'd * extract the structure directly from the req_pkt structure. This * will probably work if it's us on both ends, though. */ req_body = NULL; if ((*server_get_entry_data)(kcontext, request, client, krb5plugin_preauth_request_body, &req_body) != 0) { krb5_free_keyblock(kcontext, key); stats->failures++; return KRB5KDC_ERR_PREAUTH_FAILED; } #ifdef DEBUG fprintf(stderr, "AS key type %d, checksum type %d, %d bytes.\n", key->enctype, checksum.checksum_type, req_body->length); #endif /* Verify the checksum itself. */ checksum.contents = data->contents + 4; valid = FALSE; status = krb5_c_verify_checksum(kcontext, key, KRB5_KEYUSAGE_TGS_REQ_AUTH_CKSUM, req_body, &checksum, &valid); /* Clean up. */ krb5_free_data(kcontext, req_body); krb5_free_keyblock(kcontext, key); /* Evaluate our results. */ if ((status != 0) || (!valid)) { #ifdef DEBUG if (status != 0) { fprintf(stderr, "Error in checksum verification.\n"); } else { fprintf(stderr, "Checksum mismatch.\n"); } #endif /* Return edata to exercise code that handles edata... */ test_edata = malloc(sizeof(*test_edata)); if (test_edata != NULL) { test_edata->data = malloc(20); if (test_edata->data == NULL) { free(test_edata); } else { test_edata->length = 20; memset(test_edata->data, 'F', 20); /* fill it with junk */ *e_data = test_edata; } } stats->failures++; return KRB5KDC_ERR_PREAUTH_FAILED; } /* Return edata to exercise code that handles edata... */ test_edata = malloc(sizeof(*test_edata)); if (test_edata != NULL) { test_edata->data = malloc(20); if (test_edata->data == NULL) { free(test_edata); } else { test_edata->length = 20; memset(test_edata->data, 'S', 20); /* fill it with junk */ *e_data = test_edata; } } /* Note that preauthentication succeeded. */ enc_tkt_reply->flags |= TKT_FLG_PRE_AUTH; stats->successes++; return 0; }
/* Verify a request from a client. */ static krb5_error_code server_verify(krb5_context kcontext, struct _krb5_db_entry_new *client, krb5_data *req_pkt, krb5_kdc_req *request, krb5_enc_tkt_part *enc_tkt_reply, krb5_pa_data *data, preauth_get_entry_data_proc server_get_entry_data, void *pa_module_context, void **pa_request_context, krb5_data **e_data, krb5_authdata ***authz_data) { krb5_int32 cksumtype; krb5_checksum checksum; krb5_boolean valid; krb5_data *key_data, *req_body; krb5_keyblock *keys, *key; size_t length; int i; unsigned int j, cksumtypes_count; krb5_cksumtype *cksumtypes; krb5_error_code status; struct server_stats *stats; krb5_data *test_edata; test_svr_req_ctx *svr_req_ctx; krb5_authdata **my_authz_data = NULL; stats = pa_module_context; #ifdef DEBUG fprintf(stderr, "cksum_body: server_verify\n"); #endif /* Verify the preauth data. Start with the checksum type. */ if (data->length < 4) { stats->failures++; return KRB5KDC_ERR_PREAUTH_FAILED; } memcpy(&cksumtype, data->contents, 4); memset(&checksum, 0, sizeof(checksum)); checksum.checksum_type = ntohl(cksumtype); /* Verify that the amount of data we have left is what we expect. */ if (krb5_c_checksum_length(kcontext, checksum.checksum_type, &length) != 0) { #ifdef DEBUG fprintf(stderr, "Error determining checksum size (type = %d). " "Is it supported?\n", checksum.checksum_type); #endif stats->failures++; return KRB5KDC_ERR_SUMTYPE_NOSUPP; } if (data->length - 4 != length) { #ifdef DEBUG fprintf(stderr, "Checksum size doesn't match client packet size.\n"); #endif stats->failures++; return KRB5KDC_ERR_PREAUTH_FAILED; } checksum.length = length; /* Pull up the client's keys. */ key_data = NULL; if ((*server_get_entry_data)(kcontext, request, client, krb5plugin_preauth_keys, &key_data) != 0) { #ifdef DEBUG fprintf(stderr, "Error retrieving client keys.\n"); #endif stats->failures++; return KRB5KDC_ERR_PREAUTH_FAILED; } /* Find the key which would have been used to generate the checksum. */ keys = (krb5_keyblock *) key_data->data; key = NULL; for (i = 0; keys[i].enctype != 0; i++) { key = &keys[i]; cksumtypes_count = 0; cksumtypes = NULL; if (krb5_c_keyed_checksum_types(kcontext, key->enctype, &cksumtypes_count, &cksumtypes) != 0) continue; for (j = 0; j < cksumtypes_count; j++) { if (cksumtypes[j] == checksum.checksum_type) break; } if (cksumtypes != NULL) krb5_free_cksumtypes(kcontext, cksumtypes); if (j < cksumtypes_count) { #ifdef DEBUG fprintf(stderr, "Found checksum key.\n"); #endif break; } } if ((key == NULL) || (key->enctype == 0)) { for (i = 0; keys[i].enctype != 0; i++) krb5_free_keyblock_contents(kcontext, &keys[i]); krb5_free_data(kcontext, key_data); stats->failures++; return KRB5KDC_ERR_SUMTYPE_NOSUPP; } /* Save a copy of the key. */ if (krb5_copy_keyblock(kcontext, &keys[i], &key) != 0) { for (i = 0; keys[i].enctype != 0; i++) krb5_free_keyblock_contents(kcontext, &keys[i]); krb5_free_data(kcontext, key_data); stats->failures++; return KRB5KDC_ERR_SUMTYPE_NOSUPP; } for (i = 0; keys[i].enctype != 0; i++) krb5_free_keyblock_contents(kcontext, &keys[i]); krb5_free_data(kcontext, key_data); /* Rebuild a copy of the client's request-body. If we were serious * about doing this with any chance of working interoperability, we'd * extract the structure directly from the req_pkt structure. This * will probably work if it's us on both ends, though. */ req_body = NULL; if ((*server_get_entry_data)(kcontext, request, client, krb5plugin_preauth_request_body, &req_body) != 0) { krb5_free_keyblock(kcontext, key); stats->failures++; return KRB5KDC_ERR_PREAUTH_FAILED; } #ifdef DEBUG fprintf(stderr, "AS key type %d, checksum type %d, %d bytes.\n", key->enctype, checksum.checksum_type, req_body->length); #endif /* Verify the checksum itself. */ checksum.contents = data->contents + 4; valid = FALSE; status = krb5_c_verify_checksum(kcontext, key, KRB5_KEYUSAGE_TGS_REQ_AUTH_CKSUM, req_body, &checksum, &valid); /* Clean up. */ krb5_free_data(kcontext, req_body); krb5_free_keyblock(kcontext, key); /* Evaluate our results. */ if ((status != 0) || (!valid)) { #ifdef DEBUG if (status != 0) { fprintf(stderr, "Error in checksum verification.\n"); } else { fprintf(stderr, "Checksum mismatch.\n"); } #endif /* Return edata to exercise code that handles edata... */ test_edata = malloc(sizeof(*test_edata)); if (test_edata != NULL) { test_edata->data = malloc(20); if (test_edata->data == NULL) { free(test_edata); } else { test_edata->length = 20; memset(test_edata->data, 'F', 20); /* fill it with junk */ *e_data = test_edata; } } stats->failures++; return KRB5KDC_ERR_PREAUTH_FAILED; } /* * Return some junk authorization data just to exercise the * code path handling the returned authorization data. * * NOTE that this is NOT VALID authorization data! */ #ifdef DEBUG fprintf(stderr, "cksum_body: doing authorization data!\n"); #endif my_authz_data = malloc(2 * sizeof(*my_authz_data)); if (my_authz_data != NULL) { #if 1 /* USE_5000_AD */ #define AD_ALLOC_SIZE 5000 /* ad_header consists of a sequence tag (0x30) and length * (0x82 0x1384) followed by octet string tag (0x04) and * length (0x82 0x1380) */ krb5_octet ad_header[] = {0x30, 0x82, 0x13, 0x84, 0x04, 0x82, 0x13, 0x80}; #else #define AD_ALLOC_SIZE 100 /* ad_header consists of a sequence tag (0x30) and length * (0x62) followed by octet string tag (0x04) and length * (0x60) */ krb5_octet ad_header[] = {0x30, 0x62, 0x04, 0x60}; #endif my_authz_data[1] = NULL; my_authz_data[0] = malloc(sizeof(krb5_authdata)); if (my_authz_data[0] == NULL) { free(my_authz_data); return ENOMEM; } my_authz_data[0]->contents = malloc(AD_ALLOC_SIZE); if (my_authz_data[0]->contents == NULL) { free(my_authz_data[0]); free(my_authz_data); return ENOMEM; } memset(my_authz_data[0]->contents, '\0', AD_ALLOC_SIZE); my_authz_data[0]->magic = KV5M_AUTHDATA; my_authz_data[0]->ad_type = 1; my_authz_data[0]->length = AD_ALLOC_SIZE; memcpy(my_authz_data[0]->contents, ad_header, sizeof(ad_header)); snprintf(my_authz_data[0]->contents + sizeof(ad_header), AD_ALLOC_SIZE - sizeof(ad_header), "cksum authorization data: %d bytes worth!\n", AD_ALLOC_SIZE); *authz_data = my_authz_data; #ifdef DEBUG fprintf(stderr, "Returning %d bytes of authorization data\n", AD_ALLOC_SIZE); #endif } /* Return edata to exercise code that handles edata... */ test_edata = malloc(sizeof(*test_edata)); if (test_edata != NULL) { test_edata->data = malloc(20); if (test_edata->data == NULL) { free(test_edata); } else { test_edata->length = 20; memset(test_edata->data, 'S', 20); /* fill it with junk */ *e_data = test_edata; } } /* Return a request context to exercise code that handles it */ svr_req_ctx = malloc(sizeof(*svr_req_ctx)); if (svr_req_ctx != NULL) { svr_req_ctx->value1 = 111111; svr_req_ctx->value2 = 222222; #ifdef DEBUG fprintf(stderr, "server_verify: returning context at %p\n", svr_req_ctx); #endif } *pa_request_context = svr_req_ctx; /* Note that preauthentication succeeded. */ enc_tkt_reply->flags |= TKT_FLG_PRE_AUTH; stats->successes++; return 0; }
int ksm_rgenerate_out_msg(struct snmp_secmod_outgoing_params *parms) { krb5_auth_context auth_context = NULL; krb5_error_code retcode; krb5_ccache cc = NULL; int retval = SNMPERR_SUCCESS; krb5_data outdata, ivector; krb5_keyblock *subkey = NULL; #ifdef MIT_NEW_CRYPTO krb5_data input; krb5_enc_data output; unsigned int numcksumtypes; krb5_cksumtype *cksumtype_array; #else /* MIT_NEW_CRYPTO */ krb5_encrypt_block eblock; #endif /* MIT_NEW_CRYPTO */ size_t blocksize, encrypted_length; unsigned char *encrypted_data = NULL; int zero = 0, i; u_char *cksum_pointer, *endp = *parms->wholeMsg; krb5_cksumtype cksumtype; krb5_checksum pdu_checksum; u_char **wholeMsg = parms->wholeMsg; size_t *offset = parms->wholeMsgOffset, seq_offset; struct ksm_secStateRef *ksm_state = (struct ksm_secStateRef *) parms->secStateRef; int rc; DEBUGMSGTL(("ksm", "Starting KSM processing\n")); outdata.length = 0; outdata.data = NULL; ivector.length = 0; ivector.data = NULL; pdu_checksum.contents = NULL; if (!ksm_state) { /* * If we don't have a ksm_state, then we're a request. Get a * credential cache and build a ap_req. */ retcode = krb5_cc_default(kcontext, &cc); if (retcode) { DEBUGMSGTL(("ksm", "KSM: krb5_cc_default failed: %s\n", error_message(retcode))); snmp_set_detail(error_message(retcode)); retval = SNMPERR_KRB5; goto error; } DEBUGMSGTL(("ksm", "KSM: Set credential cache successfully\n")); /* * This seems odd, since we don't need this until later (or earlier, * depending on how you look at it), but because the most likely * errors are Kerberos at this point, I'll get this now to save * time not encoding the rest of the packet. * * Also, we need the subkey to encrypt the PDU (if required). */ retcode = krb5_mk_req(kcontext, &auth_context, AP_OPTS_MUTUAL_REQUIRED | AP_OPTS_USE_SUBKEY, (char *) service_name, parms->session->peername, NULL, cc, &outdata); if (retcode) { DEBUGMSGTL(("ksm", "KSM: krb5_mk_req failed: %s\n", error_message(retcode))); snmp_set_detail(error_message(retcode)); retval = SNMPERR_KRB5; goto error; } DEBUGMSGTL(("ksm", "KSM: ticket retrieved successfully for \"%s/%s\" " "(may not be actual ticket sname)\n", service_name, parms->session->peername)); } else { /* * Grab the auth_context from our security state reference */ auth_context = ksm_state->auth_context; /* * Bundle up an AP_REP. Note that we do this only when we * have a security state reference (which means we're in an agent * and we're sending a response). */ DEBUGMSGTL(("ksm", "KSM: Starting reply processing.\n")); retcode = krb5_mk_rep(kcontext, auth_context, &outdata); if (retcode) { DEBUGMSGTL(("ksm", "KSM: krb5_mk_rep failed: %s\n", error_message(retcode))); snmp_set_detail(error_message(retcode)); retval = SNMPERR_KRB5; goto error; } DEBUGMSGTL(("ksm", "KSM: Finished with krb5_mk_rep()\n")); } /* * If we have to encrypt the PDU, do that now */ if (parms->secLevel == SNMP_SEC_LEVEL_AUTHPRIV) { DEBUGMSGTL(("ksm", "KSM: Starting PDU encryption.\n")); /* * It's weird - * * If we're on the manager, it's a local subkey (because that's in * our AP_REQ) * * If we're on the agent, it's a remote subkey (because that comes * FROM the received AP_REQ). */ if (ksm_state) retcode = krb5_auth_con_getremotesubkey(kcontext, auth_context, &subkey); else retcode = krb5_auth_con_getlocalsubkey(kcontext, auth_context, &subkey); if (retcode) { DEBUGMSGTL(("ksm", "KSM: krb5_auth_con_getlocalsubkey failed: %s\n", error_message(retcode))); snmp_set_detail(error_message(retcode)); retval = SNMPERR_KRB5; goto error; } /* * Note that here we need to handle different things between the * old and new crypto APIs. First, we need to get the final encrypted * length of the PDU. */ #ifdef MIT_NEW_CRYPTO retcode = krb5_c_encrypt_length(kcontext, subkey->enctype, parms->scopedPduLen, &encrypted_length); if (retcode) { DEBUGMSGTL(("ksm", "Encryption length calculation failed: %s\n", error_message(retcode))); snmp_set_detail(error_message(retcode)); retval = SNMPERR_KRB5; goto error; } #else /* MIT_NEW_CRYPTO */ krb5_use_enctype(kcontext, &eblock, subkey->enctype); retcode = krb5_process_key(kcontext, &eblock, subkey); if (retcode) { DEBUGMSGTL(("ksm", "krb5_process_key failed: %s\n", error_message(retcode))); snmp_set_detail(error_message(retcode)); retval = SNMPERR_KRB5; goto error; } encrypted_length = krb5_encrypt_size(parms->scopedPduLen, eblock.crypto_entry); #endif /* MIT_NEW_CRYPTO */ encrypted_data = malloc(encrypted_length); if (!encrypted_data) { DEBUGMSGTL(("ksm", "KSM: Unable to malloc %d bytes for encrypt " "buffer: %s\n", parms->scopedPduLen, strerror(errno))); retval = SNMPERR_MALLOC; #ifndef MIT_NEW_CRYPTO krb5_finish_key(kcontext, &eblock); #endif /* ! MIT_NEW_CRYPTO */ goto error; } /* * We need to set up a blank initialization vector for the encryption. * 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); /* * Finally! Do the encryption! */ #ifdef MIT_NEW_CRYPTO input.data = (char *) parms->scopedPdu; input.length = parms->scopedPduLen; output.ciphertext.data = (char *) encrypted_data; output.ciphertext.length = encrypted_length; retcode = krb5_c_encrypt(kcontext, subkey, KSM_KEY_USAGE_ENCRYPTION, &ivector, &input, &output); #else /* MIT_NEW_CRYPTO */ retcode = krb5_encrypt(kcontext, (krb5_pointer) parms->scopedPdu, (krb5_pointer) encrypted_data, parms->scopedPduLen, &eblock, ivector.data); krb5_finish_key(kcontext, &eblock); #endif /* MIT_NEW_CRYPTO */ if (retcode) { DEBUGMSGTL(("ksm", "KSM: krb5_encrypt failed: %s\n", error_message(retcode))); retval = SNMPERR_KRB5; snmp_set_detail(error_message(retcode)); goto error; } *offset = 0; rc = asn_realloc_rbuild_string(wholeMsg, parms->wholeMsgLen, offset, 1, (u_char) (ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_OCTET_STR), encrypted_data, encrypted_length); if (rc == 0) { DEBUGMSGTL(("ksm", "Building encrypted payload failed.\n")); retval = SNMPERR_TOO_LONG; goto error; } DEBUGMSGTL(("ksm", "KSM: Encryption complete.\n")); } else { /* * Plaintext PDU (not encrypted) */ if (*parms->wholeMsgLen < parms->scopedPduLen) { DEBUGMSGTL(("ksm", "Not enough room for plaintext PDU.\n")); retval = SNMPERR_TOO_LONG; goto error; } } /* * Start encoding the msgSecurityParameters * * For now, use 0 for the response hint */ DEBUGMSGTL(("ksm", "KSM: scopedPdu added to payload\n")); seq_offset = *offset; rc = asn_realloc_rbuild_int(wholeMsg, parms->wholeMsgLen, offset, 1, (u_char) (ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_INTEGER), (long *) &zero, sizeof(zero)); if (rc == 0) { DEBUGMSGTL(("ksm", "Building ksm security parameters failed.\n")); retval = SNMPERR_TOO_LONG; goto error; } rc = asn_realloc_rbuild_string(wholeMsg, parms->wholeMsgLen, offset, 1, (u_char) (ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_OCTET_STR), (u_char *) outdata.data, outdata.length); if (rc == 0) { DEBUGMSGTL(("ksm", "Building ksm AP_REQ failed.\n")); retval = SNMPERR_TOO_LONG; goto error; } /* * Now, we need to pick the "right" checksum algorithm. For old * crypto, just pick CKSUMTYPE_RSA_MD5_DES; for new crypto, pick * one of the "approved" ones. */ #ifdef MIT_NEW_CRYPTO retcode = krb5_c_keyed_checksum_types(kcontext, subkey->enctype, &numcksumtypes, &cksumtype_array); if (retcode) { DEBUGMSGTL(("ksm", "Unable to find appropriate keyed checksum: %s\n", error_message(retcode))); snmp_set_detail(error_message(retcode)); retval = SNMPERR_KRB5; goto error; } if (numcksumtypes <= 0) { DEBUGMSGTL(("ksm", "We received a list of zero cksumtypes for this " "enctype (%d)\n", subkey->enctype)); snmp_set_detail("No valid checksum type for this encryption type"); retval = SNMPERR_KRB5; goto error; } /* * It's not clear to me from the API which checksum you're supposed * to support, so I'm taking a guess at the first one */ cksumtype = cksumtype_array[0]; krb5_free_cksumtypes(kcontext, cksumtype_array); DEBUGMSGTL(("ksm", "KSM: Choosing checksum type of %d (subkey type " "of %d)\n", cksumtype, subkey->enctype)); retcode = krb5_c_checksum_length(kcontext, cksumtype, &blocksize); if (retcode) { DEBUGMSGTL(("ksm", "Unable to determine checksum length: %s\n", error_message(retcode))); snmp_set_detail(error_message(retcode)); retval = SNMPERR_KRB5; goto error; } pdu_checksum.length = blocksize; #else /* MIT_NEW_CRYPTO */ if (ksm_state) cksumtype = ksm_state->cksumtype; else cksumtype = CKSUMTYPE_RSA_MD5_DES; 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; } pdu_checksum.length = krb5_checksum_size(kcontext, cksumtype); pdu_checksum.checksum_type = cksumtype; #endif /* MIT_NEW_CRYPTO */ /* * Note that here, we're just leaving blank space for the checksum; * we remember where that is, and we'll fill it in later. */ *offset += pdu_checksum.length; memset(*wholeMsg + *parms->wholeMsgLen - *offset, 0, pdu_checksum.length); cksum_pointer = *wholeMsg + *parms->wholeMsgLen - *offset; rc = asn_realloc_rbuild_header(wholeMsg, parms->wholeMsgLen, parms->wholeMsgOffset, 1, (u_char) (ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_OCTET_STR), pdu_checksum.length); if (rc == 0) { DEBUGMSGTL(("ksm", "Building ksm security parameters failed.\n")); retval = SNMPERR_TOO_LONG; goto error; } rc = asn_realloc_rbuild_int(wholeMsg, parms->wholeMsgLen, parms->wholeMsgOffset, 1, (u_char) (ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_OCTET_STR), (long *) &cksumtype, sizeof(cksumtype)); if (rc == 0) { DEBUGMSGTL(("ksm", "Building ksm security parameters failed.\n")); retval = SNMPERR_TOO_LONG; goto error; } rc = asn_realloc_rbuild_sequence(wholeMsg, parms->wholeMsgLen, parms->wholeMsgOffset, 1, (u_char) (ASN_SEQUENCE | ASN_CONSTRUCTOR), *offset - seq_offset); if (rc == 0) { DEBUGMSGTL(("ksm", "Building ksm security parameters failed.\n")); retval = SNMPERR_TOO_LONG; goto error; } rc = asn_realloc_rbuild_header(wholeMsg, parms->wholeMsgLen, parms->wholeMsgOffset, 1, (u_char) (ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_OCTET_STR), *offset - seq_offset); if (rc == 0) { DEBUGMSGTL(("ksm", "Building ksm security parameters failed.\n")); retval = SNMPERR_TOO_LONG; goto error; } DEBUGMSGTL(("ksm", "KSM: Security parameter encoding completed\n")); /* * We're done with the KSM security parameters - now we do the global * header and wrap up the whole PDU. */ if (*parms->wholeMsgLen < parms->globalDataLen) { DEBUGMSGTL(("ksm", "Building global data failed.\n")); retval = SNMPERR_TOO_LONG; goto error; } *offset += parms->globalDataLen; memcpy(*wholeMsg + *parms->wholeMsgLen - *offset, parms->globalData, parms->globalDataLen); rc = asn_realloc_rbuild_sequence(wholeMsg, parms->wholeMsgLen, offset, 1, (u_char) (ASN_SEQUENCE | ASN_CONSTRUCTOR), *offset); if (rc == 0) { DEBUGMSGTL(("ksm", "Building master packet sequence.\n")); retval = SNMPERR_TOO_LONG; goto error; } DEBUGMSGTL(("ksm", "KSM: PDU master packet encoding complete.\n")); /* * Now we need to checksum the entire PDU (since it's built). */ pdu_checksum.contents = malloc(pdu_checksum.length); if (!pdu_checksum.contents) { DEBUGMSGTL(("ksm", "Unable to malloc %d bytes for checksum\n", pdu_checksum.length)); retval = SNMPERR_MALLOC; goto error; } /* * If we didn't encrypt the packet, we haven't yet got the subkey. * Get that now. */ if (!subkey) { if (ksm_state) retcode = krb5_auth_con_getremotesubkey(kcontext, auth_context, &subkey); else retcode = krb5_auth_con_getlocalsubkey(kcontext, auth_context, &subkey); if (retcode) { DEBUGMSGTL(("ksm", "krb5_auth_con_getlocalsubkey failed: %s\n", error_message(retcode))); snmp_set_detail(error_message(retcode)); retval = SNMPERR_KRB5; goto error; } } #ifdef MIT_NEW_CRYPTO input.data = (char *) (*wholeMsg + *parms->wholeMsgLen - *offset); input.length = *offset; retcode = krb5_c_make_checksum(kcontext, cksumtype, subkey, KSM_KEY_USAGE_CHECKSUM, &input, &pdu_checksum); #else /* MIT_NEW_CRYPTO */ retcode = krb5_calculate_checksum(kcontext, cksumtype, *wholeMsg + *parms->wholeMsgLen - *offset, *offset, (krb5_pointer) subkey->contents, subkey->length, &pdu_checksum); #endif /* MIT_NEW_CRYPTO */ if (retcode) { DEBUGMSGTL(("ksm", "Calculate checksum failed: %s\n", error_message(retcode))); retval = SNMPERR_KRB5; snmp_set_detail(error_message(retcode)); goto error; } DEBUGMSGTL(("ksm", "KSM: Checksum calculation complete.\n")); memcpy(cksum_pointer, pdu_checksum.contents, pdu_checksum.length); DEBUGMSGTL(("ksm", "KSM: Writing checksum of %d bytes at offset %d\n", pdu_checksum.length, cksum_pointer - (*wholeMsg + 1))); DEBUGMSGTL(("ksm", "KSM: Checksum:")); for (i = 0; i < pdu_checksum.length; i++) DEBUGMSG(("ksm", " %02x", (unsigned int) pdu_checksum.contents[i])); DEBUGMSG(("ksm", "\n")); /* * If we're _not_ called as part of a response (null ksm_state), * then save the auth_context for later using our cache routines. */ if (!ksm_state) { if ((retval = ksm_insert_cache(parms->pdu->msgid, auth_context, (u_char *) parms->secName, parms->secNameLen)) != SNMPERR_SUCCESS) goto error; auth_context = NULL; } DEBUGMSGTL(("ksm", "KSM processing complete!\n")); error: if (pdu_checksum.contents) #ifdef MIT_NEW_CRYPTO krb5_free_checksum_contents(kcontext, &pdu_checksum); #else /* MIT_NEW_CRYPTO */ free(pdu_checksum.contents); #endif /* MIT_NEW_CRYPTO */ if (ivector.data) free(ivector.data); if (subkey) krb5_free_keyblock(kcontext, subkey); if (encrypted_data) free(encrypted_data); if (cc) krb5_cc_close(kcontext, cc); if (auth_context && !ksm_state) krb5_auth_con_free(kcontext, auth_context); return retval; }