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 struct sockaddr *local_addr, 91 const struct sockaddr *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[128]; 120 121 k5_print_addr(remote_addr, buf, sizeof(buf)); 122 if (name == 0) 123 name = "[unknown address type]"; 124 if (response) 125 krb5_klog_syslog(LOG_INFO, 126 "DISPATCH: repeated (retransmitted?) request " 127 "from %s, resending previous response", name); 128 else 129 krb5_klog_syslog(LOG_INFO, 130 "DISPATCH: repeated (retransmitted?) request " 131 "from %s during request processing, dropping " 132 "repeated request", name); 133 134 finish_dispatch(state, response ? 0 : KRB5KDC_ERR_DISCARD, response); 135 return; 136 } 137 138 /* Insert a NULL entry into the lookaside to indicate that this request 139 * is currently being processed. */ 140 kdc_insert_lookaside(kdc_err_context, pkt, NULL); 141 #endif 142 143 /* try TGS_REQ first; they are more common! */ 144 145 if (krb5_is_tgs_req(pkt)) 146 retval = decode_krb5_tgs_req(pkt, &req); 147 else if (krb5_is_as_req(pkt)) 148 retval = decode_krb5_as_req(pkt, &req); 149 else 150 retval = KRB5KRB_AP_ERR_MSG_TYPE; 151 if (retval) 152 goto done; 153 154 state->active_realm = setup_server_realm(handle, req->server); 155 if (state->active_realm == NULL) { 156 retval = KRB5KDC_ERR_WRONG_REALM; 157 goto done; 158 } 159 160 if (krb5_is_tgs_req(pkt)) { 161 /* process_tgs_req frees the request */ 162 retval = process_tgs_req(req, pkt, remote_addr, state->active_realm, 163 &response); 164 req = NULL; 165 } else if (krb5_is_as_req(pkt)) { 166 /* process_as_req frees the request and calls finish_dispatch_cache. */ 167 process_as_req(req, pkt, local_addr, remote_addr, state->active_realm, 168 vctx, finish_dispatch_cache, state); 169 return; 170 } 171 172 done: 173 krb5_free_kdc_req(kdc_err_context, req); 174 finish_dispatch_cache(state, retval, response); 175 } 176 177 static krb5_error_code 178 make_too_big_error(kdc_realm_t *realm, krb5_data **out) 179 { 180 krb5_context context = realm->realm_context; 181 krb5_error errpkt; 182 krb5_error_code retval; 183 krb5_data *scratch; 184 185 *out = NULL; 186 memset(&errpkt, 0, sizeof(errpkt)); 187 188 retval = krb5_us_timeofday(context, &errpkt.stime, &errpkt.susec); 189 if (retval) 190 return retval; 191 errpkt.error = KRB_ERR_RESPONSE_TOO_BIG; 192 errpkt.server = realm->realm_tgsprinc; 193 errpkt.client = NULL; 194 errpkt.text.length = 0; 195 errpkt.text.data = 0; 196 errpkt.e_data.length = 0; 197 errpkt.e_data.data = 0; 198 scratch = malloc(sizeof(*scratch)); 199 if (scratch == NULL) 200 return ENOMEM; 201 retval = krb5_mk_error(context, &errpkt, scratch); 202 if (retval) { 203 free(scratch); 204 return retval; 205 } 206 207 *out = scratch; 208 return 0; 209 } 210 211 krb5_context get_context(void *handle) 212 { 213 struct server_handle *sh = handle; 214 215 return sh->kdc_err_context; 216 } 217