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
finish_dispatch(struct dispatch_state * state,krb5_error_code code,krb5_data * response)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
finish_dispatch_cache(void * arg,krb5_error_code code,krb5_data * response)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
dispatch(void * cb,const krb5_fulladdr * local_addr,const krb5_fulladdr * remote_addr,krb5_data * pkt,int is_tcp,verto_ctx * vctx,loop_respond_fn respond,void * arg)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
make_too_big_error(kdc_realm_t * realm,krb5_data ** out)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
get_context(void * handle)212 krb5_context get_context(void *handle)
213 {
214 struct server_handle *sh = handle;
215
216 return sh->kdc_err_context;
217 }
218