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_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; }
/* 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; }
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; }