xref: /freebsd/crypto/krb5/src/kdc/dispatch.c (revision f1c4c3daccbaf3820f0e2224de53df12fc952fcc)
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 struct sockaddr * local_addr,const struct sockaddr * remote_addr,krb5_data * pkt,int is_tcp,verto_ctx * vctx,loop_respond_fn respond,void * arg)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
make_too_big_error(kdc_realm_t * realm,krb5_data ** out)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 
get_context(void * handle)211 krb5_context get_context(void *handle)
212 {
213     struct server_handle *sh = handle;
214 
215     return sh->kdc_err_context;
216 }
217