diff -u -r krb5-1.3.5.orig/src/include/k5-int.h krb5-1.3.5/src/include/k5-int.h --- krb5-1.3.5.orig/src/include/k5-int.h 2006-01-11 09:37:02.000000000 +0100 +++ krb5-1.3.5/src/include/k5-int.h 2006-01-11 11:35:19.000000000 +0100 @@ -507,14 +507,30 @@ struct addrlist; /* libos.spec */ +typedef krb5_error_code (*krb5_select_kdc_fct)(krb5_context context, + const krb5_data *reply, + int *stop, + void *data); + +krb5_error_code krb5int_continue_on_svc_unavailable(krb5_context, + const krb5_data *, + int *, void *); krb5_error_code krb5_lock_file (krb5_context, int, int); krb5_error_code krb5_unlock_file (krb5_context, int); krb5_error_code krb5_sendto_kdc (krb5_context, const krb5_data *, const krb5_data *, krb5_data *, int, int); +krb5_error_code krb5_sendto_kdc2 (krb5_context, const krb5_data *, + const krb5_data *, krb5_data *, int, int, + krb5_select_kdc_fct, void *); krb5_error_code krb5int_sendto (krb5_context, const krb5_data *, const struct addrlist *, krb5_data *, struct sockaddr *, socklen_t *, struct addrinfo **); +krb5_error_code krb5int_sendto2 (krb5_context, const krb5_data *, + const struct addrlist *, krb5_data *, + struct sockaddr *, socklen_t *, + struct addrinfo **, + krb5_select_kdc_fct, void *); krb5_error_code krb5_get_krbhst (krb5_context, const krb5_data *, char *** ); krb5_error_code krb5_free_krbhst (krb5_context, char * const * ); krb5_error_code krb5_create_secure_file (krb5_context, const char * pathname); diff -u -r krb5-1.3.5.orig/src/lib/krb5/error_tables/krb5_err.et krb5-1.3.5/src/lib/krb5/error_tables/krb5_err.et --- krb5-1.3.5.orig/src/lib/krb5/error_tables/krb5_err.et 2004-01-07 23:17:36.000000000 +0100 +++ krb5-1.3.5/src/lib/krb5/error_tables/krb5_err.et 2006-01-11 09:40:22.000000000 +0100 @@ -68,7 +68,7 @@ error_code KRB5KDC_ERR_SERVER_NOMATCH, "Requested server and ticket don't match" error_code KRB5PLACEHOLD_27, "KRB5 error code 27" error_code KRB5PLACEHOLD_28, "KRB5 error code 28" -error_code KRB5PLACEHOLD_29, "KRB5 error code 29" +error_code KRB5KDC_ERR_SVC_UNAVAILABLE, "A service is not available" error_code KRB5PLACEHOLD_30, "KRB5 error code 30" # vv 31 error_code KRB5KRB_AP_ERR_BAD_INTEGRITY, "Decrypt integrity check failed" diff -u -r krb5-1.3.5.orig/src/lib/krb5/krb/get_in_tkt.c krb5-1.3.5/src/lib/krb5/krb/get_in_tkt.c --- krb5-1.3.5.orig/src/lib/krb5/krb/get_in_tkt.c 2003-06-07 00:02:01.000000000 +0200 +++ krb5-1.3.5/src/lib/krb5/krb/get_in_tkt.c 2006-01-11 11:39:06.000000000 +0100 @@ -78,6 +78,7 @@ static krb5_error_code make_preauth_list (krb5_context, krb5_preauthtype *, int, krb5_pa_data ***); + /* * This function sends a request to the KDC, and gets back a response; * the response is parsed into ret_err_reply or ret_as_reply if the @@ -116,9 +117,10 @@ k4_version = packet->data[0]; send_again: - retval = krb5_sendto_kdc(context, packet, - krb5_princ_realm(context, request->client), - &reply, use_master, tcp_only); + retval = krb5_sendto_kdc2(context, packet, + krb5_princ_realm(context, request->client), + &reply, use_master, tcp_only, + krb5int_continue_on_svc_unavailable, NULL); if (retval) goto cleanup; diff -u -r krb5-1.3.5.orig/src/lib/krb5/krb/rd_error.c krb5-1.3.5/src/lib/krb5/krb/rd_error.c --- krb5-1.3.5.orig/src/lib/krb5/krb/rd_error.c 2002-09-03 03:13:46.000000000 +0200 +++ krb5-1.3.5/src/lib/krb5/krb/rd_error.c 2006-01-11 11:36:59.000000000 +0100 @@ -47,3 +47,28 @@ return(decode_krb5_error(enc_errbuf, dec_error)); } +krb5_error_code +krb5int_continue_on_svc_unavailable(krb5_context context, + const krb5_data *reply, + int *stop, void *data) +{ + krb5_error_code retval = 0; + + *stop = 1; + + if (krb5_is_krb_error(reply)) { + krb5_error *err_reply; + + if ((retval = decode_krb5_error(reply, &err_reply))) + /* some other error code--??? */ + return retval; + + retval = err_reply->error; + krb5_free_error(context, err_reply); + } + + *stop = (retval != KRB5KDC_ERR_SVC_UNAVAILABLE); + + return retval; +} + diff -u -r krb5-1.3.5.orig/src/lib/krb5/krb/send_tgs.c krb5-1.3.5/src/lib/krb5/krb/send_tgs.c --- krb5-1.3.5.orig/src/lib/krb5/krb/send_tgs.c 2004-08-31 21:11:43.000000000 +0200 +++ krb5-1.3.5/src/lib/krb5/krb/send_tgs.c 2006-01-11 11:39:22.000000000 +0100 @@ -261,9 +261,10 @@ /* now send request & get response from KDC */ send_again: - retval = krb5_sendto_kdc(context, scratch, - krb5_princ_realm(context, sname), - &rep->response, 0, tcp_only); + retval = krb5_sendto_kdc2(context, scratch, + krb5_princ_realm(context, sname), + &rep->response, 0, tcp_only, + krb5int_continue_on_svc_unavailable, NULL); if (retval == 0) { if (krb5_is_krb_error(&rep->response)) { if (!tcp_only) { diff -u -r krb5-1.3.5.orig/src/lib/krb5/os/sendto_kdc.c krb5-1.3.5/src/lib/krb5/os/sendto_kdc.c --- krb5-1.3.5.orig/src/lib/krb5/os/sendto_kdc.c 2006-01-11 09:37:02.000000000 +0100 +++ krb5-1.3.5/src/lib/krb5/os/sendto_kdc.c 2006-01-11 11:32:29.000000000 +0100 @@ -257,10 +257,21 @@ * when finished. */ +static krb5_error_code +stop_on_kdc_err (krb5_context context, const krb5_data *reply, + int *stop, void *data) +{ + /* default behaviour is always to return */ + *stop = 1; + + return 0; +} + krb5_error_code -krb5_sendto_kdc (krb5_context context, const krb5_data *message, - const krb5_data *realm, krb5_data *reply, - int use_master, int tcp_only) +krb5_sendto_kdc2 (krb5_context context, const krb5_data *message, + const krb5_data *realm, krb5_data *reply, + int use_master, int tcp_only, + krb5_select_kdc_fct kdc_select, void *data) { krb5_error_code retval; struct addrlist addrs; @@ -340,8 +351,8 @@ addrs.addrs[i] = tmp; break; } - retval = krb5int_sendto (context, message, &addrs, reply, 0, 0, - &replier); + retval = krb5int_sendto2 (context, message, &addrs, reply, 0, 0, + &replier, kdc_select, data); if (replier) { previous = (krb5_data *) malloc(sizeof(krb5_data)); if (previous) { @@ -368,11 +379,21 @@ } krb5int_free_addrlist (&addrs); if (retval == 0) - return 0; + return retval; } return retval; } +krb5_error_code +krb5_sendto_kdc (krb5_context context, const krb5_data *message, + const krb5_data *realm, krb5_data *reply, + int use_master, int tcp_only) +{ + return krb5_sendto_kdc2 (context, message, realm, reply, + use_master, tcp_only, + stop_on_kdc_err, NULL); +} + #if defined(_WIN32) && defined(DEBUG) static char *bogus_strerror (int xerr) { @@ -939,8 +960,26 @@ } static int -service_fds (struct select_state *selstate, - struct conn_state *conns, size_t n_conns, int *winning_conn) +skip_kdc_p (krb5_context context, struct conn_state *conn, + krb5_select_kdc_fct kdc_select, void *data) +{ + krb5_data reply; + krb5_error_code retval; + int stop = 0; + + reply.data = conn->x.in.buf; + reply.length = (conn->x.in.pos - conn->x.in.buf); + + /* XXX error code cannot be propagated */ + retval = (*kdc_select)(context, &reply, &stop, data); + + return (stop == 0); +} + +static int +service_fds (krb5_context context, struct select_state *selstate, + struct conn_state *conns, size_t n_conns, int *winning_conn, + krb5_select_kdc_fct kdc_select, void *data) { int e, selret; struct select_state sel_results; @@ -979,7 +1018,8 @@ conns[i].fd, conns[i].addr, state_strings[(int) conns[i].state]); - if (conns[i].service (&conns[i], selstate, ssflags)) { + if (conns[i].service (&conns[i], selstate, ssflags) && + !skip_kdc_p (context, &conns[i], kdc_select, data)) { dprint("fd service routine says we're done\n"); *winning_conn = i; return 1; @@ -1017,10 +1057,11 @@ */ krb5_error_code -krb5int_sendto (krb5_context context, const krb5_data *message, - const struct addrlist *addrs, krb5_data *reply, - struct sockaddr *localaddr, socklen_t *localaddrlen, - struct addrinfo **replier) +krb5int_sendto2 (krb5_context context, const krb5_data *message, + const struct addrlist *addrs, krb5_data *reply, + struct sockaddr *localaddr, socklen_t *localaddrlen, + struct addrinfo **replier, + krb5_select_kdc_fct kdc_select, void *data) { int i, pass; int delay_this_pass = 2; @@ -1082,7 +1123,8 @@ goto egress; select_state.end_time = now; select_state.end_time.tv_sec += 1; - e = service_fds(&select_state, conns, host+1, &winning_conn); + e = service_fds(context, &select_state, conns, host+1, + &winning_conn, kdc_select, data); if (e) break; if (pass > 0 && select_state.nfds == 0) @@ -1102,7 +1144,8 @@ call with the last one from the above loop, if the loop actually calls select. */ select_state.end_time.tv_sec += delay_this_pass; - e = service_fds(&select_state, conns, host+1, &winning_conn); + e = service_fds(context, &select_state, conns, host+1, + &winning_conn, kdc_select, data); if (e) break; if (select_state.nfds == 0) @@ -1146,3 +1189,14 @@ free(udpbuf); return retval; } + +krb5_error_code +krb5int_sendto (krb5_context context, const krb5_data *message, + const struct addrlist *addrs, krb5_data *reply, + struct sockaddr *localaddr, socklen_t *localaddrlen, + struct addrinfo **replier) +{ + return krb5int_sendto2 (context, message, addrs, reply, + localaddr, localaddrlen, replier, + stop_on_kdc_err, NULL); +}