Untabify kdc_preauth_encts.c
[krb5.git] / src / kdc / dispatch.c
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 <netinet/in.h>
32 #include <arpa/inet.h>
33 #include <string.h>
34
35 static krb5_int32 last_usec = 0, last_os_random = 0;
36
37 static krb5_error_code make_too_big_error (krb5_data **out);
38
39 struct dispatch_state {
40     loop_respond_fn respond;
41     void *arg;
42     krb5_data *request;
43     int is_tcp;
44 };
45
46 static void
47 finish_dispatch(void *arg, krb5_error_code code, krb5_data *response)
48 {
49     struct dispatch_state *state = arg;
50     loop_respond_fn oldrespond;
51     void *oldarg;
52
53     assert(state);
54     oldrespond = state->respond;
55     oldarg = state->arg;
56
57     if (state->is_tcp == 0 && response &&
58         response->length > max_dgram_reply_size) {
59         krb5_free_data(kdc_context, response);
60         response = NULL;
61         code = make_too_big_error(&response);
62         if (code)
63             krb5_klog_syslog(LOG_ERR, "error constructing "
64                              "KRB_ERR_RESPONSE_TOO_BIG error: %s",
65                              error_message(code));
66     }
67
68 #ifndef NOCACHE
69     /* put the response into the lookaside buffer */
70     else if (!code)
71         kdc_insert_lookaside(state->request, response);
72 #endif
73
74     free(state);
75     (*oldrespond)(oldarg, code, response);
76 }
77
78 void
79 dispatch(void *cb, struct sockaddr *local_saddr, const krb5_fulladdr *from,
80          krb5_data *pkt, int is_tcp, loop_respond_fn respond, void *arg)
81 {
82     krb5_error_code retval;
83     krb5_kdc_req *as_req;
84     krb5_int32 now, now_usec;
85     krb5_data *response = NULL;
86     struct dispatch_state *state;
87
88     state = malloc(sizeof(*state));
89     if (!state) {
90         (*respond)(arg, ENOMEM, NULL);
91         return;
92     }
93     state->respond = respond;
94     state->arg = arg;
95     state->request = pkt;
96     state->is_tcp = is_tcp;
97
98     /* decode incoming packet, and dispatch */
99
100 #ifndef NOCACHE
101     /* try the replay lookaside buffer */
102     if (kdc_check_lookaside(pkt, &response)) {
103         /* a hit! */
104         const char *name = 0;
105         char buf[46];
106
107         if (is_tcp != 0 || response->length <= max_dgram_reply_size) {
108             name = inet_ntop (ADDRTYPE2FAMILY (from->address->addrtype),
109                               from->address->contents, buf, sizeof (buf));
110             if (name == 0)
111                 name = "[unknown address type]";
112             krb5_klog_syslog(LOG_INFO,
113                              "DISPATCH: repeated (retransmitted?) request "
114                              "from %s, resending previous response",
115                              name);
116         }
117
118         finish_dispatch(state, 0, response);
119         return;
120     }
121 #endif
122
123     retval = krb5_crypto_us_timeofday(&now, &now_usec);
124     if (retval == 0) {
125         krb5_int32 usec_difference = now_usec-last_usec;
126         krb5_data data;
127         if(last_os_random == 0)
128             last_os_random = now;
129         /* Grab random data from OS every hour*/
130         if(now-last_os_random >= 60*60) {
131             krb5_c_random_os_entropy(kdc_context, 0, NULL);
132             last_os_random = now;
133         }
134
135         data.length = sizeof(krb5_int32);
136         data.data = (void *) &usec_difference;
137
138         krb5_c_random_add_entropy(kdc_context,
139                                   KRB5_C_RANDSOURCE_TIMING, &data);
140         last_usec = now_usec;
141     }
142     /* try TGS_REQ first; they are more common! */
143
144     if (krb5_is_tgs_req(pkt)) {
145         retval = process_tgs_req(pkt, from, &response);
146     } else if (krb5_is_as_req(pkt)) {
147         if (!(retval = decode_krb5_as_req(pkt, &as_req))) {
148             /*
149              * setup_server_realm() sets up the global realm-specific data
150              * pointer.
151              * process_as_req frees the request if it is called
152              */
153             if (!(retval = setup_server_realm(as_req->server))) {
154                 process_as_req(as_req, pkt, from, finish_dispatch, state);
155                 return;
156             }
157             else
158                 krb5_free_kdc_req(kdc_context, as_req);
159         }
160     } else
161         retval = KRB5KRB_AP_ERR_MSG_TYPE;
162
163     finish_dispatch(state, retval, response);
164 }
165
166 static krb5_error_code
167 make_too_big_error (krb5_data **out)
168 {
169     krb5_error errpkt;
170     krb5_error_code retval;
171     krb5_data *scratch;
172
173     *out = NULL;
174     memset(&errpkt, 0, sizeof(errpkt));
175
176     retval = krb5_us_timeofday(kdc_context, &errpkt.stime, &errpkt.susec);
177     if (retval)
178         return retval;
179     errpkt.error = KRB_ERR_RESPONSE_TOO_BIG;
180     errpkt.server = tgs_server;
181     errpkt.client = NULL;
182     errpkt.text.length = 0;
183     errpkt.text.data = 0;
184     errpkt.e_data.length = 0;
185     errpkt.e_data.data = 0;
186     scratch = malloc(sizeof(*scratch));
187     if (scratch == NULL)
188         return ENOMEM;
189     retval = krb5_mk_error(kdc_context, &errpkt, scratch);
190     if (retval) {
191         free(scratch);
192         return retval;
193     }
194
195     *out = scratch;
196     return 0;
197 }