1 /* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */ 2 /* kdc/dispatch.c - Dispatch an incoming packet */ 3 /* 4 * Copyright 1990, 2009 by the Massachusetts Institute of Technology. 5 * 6 * Export of this software from the United States of America may 7 * require a specific license from the United States Government. 8 * It is the responsibility of any person or organization contemplating 9 * export to obtain such a license before exporting. 10 * 11 * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and 12 * distribute this software and its documentation for any purpose and 13 * without fee is hereby granted, provided that the above copyright 14 * notice appear in all copies and that both that copyright notice and 15 * this permission notice appear in supporting documentation, and that 16 * the name of M.I.T. not be used in advertising or publicity pertaining 17 * to distribution of the software without specific, written prior 18 * permission. Furthermore if you modify this software you must label 19 * your software as modified software and not distribute it in such a 20 * fashion that it might be confused with the original M.I.T. software. 21 * M.I.T. makes no representations about the suitability of 22 * this software for any purpose. It is provided "as is" without express 23 * or implied warranty. 24 */ 25 26 #include "k5-int.h" 27 #include <syslog.h> 28 #include "kdc_util.h" 29 #include "extern.h" 30 #include "adm_proto.h" 31 #include "realm_data.h" 32 #include <netinet/in.h> 33 #include <arpa/inet.h> 34 #include <string.h> 35 36 static krb5_error_code make_too_big_error(kdc_realm_t *realm, krb5_data **out); 37 38 struct dispatch_state { 39 loop_respond_fn respond; 40 void *arg; 41 krb5_data *request; 42 int is_tcp; 43 kdc_realm_t *active_realm; 44 krb5_context kdc_err_context; 45 }; 46 47 static void 48 finish_dispatch(struct dispatch_state *state, krb5_error_code code, 49 krb5_data *response) 50 { 51 loop_respond_fn oldrespond = state->respond; 52 void *oldarg = state->arg; 53 54 if (state->is_tcp == 0 && response && 55 response->length > (unsigned int)max_dgram_reply_size) { 56 krb5_free_data(NULL, response); 57 response = NULL; 58 code = make_too_big_error(state->active_realm, &response); 59 if (code) 60 krb5_klog_syslog(LOG_ERR, "error constructing " 61 "KRB_ERR_RESPONSE_TOO_BIG error: %s", 62 error_message(code)); 63 } 64 65 free(state); 66 (*oldrespond)(oldarg, code, response); 67 } 68 69 static void 70 finish_dispatch_cache(void *arg, krb5_error_code code, krb5_data *response) 71 { 72 struct dispatch_state *state = arg; 73 krb5_context kdc_err_context = state->kdc_err_context; 74 75 #ifndef NOCACHE 76 /* Remove the null cache entry unless we actually want to discard this 77 * request. */ 78 if (code != KRB5KDC_ERR_DISCARD) 79 kdc_remove_lookaside(kdc_err_context, state->request); 80 81 /* Put the response into the lookaside buffer (if we produced one). */ 82 if (code == 0 && response != NULL) 83 kdc_insert_lookaside(kdc_err_context, state->request, response); 84 #endif 85 86 finish_dispatch(state, code, response); 87 } 88 89 void 90 dispatch(void *cb, const krb5_fulladdr *local_addr, 91 const krb5_fulladdr *remote_addr, krb5_data *pkt, int is_tcp, 92 verto_ctx *vctx, loop_respond_fn respond, void *arg) 93 { 94 krb5_error_code retval; 95 krb5_kdc_req *req = NULL; 96 krb5_data *response = NULL; 97 struct dispatch_state *state; 98 struct server_handle *handle = cb; 99 krb5_context kdc_err_context = handle->kdc_err_context; 100 101 state = k5alloc(sizeof(*state), &retval); 102 if (state == NULL) { 103 (*respond)(arg, retval, NULL); 104 return; 105 } 106 state->respond = respond; 107 state->arg = arg; 108 state->request = pkt; 109 state->is_tcp = is_tcp; 110 state->kdc_err_context = kdc_err_context; 111 112 /* decode incoming packet, and dispatch */ 113 114 #ifndef NOCACHE 115 /* try the replay lookaside buffer */ 116 if (kdc_check_lookaside(kdc_err_context, pkt, &response)) { 117 /* a hit! */ 118 const char *name = 0; 119 char buf[46]; 120 121 name = inet_ntop(ADDRTYPE2FAMILY(remote_addr->address->addrtype), 122 remote_addr->address->contents, buf, sizeof(buf)); 123 if (name == 0) 124 name = "[unknown address type]"; 125 if (response) 126 krb5_klog_syslog(LOG_INFO, 127 "DISPATCH: repeated (retransmitted?) request " 128 "from %s, resending previous response", name); 129 else 130 krb5_klog_syslog(LOG_INFO, 131 "DISPATCH: repeated (retransmitted?) request " 132 "from %s during request processing, dropping " 133 "repeated request", name); 134 135 finish_dispatch(state, response ? 0 : KRB5KDC_ERR_DISCARD, response); 136 return; 137 } 138 139 /* Insert a NULL entry into the lookaside to indicate that this request 140 * is currently being processed. */ 141 kdc_insert_lookaside(kdc_err_context, pkt, NULL); 142 #endif 143 144 /* try TGS_REQ first; they are more common! */ 145 146 if (krb5_is_tgs_req(pkt)) 147 retval = decode_krb5_tgs_req(pkt, &req); 148 else if (krb5_is_as_req(pkt)) 149 retval = decode_krb5_as_req(pkt, &req); 150 else 151 retval = KRB5KRB_AP_ERR_MSG_TYPE; 152 if (retval) 153 goto done; 154 155 state->active_realm = setup_server_realm(handle, req->server); 156 if (state->active_realm == NULL) { 157 retval = KRB5KDC_ERR_WRONG_REALM; 158 goto done; 159 } 160 161 if (krb5_is_tgs_req(pkt)) { 162 /* process_tgs_req frees the request */ 163 retval = process_tgs_req(req, pkt, remote_addr, state->active_realm, 164 &response); 165 req = NULL; 166 } else if (krb5_is_as_req(pkt)) { 167 /* process_as_req frees the request and calls finish_dispatch_cache. */ 168 process_as_req(req, pkt, local_addr, remote_addr, state->active_realm, 169 vctx, finish_dispatch_cache, state); 170 return; 171 } 172 173 done: 174 krb5_free_kdc_req(kdc_err_context, req); 175 finish_dispatch_cache(state, retval, response); 176 } 177 178 static krb5_error_code 179 make_too_big_error(kdc_realm_t *realm, krb5_data **out) 180 { 181 krb5_context context = realm->realm_context; 182 krb5_error errpkt; 183 krb5_error_code retval; 184 krb5_data *scratch; 185 186 *out = NULL; 187 memset(&errpkt, 0, sizeof(errpkt)); 188 189 retval = krb5_us_timeofday(context, &errpkt.stime, &errpkt.susec); 190 if (retval) 191 return retval; 192 errpkt.error = KRB_ERR_RESPONSE_TOO_BIG; 193 errpkt.server = realm->realm_tgsprinc; 194 errpkt.client = NULL; 195 errpkt.text.length = 0; 196 errpkt.text.data = 0; 197 errpkt.e_data.length = 0; 198 errpkt.e_data.data = 0; 199 scratch = malloc(sizeof(*scratch)); 200 if (scratch == NULL) 201 return ENOMEM; 202 retval = krb5_mk_error(context, &errpkt, scratch); 203 if (retval) { 204 free(scratch); 205 return retval; 206 } 207 208 *out = scratch; 209 return 0; 210 } 211 212 krb5_context get_context(void *handle) 213 { 214 struct server_handle *sh = handle; 215 216 return sh->kdc_err_context; 217 } 218