1 | /********************************************************************************************************* |
---|
2 | * Software License Agreement (BSD License) * |
---|
3 | * Author: Sebastien Decugis <sdecugis@nict.go.jp> * |
---|
4 | * * |
---|
5 | * Copyright (c) 2011, WIDE Project and NICT * |
---|
6 | * All rights reserved. * |
---|
7 | * * |
---|
8 | * Redistribution and use of this software in source and binary forms, with or without modification, are * |
---|
9 | * permitted provided that the following conditions are met: * |
---|
10 | * * |
---|
11 | * * Redistributions of source code must retain the above * |
---|
12 | * copyright notice, this list of conditions and the * |
---|
13 | * following disclaimer. * |
---|
14 | * * |
---|
15 | * * Redistributions in binary form must reproduce the above * |
---|
16 | * copyright notice, this list of conditions and the * |
---|
17 | * following disclaimer in the documentation and/or other * |
---|
18 | * materials provided with the distribution. * |
---|
19 | * * |
---|
20 | * * Neither the name of the WIDE Project or NICT nor the * |
---|
21 | * names of its contributors may be used to endorse or * |
---|
22 | * promote products derived from this software without * |
---|
23 | * specific prior written permission of WIDE Project and * |
---|
24 | * NICT. * |
---|
25 | * * |
---|
26 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED * |
---|
27 | * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A * |
---|
28 | * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR * |
---|
29 | * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * |
---|
30 | * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * |
---|
31 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR * |
---|
32 | * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * |
---|
33 | * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * |
---|
34 | *********************************************************************************************************/ |
---|
35 | |
---|
36 | #include "fdcore-internal.h" |
---|
37 | |
---|
38 | /* This file contains code to handle Capabilities Exchange messages (CER and CEA) and election process */ |
---|
39 | |
---|
40 | /* Save a connection as peer's principal */ |
---|
41 | static int set_peer_cnx(struct fd_peer * peer, struct cnxctx **cnx) |
---|
42 | { |
---|
43 | CHECK_PARAMS( peer->p_cnxctx == NULL ); |
---|
44 | |
---|
45 | /* Save the connection in peer */ |
---|
46 | peer->p_cnxctx = *cnx; |
---|
47 | *cnx = NULL; |
---|
48 | |
---|
49 | /* Set the events to be sent to the PSM */ |
---|
50 | CHECK_FCT( fd_cnx_recv_setaltfifo(peer->p_cnxctx, peer->p_events) ); |
---|
51 | |
---|
52 | /* Read the credentials if possible */ |
---|
53 | if (fd_cnx_getTLS(peer->p_cnxctx)) { |
---|
54 | CHECK_FCT( fd_cnx_getcred(peer->p_cnxctx, &peer->p_hdr.info.runtime.pir_cert_list, &peer->p_hdr.info.runtime.pir_cert_list_size) ); |
---|
55 | } |
---|
56 | |
---|
57 | /* Read the endpoints, maybe used to reconnect to the peer later */ |
---|
58 | CHECK_FCT( fd_cnx_getremoteeps(peer->p_cnxctx, &peer->p_hdr.info.pi_endpoints) ); |
---|
59 | |
---|
60 | /* Read the protocol */ |
---|
61 | peer->p_hdr.info.runtime.pir_proto = fd_cnx_getproto(peer->p_cnxctx); |
---|
62 | |
---|
63 | return 0; |
---|
64 | } |
---|
65 | |
---|
66 | /* Delete the peer connection, and cleanup associated information */ |
---|
67 | void fd_p_ce_clear_cnx(struct fd_peer * peer, struct cnxctx ** cnx_kept) |
---|
68 | { |
---|
69 | peer->p_hdr.info.runtime.pir_cert_list = NULL; |
---|
70 | peer->p_hdr.info.runtime.pir_cert_list_size = 0; |
---|
71 | peer->p_hdr.info.runtime.pir_proto = 0; |
---|
72 | |
---|
73 | if (peer->p_cnxctx) { |
---|
74 | if (cnx_kept != NULL) { |
---|
75 | *cnx_kept = peer->p_cnxctx; |
---|
76 | } else { |
---|
77 | fd_cnx_destroy(peer->p_cnxctx); |
---|
78 | } |
---|
79 | peer->p_cnxctx = NULL; |
---|
80 | } |
---|
81 | } |
---|
82 | |
---|
83 | /* Election: compare the Diameter Ids by lexical order, return true if the election is won */ |
---|
84 | static __inline__ int election_result(struct fd_peer * peer) |
---|
85 | { |
---|
86 | int ret = (strcasecmp(peer->p_hdr.info.pi_diamid, fd_g_config->cnf_diamid) < 0); |
---|
87 | if (ret) { |
---|
88 | TRACE_DEBUG(INFO, "Election WON against peer '%s'", peer->p_hdr.info.pi_diamid); |
---|
89 | } else { |
---|
90 | TRACE_DEBUG(INFO, "Election LOST against peer '%s'", peer->p_hdr.info.pi_diamid); |
---|
91 | } |
---|
92 | return ret; |
---|
93 | } |
---|
94 | |
---|
95 | /* Add AVPs about local information in a CER or CEA */ |
---|
96 | static int add_CE_info(struct msg *msg, struct cnxctx * cnx, int isi_tls, int isi_none) |
---|
97 | { |
---|
98 | struct dict_object * dictobj = NULL; |
---|
99 | struct avp * avp = NULL; |
---|
100 | union avp_value val; |
---|
101 | struct fd_list *li; |
---|
102 | |
---|
103 | /* Add the Origin-* AVPs */ |
---|
104 | CHECK_FCT( fd_msg_add_origin ( msg, 1 ) ); |
---|
105 | |
---|
106 | /* Find the model for Host-IP-Address AVP */ |
---|
107 | CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Host-IP-Address", &dictobj, ENOENT ) ); |
---|
108 | |
---|
109 | /* Add the AVP(s) -- not sure what is the purpose... We could probably only add the primary one ? */ |
---|
110 | for (li = fd_g_config->cnf_endpoints.next; li != &fd_g_config->cnf_endpoints; li = li->next) { |
---|
111 | struct fd_endpoint * ep = (struct fd_endpoint *)li; |
---|
112 | |
---|
113 | CHECK_FCT( fd_msg_avp_new ( dictobj, 0, &avp ) ); |
---|
114 | CHECK_FCT( fd_msg_avp_value_encode ( &ep->ss, avp ) ); |
---|
115 | CHECK_FCT( fd_msg_avp_add( msg, MSG_BRW_LAST_CHILD, avp ) ); |
---|
116 | } |
---|
117 | |
---|
118 | /* Vendor-Id, Product-Name, and Firmware-Revision AVPs */ |
---|
119 | CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Vendor-Id", &dictobj, ENOENT ) ); |
---|
120 | CHECK_FCT( fd_msg_avp_new ( dictobj, 0, &avp ) ); |
---|
121 | val.u32 = MY_VENDOR_ID; |
---|
122 | CHECK_FCT( fd_msg_avp_setvalue( avp, &val ) ); |
---|
123 | CHECK_FCT( fd_msg_avp_add( msg, MSG_BRW_LAST_CHILD, avp ) ); |
---|
124 | |
---|
125 | CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Product-Name", &dictobj, ENOENT ) ); |
---|
126 | CHECK_FCT( fd_msg_avp_new ( dictobj, 0, &avp ) ); |
---|
127 | val.os.data = (unsigned char *)FD_PROJECT_NAME; |
---|
128 | val.os.len = strlen(FD_PROJECT_NAME); |
---|
129 | CHECK_FCT( fd_msg_avp_setvalue( avp, &val ) ); |
---|
130 | CHECK_FCT( fd_msg_avp_add( msg, MSG_BRW_LAST_CHILD, avp ) ); |
---|
131 | |
---|
132 | CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Firmware-Revision", &dictobj, ENOENT ) ); |
---|
133 | CHECK_FCT( fd_msg_avp_new ( dictobj, 0, &avp ) ); |
---|
134 | val.u32 = (uint32_t)(FD_PROJECT_VERSION_MAJOR * 10000 + FD_PROJECT_VERSION_MINOR * 100 + FD_PROJECT_VERSION_REV); |
---|
135 | CHECK_FCT( fd_msg_avp_setvalue( avp, &val ) ); |
---|
136 | CHECK_FCT( fd_msg_avp_add( msg, MSG_BRW_LAST_CHILD, avp ) ); |
---|
137 | |
---|
138 | |
---|
139 | /* Add the Inband-Security-Id AVP if needed */ |
---|
140 | if (isi_tls || isi_none) { |
---|
141 | CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Inband-Security-Id", &dictobj, ENOENT ) ); |
---|
142 | |
---|
143 | if (isi_none) { |
---|
144 | CHECK_FCT( fd_msg_avp_new ( dictobj, 0, &avp ) ); |
---|
145 | val.u32 = ACV_ISI_NO_INBAND_SECURITY; |
---|
146 | CHECK_FCT( fd_msg_avp_setvalue( avp, &val ) ); |
---|
147 | CHECK_FCT( fd_msg_avp_add( msg, MSG_BRW_LAST_CHILD, avp ) ); |
---|
148 | } |
---|
149 | |
---|
150 | if (isi_tls) { |
---|
151 | CHECK_FCT( fd_msg_avp_new ( dictobj, 0, &avp ) ); |
---|
152 | val.u32 = ACV_ISI_TLS; |
---|
153 | CHECK_FCT( fd_msg_avp_setvalue( avp, &val ) ); |
---|
154 | CHECK_FCT( fd_msg_avp_add( msg, MSG_BRW_LAST_CHILD, avp ) ); |
---|
155 | } |
---|
156 | } |
---|
157 | |
---|
158 | /* List of local applications */ |
---|
159 | { |
---|
160 | struct dict_object * dictobj_auth = NULL; |
---|
161 | struct dict_object * dictobj_acct = NULL; |
---|
162 | struct dict_object * dictobj_vid = NULL; |
---|
163 | |
---|
164 | CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Vendor-Specific-Application-Id", &dictobj, ENOENT ) ); |
---|
165 | CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Vendor-Id", &dictobj_vid, ENOENT ) ); |
---|
166 | CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Auth-Application-Id", &dictobj_auth, ENOENT ) ); |
---|
167 | CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Acct-Application-Id", &dictobj_acct, ENOENT ) ); |
---|
168 | |
---|
169 | for (li = fd_g_config->cnf_apps.next; li != &fd_g_config->cnf_apps; li = li->next) { |
---|
170 | struct fd_app * a = (struct fd_app *)(li); |
---|
171 | |
---|
172 | if (a->flags.auth) { |
---|
173 | CHECK_FCT( fd_msg_avp_new ( dictobj_auth, 0, &avp ) ); |
---|
174 | val.u32 = a->appid; |
---|
175 | CHECK_FCT( fd_msg_avp_setvalue( avp, &val ) ); |
---|
176 | if (a->vndid != 0) { |
---|
177 | struct avp * avp2 = NULL; |
---|
178 | CHECK_FCT( fd_msg_avp_new ( dictobj, 0, &avp2 ) ); |
---|
179 | CHECK_FCT( fd_msg_avp_add( avp2, MSG_BRW_LAST_CHILD, avp ) ); |
---|
180 | avp = avp2; |
---|
181 | CHECK_FCT( fd_msg_avp_new ( dictobj_vid, 0, &avp2 ) ); |
---|
182 | val.u32 = a->vndid; |
---|
183 | CHECK_FCT( fd_msg_avp_setvalue( avp2, &val ) ); |
---|
184 | CHECK_FCT( fd_msg_avp_add( avp, MSG_BRW_LAST_CHILD, avp2 ) ); |
---|
185 | } |
---|
186 | CHECK_FCT( fd_msg_avp_add( msg, MSG_BRW_LAST_CHILD, avp ) ); |
---|
187 | } |
---|
188 | if (a->flags.acct) { |
---|
189 | CHECK_FCT( fd_msg_avp_new ( dictobj_acct, 0, &avp ) ); |
---|
190 | val.u32 = a->appid; |
---|
191 | CHECK_FCT( fd_msg_avp_setvalue( avp, &val ) ); |
---|
192 | if (a->vndid != 0) { |
---|
193 | struct avp * avp2 = NULL; |
---|
194 | CHECK_FCT( fd_msg_avp_new ( dictobj, 0, &avp2 ) ); |
---|
195 | CHECK_FCT( fd_msg_avp_add( avp2, MSG_BRW_LAST_CHILD, avp ) ); |
---|
196 | avp = avp2; |
---|
197 | CHECK_FCT( fd_msg_avp_new ( dictobj_vid, 0, &avp2 ) ); |
---|
198 | val.u32 = a->vndid; |
---|
199 | CHECK_FCT( fd_msg_avp_setvalue( avp2, &val ) ); |
---|
200 | CHECK_FCT( fd_msg_avp_add( avp, MSG_BRW_LAST_CHILD, avp2 ) ); |
---|
201 | } |
---|
202 | CHECK_FCT( fd_msg_avp_add( msg, MSG_BRW_LAST_CHILD, avp ) ); |
---|
203 | } |
---|
204 | } |
---|
205 | |
---|
206 | /* do not forget the relay application */ |
---|
207 | if (! fd_g_config->cnf_flags.no_fwd) { |
---|
208 | CHECK_FCT( fd_msg_avp_new ( dictobj_auth, 0, &avp ) ); |
---|
209 | val.u32 = AI_RELAY; |
---|
210 | CHECK_FCT( fd_msg_avp_setvalue( avp, &val ) ); |
---|
211 | CHECK_FCT( fd_msg_avp_add( msg, MSG_BRW_LAST_CHILD, avp ) ); |
---|
212 | } |
---|
213 | } |
---|
214 | |
---|
215 | /* Add the list of supported vendors */ |
---|
216 | { |
---|
217 | uint32_t * array = fd_dict_get_vendorid_list(fd_g_config->cnf_dict); |
---|
218 | if (array) { |
---|
219 | int i = 0; |
---|
220 | CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Supported-Vendor-Id", &dictobj, ENOENT ) ); |
---|
221 | |
---|
222 | while (array[i] != 0) { |
---|
223 | CHECK_FCT( fd_msg_avp_new ( dictobj, 0, &avp ) ); |
---|
224 | val.u32 = array[i]; |
---|
225 | CHECK_FCT( fd_msg_avp_setvalue( avp, &val ) ); |
---|
226 | CHECK_FCT( fd_msg_avp_add( msg, MSG_BRW_LAST_CHILD, avp ) ); |
---|
227 | i++; |
---|
228 | } |
---|
229 | |
---|
230 | free(array); |
---|
231 | } |
---|
232 | } |
---|
233 | |
---|
234 | return 0; |
---|
235 | } |
---|
236 | |
---|
237 | /* Remove any information saved from a previous CER/CEA exchange */ |
---|
238 | static void cleanup_remote_CE_info(struct fd_peer * peer) |
---|
239 | { |
---|
240 | /* free linked information */ |
---|
241 | free(peer->p_hdr.info.runtime.pir_realm); |
---|
242 | free(peer->p_hdr.info.runtime.pir_prodname); |
---|
243 | while (!FD_IS_LIST_EMPTY(&peer->p_hdr.info.runtime.pir_apps)) { |
---|
244 | struct fd_list * li = peer->p_hdr.info.runtime.pir_apps.next; |
---|
245 | fd_list_unlink(li); |
---|
246 | free(li); |
---|
247 | } |
---|
248 | /* note: pir_cert_list needs not be freed (belongs to gnutls) */ |
---|
249 | |
---|
250 | /* cleanup the area */ |
---|
251 | memset(&peer->p_hdr.info.runtime, 0, sizeof(peer->p_hdr.info.runtime)); |
---|
252 | |
---|
253 | /* reinit the list */ |
---|
254 | fd_list_init(&peer->p_hdr.info.runtime.pir_apps, peer); |
---|
255 | |
---|
256 | /* Remove previously advertised endpoints */ |
---|
257 | fd_ep_clearflags( &peer->p_hdr.info.pi_endpoints, EP_FL_ADV ); |
---|
258 | } |
---|
259 | |
---|
260 | /* Extract information sent by the remote peer and save it in our peer structure */ |
---|
261 | static int save_remote_CE_info(struct msg * msg, struct fd_peer * peer, struct fd_pei * error, uint32_t *rc) |
---|
262 | { |
---|
263 | struct avp * avp = NULL; |
---|
264 | |
---|
265 | cleanup_remote_CE_info(peer); |
---|
266 | |
---|
267 | CHECK_FCT( fd_msg_browse( msg, MSG_BRW_FIRST_CHILD, &avp, NULL) ); |
---|
268 | |
---|
269 | /* Loop on all AVPs and save what we are interrested into */ |
---|
270 | while (avp) { |
---|
271 | struct avp_hdr * hdr; |
---|
272 | |
---|
273 | CHECK_FCT( fd_msg_avp_hdr( avp, &hdr ) ); |
---|
274 | |
---|
275 | if (hdr->avp_flags & AVP_FLAG_VENDOR) { |
---|
276 | /* Ignore all vendor-specific AVPs in CER/CEA because we don't support any currently */ |
---|
277 | TRACE_DEBUG(FULL, "Ignored a vendor AVP in CER / CEA"); |
---|
278 | fd_msg_dump_one(FULL, avp); |
---|
279 | goto next; |
---|
280 | } |
---|
281 | |
---|
282 | switch (hdr->avp_code) { |
---|
283 | case AC_RESULT_CODE: /* Result-Code */ |
---|
284 | if (hdr->avp_value == NULL) { |
---|
285 | /* This is a sanity check */ |
---|
286 | TRACE_DEBUG(NONE, "Ignored an AVP with unset value in CER/CEA"); |
---|
287 | fd_msg_dump_one(NONE, avp); |
---|
288 | ASSERT(0); /* To check if this really happens, and understand why... */ |
---|
289 | goto next; |
---|
290 | } |
---|
291 | |
---|
292 | if (rc) |
---|
293 | *rc = hdr->avp_value->u32; |
---|
294 | break; |
---|
295 | |
---|
296 | case AC_ORIGIN_HOST: /* Origin-Host */ |
---|
297 | if (hdr->avp_value == NULL) { |
---|
298 | /* This is a sanity check */ |
---|
299 | TRACE_DEBUG(NONE, "Ignored an AVP with unset value in CER/CEA"); |
---|
300 | fd_msg_dump_one(NONE, avp); |
---|
301 | ASSERT(0); /* To check if this really happens, and understand why... */ |
---|
302 | goto next; |
---|
303 | } |
---|
304 | |
---|
305 | /* We check that the value matches what we know, otherwise disconnect the peer */ |
---|
306 | if (fd_os_almostcasesrch(hdr->avp_value->os.data, hdr->avp_value->os.len, |
---|
307 | peer->p_hdr.info.pi_diamid, peer->p_hdr.info.pi_diamidlen, NULL)) { |
---|
308 | TRACE_DEBUG(INFO, "Received a message with Origin-Host set to '%.*s' while expecting '%s'\n", |
---|
309 | hdr->avp_value->os.len, hdr->avp_value->os.data, peer->p_hdr.info.pi_diamid); |
---|
310 | error->pei_errcode = "ER_DIAMETER_AVP_NOT_ALLOWED"; |
---|
311 | error->pei_message = "Your Origin-Host value does not match my configuration."; |
---|
312 | error->pei_avp = avp; |
---|
313 | return EINVAL; |
---|
314 | } |
---|
315 | |
---|
316 | break; |
---|
317 | |
---|
318 | case AC_ORIGIN_REALM: /* Origin-Realm */ |
---|
319 | if (hdr->avp_value == NULL) { |
---|
320 | /* This is a sanity check */ |
---|
321 | TRACE_DEBUG(NONE, "Ignored an AVP with unset value in CER/CEA"); |
---|
322 | fd_msg_dump_one(NONE, avp); |
---|
323 | ASSERT(0); /* To check if this really happens, and understand why... */ |
---|
324 | goto next; |
---|
325 | } |
---|
326 | |
---|
327 | /* In case of multiple AVPs */ |
---|
328 | if (peer->p_hdr.info.runtime.pir_realm) { |
---|
329 | TRACE_DEBUG(INFO, "Multiple instances of the Origin-Realm AVP"); |
---|
330 | error->pei_errcode = "ER_DIAMETER_AVP_OCCURS_TOO_MANY_TIMES"; |
---|
331 | error->pei_message = "I found several Origin-Realm AVPs"; |
---|
332 | error->pei_avp = avp; |
---|
333 | return EINVAL; |
---|
334 | } |
---|
335 | |
---|
336 | /* If the octet string contains a \0 */ |
---|
337 | if (!fd_os_is_valid_DiameterIdentity(hdr->avp_value->os.data, hdr->avp_value->os.len)) { |
---|
338 | error->pei_errcode = "ER_DIAMETER_INVALID_AVP_VALUE"; |
---|
339 | error->pei_message = "Your Origin-Realm contains invalid characters."; |
---|
340 | error->pei_avp = avp; |
---|
341 | return EINVAL; |
---|
342 | } |
---|
343 | |
---|
344 | /* Save the value */ |
---|
345 | CHECK_MALLOC( peer->p_hdr.info.runtime.pir_realm = os0dup( hdr->avp_value->os.data, hdr->avp_value->os.len ) ); |
---|
346 | peer->p_hdr.info.runtime.pir_realmlen = hdr->avp_value->os.len; |
---|
347 | break; |
---|
348 | |
---|
349 | case AC_HOST_IP_ADDRESS: /* Host-IP-Address */ |
---|
350 | if (hdr->avp_value == NULL) { |
---|
351 | /* This is a sanity check */ |
---|
352 | TRACE_DEBUG(NONE, "Ignored an AVP with unset value in CER/CEA"); |
---|
353 | fd_msg_dump_one(NONE, avp); |
---|
354 | ASSERT(0); /* To check if this really happens, and understand why... */ |
---|
355 | goto next; |
---|
356 | } |
---|
357 | { |
---|
358 | sSS ss; |
---|
359 | |
---|
360 | /* Get the sockaddr value */ |
---|
361 | memset(&ss, 0, sizeof(ss)); |
---|
362 | CHECK_FCT_DO( fd_msg_avp_value_interpret( avp, &ss), |
---|
363 | { |
---|
364 | /* in case of error, assume the AVP value was wrong */ |
---|
365 | error->pei_errcode = "ER_DIAMETER_INVALID_AVP_VALUE"; |
---|
366 | error->pei_avp = avp; |
---|
367 | return EINVAL; |
---|
368 | } ); |
---|
369 | |
---|
370 | /* Save this endpoint in the list as advertized */ |
---|
371 | CHECK_FCT( fd_ep_add_merge( &peer->p_hdr.info.pi_endpoints, (sSA *)&ss, sizeof(sSS), EP_FL_ADV ) ); |
---|
372 | } |
---|
373 | break; |
---|
374 | |
---|
375 | case AC_VENDOR_ID: /* Vendor-Id */ |
---|
376 | if (hdr->avp_value == NULL) { |
---|
377 | /* This is a sanity check */ |
---|
378 | TRACE_DEBUG(NONE, "Ignored an AVP with unset value in CER/CEA"); |
---|
379 | fd_msg_dump_one(NONE, avp); |
---|
380 | ASSERT(0); /* To check if this really happens, and understand why... */ |
---|
381 | goto next; |
---|
382 | } |
---|
383 | |
---|
384 | /* In case of multiple AVPs */ |
---|
385 | if (peer->p_hdr.info.runtime.pir_vendorid) { |
---|
386 | TRACE_DEBUG(INFO, "Multiple instances of the Vendor-Id AVP"); |
---|
387 | error->pei_errcode = "ER_DIAMETER_AVP_OCCURS_TOO_MANY_TIMES"; |
---|
388 | error->pei_message = "I found several Vendor-Id AVPs"; |
---|
389 | error->pei_avp = avp; |
---|
390 | return EINVAL; |
---|
391 | } |
---|
392 | |
---|
393 | peer->p_hdr.info.runtime.pir_vendorid = hdr->avp_value->u32; |
---|
394 | break; |
---|
395 | |
---|
396 | case AC_PRODUCT_NAME: /* Product-Name */ |
---|
397 | if (hdr->avp_value == NULL) { |
---|
398 | /* This is a sanity check */ |
---|
399 | TRACE_DEBUG(NONE, "Ignored an AVP with unset value in CER/CEA"); |
---|
400 | fd_msg_dump_one(NONE, avp); |
---|
401 | ASSERT(0); /* To check if this really happens, and understand why... */ |
---|
402 | goto next; |
---|
403 | } |
---|
404 | |
---|
405 | /* In case of multiple AVPs */ |
---|
406 | if (peer->p_hdr.info.runtime.pir_prodname) { |
---|
407 | TRACE_DEBUG(INFO, "Multiple instances of the Product-Name AVP"); |
---|
408 | error->pei_errcode = "ER_DIAMETER_AVP_OCCURS_TOO_MANY_TIMES"; |
---|
409 | error->pei_message = "I found several Product-Name AVPs"; |
---|
410 | error->pei_avp = avp; |
---|
411 | return EINVAL; |
---|
412 | } |
---|
413 | |
---|
414 | CHECK_MALLOC( peer->p_hdr.info.runtime.pir_prodname = calloc( hdr->avp_value->os.len + 1, 1 ) ); |
---|
415 | memcpy(peer->p_hdr.info.runtime.pir_prodname, hdr->avp_value->os.data, hdr->avp_value->os.len); |
---|
416 | break; |
---|
417 | |
---|
418 | case AC_ORIGIN_STATE_ID: /* Origin-State-Id */ |
---|
419 | if (hdr->avp_value == NULL) { |
---|
420 | /* This is a sanity check */ |
---|
421 | TRACE_DEBUG(NONE, "Ignored an AVP with unset value in CER/CEA"); |
---|
422 | fd_msg_dump_one(NONE, avp); |
---|
423 | ASSERT(0); /* To check if this really happens, and understand why... */ |
---|
424 | goto next; |
---|
425 | } |
---|
426 | |
---|
427 | /* In case of multiple AVPs */ |
---|
428 | if (peer->p_hdr.info.runtime.pir_orstate) { |
---|
429 | TRACE_DEBUG(INFO, "Multiple instances of the Origin-State-Id AVP"); |
---|
430 | error->pei_errcode = "ER_DIAMETER_AVP_OCCURS_TOO_MANY_TIMES"; |
---|
431 | error->pei_message = "I found several Origin-State-Id AVPs"; |
---|
432 | error->pei_avp = avp; |
---|
433 | return EINVAL; |
---|
434 | } |
---|
435 | |
---|
436 | peer->p_hdr.info.runtime.pir_orstate = hdr->avp_value->u32; |
---|
437 | break; |
---|
438 | |
---|
439 | case AC_SUPPORTED_VENDOR_ID: /* Supported-Vendor-Id */ |
---|
440 | if (hdr->avp_value == NULL) { |
---|
441 | /* This is a sanity check */ |
---|
442 | TRACE_DEBUG(NONE, "Ignored an AVP with unset value in CER/CEA"); |
---|
443 | fd_msg_dump_one(NONE, avp); |
---|
444 | ASSERT(0); /* To check if this really happens, and understand why... */ |
---|
445 | goto next; |
---|
446 | } |
---|
447 | |
---|
448 | TRACE_DEBUG(FULL, "'%s' claims support for a subset of vendor %d features.", peer->p_hdr.info.pi_diamid, hdr->avp_value->u32); |
---|
449 | /* not that it makes a difference for us... |
---|
450 | -- if an application actually needs this info, we could save it somewhere. |
---|
451 | */ |
---|
452 | break; |
---|
453 | |
---|
454 | case AC_VENDOR_SPECIFIC_APPLICATION_ID: /* Vendor-Specific-Application-Id (grouped)*/ |
---|
455 | { |
---|
456 | struct avp * inavp = NULL; |
---|
457 | application_id_t aid = 0; |
---|
458 | vendor_id_t vid = 0; |
---|
459 | int auth = 0; |
---|
460 | int acct = 0; |
---|
461 | |
---|
462 | /* get the first child AVP */ |
---|
463 | CHECK_FCT( fd_msg_browse(avp, MSG_BRW_FIRST_CHILD, &inavp, NULL) ); |
---|
464 | |
---|
465 | while (inavp) { |
---|
466 | struct avp_hdr * inhdr; |
---|
467 | CHECK_FCT( fd_msg_avp_hdr( inavp, &inhdr ) ); |
---|
468 | |
---|
469 | if (inhdr->avp_flags & AVP_FLAG_VENDOR) { |
---|
470 | TRACE_DEBUG(FULL, "Ignored a vendor AVP inside Vendor-Specific-Application-Id AVP"); |
---|
471 | fd_msg_dump_one(FULL, avp); |
---|
472 | goto innext; |
---|
473 | } |
---|
474 | |
---|
475 | if (inhdr->avp_value == NULL) { |
---|
476 | /* This is a sanity check */ |
---|
477 | TRACE_DEBUG(NONE, "Ignored an AVP with unset value in CER/CEA"); |
---|
478 | fd_msg_dump_one(NONE, avp); |
---|
479 | ASSERT(0); /* To check if this really happens, and understand why... */ |
---|
480 | goto innext; |
---|
481 | } |
---|
482 | switch (inhdr->avp_code) { |
---|
483 | case AC_VENDOR_ID: /* Vendor-Id */ |
---|
484 | vid = inhdr->avp_value->u32; |
---|
485 | break; |
---|
486 | case AC_AUTH_APPLICATION_ID: /* Auth-Application-Id */ |
---|
487 | aid = inhdr->avp_value->u32; |
---|
488 | auth += 1; |
---|
489 | break; |
---|
490 | case AC_ACCT_APPLICATION_ID: /* Acct-Application-Id */ |
---|
491 | aid = inhdr->avp_value->u32; |
---|
492 | acct += 1; |
---|
493 | break; |
---|
494 | /* ignore other AVPs */ |
---|
495 | } |
---|
496 | |
---|
497 | innext: |
---|
498 | /* Go to next in AVP */ |
---|
499 | CHECK_FCT( fd_msg_browse(inavp, MSG_BRW_NEXT, &inavp, NULL) ); |
---|
500 | } |
---|
501 | |
---|
502 | if (auth + acct != 1) { |
---|
503 | TRACE_DEBUG(FULL, "Invalid Vendor-Specific-Application-Id AVP received, ignored"); |
---|
504 | fd_msg_dump_one(FULL, avp); |
---|
505 | error->pei_errcode = "ER_DIAMETER_INVALID_AVP_VALUE"; |
---|
506 | error->pei_avp = avp; |
---|
507 | return EINVAL; |
---|
508 | } else { |
---|
509 | /* Add an entry in the list */ |
---|
510 | CHECK_FCT( fd_app_merge(&peer->p_hdr.info.runtime.pir_apps, aid, vid, auth, acct) ); |
---|
511 | } |
---|
512 | } |
---|
513 | break; |
---|
514 | |
---|
515 | case AC_AUTH_APPLICATION_ID: /* Auth-Application-Id */ |
---|
516 | if (hdr->avp_value == NULL) { |
---|
517 | /* This is a sanity check */ |
---|
518 | TRACE_DEBUG(NONE, "Ignored an AVP with unset value in CER/CEA"); |
---|
519 | fd_msg_dump_one(NONE, avp); |
---|
520 | ASSERT(0); /* To check if this really happens, and understand why... */ |
---|
521 | goto next; |
---|
522 | } |
---|
523 | |
---|
524 | if (hdr->avp_value->u32 == AI_RELAY) { |
---|
525 | peer->p_hdr.info.runtime.pir_relay = 1; |
---|
526 | } else { |
---|
527 | CHECK_FCT( fd_app_merge(&peer->p_hdr.info.runtime.pir_apps, hdr->avp_value->u32, 0, 1, 0) ); |
---|
528 | } |
---|
529 | break; |
---|
530 | |
---|
531 | case AC_ACCT_APPLICATION_ID: /* Acct-Application-Id */ |
---|
532 | if (hdr->avp_value == NULL) { |
---|
533 | /* This is a sanity check */ |
---|
534 | TRACE_DEBUG(NONE, "Ignored an AVP with unset value in CER/CEA"); |
---|
535 | fd_msg_dump_one(NONE, avp); |
---|
536 | ASSERT(0); /* To check if this really happens, and understand why... */ |
---|
537 | goto next; |
---|
538 | } |
---|
539 | |
---|
540 | if (hdr->avp_value->u32 == AI_RELAY) { |
---|
541 | /* Not clear if the relay application can be inside this AVP... */ |
---|
542 | peer->p_hdr.info.runtime.pir_relay = 1; |
---|
543 | } else { |
---|
544 | CHECK_FCT( fd_app_merge(&peer->p_hdr.info.runtime.pir_apps, hdr->avp_value->u32, 0, 0, 1) ); |
---|
545 | } |
---|
546 | break; |
---|
547 | |
---|
548 | case AC_FIRMWARE_REVISION: /* Firmware-Revision */ |
---|
549 | if (hdr->avp_value == NULL) { |
---|
550 | /* This is a sanity check */ |
---|
551 | TRACE_DEBUG(NONE, "Ignored an AVP with unset value in CER/CEA"); |
---|
552 | fd_msg_dump_one(NONE, avp); |
---|
553 | ASSERT(0); /* To check if this really happens, and understand why... */ |
---|
554 | goto next; |
---|
555 | } |
---|
556 | |
---|
557 | peer->p_hdr.info.runtime.pir_firmrev = hdr->avp_value->u32; |
---|
558 | break; |
---|
559 | |
---|
560 | case AC_INBAND_SECURITY_ID: /* Inband-Security-Id */ |
---|
561 | if (hdr->avp_value == NULL) { |
---|
562 | /* This is a sanity check */ |
---|
563 | TRACE_DEBUG(NONE, "Ignored an AVP with unset value in CER/CEA"); |
---|
564 | fd_msg_dump_one(NONE, avp); |
---|
565 | ASSERT(0); /* To check if this really happens, and understand why... */ |
---|
566 | goto next; |
---|
567 | } |
---|
568 | if (hdr->avp_value->u32 >= 32 ) { |
---|
569 | error->pei_errcode = "ER_DIAMETER_INVALID_AVP_VALUE"; |
---|
570 | error->pei_message = "I don't support this Inband-Security-Id value (yet)."; |
---|
571 | error->pei_avp = avp; |
---|
572 | return EINVAL; |
---|
573 | } |
---|
574 | peer->p_hdr.info.runtime.pir_isi |= (1 << hdr->avp_value->u32); |
---|
575 | break; |
---|
576 | } |
---|
577 | |
---|
578 | next: |
---|
579 | /* Go to next AVP */ |
---|
580 | CHECK_FCT( fd_msg_browse(avp, MSG_BRW_NEXT, &avp, NULL) ); |
---|
581 | } |
---|
582 | |
---|
583 | return 0; |
---|
584 | } |
---|
585 | |
---|
586 | /* Create a CER message for sending */ |
---|
587 | static int create_CER(struct fd_peer * peer, struct cnxctx * cnx, struct msg ** cer) |
---|
588 | { |
---|
589 | int isi_tls = 0; |
---|
590 | int isi_none = 0; |
---|
591 | |
---|
592 | /* Find CER dictionary object and create an instance */ |
---|
593 | CHECK_FCT( fd_msg_new ( fd_dict_cmd_CER, MSGFL_ALLOC_ETEID, cer ) ); |
---|
594 | |
---|
595 | /* Do we need Inband-Security-Id AVPs ? If we're already using TLS, we don't... */ |
---|
596 | if (!fd_cnx_getTLS(cnx)) { |
---|
597 | isi_none = peer->p_hdr.info.config.pic_flags.sec & PI_SEC_NONE; /* we add it even if the peer does not use the old mechanism, it is impossible to distinguish */ |
---|
598 | isi_tls = peer->p_hdr.info.config.pic_flags.sec & PI_SEC_TLS_OLD; |
---|
599 | } |
---|
600 | |
---|
601 | /* Add the information about the local peer */ |
---|
602 | CHECK_FCT( add_CE_info(*cer, cnx, isi_tls, isi_none) ); |
---|
603 | |
---|
604 | /* Done! */ |
---|
605 | return 0; |
---|
606 | } |
---|
607 | |
---|
608 | |
---|
609 | /* Continue with the initiator side */ |
---|
610 | static int to_waitcea(struct fd_peer * peer, struct cnxctx * cnx) |
---|
611 | { |
---|
612 | /* We sent a CER on the connection, set the event queue so that we receive the CEA */ |
---|
613 | CHECK_FCT( set_peer_cnx(peer, &cnx) ); |
---|
614 | |
---|
615 | /* Change state and reset the timer */ |
---|
616 | CHECK_FCT( fd_psm_change_state(peer, STATE_WAITCEA) ); |
---|
617 | fd_psm_next_timeout(peer, 0, CEA_TIMEOUT); |
---|
618 | |
---|
619 | return 0; |
---|
620 | } |
---|
621 | |
---|
622 | /* Reject an incoming connection attempt */ |
---|
623 | static void receiver_reject(struct cnxctx ** recv_cnx, struct msg ** cer, struct fd_pei * error) |
---|
624 | { |
---|
625 | /* Create and send the CEA with appropriate error code */ |
---|
626 | CHECK_FCT_DO( fd_msg_new_answer_from_req ( fd_g_config->cnf_dict, cer, MSGFL_ANSW_ERROR ), goto destroy ); |
---|
627 | CHECK_FCT_DO( fd_msg_rescode_set(*cer, error->pei_errcode, error->pei_message, error->pei_avp, 1 ), goto destroy ); |
---|
628 | CHECK_FCT_DO( fd_out_send(cer, *recv_cnx, NULL, FD_CNX_ORDERED), goto destroy ); |
---|
629 | |
---|
630 | /* And now destroy this connection */ |
---|
631 | destroy: |
---|
632 | fd_cnx_destroy(*recv_cnx); |
---|
633 | *recv_cnx = NULL; |
---|
634 | if (*cer) { |
---|
635 | fd_msg_log(FD_MSG_LOG_DROPPED, *cer, "An error occurred while rejecting a CER."); |
---|
636 | fd_msg_free(*cer); |
---|
637 | *cer = NULL; |
---|
638 | } |
---|
639 | } |
---|
640 | |
---|
641 | /* We have established a new connection to the remote peer, send CER and eventually process the election */ |
---|
642 | int fd_p_ce_handle_newcnx(struct fd_peer * peer, struct cnxctx * initiator) |
---|
643 | { |
---|
644 | struct msg * cer = NULL; |
---|
645 | |
---|
646 | /* Send CER on the new connection */ |
---|
647 | CHECK_FCT( create_CER(peer, initiator, &cer) ); |
---|
648 | CHECK_FCT( fd_out_send(&cer, initiator, peer, FD_CNX_ORDERED) ); |
---|
649 | |
---|
650 | /* Are we doing an election ? */ |
---|
651 | if (fd_peer_getstate(peer) == STATE_WAITCNXACK_ELEC) { |
---|
652 | if (election_result(peer)) { |
---|
653 | /* Close initiator connection */ |
---|
654 | fd_cnx_destroy(initiator); |
---|
655 | |
---|
656 | /* Process with the receiver side */ |
---|
657 | CHECK_FCT( fd_p_ce_process_receiver(peer) ); |
---|
658 | |
---|
659 | } else { |
---|
660 | struct fd_pei pei; |
---|
661 | memset(&pei, 0, sizeof(pei)); |
---|
662 | pei.pei_errcode = "ELECTION_LOST"; |
---|
663 | |
---|
664 | /* Answer an ELECTION LOST to the receiver side */ |
---|
665 | receiver_reject(&peer->p_receiver, &peer->p_cer, &pei); |
---|
666 | CHECK_FCT( to_waitcea(peer, initiator) ); |
---|
667 | } |
---|
668 | } else { |
---|
669 | /* No election (yet) */ |
---|
670 | CHECK_FCT( to_waitcea(peer, initiator) ); |
---|
671 | } |
---|
672 | |
---|
673 | return 0; |
---|
674 | } |
---|
675 | |
---|
676 | /* We have received a Capabilities Exchange message on the peer connection */ |
---|
677 | int fd_p_ce_msgrcv(struct msg ** msg, int req, struct fd_peer * peer) |
---|
678 | { |
---|
679 | uint32_t rc = 0; |
---|
680 | int st; |
---|
681 | struct fd_pei pei; |
---|
682 | |
---|
683 | TRACE_ENTRY("%p %p", msg, peer); |
---|
684 | CHECK_PARAMS( msg && *msg && CHECK_PEER(peer) ); |
---|
685 | |
---|
686 | /* The only valid situation where we are called is in WAITCEA and we receive a CEA (we may have won an election) */ |
---|
687 | |
---|
688 | /* Note : to implement Capabilities Update, we would need to change here */ |
---|
689 | |
---|
690 | /* If it is a CER, just reply an error */ |
---|
691 | if (req) { |
---|
692 | /* Create the error message */ |
---|
693 | CHECK_FCT( fd_msg_new_answer_from_req ( fd_g_config->cnf_dict, msg, MSGFL_ANSW_ERROR ) ); |
---|
694 | |
---|
695 | /* Set the error code */ |
---|
696 | CHECK_FCT( fd_msg_rescode_set(*msg, "ER_DIAMETER_UNABLE_TO_COMPLY", "No CER allowed in current state", NULL, 1 ) ); |
---|
697 | |
---|
698 | /* msg now contains an answer message to send back */ |
---|
699 | CHECK_FCT_DO( fd_out_send(msg, NULL, peer, FD_CNX_ORDERED), /* In case of error the message has already been dumped */ ); |
---|
700 | } |
---|
701 | |
---|
702 | /* If the state is not WAITCEA, just discard the message */ |
---|
703 | if (req || ((st = fd_peer_getstate(peer)) != STATE_WAITCEA)) { |
---|
704 | if (*msg) { |
---|
705 | fd_msg_log( FD_MSG_LOG_DROPPED, *msg, "Received CER/CEA while in '%s' state.\n", STATE_STR(st)); |
---|
706 | CHECK_FCT_DO( fd_msg_free(*msg), /* continue */); |
---|
707 | *msg = NULL; |
---|
708 | } |
---|
709 | |
---|
710 | return 0; |
---|
711 | } |
---|
712 | |
---|
713 | memset(&pei, 0, sizeof(pei)); |
---|
714 | |
---|
715 | /* Save info from the CEA into the peer */ |
---|
716 | CHECK_FCT_DO( save_remote_CE_info(*msg, peer, &pei, &rc), goto cleanup ); |
---|
717 | |
---|
718 | /* Dispose of the message, we don't need it anymore */ |
---|
719 | CHECK_FCT_DO( fd_msg_free(*msg), /* continue */ ); |
---|
720 | *msg = NULL; |
---|
721 | |
---|
722 | /* Check the Result-Code */ |
---|
723 | switch (rc) { |
---|
724 | case ER_DIAMETER_SUCCESS: |
---|
725 | /* No problem, we can continue */ |
---|
726 | break; |
---|
727 | |
---|
728 | case ER_DIAMETER_TOO_BUSY: |
---|
729 | /* Retry later */ |
---|
730 | TRACE_DEBUG(INFO, "Peer %s replied a CEA with Result-Code AVP DIAMETER_TOO_BUSY, will retry later.", peer->p_hdr.info.pi_diamid); |
---|
731 | fd_psm_cleanup(peer, 0); |
---|
732 | fd_psm_next_timeout(peer, 0, 300); |
---|
733 | return 0; |
---|
734 | |
---|
735 | case ER_ELECTION_LOST: |
---|
736 | /* Ok, just wait for a little while for the CER to be processed on the other connection. */ |
---|
737 | TRACE_DEBUG(FULL, "Peer %s replied a CEA with Result-Code AVP ELECTION_LOST, waiting for events.", peer->p_hdr.info.pi_diamid); |
---|
738 | return 0; |
---|
739 | |
---|
740 | default: |
---|
741 | /* In any other case, we abort all attempts to connect to this peer */ |
---|
742 | TRACE_DEBUG(INFO, "Peer %s replied a CEA with Result-Code %d, aborting connection attempts.", peer->p_hdr.info.pi_diamid, rc); |
---|
743 | return EINVAL; |
---|
744 | } |
---|
745 | |
---|
746 | /* Handshake if needed, start clear otherwise */ |
---|
747 | if ( ! fd_cnx_getTLS(peer->p_cnxctx) ) { |
---|
748 | int todo = peer->p_hdr.info.config.pic_flags.sec & peer->p_hdr.info.runtime.pir_isi ; |
---|
749 | /* Special case: if the peer did not send a ISI AVP */ |
---|
750 | if (peer->p_hdr.info.runtime.pir_isi == 0) |
---|
751 | todo = peer->p_hdr.info.config.pic_flags.sec; |
---|
752 | |
---|
753 | if (todo == PI_SEC_NONE) { |
---|
754 | /* Ok for clear connection */ |
---|
755 | TRACE_DEBUG(INFO, "No TLS protection negotiated with peer '%s'.", peer->p_hdr.info.pi_diamid); |
---|
756 | CHECK_FCT( fd_cnx_start_clear(peer->p_cnxctx, 1) ); |
---|
757 | } else { |
---|
758 | |
---|
759 | fd_psm_change_state(peer, STATE_OPEN_HANDSHAKE); |
---|
760 | CHECK_FCT_DO( fd_cnx_handshake(peer->p_cnxctx, GNUTLS_CLIENT, peer->p_hdr.info.config.pic_priority, NULL), |
---|
761 | { |
---|
762 | /* Handshake failed ... */ |
---|
763 | fd_log_debug("TLS Handshake failed with peer '%s', resetting the connection\n", peer->p_hdr.info.pi_diamid); |
---|
764 | goto cleanup; |
---|
765 | } ); |
---|
766 | |
---|
767 | /* Retrieve the credentials */ |
---|
768 | CHECK_FCT( fd_cnx_getcred(peer->p_cnxctx, &peer->p_hdr.info.runtime.pir_cert_list, &peer->p_hdr.info.runtime.pir_cert_list_size) ); |
---|
769 | } |
---|
770 | } |
---|
771 | |
---|
772 | /* Move to next state */ |
---|
773 | if (peer->p_flags.pf_cnx_pb) { |
---|
774 | fd_psm_change_state(peer, STATE_REOPEN ); |
---|
775 | CHECK_FCT( fd_p_dw_reopen(peer) ); |
---|
776 | } else { |
---|
777 | fd_psm_change_state(peer, STATE_OPEN ); |
---|
778 | fd_psm_next_timeout(peer, 1, peer->p_hdr.info.config.pic_twtimer ?: fd_g_config->cnf_timer_tw); |
---|
779 | } |
---|
780 | |
---|
781 | return 0; |
---|
782 | |
---|
783 | cleanup: |
---|
784 | fd_p_ce_clear_cnx(peer, NULL); |
---|
785 | |
---|
786 | /* Send the error to the peer */ |
---|
787 | CHECK_FCT( fd_event_send(peer->p_events, FDEVP_CNX_ERROR, 0, NULL) ); |
---|
788 | |
---|
789 | return 0; |
---|
790 | } |
---|
791 | |
---|
792 | /* Handle the receiver side to go to OPEN or OPEN_NEW state (any election is resolved) */ |
---|
793 | int fd_p_ce_process_receiver(struct fd_peer * peer) |
---|
794 | { |
---|
795 | struct fd_pei pei; |
---|
796 | struct msg * msg = NULL; |
---|
797 | int isi = 0; |
---|
798 | int fatal = 0; |
---|
799 | int tls_sync=0; |
---|
800 | |
---|
801 | TRACE_ENTRY("%p", peer); |
---|
802 | |
---|
803 | CHECK_FCT( set_peer_cnx(peer, &peer->p_receiver) ); |
---|
804 | msg = peer->p_cer; |
---|
805 | peer->p_cer = NULL; |
---|
806 | |
---|
807 | memset(&pei, 0, sizeof(pei)); |
---|
808 | |
---|
809 | /* Parse the content of the received CER */ |
---|
810 | CHECK_FCT_DO( save_remote_CE_info(msg, peer, &pei, NULL), goto error_abort ); |
---|
811 | |
---|
812 | /* Validate the realm if needed */ |
---|
813 | if (peer->p_hdr.info.config.pic_realm) { |
---|
814 | size_t len = strlen(peer->p_hdr.info.config.pic_realm); |
---|
815 | if (fd_os_almostcasesrch(peer->p_hdr.info.config.pic_realm, len, peer->p_hdr.info.runtime.pir_realm, peer->p_hdr.info.runtime.pir_realmlen, NULL)) { |
---|
816 | TRACE_DEBUG(INFO, "Rejected CER from peer '%s', realm mismatch with configured value (returning DIAMETER_UNKNOWN_PEER).\n", peer->p_hdr.info.pi_diamid); |
---|
817 | pei.pei_errcode = "DIAMETER_UNKNOWN_PEER"; /* maybe AVP_NOT_ALLOWED would be better fit? */ |
---|
818 | goto error_abort; |
---|
819 | } |
---|
820 | } |
---|
821 | |
---|
822 | /* Save the credentials if handshake already occurred */ |
---|
823 | if ( fd_cnx_getTLS(peer->p_cnxctx) ) { |
---|
824 | CHECK_FCT( fd_cnx_getcred(peer->p_cnxctx, &peer->p_hdr.info.runtime.pir_cert_list, &peer->p_hdr.info.runtime.pir_cert_list_size) ); |
---|
825 | } |
---|
826 | |
---|
827 | /* Validate the peer if needed */ |
---|
828 | if (peer->p_flags.pf_responder) { |
---|
829 | int res = fd_peer_validate( peer ); |
---|
830 | if (res < 0) { |
---|
831 | TRACE_DEBUG(INFO, "Rejected CER from peer '%s', validation failed (returning DIAMETER_UNKNOWN_PEER).\n", peer->p_hdr.info.pi_diamid); |
---|
832 | pei.pei_errcode = "DIAMETER_UNKNOWN_PEER"; |
---|
833 | goto error_abort; |
---|
834 | } |
---|
835 | CHECK_FCT( res ); |
---|
836 | } |
---|
837 | |
---|
838 | /* Check if we have common applications */ |
---|
839 | if ( fd_g_config->cnf_flags.no_fwd && (! peer->p_hdr.info.runtime.pir_relay) ) { |
---|
840 | int got_common; |
---|
841 | CHECK_FCT( fd_app_check_common( &fd_g_config->cnf_apps, &peer->p_hdr.info.runtime.pir_apps, &got_common) ); |
---|
842 | if (!got_common) { |
---|
843 | TRACE_DEBUG(INFO, "No common application with peer '%s', sending DIAMETER_NO_COMMON_APPLICATION", peer->p_hdr.info.pi_diamid); |
---|
844 | pei.pei_errcode = "DIAMETER_NO_COMMON_APPLICATION"; |
---|
845 | fatal = 1; |
---|
846 | goto error_abort; |
---|
847 | } |
---|
848 | } |
---|
849 | |
---|
850 | /* Do we agree on ISI ? */ |
---|
851 | if ( ! fd_cnx_getTLS(peer->p_cnxctx) ) { |
---|
852 | |
---|
853 | /* In case of responder, the validate callback must have set the config.pic_flags.sec value already */ |
---|
854 | |
---|
855 | /* First case: we are not using old mechanism: ISI are deprecated, we ignore it. */ |
---|
856 | if ( ! (peer->p_hdr.info.config.pic_flags.sec & PI_SEC_TLS_OLD)) { |
---|
857 | /* Just check then that the peer configuration allows for IPsec protection */ |
---|
858 | if (peer->p_hdr.info.config.pic_flags.sec & PI_SEC_NONE) { |
---|
859 | isi = PI_SEC_NONE; |
---|
860 | } else { |
---|
861 | /* otherwise, we should have already been protected. Reject */ |
---|
862 | TRACE_DEBUG(INFO, "Non TLS-protected CER/CEA exchanges are not allowed with this peer, rejecting."); |
---|
863 | } |
---|
864 | } else { |
---|
865 | /* The old mechanism is allowed with this peer. Now, look into the ISI AVP values */ |
---|
866 | |
---|
867 | /* In case no ISI was present anyway: */ |
---|
868 | if (!peer->p_hdr.info.runtime.pir_isi) { |
---|
869 | TRACE_DEBUG(INFO, "Inband-Security-Id AVP is missing in received CER."); |
---|
870 | if (peer->p_hdr.info.config.pic_flags.sec & PI_SEC_NONE) { |
---|
871 | isi = PI_SEC_NONE; |
---|
872 | TRACE_DEBUG(INFO, "IPsec protection allowed by configuration, allowing this mechanism to be used."); |
---|
873 | } else { |
---|
874 | /* otherwise, we should have already been protected. Reject */ |
---|
875 | TRACE_DEBUG(INFO, "Rejecting the peer connection (please allow IPsec here or configure TLS in the remote peer)."); |
---|
876 | } |
---|
877 | } else { |
---|
878 | /* OK, the remote peer did send the ISI AVP. */ |
---|
879 | if ((peer->p_hdr.info.config.pic_flags.sec & PI_SEC_NONE) && (peer->p_hdr.info.runtime.pir_isi & PI_SEC_NONE)) { |
---|
880 | /* We have allowed IPsec */ |
---|
881 | isi = PI_SEC_NONE; |
---|
882 | } else if (peer->p_hdr.info.runtime.pir_isi & PI_SEC_TLS_OLD) { |
---|
883 | /* We can agree on TLS */ |
---|
884 | isi = PI_SEC_TLS_OLD; |
---|
885 | } else { |
---|
886 | TRACE_DEBUG(INFO, "Remote peer requested IPsec protection, but local configuration forbids it."); |
---|
887 | } |
---|
888 | } |
---|
889 | } |
---|
890 | |
---|
891 | /* If we did not find an agreement */ |
---|
892 | if (!isi) { |
---|
893 | TRACE_DEBUG(INFO, "No common security mechanism with '%s', sending DIAMETER_NO_COMMON_SECURITY", peer->p_hdr.info.pi_diamid); |
---|
894 | pei.pei_errcode = "DIAMETER_NO_COMMON_SECURITY"; |
---|
895 | fatal = 1; |
---|
896 | goto error_abort; |
---|
897 | } |
---|
898 | |
---|
899 | /* Do not send the ISI IPsec if we are using the new mechanism */ |
---|
900 | if ((isi == PI_SEC_NONE) && (! (peer->p_hdr.info.config.pic_flags.sec & PI_SEC_TLS_OLD))) |
---|
901 | isi = 0; |
---|
902 | } |
---|
903 | |
---|
904 | /* Reply a CEA */ |
---|
905 | CHECK_FCT( fd_msg_new_answer_from_req ( fd_g_config->cnf_dict, &msg, 0 ) ); |
---|
906 | CHECK_FCT( fd_msg_rescode_set(msg, "DIAMETER_SUCCESS", NULL, NULL, 0 ) ); |
---|
907 | CHECK_FCT( add_CE_info(msg, peer->p_cnxctx, isi & PI_SEC_TLS_OLD, isi & PI_SEC_NONE) ); |
---|
908 | CHECK_FCT( fd_out_send(&msg, peer->p_cnxctx, peer, FD_CNX_ORDERED ) ); |
---|
909 | |
---|
910 | /* Handshake if needed */ |
---|
911 | if (isi & PI_SEC_TLS_OLD) { |
---|
912 | fd_psm_change_state(peer, STATE_OPEN_HANDSHAKE); |
---|
913 | CHECK_FCT_DO( fd_cnx_handshake(peer->p_cnxctx, GNUTLS_SERVER, peer->p_hdr.info.config.pic_priority, NULL), |
---|
914 | { |
---|
915 | /* Handshake failed ... */ |
---|
916 | fd_log_debug("TLS Handshake failed with peer '%s', resetting the connection\n", peer->p_hdr.info.pi_diamid); |
---|
917 | goto cleanup; |
---|
918 | } ); |
---|
919 | |
---|
920 | /* Retrieve the credentials */ |
---|
921 | CHECK_FCT( fd_cnx_getcred(peer->p_cnxctx, &peer->p_hdr.info.runtime.pir_cert_list, &peer->p_hdr.info.runtime.pir_cert_list_size) ); |
---|
922 | |
---|
923 | /* Call second validation callback if needed */ |
---|
924 | if (peer->p_cb2) { |
---|
925 | TRACE_DEBUG(FULL, "Calling second validation callback for %s", peer->p_hdr.info.pi_diamid); |
---|
926 | CHECK_FCT_DO( (*peer->p_cb2)( &peer->p_hdr.info ), |
---|
927 | { |
---|
928 | TRACE_DEBUG(INFO, "Validation callback rejected the peer %s after handshake", peer->p_hdr.info.pi_diamid); |
---|
929 | CHECK_FCT( fd_psm_terminate( peer, "DO_NOT_WANT_TO_TALK_TO_YOU" ) ); |
---|
930 | return 0; |
---|
931 | } ); |
---|
932 | } |
---|
933 | tls_sync = 1; |
---|
934 | } else { |
---|
935 | if ( ! fd_cnx_getTLS(peer->p_cnxctx) ) { |
---|
936 | TRACE_DEBUG(INFO, "No TLS protection negotiated with peer '%s'.", peer->p_hdr.info.pi_diamid); |
---|
937 | CHECK_FCT( fd_cnx_start_clear(peer->p_cnxctx, 1) ); |
---|
938 | } |
---|
939 | } |
---|
940 | |
---|
941 | /* Move to OPEN or REOPEN state */ |
---|
942 | if (peer->p_flags.pf_cnx_pb) { |
---|
943 | fd_psm_change_state(peer, STATE_REOPEN ); |
---|
944 | CHECK_FCT( fd_p_dw_reopen(peer) ); |
---|
945 | } else { |
---|
946 | if ((!tls_sync) && (fd_cnx_isMultichan(peer->p_cnxctx))) { |
---|
947 | fd_psm_change_state(peer, STATE_OPEN_NEW ); |
---|
948 | /* send DWR */ |
---|
949 | CHECK_FCT( fd_p_dw_timeout(peer) ); |
---|
950 | } else { |
---|
951 | |
---|
952 | fd_psm_change_state(peer, STATE_OPEN ); |
---|
953 | fd_psm_next_timeout(peer, 1, peer->p_hdr.info.config.pic_twtimer ?: fd_g_config->cnf_timer_tw); |
---|
954 | } |
---|
955 | } |
---|
956 | |
---|
957 | return 0; |
---|
958 | |
---|
959 | error_abort: |
---|
960 | if (pei.pei_errcode) { |
---|
961 | /* Send the error */ |
---|
962 | receiver_reject(&peer->p_cnxctx, &msg, &pei); |
---|
963 | } |
---|
964 | |
---|
965 | cleanup: |
---|
966 | if (msg) { |
---|
967 | fd_msg_log(FD_MSG_LOG_DROPPED, msg, "An error occurred while processing a CER."); |
---|
968 | fd_msg_free(msg); |
---|
969 | } |
---|
970 | fd_p_ce_clear_cnx(peer, NULL); |
---|
971 | |
---|
972 | /* Send the error to the peer */ |
---|
973 | CHECK_FCT( fd_event_send(peer->p_events, fatal ? FDEVP_TERMINATE : FDEVP_CNX_ERROR, 0, NULL) ); |
---|
974 | |
---|
975 | return 0; |
---|
976 | } |
---|
977 | |
---|
978 | /* We have received a CER on a new connection for this peer */ |
---|
979 | int fd_p_ce_handle_newCER(struct msg ** msg, struct fd_peer * peer, struct cnxctx ** cnx, int valid) |
---|
980 | { |
---|
981 | struct fd_pei pei; |
---|
982 | int cur_state = fd_peer_getstate(peer); |
---|
983 | memset(&pei, 0, sizeof(pei)); |
---|
984 | |
---|
985 | switch (cur_state) { |
---|
986 | case STATE_CLOSED: |
---|
987 | peer->p_receiver = *cnx; |
---|
988 | *cnx = NULL; |
---|
989 | peer->p_cer = *msg; |
---|
990 | *msg = NULL; |
---|
991 | CHECK_FCT( fd_p_ce_process_receiver(peer) ); |
---|
992 | break; |
---|
993 | |
---|
994 | case STATE_WAITCNXACK: |
---|
995 | /* Save the parameters in the peer, move to STATE_WAITCNXACK_ELEC */ |
---|
996 | peer->p_receiver = *cnx; |
---|
997 | *cnx = NULL; |
---|
998 | peer->p_cer = *msg; |
---|
999 | *msg = NULL; |
---|
1000 | CHECK_FCT( fd_psm_change_state(peer, STATE_WAITCNXACK_ELEC) ); |
---|
1001 | break; |
---|
1002 | |
---|
1003 | case STATE_WAITCEA: |
---|
1004 | if (election_result(peer)) { |
---|
1005 | |
---|
1006 | /* Close initiator connection (was already set as principal) */ |
---|
1007 | fd_p_ce_clear_cnx(peer, NULL); |
---|
1008 | |
---|
1009 | /* and go on with the receiver side */ |
---|
1010 | peer->p_receiver = *cnx; |
---|
1011 | *cnx = NULL; |
---|
1012 | peer->p_cer = *msg; |
---|
1013 | *msg = NULL; |
---|
1014 | CHECK_FCT( fd_p_ce_process_receiver(peer) ); |
---|
1015 | |
---|
1016 | } else { |
---|
1017 | |
---|
1018 | /* Answer an ELECTION LOST to the receiver side and continue */ |
---|
1019 | pei.pei_errcode = "ELECTION_LOST"; |
---|
1020 | pei.pei_message = "Please answer my CER instead, you won the election."; |
---|
1021 | receiver_reject(cnx, msg, &pei); |
---|
1022 | } |
---|
1023 | break; |
---|
1024 | |
---|
1025 | default: |
---|
1026 | pei.pei_errcode = "DIAMETER_UNABLE_TO_COMPLY"; /* INVALID COMMAND? in case of Capabilities-Updates? */ |
---|
1027 | pei.pei_message = "Invalid state to receive a new connection attempt."; |
---|
1028 | receiver_reject(cnx, msg, &pei); |
---|
1029 | } |
---|
1030 | |
---|
1031 | return 0; |
---|
1032 | } |
---|