1 /*
2 ** fbconnspec.c
3 ** IPFIX Connection Specifier implementation
4 **
5 ** ------------------------------------------------------------------------
6 ** Copyright (C) 2006-2019 Carnegie Mellon University. All Rights Reserved.
7 ** ------------------------------------------------------------------------
8 ** Authors: Brian Trammell
9 ** ------------------------------------------------------------------------
10 ** @OPENSOURCE_LICENSE_START@
11 ** libfixbuf 2.0
12 **
13 ** Copyright 2018-2019 Carnegie Mellon University. All Rights Reserved.
14 **
15 ** NO WARRANTY. THIS CARNEGIE MELLON UNIVERSITY AND SOFTWARE
16 ** ENGINEERING INSTITUTE MATERIAL IS FURNISHED ON AN "AS-IS"
17 ** BASIS. CARNEGIE MELLON UNIVERSITY MAKES NO WARRANTIES OF ANY KIND,
18 ** EITHER EXPRESSED OR IMPLIED, AS TO ANY MATTER INCLUDING, BUT NOT
19 ** LIMITED TO, WARRANTY OF FITNESS FOR PURPOSE OR MERCHANTABILITY,
20 ** EXCLUSIVITY, OR RESULTS OBTAINED FROM USE OF THE
21 ** MATERIAL. CARNEGIE MELLON UNIVERSITY DOES NOT MAKE ANY WARRANTY OF
22 ** ANY KIND WITH RESPECT TO FREEDOM FROM PATENT, TRADEMARK, OR
23 ** COPYRIGHT INFRINGEMENT.
24 **
25 ** Released under a GNU-Lesser GPL 3.0-style license, please see
26 ** LICENSE.txt or contact permission@sei.cmu.edu for full terms.
27 **
28 ** [DISTRIBUTION STATEMENT A] This material has been approved for
29 ** public release and unlimited distribution. Please see Copyright
30 ** notice for non-US Government use and distribution.
31 **
32 ** Carnegie Mellon(R) and CERT(R) are registered in the U.S. Patent
33 ** and Trademark Office by Carnegie Mellon University.
34 **
35 ** DM18-0325
36 ** @OPENSOURCE_LICENSE_END@
37 ** ------------------------------------------------------------------------
38 */
39
40 #define _FIXBUF_SOURCE_
41 #include <fixbuf/private.h>
42
43
44
45 #define HAVE_GETADDRINFO 1
46
47 #if !HAVE_GETADDRINFO
48 struct addrinfo {
49 int ai_family; /* protocol family for socket */
50 int ai_socktype; /* socket type */
51 int ai_protocol; /* protocol for socket */
52 socklen_t ai_addrlen; /* length of socket-address */
53 struct sockaddr *ai_addr; /* socket-address for socket */
54 struct addrinfo *ai_next; /* pointer to next in list */
55 };
56 #endif
57
58 #if HAVE_GETADDRINFO
59
fbConnSpecFreeAI(fbConnSpec_t * spec)60 static void fbConnSpecFreeAI(
61 fbConnSpec_t *spec)
62 {
63 if (spec->vai) {
64 freeaddrinfo((struct addrinfo *)spec->vai);
65 spec->vai = NULL;
66 }
67 }
68
fbConnSpecLookupAI(fbConnSpec_t * spec,gboolean passive,GError ** err)69 gboolean fbConnSpecLookupAI(
70 fbConnSpec_t *spec,
71 gboolean passive,
72 GError **err)
73 {
74 struct addrinfo hints;
75 struct addrinfo * tempaddr = NULL;
76 int ai_err;
77
78 /* free old addrinfo if necessary */
79 fbConnSpecFreeAI(spec);
80
81 /* set up hints */
82 memset(&hints, 0, sizeof(hints));
83
84 /* some ancient linuxen won't let you specify this */
85 #ifdef AI_ADDRCONFIG
86 hints.ai_flags = AI_ADDRCONFIG;
87 #endif
88 if (passive) hints.ai_flags |= AI_PASSIVE;
89 hints.ai_family = AF_UNSPEC;
90
91 /* determine socket type and protocol */
92 switch (spec->transport) {
93 /*
94 * Yeah, this is wrong. Use SOCK_STREAM/IPPROTO_TCP for SCTP.
95 * getaddrinfo(2) doesn't take SCTP hints. We'll rewrite the socktype and
96 * protocol later at connection time.
97 */
98 #if FB_ENABLE_SCTP
99 case FB_SCTP:
100 #if HAVE_OPENSSL_DTLS_SCTP
101 case FB_DTLS_SCTP:
102 #endif
103 #endif
104 case FB_TCP:
105 #if HAVE_OPENSSL
106 case FB_TLS_TCP:
107 #endif
108 hints.ai_socktype = SOCK_STREAM;
109 hints.ai_protocol = IPPROTO_TCP;
110 break;
111 case FB_UDP:
112 #if HAVE_OPENSSL_DTLS
113 case FB_DTLS_UDP:
114 #endif
115 hints.ai_socktype = SOCK_DGRAM;
116 hints.ai_protocol = IPPROTO_UDP;
117 break;
118 default:
119 g_assert_not_reached();
120 }
121
122 /* get addrinfo for host/port */
123 if ((ai_err = getaddrinfo(spec->host, spec->svc, &hints, &tempaddr))) {
124 g_set_error(err, FB_ERROR_DOMAIN, FB_ERROR_CONN,
125 "error looking up address %s:%s: %s",
126 spec->host ? spec->host : "*", spec->svc,
127 gai_strerror(ai_err));
128 return FALSE;
129 }
130 spec->vai = tempaddr;
131
132 /* lookup succeeded. */
133 return TRUE;
134 }
135
136 #else
137
fbConnSpecFreeAI(fbConnSpec_t * spec)138 static void fbConnSpecFreeAI(
139 fbConnSpec_t *spec)
140 {
141 struct addrinfo *ai;
142
143 if (spec->vai) {
144 ai = (struct addrinfo *)spec->vai;
145 g_free(ai->ai_addr);
146 g_free(ai);
147 spec->vai = NULL;
148 }
149 }
150
fbConnSpecLookupAI(fbConnSpec_t * spec,gboolean passive,GError ** err)151 gboolean fbConnSpecLookupAI(
152 fbConnSpec_t *spec,
153 gboolean passive,
154 GError **err)
155 {
156 struct sockaddr_in *sa = NULL;
157 struct hostent *he = NULL;
158 struct servent *se = NULL;
159 unsigned long svcaddrlong;
160 char *svcaddrend;
161 struct addrinfo *ai = NULL;
162
163 /* free old addrinfo if necessary */
164 fbConnSpecFreeAI(spec);
165
166 /* create a sockaddr */
167 sa = g_new0(struct sockaddr_in, 1);
168
169 /* get service address */
170 svcaddrlong = strtoul(spec->svc, &svcaddrend, 10);
171 if (svcaddrend != svcaddr) {
172 /* Convert long to net-order uint16_t */
173 sa->sin_port = g_htons((uint16_t)svcaddrlong);
174 } else {
175 struct servent *se;
176 /* Do service lookup */
177 if (!(se = getservbyname(spec->svc, "udp"))) {
178 g_set_error(err, FB_ERROR_DOMAIN, FB_ERROR_CONN,
179 "error looking up service %s", spec->svc);
180 g_free(sa);
181 return FALSE;
182 }
183 sa->sin_port = se->s_port;
184 }
185
186 /* get host address */
187 if (spec->host) {
188 if (!(he = gethostbyname(spec->host))) {
189 g_set_error(err, FB_ERROR_DOMAIN, FB_ERROR_CONN,
190 "error looking up host %s: %s",
191 spec->host, hstrerror(h_errno));
192 g_free(sa);
193 return FALSE;
194 }
195 sa->sin_addr.s_addr = *(he->h_addr);
196 } else {
197 if (passive) {
198 sa->sin_addr.s_addr = htonl(INADDR_ANY);
199 } else {
200 g_set_error(err, FB_ERROR_DOMAIN, FB_ERROR_CONN,
201 "cannot connect() without host address");
202 g_free(sa);
203 return FALSE;
204 }
205 }
206
207 /* fake up a struct addrinfo */
208 ai = g_new0(struct addrinfo, 1);
209 ai->ai_family = AF_INET;
210 ai->ai_addrlen = sizeof(struct sockaddr_in);
211 ai->ai_addr = sa;
212
213 /* get socktype and protocol from transport */
214 switch (spec->transport) {
215 #if FB_ENABLE_SCTP
216 case FB_SCTP:
217 #if HAVE_OPENSSL_DTLS_SCTP
218 case FB_DTLS_SCTP:
219 #endif
220 ai->ai_socktype = SOCK_SEQPACKET;
221 ai->ai_protocol = 0;
222 break;
223 #endif
224 case FB_TCP:
225 #if HAVE_OPENSSL
226 case FB_TLS_TCP:
227 #endif
228 ai->ai_socktype = SOCK_STREAM;
229 ai->ai_protocol = IPPROTO_TCP;
230 break;
231 case FB_UDP:
232 #if HAVE_OPENSSL_DTLS
233 case FB_DTLS_UDP:
234 #endif
235 ai->ai_socktype = SOCK_DGRAM;
236 ai->ai_protocol = IPPROTO_UDP;
237 break;
238 default:
239 g_assert_not_reached();
240 }
241
242 spec->vai = ai;
243 return TRUE;
244 }
245
246 #endif /* HAVE_GETADDRINFO */
247
248 #if HAVE_OPENSSL
249
fbConnSpecGetTLSPassword(char * pwbuf,int pwsz,int rwflag,void * vpwstr)250 static int fbConnSpecGetTLSPassword(
251 char *pwbuf,
252 int pwsz,
253 int rwflag,
254 void *vpwstr)
255 {
256 (void)rwflag;
257
258 if (vpwstr) {
259 strncpy(pwbuf, (const char *)vpwstr, pwsz);
260 return strlen(pwbuf);
261 } else {
262 *pwbuf = '\0';
263 return 0;
264 }
265 }
266
fbConnSpecVerifyTLSCert(int pvok,X509_STORE_CTX * x509_ctx)267 static int fbConnSpecVerifyTLSCert(
268 int pvok,
269 X509_STORE_CTX *x509_ctx)
270 {
271 (void)pvok;
272 (void)x509_ctx;
273 return 1;
274 }
275
fbConnSpecInitTLS(fbConnSpec_t * spec,gboolean passive,GError ** err)276 gboolean fbConnSpecInitTLS(
277 fbConnSpec_t *spec,
278 gboolean passive,
279 GError **err)
280 {
281 const SSL_METHOD *tlsmeth = NULL;
282 SSL_CTX *ssl_ctx = NULL;
283 gboolean ok = TRUE;
284
285 /* Initialize the library and error strings */
286 SSL_library_init();
287 SSL_load_error_strings();
288
289 /*
290 * Select a TLS method based on passivity and transport.
291 * Shortcircuit on no TLS initialization necessary for sockets.
292 */
293 switch (spec->transport) {
294 #if FB_ENABLE_SCTP
295 case FB_SCTP:
296 #endif
297 case FB_TCP:
298 case FB_UDP:
299 return TRUE;
300 #if HAVE_OPENSSL_DTLS_SCTP
301 case FB_DTLS_SCTP:
302 tlsmeth = passive ? DTLSv1_server_method() : DTLSv1_client_method();
303 break;
304 #endif
305 case FB_TLS_TCP:
306 tlsmeth = passive ? TLSv1_server_method() : TLSv1_client_method();
307 break;
308 #if HAVE_OPENSSL_DTLS
309 case FB_DTLS_UDP:
310 tlsmeth = passive ? DTLSv1_server_method() : DTLSv1_client_method();
311 break;
312 #endif
313 default:
314 g_set_error(err, FB_ERROR_DOMAIN, FB_ERROR_IMPL,
315 "Unsupported TLS method.");
316 return FALSE;
317 }
318
319 /* Verify we have all the files we need */
320 g_assert(spec->ssl_ca_file);
321 g_assert(spec->ssl_cert_file);
322 g_assert(spec->ssl_key_file);
323
324 /* nuke the old context if there is one */
325 if (spec->vssl_ctx) {
326 SSL_CTX_free((SSL_CTX *)spec->vssl_ctx);
327 spec->vssl_ctx = NULL;
328 }
329
330 /* create an SSL_CTX object */
331 ssl_ctx = SSL_CTX_new(tlsmeth);
332
333 if (!ssl_ctx) {
334 g_set_error(err, FB_ERROR_DOMAIN, FB_ERROR_CONN,
335 "Cannot create SSL context: %s",
336 ERR_error_string(ERR_get_error(), NULL));
337 while (ERR_get_error());
338 ok = FALSE;
339 goto end;
340 }
341
342 /* Set up password callback */
343 SSL_CTX_set_default_passwd_cb(ssl_ctx, fbConnSpecGetTLSPassword);
344 SSL_CTX_set_default_passwd_cb_userdata(ssl_ctx, spec->ssl_key_pass);
345
346 /* Load CA certificate */
347 if (SSL_CTX_load_verify_locations(ssl_ctx,
348 spec->ssl_ca_file, NULL) != 1) {
349 ok = FALSE;
350 g_set_error(err, FB_ERROR_DOMAIN, FB_ERROR_CONN,
351 "Failed to load certificate authority file %s: %s",
352 spec->ssl_ca_file, ERR_error_string(ERR_get_error(), NULL));
353 while (ERR_get_error());
354 goto end;
355 }
356
357 /* Load certificate */
358 if (SSL_CTX_use_certificate_chain_file(ssl_ctx,
359 spec->ssl_cert_file) != 1) {
360 ok = FALSE;
361 g_set_error(err, FB_ERROR_DOMAIN, FB_ERROR_CONN,
362 "Failed to load certificate file %s: %s",
363 spec->ssl_cert_file,
364 ERR_error_string(ERR_get_error(), NULL));
365 while (ERR_get_error());
366 goto end;
367 }
368
369 /* Load private key */
370 if (SSL_CTX_use_PrivateKey_file(ssl_ctx,
371 spec->ssl_key_file,
372 SSL_FILETYPE_PEM) != 1) {
373 ok = FALSE;
374 g_set_error(err, FB_ERROR_DOMAIN, FB_ERROR_CONN,
375 "Failed to load private key file %s: %s",
376 spec->ssl_key_file,
377 ERR_error_string(ERR_get_error(), NULL));
378 while (ERR_get_error());
379 goto end;
380 }
381
382 /* Require verification */
383 SSL_CTX_set_verify(ssl_ctx,
384 SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT,
385 fbConnSpecVerifyTLSCert);
386
387 /* Stash SSL context in specifier */
388 spec->vssl_ctx = ssl_ctx;
389
390 end:
391 /* free incomplete SSL context */
392 if (!ok) SSL_CTX_free(ssl_ctx);
393 return ok;
394 }
395
396 #endif /* HAVE_OPENSSL */
397
fbConnSpecCopy(fbConnSpec_t * spec)398 fbConnSpec_t *fbConnSpecCopy(
399 fbConnSpec_t *spec)
400 {
401 fbConnSpec_t *newspec = g_slice_new0(fbConnSpec_t);
402
403 newspec->transport = spec->transport;
404 newspec->host = spec->host ? g_strdup(spec->host) : NULL;
405 newspec->svc = spec->svc ? g_strdup(spec->svc) : NULL;
406 newspec->ssl_ca_file = spec->ssl_ca_file ?
407 g_strdup(spec->ssl_ca_file) : NULL;
408 newspec->ssl_cert_file = spec->ssl_cert_file ?
409 g_strdup(spec->ssl_cert_file) : NULL;
410 newspec->ssl_key_file = spec->ssl_key_file ?
411 g_strdup(spec->ssl_key_file) : NULL;
412 newspec->ssl_key_pass = spec->ssl_key_pass ?
413 g_strdup(spec->ssl_key_pass) : NULL;
414 newspec->vai = NULL;
415 newspec->vssl_ctx = NULL;
416
417 return newspec;
418 }
419
fbConnSpecFree(fbConnSpec_t * spec)420 void fbConnSpecFree(
421 fbConnSpec_t *spec)
422 {
423 if (!spec) {
424 return;
425 }
426 if (spec->host) g_free(spec->host);
427 if (spec->svc) g_free(spec->svc);
428 if (spec->ssl_ca_file) g_free(spec->ssl_ca_file);
429 if (spec->ssl_cert_file) g_free(spec->ssl_cert_file);
430 if (spec->ssl_key_file) g_free(spec->ssl_key_file);
431 if (spec->ssl_key_pass) g_free(spec->ssl_key_pass);
432 fbConnSpecFreeAI(spec);
433 #if HAVE_OPENSSL
434 if (spec->vssl_ctx) {
435 SSL_CTX_free((SSL_CTX *)spec->vssl_ctx);
436 }
437 #endif
438 g_slice_free1(sizeof(fbConnSpec_t), spec);
439 }
440
441 #if HAVE_SPREAD
442
fbConnSpreadCopy(fbSpreadParams_t * params)443 fbSpreadSpec_t *fbConnSpreadCopy(
444 fbSpreadParams_t *params)
445 {
446 int n = 0;
447 char **g = 0;
448 fbSpreadSpec_t *spec = g_slice_new0( fbSpreadSpec_t );
449
450 spec->session = params->session;
451 spec->daemon = params->daemon ? g_strdup( params->daemon ) : NULL;
452
453 for (g=params->groups; *g; ++g)
454 {
455 if (*g[0])
456 ++spec->num_groups;
457 }
458
459 spec->groups = g_new0( sp_groupname_t, spec->num_groups );
460 for (g=params->groups; *g; ++g)
461 {
462 /* have to copy one less than max. Template groups will
463 be automatically created by appending 'T' to the groups
464 specified here. */
465 if (*g[0])
466 strncpy( spec->groups[n++].name, *g, MAX_GROUP_NAME-1 );
467 }
468
469 spec->recv_max_groups = FB_SPREAD_NUM_GROUPS;
470 spec->recv_groups = g_new0( sp_groupname_t, spec->recv_max_groups );
471
472 spec->recv_max = FB_SPREAD_MTU;
473 spec->recv_mess = g_new0( char, spec->recv_max );
474 spec->num_groups_to_send = 0;
475 fbSessionSetGroupParams(spec->session, spec->groups, spec->num_groups);
476
477 return spec;
478 }
479
fbConnSpreadFree(fbSpreadSpec_t * spec)480 void fbConnSpreadFree(
481 fbSpreadSpec_t *spec)
482 {
483 if (spec->daemon)
484 g_free(spec->daemon);
485 if (spec->groups)
486 g_free(spec->groups);
487 if (spec->recv_groups)
488 g_free(spec->recv_groups);
489 if (spec->recv_mess)
490 g_free(spec->recv_mess);
491 if (spec->groups_to_send)
492 g_free(spec->groups_to_send);
493
494 g_slice_free(fbSpreadSpec_t, spec);
495 }
496
fbConnSpreadError(int err)497 const char * fbConnSpreadError(
498 int err)
499 {
500 switch (err)
501 {
502 case ILLEGAL_GROUP: return "illegal group";
503 case ILLEGAL_SESSION: return "illegal session";
504 case CONNECTION_CLOSED: return "connection closed";
505 case ILLEGAL_SPREAD: return "illegal daemon name";
506 case COULD_NOT_CONNECT: return "could not connect";
507 case REJECT_VERSION: return "client/daemon version mismatch";
508 case REJECT_NO_NAME: return "name with no name length, or no name and name length";
509 case REJECT_ILLEGAL_NAME: return "illegal name (length or character)";
510 case REJECT_NOT_UNIQUE: return "name not unique";
511 default:
512 break;
513 }
514 return "unknown error";
515 }
516
517 #endif /* HAVE_SPREAD */
518