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