static void test_kdc_insert_lookaside_cache_expire(void **state) { struct entry *e; krb5_context context = *state; krb5_data req1 = string2data("I'm a test request"); krb5_data rep1 = string2data("I'm a test response"); size_t e1_size = entry_size(&req1, &rep1); krb5_data req2 = string2data("I'm a different test request"); size_t e2_size = entry_size(&req2, NULL); struct entry *hash1_ent, *hash2_ent, *exp_ent; time_return(0, 0); kdc_insert_lookaside(context, &req1, &rep1); hash1_ent = k5_hashtab_get(hash_table, req1.data, req1.length); assert_non_null(hash1_ent); assert_true(data_eq(hash1_ent->req_packet, req1)); assert_true(data_eq(hash1_ent->reply_packet, rep1)); exp_ent = K5_TAILQ_FIRST(&expiration_queue); assert_true(data_eq(exp_ent->req_packet, req1)); assert_true(data_eq(exp_ent->reply_packet, rep1)); assert_int_equal(num_entries, 1); assert_int_equal(total_size, e1_size); /* Increase hits on entry */ e = k5_hashtab_get(hash_table, req1.data, req1.length); assert_non_null(e); e->num_hits = 5; time_return(STALE_TIME + 1, 0); kdc_insert_lookaside(context, &req2, NULL); assert_null(k5_hashtab_get(hash_table, req1.data, req1.length)); assert_int_equal(max_hits_per_entry, 5); hash2_ent = k5_hashtab_get(hash_table, req2.data, req2.length); assert_non_null(hash2_ent); assert_true(data_eq(hash2_ent->req_packet, req2)); assert_int_equal(hash2_ent-> reply_packet.length, 0); exp_ent = K5_TAILQ_FIRST(&expiration_queue); assert_true(data_eq(exp_ent->req_packet, req2)); assert_int_equal(exp_ent->reply_packet.length, 0); assert_int_equal(num_entries, 1); assert_int_equal(total_size, e2_size); }
static void test_kdc_insert_lookaside_multiple(void **state) { krb5_context context = *state; krb5_data req1 = string2data("I'm a test request"); krb5_data rep1 = string2data("I'm a test response"); size_t e1_size = entry_size(&req1, &rep1); krb5_data req2 = string2data("I'm a different test request"); size_t e2_size = entry_size(&req2, NULL); struct entry *hash1_ent, *hash2_ent, *exp_first, *exp_last; time_return(0, 0); kdc_insert_lookaside(context, &req1, &rep1); hash1_ent = k5_hashtab_get(hash_table, req1.data, req1.length); assert_non_null(hash1_ent); assert_true(data_eq(hash1_ent->req_packet, req1)); assert_true(data_eq(hash1_ent->reply_packet, rep1)); exp_first = K5_TAILQ_FIRST(&expiration_queue); assert_true(data_eq(exp_first->req_packet, req1)); assert_true(data_eq(exp_first->reply_packet, rep1)); assert_int_equal(num_entries, 1); assert_int_equal(total_size, e1_size); time_return(0, 0); kdc_insert_lookaside(context, &req2, NULL); hash2_ent = k5_hashtab_get(hash_table, req2.data, req2.length); assert_non_null(hash2_ent); assert_true(data_eq(hash2_ent->req_packet, req2)); assert_int_equal(hash2_ent->reply_packet.length, 0); exp_last = K5_TAILQ_LAST(&expiration_queue, entry_queue); assert_true(data_eq(exp_last->req_packet, req2)); assert_int_equal(exp_last->reply_packet.length, 0); assert_int_equal(num_entries, 2); assert_int_equal(total_size, e1_size + e2_size); }
static void test_kdc_insert_lookaside_no_reply(void **state) { krb5_context context = *state; krb5_data req = string2data("I'm a test request"); struct entry *hash_ent, *exp_ent; time_return(0, 0); kdc_insert_lookaside(context, &req, NULL); hash_ent = k5_hashtab_get(hash_table, req.data, req.length); assert_non_null(hash_ent); assert_true(data_eq(hash_ent->req_packet, req)); assert_int_equal(hash_ent->reply_packet.length, 0); exp_ent = K5_TAILQ_FIRST(&expiration_queue); assert_true(data_eq(exp_ent->req_packet, req)); assert_int_equal(exp_ent->reply_packet.length, 0); assert_int_equal(num_entries, 1); assert_int_equal(total_size, entry_size(&req, NULL)); }
static void finish_dispatch(void *arg, krb5_error_code code, krb5_data *response) { struct dispatch_state *state = arg; loop_respond_fn oldrespond; void *oldarg; assert(state); oldrespond = state->respond; oldarg = state->arg; #ifndef NOCACHE /* Remove our NULL cache entry to indicate request completion. */ kdc_remove_lookaside(kdc_context, state->request); #endif if (state->is_tcp == 0 && response && response->length > max_dgram_reply_size) { krb5_free_data(kdc_context, response); response = NULL; code = make_too_big_error(&response); if (code) krb5_klog_syslog(LOG_ERR, "error constructing " "KRB_ERR_RESPONSE_TOO_BIG error: %s", error_message(code)); } #ifndef NOCACHE /* put the response into the lookaside buffer */ else if (!code && response) kdc_insert_lookaside(state->request, response); #endif free(state); (*oldrespond)(oldarg, code, response); }
krb5_error_code dispatch(krb5_data *pkt, const krb5_fulladdr *from, krb5_data **response) { krb5_error_code retval; krb5_kdc_req *as_req; krb5_int32 now, now_usec; /* decode incoming packet, and dispatch */ #ifndef NOCACHE /* try the replay lookaside buffer */ if (kdc_check_lookaside(pkt, response)) { /* a hit! */ const char *name = 0; char buf[46]; name = inet_ntop (ADDRTYPE2FAMILY (from->address->addrtype), from->address->contents, buf, sizeof (buf)); if (name == 0) name = "[unknown address type]"; krb5_klog_syslog(LOG_INFO, "DISPATCH: repeated (retransmitted?) request from %s, resending previous response", name); return 0; } #endif retval = krb5_crypto_us_timeofday(&now, &now_usec); if (retval == 0) { krb5_int32 usec_difference = now_usec-last_usec; krb5_data data; if(last_os_random == 0) last_os_random = now; /* Grab random data from OS every hour*/ if(now-last_os_random >= 60*60) { krb5_c_random_os_entropy(kdc_context, 0, NULL); last_os_random = now; } data.length = sizeof(krb5_int32); data.data = (void *) &usec_difference; krb5_c_random_add_entropy(kdc_context, KRB5_C_RANDSOURCE_TIMING, &data); last_usec = now_usec; } /* try TGS_REQ first; they are more common! */ if (krb5_is_tgs_req(pkt)) { retval = process_tgs_req(pkt, from, response); } else if (krb5_is_as_req(pkt)) { if (!(retval = decode_krb5_as_req(pkt, &as_req))) { /* * setup_server_realm() sets up the global realm-specific data * pointer. * process_as_req frees the request if it is called */ if (!(retval = setup_server_realm(as_req->server))) { retval = process_as_req(as_req, pkt, from, response); } else krb5_free_kdc_req(kdc_context, as_req); } } else retval = KRB5KRB_AP_ERR_MSG_TYPE; #ifndef NOCACHE /* put the response into the lookaside buffer */ if (!retval && *response != NULL) kdc_insert_lookaside(pkt, *response); #endif return retval; }
void dispatch(void *cb, struct sockaddr *local_saddr, const krb5_fulladdr *from, krb5_data *pkt, int is_tcp, verto_ctx *vctx, loop_respond_fn respond, void *arg) { krb5_error_code retval; krb5_kdc_req *as_req; krb5_int32 now, now_usec; krb5_data *response = NULL; struct dispatch_state *state; state = malloc(sizeof(*state)); if (!state) { (*respond)(arg, ENOMEM, NULL); return; } state->respond = respond; state->arg = arg; state->request = pkt; state->is_tcp = is_tcp; /* decode incoming packet, and dispatch */ #ifndef NOCACHE /* try the replay lookaside buffer */ if (kdc_check_lookaside(pkt, &response)) { /* a hit! */ const char *name = 0; char buf[46]; if (!response || is_tcp != 0 || response->length <= max_dgram_reply_size) { name = inet_ntop (ADDRTYPE2FAMILY (from->address->addrtype), from->address->contents, buf, sizeof (buf)); if (name == 0) name = "[unknown address type]"; if (response) krb5_klog_syslog(LOG_INFO, "DISPATCH: repeated (retransmitted?) request " "from %s, resending previous response", name); else krb5_klog_syslog(LOG_INFO, "DISPATCH: repeated (retransmitted?) request " "from %s during request processing, dropping " "repeated request", name); } finish_dispatch(state, response ? 0 : KRB5KDC_ERR_DISCARD, response); return; } /* Insert a NULL entry into the lookaside to indicate that this request * is currently being processed. */ kdc_insert_lookaside(pkt, NULL); #endif retval = krb5_crypto_us_timeofday(&now, &now_usec); if (retval == 0) { krb5_int32 usec_difference = now_usec-last_usec; krb5_data data; if(last_os_random == 0) last_os_random = now; /* Grab random data from OS every hour*/ if(now-last_os_random >= 60*60) { krb5_c_random_os_entropy(kdc_context, 0, NULL); last_os_random = now; } data.length = sizeof(krb5_int32); data.data = (void *) &usec_difference; krb5_c_random_add_entropy(kdc_context, KRB5_C_RANDSOURCE_TIMING, &data); last_usec = now_usec; } /* try TGS_REQ first; they are more common! */ if (krb5_is_tgs_req(pkt)) { retval = process_tgs_req(pkt, from, &response); } else if (krb5_is_as_req(pkt)) { if (!(retval = decode_krb5_as_req(pkt, &as_req))) { /* * setup_server_realm() sets up the global realm-specific data * pointer. * process_as_req frees the request if it is called */ if (!(retval = setup_server_realm(as_req->server))) { process_as_req(as_req, pkt, from, vctx, finish_dispatch, state); return; } else krb5_free_kdc_req(kdc_context, as_req); } } else retval = KRB5KRB_AP_ERR_MSG_TYPE; finish_dispatch(state, retval, response); }