/src/net-snmp/snmplib/transports/snmpDTLSUDPDomain.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* Portions of this file are subject to the following copyright(s). See |
2 | | * the Net-SNMP's COPYING file for more details and other copyrights |
3 | | * that may apply: |
4 | | */ |
5 | | /* |
6 | | * Portions of this file are copyrighted by: |
7 | | * Copyright Copyright 2003 Sun Microsystems, Inc. All rights reserved. |
8 | | * Use is subject to license terms specified in the COPYING file |
9 | | * distributed with the Net-SNMP package. |
10 | | */ |
11 | | /* |
12 | | * See the following web pages for useful documentation on this transport: |
13 | | * http://www.net-snmp.org/wiki/index.php/TUT:Using_TLS |
14 | | * http://www.net-snmp.org/wiki/index.php/Using_DTLS |
15 | | */ |
16 | | |
17 | | #include <net-snmp/net-snmp-config.h> |
18 | | |
19 | | #ifdef HAVE_LIBSSL_DTLS |
20 | | |
21 | | #include <net-snmp/net-snmp-features.h> |
22 | | |
23 | | netsnmp_feature_require(cert_util); |
24 | | netsnmp_feature_require(sockaddr_size); |
25 | | |
26 | | #include "snmpIPBaseDomain.h" |
27 | | #include <net-snmp/library/snmpDTLSUDPDomain.h> |
28 | | #include <net-snmp/library/snmpUDPIPv6Domain.h> |
29 | | #include <net-snmp/library/snmp_assert.h> |
30 | | #include <net-snmp/library/snmp_impl.h> |
31 | | |
32 | | #include <stdio.h> |
33 | | #include <sys/types.h> |
34 | | #include <ctype.h> |
35 | | #include <errno.h> |
36 | | |
37 | | #ifdef HAVE_STRING_H |
38 | | #include <string.h> |
39 | | #else |
40 | | #include <strings.h> |
41 | | #endif |
42 | | #ifdef HAVE_STDLIB_H |
43 | | #include <stdlib.h> |
44 | | #endif |
45 | | #ifdef HAVE_UNISTD_H |
46 | | #include <unistd.h> |
47 | | #endif |
48 | | #ifdef HAVE_SYS_SOCKET_H |
49 | | #include <sys/socket.h> |
50 | | #endif |
51 | | #ifdef HAVE_NETINET_IN_H |
52 | | #include <netinet/in.h> |
53 | | #endif |
54 | | #ifdef HAVE_ARPA_INET_H |
55 | | #include <arpa/inet.h> |
56 | | #endif |
57 | | #ifdef HAVE_NETDB_H |
58 | | #include <netdb.h> |
59 | | #endif |
60 | | #ifdef HAVE_SYS_UIO_H |
61 | | #include <sys/uio.h> |
62 | | #endif |
63 | | |
64 | | #include "../memcheck.h" |
65 | | |
66 | | #include <net-snmp/types.h> |
67 | | #include <net-snmp/output_api.h> |
68 | | #include <net-snmp/config_api.h> |
69 | | |
70 | | #include <net-snmp/library/snmp_transport.h> |
71 | | #include <net-snmp/library/system.h> |
72 | | #include <net-snmp/library/tools.h> |
73 | | #include <net-snmp/library/callback.h> |
74 | | |
75 | | #include "openssl/bio.h" |
76 | | #include "openssl/ssl.h" |
77 | | #include "openssl/err.h" |
78 | | #include "openssl/rand.h" |
79 | | |
80 | | #include <net-snmp/library/snmpSocketBaseDomain.h> |
81 | | #include <net-snmp/library/snmpTLSBaseDomain.h> |
82 | | #include <net-snmp/library/snmpUDPDomain.h> |
83 | | #include <net-snmp/library/cert_util.h> |
84 | | #include <net-snmp/library/snmp_openssl.h> |
85 | | |
86 | | #ifndef INADDR_NONE |
87 | | #define INADDR_NONE -1 |
88 | | #endif |
89 | | |
90 | 0 | #define WE_ARE_SERVER 0 |
91 | 0 | #define WE_ARE_CLIENT 1 |
92 | | |
93 | | const oid netsnmpDTLSUDPDomain[] = { TRANSPORT_DOMAIN_DTLS_UDP_IP }; |
94 | | size_t netsnmpDTLSUDPDomain_len = OID_LENGTH(netsnmpDTLSUDPDomain); |
95 | | |
96 | | static netsnmp_tdomain dtlsudpDomain; |
97 | | #ifdef NETSNMP_TRANSPORT_UDPIPV6_DOMAIN |
98 | | static int openssl_addr_index6 = 0; |
99 | | #endif |
100 | | |
101 | | /* this stores openssl credentials for each connection since openssl |
102 | | can't do it for us at the moment; hopefully future versions will |
103 | | change */ |
104 | | typedef struct bio_cache_s { |
105 | | BIO *read_bio; /* OpenSSL will read its incoming SSL packets from here */ |
106 | | BIO *write_bio; /* OpenSSL will write its outgoing SSL packets to here */ |
107 | | netsnmp_sockaddr_storage sas; |
108 | | u_int flags; |
109 | | struct bio_cache_s *next; |
110 | | int msgnum; |
111 | | char *write_cache; |
112 | | size_t write_cache_len; |
113 | | _netsnmpTLSBaseData *tlsdata; |
114 | | } bio_cache; |
115 | | |
116 | | /** bio_cache flags */ |
117 | 0 | #define NETSNMP_BIO_HAVE_COOKIE 0x0001 /* verified cookie */ |
118 | 0 | #define NETSNMP_BIO_CONNECTED 0x0002 /* received decoded data */ |
119 | 0 | #define NETSNMP_BIO_DISCONNECTED 0x0004 /* peer shutdown */ |
120 | | |
121 | | static bio_cache *biocache = NULL; |
122 | | |
123 | | static int openssl_addr_index = 0; |
124 | | |
125 | | #ifdef HAVE_SSL_CTX_SET_COOKIE_GENERATE_CB |
126 | | static int netsnmp_dtls_verify_cookie(SSL *ssl, |
127 | | SECOND_APPVERIFY_COOKIE_CB_ARG_QUALIFIER |
128 | | unsigned char *cookie, |
129 | | unsigned int cookie_len); |
130 | | static int netsnmp_dtls_gen_cookie(SSL *ssl, unsigned char *cookie, |
131 | | unsigned int *cookie_len); |
132 | | #endif |
133 | | |
134 | | /* this stores remote connections in a list to search through */ |
135 | | /* XXX: optimize for searching */ |
136 | | /* XXX: handle state issues for new connections to reduce DOS issues */ |
137 | | /* (TLS should do this, but openssl can't do more than one ctx per sock */ |
138 | | /* XXX: put a timer on the cache for expirary purposes */ |
139 | | static bio_cache *find_bio_cache(const netsnmp_sockaddr_storage *from_addr) |
140 | 0 | { |
141 | 0 | bio_cache *cachep = NULL; |
142 | | |
143 | 0 | for (cachep = biocache; cachep; cachep = cachep->next) { |
144 | |
|
145 | 0 | if (cachep->sas.sa.sa_family != from_addr->sa.sa_family) |
146 | 0 | continue; |
147 | | |
148 | 0 | if ((from_addr->sa.sa_family == AF_INET) && |
149 | 0 | ((cachep->sas.sin.sin_addr.s_addr != |
150 | 0 | from_addr->sin.sin_addr.s_addr) || |
151 | 0 | (cachep->sas.sin.sin_port != from_addr->sin.sin_port))) |
152 | 0 | continue; |
153 | 0 | #ifdef NETSNMP_TRANSPORT_UDPIPV6_DOMAIN |
154 | 0 | else if ((from_addr->sa.sa_family == AF_INET6) && |
155 | 0 | ((cachep->sas.sin6.sin6_port != from_addr->sin6.sin6_port) || |
156 | 0 | (cachep->sas.sin6.sin6_scope_id != |
157 | 0 | from_addr->sin6.sin6_scope_id) || |
158 | 0 | (memcmp(cachep->sas.sin6.sin6_addr.s6_addr, |
159 | 0 | from_addr->sin6.sin6_addr.s6_addr, |
160 | 0 | sizeof(from_addr->sin6.sin6_addr.s6_addr)) != 0))) |
161 | 0 | continue; |
162 | 0 | #endif |
163 | | /* found an existing connection */ |
164 | 0 | break; |
165 | 0 | } |
166 | 0 | return cachep; |
167 | 0 | } |
168 | | |
169 | | /* removes a single cache entry and returns SUCCESS on finding and |
170 | | removing it. */ |
171 | | static int remove_bio_cache(bio_cache *thiscache) |
172 | 0 | { |
173 | 0 | bio_cache *cachep = NULL, *prevcache = NULL; |
174 | |
|
175 | 0 | cachep = biocache; |
176 | 0 | while (cachep) { |
177 | 0 | if (cachep == thiscache) { |
178 | | |
179 | | /* remove it from the list */ |
180 | 0 | if (NULL == prevcache) { |
181 | | /* at the first cache in the list */ |
182 | 0 | biocache = thiscache->next; |
183 | 0 | } else { |
184 | 0 | prevcache->next = thiscache->next; |
185 | 0 | } |
186 | |
|
187 | 0 | return SNMPERR_SUCCESS; |
188 | 0 | } |
189 | 0 | prevcache = cachep; |
190 | 0 | cachep = cachep->next; |
191 | 0 | } |
192 | 0 | return SNMPERR_GENERR; |
193 | 0 | } |
194 | | |
195 | | /* frees the contents of a bio_cache */ |
196 | | static void free_bio_cache(bio_cache *cachep) |
197 | 0 | { |
198 | | /* These are freed by the SSL_free() call */ |
199 | | /* |
200 | | BIO_free(cachep->read_bio); |
201 | | BIO_free(cachep->write_bio); |
202 | | */ |
203 | 0 | DEBUGMSGTL(("9:dtlsudp:bio_cache", "releasing %p\n", cachep)); |
204 | 0 | SNMP_FREE(cachep->write_cache); |
205 | 0 | netsnmp_tlsbase_free_tlsdata(cachep->tlsdata); |
206 | 0 | } |
207 | | |
208 | | static void remove_and_free_bio_cache(bio_cache *cachep) |
209 | 0 | { |
210 | | /** no debug, remove_bio_cache does it */ |
211 | 0 | remove_bio_cache(cachep); |
212 | 0 | free_bio_cache(cachep); |
213 | 0 | } |
214 | | |
215 | | |
216 | | /* XXX: lots of malloc/state cleanup needed */ |
217 | 0 | #define DIEHERE(msg) do { snmp_log(LOG_ERR, "%s\n", msg); return NULL; } while(0) |
218 | | |
219 | | static bio_cache * |
220 | | start_new_cached_connection(netsnmp_transport *t, |
221 | | const netsnmp_sockaddr_storage *remote_addr, |
222 | | int we_are_client) |
223 | 0 | { |
224 | 0 | bio_cache *cachep = NULL; |
225 | 0 | _netsnmpTLSBaseData *tlsdata; |
226 | |
|
227 | 0 | DEBUGTRACETOK("9:dtlsudp"); |
228 | | |
229 | | /* RFC5953: section 5.3.1, step 1: |
230 | | 1) The snmpTlstmSessionOpens counter is incremented. |
231 | | */ |
232 | 0 | if (we_are_client) |
233 | 0 | snmp_increment_statistic(STAT_TLSTM_SNMPTLSTMSESSIONOPENS); |
234 | |
|
235 | 0 | if (!t->sock) |
236 | 0 | DIEHERE("no socket passed in to start_new_cached_connection\n"); |
237 | 0 | if (!remote_addr) |
238 | 0 | DIEHERE("no remote_addr passed in to start_new_cached_connection\n"); |
239 | | |
240 | 0 | cachep = SNMP_MALLOC_TYPEDEF(bio_cache); |
241 | 0 | if (!cachep) |
242 | 0 | return NULL; |
243 | | |
244 | | /* allocate our TLS specific data */ |
245 | 0 | if (NULL == (tlsdata = netsnmp_tlsbase_allocate_tlsdata(t, !we_are_client))) { |
246 | 0 | SNMP_FREE(cachep); |
247 | 0 | return NULL; |
248 | 0 | } |
249 | 0 | cachep->tlsdata = tlsdata; |
250 | | |
251 | | /* RFC5953: section 5.3.1, step 1: |
252 | | 2) The client selects the appropriate certificate and cipher_suites |
253 | | for the key agreement based on the tmSecurityName and the |
254 | | tmRequestedSecurityLevel for the session. For sessions being |
255 | | established as a result of a SNMP-TARGET-MIB based operation, the |
256 | | certificate will potentially have been identified via the |
257 | | snmpTlstmParamsTable mapping and the cipher_suites will have to |
258 | | be taken from system-wide or implementation-specific |
259 | | configuration. If no row in the snmpTlstmParamsTable exists then |
260 | | implementations MAY choose to establish the connection using a |
261 | | default client certificate available to the application. |
262 | | Otherwise, the certificate and appropriate cipher_suites will |
263 | | need to be passed to the openSession() ASI as supplemental |
264 | | information or configured through an implementation-dependent |
265 | | mechanism. It is also implementation-dependent and possibly |
266 | | policy-dependent how tmRequestedSecurityLevel will be used to |
267 | | influence the security capabilities provided by the (D)TLS |
268 | | connection. However this is done, the security capabilities |
269 | | provided by (D)TLS MUST be at least as high as the level of |
270 | | security indicated by the tmRequestedSecurityLevel parameter. |
271 | | The actual security level of the session is reported in the |
272 | | tmStateReference cache as tmSecurityLevel. For (D)TLS to provide |
273 | | strong authentication, each principal acting as a command |
274 | | generator SHOULD have its own certificate. |
275 | | */ |
276 | | /* Implementation notes: |
277 | | + This Information is passed in via the transport and default |
278 | | paremeters |
279 | | */ |
280 | | /* see if we have base configuration to copy in to this new one */ |
281 | 0 | if (NULL != t->data && t->data_length == sizeof(_netsnmpTLSBaseData)) { |
282 | 0 | _netsnmpTLSBaseData *parentdata = t->data; |
283 | 0 | if (parentdata->our_identity) |
284 | 0 | tlsdata->our_identity = strdup(parentdata->our_identity); |
285 | 0 | if (parentdata->their_identity) |
286 | 0 | tlsdata->their_identity = strdup(parentdata->their_identity); |
287 | 0 | if (parentdata->their_fingerprint) |
288 | 0 | tlsdata->their_fingerprint = strdup(parentdata->their_fingerprint); |
289 | 0 | if (parentdata->trust_cert) |
290 | 0 | tlsdata->trust_cert = strdup(parentdata->trust_cert); |
291 | 0 | if (parentdata->their_hostname) |
292 | 0 | tlsdata->their_hostname = strdup(parentdata->their_hostname); |
293 | 0 | } |
294 | | |
295 | 0 | DEBUGMSGTL(("dtlsudp", "starting a new connection\n")); |
296 | 0 | cachep->next = biocache; |
297 | 0 | biocache = cachep; |
298 | |
|
299 | 0 | if (remote_addr->sa.sa_family == AF_INET) |
300 | 0 | memcpy(&cachep->sas.sin, &remote_addr->sin, sizeof(remote_addr->sin)); |
301 | 0 | #ifdef NETSNMP_TRANSPORT_UDPIPV6_DOMAIN |
302 | 0 | else if (remote_addr->sa.sa_family == AF_INET6) |
303 | 0 | memcpy(&cachep->sas.sin6, &remote_addr->sin6, sizeof(remote_addr->sin6)); |
304 | 0 | #endif |
305 | 0 | else |
306 | 0 | DIEHERE("unknown address family"); |
307 | | |
308 | | /* create caching memory bios for OpenSSL to read and write to */ |
309 | | |
310 | 0 | cachep->read_bio = BIO_new(BIO_s_mem()); /* openssl reads from */ |
311 | 0 | if (!cachep->read_bio) |
312 | 0 | DIEHERE("failed to create the openssl read_bio"); |
313 | | |
314 | 0 | cachep->write_bio = BIO_new(BIO_s_mem()); /* openssl writes to */ |
315 | 0 | if (!cachep->write_bio) { |
316 | 0 | BIO_free(cachep->read_bio); |
317 | 0 | cachep->read_bio = NULL; |
318 | 0 | DIEHERE("failed to create the openssl write_bio"); |
319 | 0 | } |
320 | | |
321 | 0 | BIO_set_mem_eof_return(cachep->read_bio, -1); |
322 | 0 | BIO_set_mem_eof_return(cachep->write_bio, -1); |
323 | |
|
324 | 0 | if (we_are_client) { |
325 | | /* we're the client */ |
326 | 0 | DEBUGMSGTL(("dtlsudp", |
327 | 0 | "starting a new connection as a client to sock: %d\n", |
328 | 0 | t->sock)); |
329 | 0 | tlsdata->ssl = SSL_new(sslctx_client_setup(DTLS_method(), tlsdata)); |
330 | | |
331 | | /* XXX: session setting 735 */ |
332 | 0 | } else { |
333 | | /* we're the server */ |
334 | 0 | SSL_CTX *ctx = sslctx_server_setup(DTLS_method()); |
335 | 0 | if (!ctx) { |
336 | 0 | BIO_free(cachep->read_bio); |
337 | 0 | BIO_free(cachep->write_bio); |
338 | 0 | cachep->read_bio = NULL; |
339 | 0 | cachep->write_bio = NULL; |
340 | 0 | DIEHERE("failed to create the SSL Context"); |
341 | 0 | } |
342 | | |
343 | 0 | #ifdef HAVE_SSL_CTX_SET_COOKIE_GENERATE_CB |
344 | | /* turn on cookie exchange */ |
345 | | /* Set DTLS cookie generation and verification callbacks */ |
346 | 0 | SSL_CTX_set_cookie_generate_cb(ctx, netsnmp_dtls_gen_cookie); |
347 | 0 | SSL_CTX_set_cookie_verify_cb(ctx, netsnmp_dtls_verify_cookie); |
348 | 0 | #endif |
349 | |
|
350 | 0 | tlsdata->ssl = SSL_new(ctx); |
351 | 0 | } |
352 | | |
353 | 0 | if (!tlsdata->ssl) { |
354 | 0 | BIO_free(cachep->read_bio); |
355 | 0 | BIO_free(cachep->write_bio); |
356 | 0 | cachep->read_bio = NULL; |
357 | 0 | cachep->write_bio = NULL; |
358 | 0 | DIEHERE("failed to create the SSL session structure"); |
359 | 0 | } |
360 | | |
361 | 0 | SSL_set_mode(tlsdata->ssl, SSL_MODE_AUTO_RETRY); |
362 | | |
363 | | /* set the bios that openssl should read from and write to */ |
364 | | /* (and we'll do the opposite) */ |
365 | 0 | SSL_set_bio(tlsdata->ssl, cachep->read_bio, cachep->write_bio); |
366 | | |
367 | | /* RFC5953: section 5.3.1, step 1: |
368 | | 3) Using the destTransportDomain and destTransportAddress values, |
369 | | the client will initiate the (D)TLS handshake protocol to |
370 | | establish session keys for message integrity and encryption. |
371 | | |
372 | | If the attempt to establish a session is unsuccessful, then |
373 | | snmpTlstmSessionOpenErrors is incremented, an error indication is |
374 | | returned, and processing stops. If the session failed to open |
375 | | because the presented server certificate was unknown or invalid |
376 | | then the snmpTlstmSessionUnknownServerCertificate or |
377 | | snmpTlstmSessionInvalidServerCertificates MUST be incremented and |
378 | | a snmpTlstmServerCertificateUnknown or |
379 | | snmpTlstmServerInvalidCertificate notification SHOULD be sent as |
380 | | appropriate. Reasons for server certificate invalidation |
381 | | includes, but is not limited to, cryptographic validation |
382 | | failures and an unexpected presented certificate identity. |
383 | | */ |
384 | | /* Implementation notes: |
385 | | + Because we're working asynchronously the real "end" point of |
386 | | opening a connection doesn't occur here as certificate |
387 | | verification and other things needs to happen first in the |
388 | | verify callback, etc. See the netsnmp_dtlsudp_recv() |
389 | | function for the final processing. |
390 | | */ |
391 | | /* set the SSL notion of we_are_client/server */ |
392 | 0 | if (we_are_client) |
393 | 0 | SSL_set_connect_state(tlsdata->ssl); |
394 | 0 | else { |
395 | | /* XXX: we need to only create cache entries when cookies succeed */ |
396 | |
|
397 | 0 | SSL_set_options(tlsdata->ssl, SSL_OP_COOKIE_EXCHANGE); |
398 | |
|
399 | 0 | SSL_set_ex_data(tlsdata->ssl, openssl_addr_index, cachep); |
400 | |
|
401 | 0 | SSL_set_accept_state(tlsdata->ssl); |
402 | 0 | } |
403 | | |
404 | | /* RFC5953: section 5.3.1, step 1: |
405 | | 6) The TLSTM-specific session identifier (tlstmSessionID) is set in |
406 | | the tmSessionID of the tmStateReference passed to the TLS |
407 | | Transport Model to indicate that the session has been established |
408 | | successfully and to point to a specific (D)TLS connection for |
409 | | future use. The tlstmSessionID is also stored in the LCD for |
410 | | later lookup during processing of incoming messages |
411 | | (Section 5.1.2). |
412 | | */ |
413 | | /* Implementation notes: |
414 | | + our sessionID is stored as the transport's data pointer member |
415 | | */ |
416 | 0 | DEBUGMSGT(("9:dtlsudp:bio_cache:created", "%p\n", cachep)); |
417 | |
|
418 | 0 | return cachep; |
419 | 0 | } |
420 | | |
421 | | static bio_cache * |
422 | | find_or_create_bio_cache(netsnmp_transport *t, |
423 | | const netsnmp_sockaddr_storage *from_addr, |
424 | | int we_are_client) |
425 | 0 | { |
426 | 0 | bio_cache *cachep = find_bio_cache(from_addr); |
427 | |
|
428 | 0 | if (NULL == cachep) { |
429 | | /* none found; need to start a new context */ |
430 | 0 | cachep = start_new_cached_connection(t, from_addr, we_are_client); |
431 | 0 | if (NULL == cachep) { |
432 | 0 | snmp_log(LOG_ERR, "failed to open a new dtls connection\n"); |
433 | 0 | } |
434 | 0 | } else { |
435 | 0 | DEBUGMSGT(("9:dtlsudp:bio_cache:found", "%p\n", cachep)); |
436 | 0 | } |
437 | 0 | return cachep; |
438 | 0 | } |
439 | | |
440 | | static const netsnmp_indexed_addr_pair * |
441 | | _extract_addr_pair(netsnmp_transport *t, const void *opaque, int olen) |
442 | 0 | { |
443 | 0 | if (opaque) { |
444 | 0 | switch (olen) { |
445 | 0 | case sizeof(netsnmp_tmStateReference): { |
446 | 0 | const netsnmp_tmStateReference *tmStateRef = opaque; |
447 | |
|
448 | 0 | if (tmStateRef->have_addresses) |
449 | 0 | return &tmStateRef->addresses; |
450 | 0 | break; |
451 | 0 | } |
452 | 0 | default: |
453 | 0 | netsnmp_assert(0); |
454 | 0 | } |
455 | 0 | } |
456 | 0 | if (t && t->data) { |
457 | 0 | switch (t->data_length) { |
458 | 0 | case sizeof(netsnmp_indexed_addr_pair): |
459 | 0 | return t->data; |
460 | 0 | case sizeof(_netsnmpTLSBaseData): { |
461 | 0 | _netsnmpTLSBaseData *tlsdata = t->data; |
462 | |
|
463 | 0 | return tlsdata->addr; |
464 | 0 | } |
465 | 0 | default: |
466 | 0 | netsnmp_assert(0); |
467 | 0 | } |
468 | 0 | } |
469 | | |
470 | 0 | return NULL; |
471 | 0 | } |
472 | | |
473 | | static const struct sockaddr * |
474 | | _find_remote_sockaddr(netsnmp_transport *t, const void *opaque, int olen, |
475 | | int *socklen) |
476 | 0 | { |
477 | 0 | const netsnmp_indexed_addr_pair *addr_pair; |
478 | 0 | const struct sockaddr *sa = NULL; |
479 | |
|
480 | 0 | addr_pair = _extract_addr_pair(t, opaque, olen); |
481 | 0 | if (NULL == addr_pair) |
482 | 0 | return NULL; |
483 | | |
484 | 0 | sa = &addr_pair->remote_addr.sa; |
485 | 0 | *socklen = netsnmp_sockaddr_size(sa); |
486 | 0 | return sa; |
487 | 0 | } |
488 | | |
489 | | |
490 | | /* |
491 | | * Reads data from our internal openssl outgoing BIO and sends any |
492 | | * queued packets out the UDP port |
493 | | */ |
494 | | static int |
495 | | _netsnmp_send_queued_dtls_pkts(netsnmp_transport *t, bio_cache *cachep) |
496 | 0 | { |
497 | 0 | int outsize, rc2; |
498 | 0 | void *outbuf; |
499 | | |
500 | 0 | DEBUGTRACETOK("9:dtlsudp"); |
501 | | |
502 | | /* for memory bios, we now read from openssl's write |
503 | | buffer (ie, the packet to go out) and send it out |
504 | | the udp port manually */ |
505 | |
|
506 | 0 | outsize = BIO_ctrl_pending(cachep->write_bio); |
507 | 0 | outbuf = malloc(outsize); |
508 | 0 | if (outsize > 0 && outbuf) { |
509 | 0 | int socksize; |
510 | 0 | void *sa; |
511 | |
|
512 | 0 | DEBUGMSGTL(("dtlsudp", "have %d bytes to send\n", outsize)); |
513 | |
|
514 | 0 | outsize = BIO_read(cachep->write_bio, outbuf, outsize); |
515 | 0 | MAKE_MEM_DEFINED(outbuf, outsize); |
516 | 0 | sa = NETSNMP_REMOVE_CONST(struct sockaddr *, |
517 | 0 | _find_remote_sockaddr(t, NULL, 0, &socksize)); |
518 | 0 | if (NULL == sa) |
519 | 0 | sa = &cachep->sas.sa; |
520 | 0 | socksize = netsnmp_sockaddr_size(sa); |
521 | 0 | rc2 = t->base_transport->f_send(t, outbuf, outsize, &sa, &socksize); |
522 | 0 | if (rc2 == -1) { |
523 | 0 | snmp_log(LOG_ERR, "failed to send a DTLS specific packet\n"); |
524 | 0 | } |
525 | 0 | } else if (outsize == 0) { |
526 | 0 | DEBUGMSGTL(("9:dtlsudp", "have 0 bytes to send\n")); |
527 | 0 | } else { |
528 | 0 | DEBUGMSGTL(("9:dtlsudp", "buffer allocation failed\n")); |
529 | 0 | } |
530 | |
|
531 | 0 | free(outbuf); |
532 | |
|
533 | 0 | return outsize; |
534 | 0 | } |
535 | | |
536 | | /* |
537 | | * If we have any outgoing SNMP data queued that OpenSSL/DTLS couldn't send |
538 | | * (likely due to DTLS control packets needing to go out first) |
539 | | * then this function attempts to send them. |
540 | | */ |
541 | | /* returns SNMPERR_SUCCESS if we succeeded in getting the data out */ |
542 | | /* returns SNMPERR_GENERR if we still need more time */ |
543 | | static int |
544 | | _netsnmp_bio_try_and_write_buffered(netsnmp_transport *t, bio_cache *cachep) |
545 | 0 | { |
546 | 0 | int rc; |
547 | 0 | _netsnmpTLSBaseData *tlsdata; |
548 | | |
549 | 0 | DEBUGTRACETOK("9:dtlsudp"); |
550 | |
|
551 | 0 | tlsdata = cachep->tlsdata; |
552 | | |
553 | | /* make sure we have something to write */ |
554 | 0 | if (!cachep->write_cache || cachep->write_cache_len == 0) |
555 | 0 | return SNMPERR_SUCCESS; |
556 | | |
557 | 0 | DEBUGMSGTL(("dtlsudp", "Trying to write %" NETSNMP_PRIz "d of buffered data\n", |
558 | 0 | cachep->write_cache_len)); |
559 | | |
560 | | /* try and write out the cached data */ |
561 | 0 | rc = SSL_write(tlsdata->ssl, cachep->write_cache, cachep->write_cache_len); |
562 | |
|
563 | 0 | while (rc == -1) { |
564 | 0 | int errnum = SSL_get_error(tlsdata->ssl, rc); |
565 | 0 | int bytesout; |
566 | | |
567 | | /* don't treat want_read/write errors as real errors */ |
568 | 0 | if (errnum != SSL_ERROR_WANT_READ && |
569 | 0 | errnum != SSL_ERROR_WANT_WRITE) { |
570 | 0 | DEBUGMSGTL(("dtlsudp", "ssl_write error (of buffered data)\n")); |
571 | 0 | _openssl_log_error(rc, tlsdata->ssl, "SSL_write"); |
572 | 0 | return SNMPERR_GENERR; |
573 | 0 | } |
574 | | |
575 | | /* check to see if we have outgoing DTLS packets to send */ |
576 | | /* (SSL_write could have created DTLS control packets) */ |
577 | 0 | bytesout = _netsnmp_send_queued_dtls_pkts(t, cachep); |
578 | | |
579 | | /* If want_read/write but failed to actually send anything |
580 | | then we need to wait for the other side, so quit */ |
581 | 0 | if (bytesout <= 0) { |
582 | | /* sending failed; must wait longer */ |
583 | 0 | return SNMPERR_GENERR; |
584 | 0 | } |
585 | | |
586 | | /* retry writing */ |
587 | 0 | DEBUGMSGTL(("9:dtlsudp", "recalling ssl_write\n")); |
588 | 0 | rc = SSL_write(tlsdata->ssl, cachep->write_cache, |
589 | 0 | cachep->write_cache_len); |
590 | 0 | } |
591 | | |
592 | 0 | if (rc > 0) |
593 | 0 | cachep->msgnum++; |
594 | | |
595 | 0 | if (_netsnmp_send_queued_dtls_pkts(t, cachep) > 0) { |
596 | 0 | SNMP_FREE(cachep->write_cache); |
597 | 0 | cachep->write_cache_len = 0; |
598 | 0 | DEBUGMSGTL(("dtlsudp", " Write was successful\n")); |
599 | 0 | return SNMPERR_SUCCESS; |
600 | 0 | } |
601 | 0 | DEBUGMSGTL(("dtlsudp", " failed to send over UDP socket\n")); |
602 | 0 | return SNMPERR_GENERR; |
603 | 0 | } |
604 | | |
605 | | static int |
606 | | _netsnmp_add_buffered_data(bio_cache *cachep, const char *buf, size_t size) |
607 | 0 | { |
608 | 0 | if (cachep->write_cache && cachep->write_cache_len > 0) { |
609 | 0 | size_t newsize = cachep->write_cache_len + size; |
610 | |
|
611 | 0 | char *newbuf = realloc(cachep->write_cache, newsize); |
612 | 0 | if (NULL == newbuf) { |
613 | | /* ack! malloc failure */ |
614 | | /* XXX: free and close */ |
615 | 0 | return SNMPERR_GENERR; |
616 | 0 | } |
617 | 0 | cachep->write_cache = newbuf; |
618 | | |
619 | | /* write the new packet to the end */ |
620 | 0 | memcpy(cachep->write_cache + cachep->write_cache_len, |
621 | 0 | buf, size); |
622 | 0 | cachep->write_cache_len = newsize; |
623 | 0 | } else { |
624 | 0 | cachep->write_cache = netsnmp_memdup(buf, size); |
625 | 0 | if (!cachep->write_cache) { |
626 | | /* ack! malloc failure */ |
627 | | /* XXX: free and close */ |
628 | 0 | return SNMPERR_GENERR; |
629 | 0 | } |
630 | 0 | cachep->write_cache_len = size; |
631 | 0 | } |
632 | 0 | return SNMPERR_SUCCESS; |
633 | 0 | } |
634 | | |
635 | | static int |
636 | | netsnmp_dtlsudp_recv(netsnmp_transport *t, void *buf, int size, |
637 | | void **opaque, int *olength) |
638 | 0 | { |
639 | 0 | int rc = -1; |
640 | 0 | netsnmp_indexed_addr_pair *addr_pair = NULL; |
641 | 0 | netsnmp_tmStateReference *tmStateRef = NULL; |
642 | 0 | _netsnmpTLSBaseData *tlsdata; |
643 | 0 | bio_cache *cachep; |
644 | |
|
645 | 0 | DEBUGTRACETOK("9:dtlsudp"); |
646 | |
|
647 | 0 | if (NULL == t || t->sock == 0) |
648 | 0 | return -1; |
649 | | |
650 | | /* create a tmStateRef cache for slow fill-in */ |
651 | 0 | tmStateRef = SNMP_MALLOC_TYPEDEF(netsnmp_tmStateReference); |
652 | |
|
653 | 0 | if (tmStateRef == NULL) { |
654 | 0 | *opaque = NULL; |
655 | 0 | *olength = 0; |
656 | 0 | return -1; |
657 | 0 | } |
658 | | |
659 | | /* Set the transportDomain */ |
660 | 0 | memcpy(tmStateRef->transportDomain, |
661 | 0 | netsnmpDTLSUDPDomain, sizeof(netsnmpDTLSUDPDomain[0]) * |
662 | 0 | netsnmpDTLSUDPDomain_len); |
663 | 0 | tmStateRef->transportDomainLen = netsnmpDTLSUDPDomain_len; |
664 | |
|
665 | 0 | addr_pair = &tmStateRef->addresses; |
666 | 0 | tmStateRef->have_addresses = 1; |
667 | |
|
668 | 0 | while (rc < 0) { |
669 | 0 | void *opaque = NULL; |
670 | 0 | int olen; |
671 | 0 | rc = t->base_transport->f_recv(t, buf, size, &opaque, &olen); |
672 | 0 | if (rc > 0) { |
673 | 0 | if (olen > sizeof(*addr_pair)) |
674 | 0 | snmp_log(LOG_ERR, "%s: from address length %d > %d\n", |
675 | 0 | NETSNMP_FUNCTION, olen, (int)sizeof(*addr_pair)); |
676 | 0 | memcpy(addr_pair, opaque, SNMP_MIN(sizeof(*addr_pair), olen)); |
677 | 0 | } |
678 | 0 | SNMP_FREE(opaque); |
679 | 0 | if (rc < 0 && errno != EINTR) { |
680 | 0 | break; |
681 | 0 | } |
682 | 0 | } |
683 | |
|
684 | 0 | DEBUGMSGTL(("dtlsudp", "received %d raw bytes on way to dtls\n", rc)); |
685 | 0 | if (rc < 0) { |
686 | 0 | DEBUGMSGTL(("dtlsudp", "recvfrom fd %d err %d (\"%s\")\n", |
687 | 0 | t->sock, errno, strerror(errno))); |
688 | 0 | SNMP_FREE(tmStateRef); |
689 | 0 | return -1; |
690 | 0 | } |
691 | | |
692 | | /* now that we have the from address filled in, we can look up |
693 | | the openssl context and have openssl read and process |
694 | | appropriately */ |
695 | | |
696 | | /* RFC5953: section 5.1, step 1: |
697 | | 1) Determine the tlstmSessionID for the incoming message. The |
698 | | tlstmSessionID MUST be a unique session identifier for this |
699 | | (D)TLS connection. The contents and format of this identifier |
700 | | are implementation-dependent as long as it is unique to the |
701 | | session. A session identifier MUST NOT be reused until all |
702 | | references to it are no longer in use. The tmSessionID is equal |
703 | | to the tlstmSessionID discussed in Section 5.1.1. tmSessionID |
704 | | refers to the session identifier when stored in the |
705 | | tmStateReference and tlstmSessionID refers to the session |
706 | | identifier when stored in the LCD. They MUST always be equal |
707 | | when processing a given session's traffic. |
708 | | |
709 | | If this is the first message received through this session and |
710 | | the session does not have an assigned tlstmSessionID yet then the |
711 | | snmpTlstmSessionAccepts counter is incremented and a |
712 | | tlstmSessionID for the session is created. This will only happen |
713 | | on the server side of a connection because a client would have |
714 | | already assigned a tlstmSessionID during the openSession() |
715 | | invocation. Implementations may have performed the procedures |
716 | | described in Section 5.3.2 prior to this point or they may |
717 | | perform them now, but the procedures described in Section 5.3.2 |
718 | | MUST be performed before continuing beyond this point. |
719 | | */ |
720 | | |
721 | | /* RFC5953: section 5.1, step 2: |
722 | | 2) Create a tmStateReference cache for the subsequent reference and |
723 | | assign the following values within it: |
724 | | |
725 | | tmTransportDomain = snmpTLSTCPDomain or snmpDTLSUDPDomain as |
726 | | appropriate. |
727 | | |
728 | | tmTransportAddress = The address the message originated from. |
729 | | |
730 | | tmSecurityLevel = The derived tmSecurityLevel for the session, |
731 | | as discussed in Section 3.1.2 and Section 5.3. |
732 | | |
733 | | tmSecurityName = The derived tmSecurityName for the session as |
734 | | discussed in Section 5.3. This value MUST be constant during |
735 | | the lifetime of the session. |
736 | | |
737 | | tmSessionID = The tlstmSessionID described in step 1 above. |
738 | | */ |
739 | | |
740 | | /* if we don't have a cachep for this connection then |
741 | | we're receiving something new and are the server |
742 | | side */ |
743 | 0 | cachep = |
744 | 0 | find_or_create_bio_cache(t, &addr_pair->remote_addr, WE_ARE_SERVER); |
745 | 0 | if (NULL == cachep) { |
746 | 0 | snmp_increment_statistic(STAT_TLSTM_SNMPTLSTMSESSIONACCEPTS); |
747 | 0 | SNMP_FREE(tmStateRef); |
748 | 0 | return -1; |
749 | 0 | } |
750 | 0 | tlsdata = cachep->tlsdata; |
751 | 0 | if (NULL == tlsdata->ssl) { |
752 | | /* |
753 | | * this happens when the server starts but doesn't have an |
754 | | * identity and a client connects... |
755 | | */ |
756 | 0 | snmp_log(LOG_ERR, |
757 | 0 | "DTLSUDP: missing tlsdata!\n"); |
758 | | /*snmp_increment_statistic( XXX-rks ??? );*/ |
759 | 0 | SNMP_FREE(tmStateRef); |
760 | 0 | return -1; |
761 | 0 | } |
762 | | |
763 | | /* Implementation notes: |
764 | | - we use the t->data memory pointer as the session ID |
765 | | - the transport domain is already the correct type if we got here |
766 | | - if we don't have a session yet (eg, no tmSessionID from the |
767 | | specs) then we create one automatically here. |
768 | | */ |
769 | | |
770 | | /* write the received buffer to the memory-based input bio */ |
771 | 0 | BIO_write(cachep->read_bio, buf, rc); |
772 | | |
773 | | /* RFC5953: section 5.1, step 3: |
774 | | 3) The incomingMessage and incomingMessageLength are assigned values |
775 | | from the (D)TLS processing. |
776 | | */ |
777 | | /* Implementation notes: |
778 | | + rc = incomingMessageLength |
779 | | + buf = IncomingMessage |
780 | | */ |
781 | | |
782 | | /* XXX: in Wes' other example we do a SSL_pending() call |
783 | | too to ensure we're ready to read... it's possible |
784 | | that buffered stuff in openssl won't be caught by the |
785 | | net-snmp select loop because it's already been pulled |
786 | | out; need to deal with this) */ |
787 | 0 | rc = SSL_read(tlsdata->ssl, buf, size); |
788 | 0 | MAKE_MEM_DEFINED(&rc, sizeof(rc)); |
789 | 0 | if (rc > 0) |
790 | 0 | MAKE_MEM_DEFINED(buf, rc); |
791 | | |
792 | | /* |
793 | | * moved netsnmp_openssl_null_checks to netsnmp_tlsbase_wrapup_recv. |
794 | | * currently netsnmp_tlsbase_wrapup_recv is where we check for |
795 | | * algorithm compliance, but we (sometimes) know the algorithms |
796 | | * at this point, so we could bail earlier (here)... |
797 | | */ |
798 | |
|
799 | 0 | while (rc == -1) { |
800 | 0 | int errnum = SSL_get_error(tlsdata->ssl, rc); |
801 | 0 | int bytesout; |
802 | | |
803 | | /* don't treat want_read/write errors as real errors */ |
804 | 0 | if (errnum != SSL_ERROR_WANT_READ && |
805 | 0 | errnum != SSL_ERROR_WANT_WRITE) { |
806 | 0 | _openssl_log_error(rc, tlsdata->ssl, "SSL_read"); |
807 | 0 | break; |
808 | 0 | } |
809 | | |
810 | | /* check to see if we have outgoing DTLS packets to send */ |
811 | | /* (SSL_read could have created DTLS control packets) */ |
812 | 0 | bytesout = _netsnmp_send_queued_dtls_pkts(t, cachep); |
813 | | |
814 | | /* If want_read/write but failed to actually send |
815 | | anything then we need to wait for the other side, |
816 | | so quit */ |
817 | 0 | if (bytesout <= 0) |
818 | 0 | break; |
819 | | |
820 | | /* retry reading */ |
821 | 0 | DEBUGMSGTL(("9:dtlsudp", "recalling ssl_read\n")); |
822 | 0 | rc = SSL_read(tlsdata->ssl, buf, size); |
823 | 0 | MAKE_MEM_DEFINED(&rc, sizeof(rc)); |
824 | 0 | if (rc > 0) |
825 | 0 | MAKE_MEM_DEFINED(buf, rc); |
826 | 0 | } |
827 | |
|
828 | 0 | if (rc == -1) { |
829 | 0 | SNMP_FREE(tmStateRef); |
830 | |
|
831 | 0 | DEBUGMSGTL(("9:dtlsudp", "no decoded data from dtls\n")); |
832 | |
|
833 | 0 | if (SSL_get_error(tlsdata->ssl, rc) == SSL_ERROR_WANT_READ) { |
834 | 0 | DEBUGMSGTL(("9:dtlsudp", "ssl error want read\n")); |
835 | | |
836 | | /* see if we have buffered write date to send out first */ |
837 | 0 | if (cachep->write_cache) { |
838 | 0 | _netsnmp_bio_try_and_write_buffered(t, cachep); |
839 | | /* XXX: check error or not here? */ |
840 | | /* (what would we do differently?) */ |
841 | 0 | } |
842 | |
|
843 | 0 | rc = -1; /* XXX: it's ok, but what's the right return? */ |
844 | 0 | } |
845 | 0 | else |
846 | 0 | _openssl_log_error(rc, tlsdata->ssl, "SSL_read"); |
847 | |
|
848 | | #if 0 /* to dump cache if we don't have a cookie, this is where to do it */ |
849 | | if (!(cachep->flags & NETSNMP_BIO_HAVE_COOKIE)) |
850 | | remove_and_free_bio_cache(cachep); |
851 | | #endif |
852 | 0 | return rc; |
853 | 0 | } |
854 | | |
855 | 0 | DEBUGMSGTL(("dtlsudp", "received %d decoded bytes from dtls\n", rc)); |
856 | |
|
857 | 0 | if ((0 == rc) && (SSL_get_shutdown(tlsdata->ssl) & SSL_RECEIVED_SHUTDOWN)) { |
858 | 0 | DEBUGMSGTL(("dtlsudp", "peer disconnected\n")); |
859 | 0 | cachep->flags |= NETSNMP_BIO_DISCONNECTED; |
860 | 0 | remove_and_free_bio_cache(cachep); |
861 | 0 | SNMP_FREE(tmStateRef); |
862 | 0 | return rc; |
863 | 0 | } |
864 | 0 | cachep->flags |= NETSNMP_BIO_CONNECTED; |
865 | | |
866 | | /* Until we've locally assured ourselves that all is well in |
867 | | certificate-verification-land we need to be prepared to stop |
868 | | here and ensure all our required checks have been done. */ |
869 | 0 | if (0 == (tlsdata->flags & NETSNMP_TLSBASE_CERT_FP_VERIFIED)) { |
870 | 0 | int verifyresult; |
871 | |
|
872 | 0 | if (tlsdata->flags & NETSNMP_TLSBASE_IS_CLIENT) { |
873 | | |
874 | | /* verify that the server's certificate is the correct one */ |
875 | | |
876 | | /* RFC5953: section 5.3.1, step 1: |
877 | | 3) Using the destTransportDomain and |
878 | | destTransportAddress values, the client will |
879 | | initiate the (D)TLS handshake protocol to establish |
880 | | session keys for message integrity and encryption. |
881 | | |
882 | | If the attempt to establish a session is |
883 | | unsuccessful, then snmpTlstmSessionOpenErrors is |
884 | | incremented, an error indication is returned, and |
885 | | processing stops. If the session failed to open |
886 | | because the presented server certificate was |
887 | | unknown or invalid then the |
888 | | snmpTlstmSessionUnknownServerCertificate or |
889 | | snmpTlstmSessionInvalidServerCertificates MUST be |
890 | | incremented and a snmpTlstmServerCertificateUnknown |
891 | | or snmpTlstmServerInvalidCertificate notification |
892 | | SHOULD be sent as appropriate. Reasons for server |
893 | | certificate invalidation includes, but is not |
894 | | limited to, cryptographic validation failures and |
895 | | an unexpected presented certificate identity. |
896 | | */ |
897 | | /* RFC5953: section 5.3.1, step 1: |
898 | | 4) The (D)TLS client MUST then verify that the (D)TLS |
899 | | server's presented certificate is the expected |
900 | | certificate. The (D)TLS client MUST NOT transmit |
901 | | SNMP messages until the server certificate has been |
902 | | authenticated, the client certificate has been |
903 | | transmitted and the TLS connection has been fully |
904 | | established. |
905 | | |
906 | | If the connection is being established from |
907 | | configuration based on SNMP-TARGET-MIB |
908 | | configuration, then the snmpTlstmAddrTable |
909 | | DESCRIPTION clause describes how the verification |
910 | | is done (using either a certificate fingerprint, or |
911 | | an identity authenticated via certification path |
912 | | validation). |
913 | | |
914 | | If the connection is being established for reasons |
915 | | other than configuration found in the |
916 | | SNMP-TARGET-MIB then configuration and procedures |
917 | | outside the scope of this document should be |
918 | | followed. Configuration mechanisms SHOULD be |
919 | | similar in nature to those defined in the |
920 | | snmpTlstmAddrTable to ensure consistency across |
921 | | management configuration systems. For example, a |
922 | | command-line tool for generating SNMP GETs might |
923 | | support specifying either the server's certificate |
924 | | fingerprint or the expected host name as a command |
925 | | line argument. |
926 | | */ |
927 | | /* RFC5953: section 5.3.1, step 1: |
928 | | 5) (D)TLS provides assurance that the authenticated |
929 | | identity has been signed by a trusted configured |
930 | | certification authority. If verification of the |
931 | | server's certificate fails in any way (for example |
932 | | because of failures in cryptographic verification |
933 | | or the presented identity did not match the |
934 | | expected named entity) then the session |
935 | | establishment MUST fail, the |
936 | | snmpTlstmSessionInvalidServerCertificates object is |
937 | | incremented. If the session can not be opened for |
938 | | any reason at all, including cryptographic |
939 | | verification failures and snmpTlstmCertToTSNTable |
940 | | lookup failures, then the |
941 | | snmpTlstmSessionOpenErrors counter is incremented |
942 | | and processing stops. |
943 | | */ |
944 | | |
945 | | /* Implementation notes: |
946 | | + in the following function the server's certificate and |
947 | | presented commonname or subjectAltName is checked |
948 | | according to the rules in the snmpTlstmAddrTable. |
949 | | */ |
950 | 0 | if ((verifyresult = netsnmp_tlsbase_verify_server_cert(tlsdata->ssl, tlsdata)) |
951 | 0 | != SNMPERR_SUCCESS) { |
952 | 0 | if (verifyresult == SNMPERR_TLS_NO_CERTIFICATE) { |
953 | | /* assume we simply haven't received it yet and there |
954 | | is more data to wait-for or send */ |
955 | | /* XXX: probably need to check for whether we should |
956 | | send stuff from our end to continue the transaction |
957 | | */ |
958 | 0 | SNMP_FREE(tmStateRef); |
959 | 0 | return -1; |
960 | 0 | } else { |
961 | | /* XXX: free needed memory */ |
962 | 0 | snmp_log(LOG_ERR, |
963 | 0 | "DTLSUDP: failed to verify ssl certificate (of the server)\n"); |
964 | 0 | snmp_increment_statistic(STAT_TLSTM_SNMPTLSTMSESSIONUNKNOWNSERVERCERTIFICATE); |
965 | | /* Step 5 says these are always incremented */ |
966 | 0 | snmp_increment_statistic(STAT_TLSTM_SNMPTLSTMSESSIONINVALIDSERVERCERTIFICATES); |
967 | 0 | snmp_increment_statistic(STAT_TLSTM_SNMPTLSTMSESSIONOPENERRORS); |
968 | 0 | SNMP_FREE(tmStateRef); |
969 | 0 | return -1; |
970 | 0 | } |
971 | 0 | } |
972 | 0 | tlsdata->flags |= NETSNMP_TLSBASE_CERT_FP_VERIFIED; |
973 | 0 | DEBUGMSGTL(("dtlsudp", "Verified the server's certificate\n")); |
974 | 0 | } else { |
975 | 0 | #ifndef NETSNMP_NO_LISTEN_SUPPORT |
976 | | /* verify that the client's certificate is the correct one */ |
977 | | |
978 | 0 | if ((verifyresult = netsnmp_tlsbase_verify_client_cert(tlsdata->ssl, tlsdata)) |
979 | 0 | != SNMPERR_SUCCESS) { |
980 | 0 | if (verifyresult == SNMPERR_TLS_NO_CERTIFICATE) { |
981 | | /* assume we simply haven't received it yet and there |
982 | | is more data to wait-for or send */ |
983 | | /* XXX: probably need to check for whether we should |
984 | | send stuff from our end to continue the transaction |
985 | | */ |
986 | 0 | SNMP_FREE(tmStateRef); |
987 | 0 | return -1; |
988 | 0 | } else { |
989 | | /* XXX: free needed memory */ |
990 | 0 | snmp_log(LOG_ERR, |
991 | 0 | "DTLSUDP: failed to verify ssl certificate (of the client)\n"); |
992 | 0 | snmp_increment_statistic(STAT_TLSTM_SNMPTLSTMSESSIONINVALIDCLIENTCERTIFICATES); |
993 | 0 | SNMP_FREE(tmStateRef); |
994 | 0 | return -1; |
995 | 0 | } |
996 | 0 | } |
997 | 0 | tlsdata->flags |= NETSNMP_TLSBASE_CERT_FP_VERIFIED; |
998 | 0 | DEBUGMSGTL(("dtlsudp", "Verified the client's certificate\n")); |
999 | | #else /* NETSNMP_NO_LISTEN_SUPPORT */ |
1000 | | return NULL; |
1001 | | #endif /* NETSNMP_NO_LISTEN_SUPPORT */ |
1002 | 0 | } |
1003 | 0 | } |
1004 | | |
1005 | 0 | if (rc > 0) |
1006 | 0 | cachep->msgnum++; |
1007 | |
|
1008 | 0 | if (BIO_ctrl_pending(cachep->write_bio) > 0) { |
1009 | 0 | _netsnmp_send_queued_dtls_pkts(t, cachep); |
1010 | 0 | } |
1011 | |
|
1012 | 0 | DEBUGIF ("9:dtlsudp") { |
1013 | 0 | char *str = |
1014 | 0 | t->base_transport->f_fmtaddr(t, addr_pair, |
1015 | 0 | sizeof(netsnmp_indexed_addr_pair)); |
1016 | 0 | DEBUGMSGTL(("9:dtlsudp", |
1017 | 0 | "recvfrom fd %d got %d bytes (from %s)\n", |
1018 | 0 | t->sock, rc, str)); |
1019 | 0 | free(str); |
1020 | 0 | } |
1021 | | |
1022 | | /* see if we have buffered write date to send out first */ |
1023 | 0 | if (cachep->write_cache) { |
1024 | 0 | if (SNMPERR_GENERR == |
1025 | 0 | _netsnmp_bio_try_and_write_buffered(t, cachep)) { |
1026 | | /* we still have data that can't get out in the buffer */ |
1027 | | /* XXX: nothing to do here? */ |
1028 | 0 | } |
1029 | 0 | } |
1030 | |
|
1031 | 0 | if (netsnmp_tlsbase_wrapup_recv(tmStateRef, tlsdata, opaque, olength) != |
1032 | 0 | SNMPERR_SUCCESS) |
1033 | 0 | return SNMPERR_GENERR; |
1034 | | |
1035 | | /* RFC5953: section 5.1, step 4: |
1036 | | 4) The TLS Transport Model passes the transportDomain, |
1037 | | transportAddress, incomingMessage, and incomingMessageLength to |
1038 | | the Dispatcher using the receiveMessage ASI: |
1039 | | |
1040 | | statusInformation = |
1041 | | receiveMessage( |
1042 | | IN transportDomain -- snmpTLSTCPDomain or snmpDTLSUDPDomain, |
1043 | | IN transportAddress -- address for the received message |
1044 | | IN incomingMessage -- the whole SNMP message from (D)TLS |
1045 | | IN incomingMessageLength -- the length of the SNMP message |
1046 | | IN tmStateReference -- transport info |
1047 | | ) |
1048 | | */ |
1049 | | /* Implementation notes: those parameters are all passed outward |
1050 | | using the functions arguments and the return code below (the length) */ |
1051 | | |
1052 | 0 | return rc; |
1053 | 0 | } |
1054 | | |
1055 | | |
1056 | | |
1057 | | static int |
1058 | | netsnmp_dtlsudp_send(netsnmp_transport *t, const void *buf, int size, |
1059 | | void **opaque, int *olength) |
1060 | 0 | { |
1061 | 0 | int rc = -1; |
1062 | 0 | const netsnmp_indexed_addr_pair *addr_pair = NULL; |
1063 | 0 | bio_cache *cachep = NULL; |
1064 | 0 | const netsnmp_tmStateReference *tmStateRef = NULL; |
1065 | 0 | void *outbuf; |
1066 | 0 | _netsnmpTLSBaseData *tlsdata = NULL; |
1067 | 0 | int socksize; |
1068 | 0 | void *sa; |
1069 | | |
1070 | 0 | DEBUGTRACETOK("9:dtlsudp"); |
1071 | 0 | DEBUGMSGTL(("dtlsudp", "sending %d bytes\n", size)); |
1072 | |
|
1073 | 0 | if (NULL == t || t->sock <= 0) { |
1074 | 0 | snmp_increment_statistic(STAT_TLSTM_SNMPTLSTMSESSIONINVALIDCACHES); |
1075 | 0 | snmp_log(LOG_ERR, "invalid netsnmp_dtlsudp_send usage\n"); |
1076 | 0 | return -1; |
1077 | 0 | } |
1078 | | |
1079 | | /* determine remote addresses */ |
1080 | 0 | addr_pair = _extract_addr_pair(t, opaque ? *opaque : NULL, |
1081 | 0 | olength ? *olength : 0); |
1082 | 0 | if (NULL == addr_pair) { |
1083 | | /* RFC5953: section 5.2, step 1: |
1084 | | 1) If tmStateReference does not refer to a cache containing values |
1085 | | for tmTransportDomain, tmTransportAddress, tmSecurityName, |
1086 | | tmRequestedSecurityLevel, and tmSameSecurity, then increment the |
1087 | | snmpTlstmSessionInvalidCaches counter, discard the message, and |
1088 | | return the error indication in the statusInformation. Processing |
1089 | | of this message stops. |
1090 | | */ |
1091 | 0 | snmp_increment_statistic(STAT_TLSTM_SNMPTLSTMSESSIONINVALIDCACHES); |
1092 | 0 | snmp_log(LOG_ERR, "dtlsudp_send: can't get address to send to\n"); |
1093 | 0 | return -1; |
1094 | 0 | } |
1095 | | |
1096 | | /* RFC5953: section 5.2, step 2: |
1097 | | 2) Extract the tmSessionID, tmTransportDomain, tmTransportAddress, |
1098 | | tmSecurityName, tmRequestedSecurityLevel, and tmSameSecurity |
1099 | | values from the tmStateReference. Note: The tmSessionID value |
1100 | | may be undefined if no session exists yet over which the message |
1101 | | can be sent. |
1102 | | */ |
1103 | | /* Implementation notes: |
1104 | | - we use the t->data memory pointer as the session ID |
1105 | | - the transport domain is already the correct type if we got here |
1106 | | - if we don't have a session yet (eg, no tmSessionID from the |
1107 | | specs) then we create one automatically here. |
1108 | | */ |
1109 | 0 | if (opaque != NULL && *opaque != NULL && |
1110 | 0 | olength != NULL && *olength == sizeof(netsnmp_tmStateReference)) |
1111 | 0 | tmStateRef = *opaque; |
1112 | | |
1113 | | |
1114 | | /* RFC5953: section 5.2, step 3: |
1115 | | 3) If tmSameSecurity is true and either tmSessionID is undefined or |
1116 | | refers to a session that is no longer open then increment the |
1117 | | snmpTlstmSessionNoSessions counter, discard the message and |
1118 | | return the error indication in the statusInformation. Processing |
1119 | | of this message stops. |
1120 | | */ |
1121 | | /* RFC5953: section 5.2, step 4: |
1122 | | 4) If tmSameSecurity is false and tmSessionID refers to a session |
1123 | | that is no longer available then an implementation SHOULD open a |
1124 | | new session using the openSession() ASI (described in greater |
1125 | | detail in step 5b). Instead of opening a new session an |
1126 | | implementation MAY return a snmpTlstmSessionNoSessions error to |
1127 | | the calling module and stop processing of the message. |
1128 | | */ |
1129 | | /* Implementation Notes: |
1130 | | - We would never get here if the sessionID was different. We |
1131 | | tie packets directly to the transport object and it could |
1132 | | never be sent back over a different transport, which is what |
1133 | | the above text is trying to prevent. |
1134 | | - Auto-connections are handled higher in the Net-SNMP library stack |
1135 | | */ |
1136 | | |
1137 | | /* RFC5953: section 5.2, step 5: |
1138 | | 5) If tmSessionID is undefined, then use tmTransportDomain, |
1139 | | tmTransportAddress, tmSecurityName and tmRequestedSecurityLevel |
1140 | | to see if there is a corresponding entry in the LCD suitable to |
1141 | | send the message over. |
1142 | | |
1143 | | 5a) If there is a corresponding LCD entry, then this session |
1144 | | will be used to send the message. |
1145 | | |
1146 | | 5b) If there is no corresponding LCD entry, then open a session |
1147 | | using the openSession() ASI (discussed further in |
1148 | | Section 5.3.1). Implementations MAY wish to offer message |
1149 | | buffering to prevent redundant openSession() calls for the |
1150 | | same cache entry. If an error is returned from |
1151 | | openSession(), then discard the message, discard the |
1152 | | tmStateReference, increment the snmpTlstmSessionOpenErrors, |
1153 | | return an error indication to the calling module and stop |
1154 | | processing of the message. |
1155 | | */ |
1156 | | |
1157 | | /* we're always a client if we're sending to something unknown yet */ |
1158 | 0 | if (NULL == |
1159 | 0 | (cachep = find_or_create_bio_cache(t, &addr_pair->remote_addr, |
1160 | 0 | WE_ARE_CLIENT))) { |
1161 | 0 | snmp_increment_statistic(STAT_TLSTM_SNMPTLSTMSESSIONOPENERRORS); |
1162 | 0 | return -1; |
1163 | 0 | } |
1164 | | |
1165 | 0 | tlsdata = cachep->tlsdata; |
1166 | 0 | if (NULL == tlsdata || NULL == tlsdata->ssl) { |
1167 | | /** xxx mem leak? free created bio cache? */ |
1168 | 0 | snmp_increment_statistic(STAT_TLSTM_SNMPTLSTMSESSIONNOSESSIONS); |
1169 | 0 | snmp_log(LOG_ERR, "bad tls data or ssl ptr in netsnmp_dtlsudp_send\n"); |
1170 | 0 | return -1; |
1171 | 0 | } |
1172 | | |
1173 | 0 | if (!tlsdata->securityName && tmStateRef && |
1174 | 0 | tmStateRef->securityNameLen > 0) { |
1175 | 0 | tlsdata->securityName = strdup(tmStateRef->securityName); |
1176 | 0 | } |
1177 | | |
1178 | | /* see if we have previous outgoing data to send */ |
1179 | 0 | if (cachep->write_cache) { |
1180 | 0 | if (SNMPERR_GENERR == _netsnmp_bio_try_and_write_buffered(t, cachep)) { |
1181 | | /* we still have data that can't get out in the buffer */ |
1182 | |
|
1183 | 0 | DEBUGIF ("9:dtlsudp") { |
1184 | 0 | char *str = t->base_transport->f_fmtaddr(t, addr_pair, |
1185 | 0 | sizeof(netsnmp_indexed_addr_pair)); |
1186 | 0 | DEBUGMSGTL(("9:dtlsudp", "cached %d bytes for %s on fd %d\n", |
1187 | 0 | size, str, t->sock)); |
1188 | 0 | free(str); |
1189 | 0 | } |
1190 | | |
1191 | | /* add the new data to the end of the existing cache */ |
1192 | 0 | if (_netsnmp_add_buffered_data(cachep, buf, size) != |
1193 | 0 | SNMPERR_SUCCESS) { |
1194 | | /* XXX: free and close */ |
1195 | 0 | } |
1196 | 0 | return -1; |
1197 | 0 | } |
1198 | 0 | } |
1199 | | |
1200 | 0 | DEBUGIF ("9:dtlsudp") { |
1201 | 0 | char *str = t->base_transport->f_fmtaddr(t, addr_pair, |
1202 | 0 | sizeof(netsnmp_indexed_addr_pair)); |
1203 | 0 | DEBUGMSGTL(("9:dtlsudp", "send %d bytes from %p to %s on fd %d\n", |
1204 | 0 | size, buf, str, t->sock)); |
1205 | 0 | free(str); |
1206 | 0 | } |
1207 | | |
1208 | | /* RFC5953: section 5.2, step 6: |
1209 | | 6) Using either the session indicated by the tmSessionID if there |
1210 | | was one or the session resulting from a previous step (4 or 5), |
1211 | | pass the outgoingMessage to (D)TLS for encapsulation and |
1212 | | transmission. |
1213 | | */ |
1214 | 0 | rc = SSL_write(tlsdata->ssl, buf, size); |
1215 | |
|
1216 | 0 | while (rc == -1) { |
1217 | 0 | int bytesout; |
1218 | 0 | int errnum = SSL_get_error(tlsdata->ssl, rc); |
1219 | | |
1220 | | /* don't treat want_read/write errors as real errors */ |
1221 | 0 | if (errnum != SSL_ERROR_WANT_READ && |
1222 | 0 | errnum != SSL_ERROR_WANT_WRITE) { |
1223 | 0 | DEBUGMSGTL(("dtlsudp", "ssl_write error\n")); |
1224 | 0 | _openssl_log_error(rc, tlsdata->ssl, "SSL_write"); |
1225 | 0 | break; |
1226 | 0 | } |
1227 | | |
1228 | | /* check to see if we have outgoing DTLS packets to send */ |
1229 | | /* (SSL_read could have created DTLS control packets) */ |
1230 | 0 | bytesout = _netsnmp_send_queued_dtls_pkts(t, cachep); |
1231 | | |
1232 | | /* If want_read/write but failed to actually send |
1233 | | anything then we need to wait for the other side, |
1234 | | so quit */ |
1235 | 0 | if (bytesout <= 0) { |
1236 | | /* We need more data written to or read from the socket |
1237 | | but we're failing to do so and need to wait till the |
1238 | | socket is ready again; unfortunately this means we need |
1239 | | to buffer the SNMP data temporarily in the mean time */ |
1240 | |
|
1241 | 0 | DEBUGMSGTL(("9:dtlsudp", "cached %d bytes for fd %d\n", size, |
1242 | 0 | t->sock)); |
1243 | | |
1244 | | /* remember the packet */ |
1245 | 0 | if (_netsnmp_add_buffered_data(cachep, buf, size) != |
1246 | 0 | SNMPERR_SUCCESS) { |
1247 | | |
1248 | | /* XXX: free and close */ |
1249 | 0 | return -1; |
1250 | 0 | } |
1251 | | |
1252 | | /* exit out of the loop until we get called again from |
1253 | | socket data */ |
1254 | 0 | break; |
1255 | 0 | } |
1256 | 0 | DEBUGMSGTL(("9:dtlsudp", "recalling ssl_write\n")); |
1257 | 0 | rc = SSL_write(tlsdata->ssl, buf, size); |
1258 | 0 | } |
1259 | | |
1260 | 0 | if (rc > 0) |
1261 | 0 | cachep->msgnum++; |
1262 | | |
1263 | | /* for memory bios, we now read from openssl's write buffer (ie, |
1264 | | the packet to go out) and send it out the udp port manually */ |
1265 | 0 | rc = BIO_ctrl_pending(cachep->write_bio); |
1266 | 0 | if (rc <= 0) { |
1267 | | /* in theory an ok thing */ |
1268 | 0 | return 0; |
1269 | 0 | } |
1270 | 0 | outbuf = malloc(rc); |
1271 | 0 | if (!outbuf) |
1272 | 0 | return -1; |
1273 | 0 | rc = BIO_read(cachep->write_bio, outbuf, rc); |
1274 | 0 | MAKE_MEM_DEFINED(outbuf, rc); |
1275 | 0 | socksize = netsnmp_sockaddr_size(&cachep->sas.sa); |
1276 | 0 | sa = &cachep->sas.sa; |
1277 | 0 | rc = t->base_transport->f_send(t, outbuf, rc, &sa, &socksize); |
1278 | 0 | free(outbuf); |
1279 | |
|
1280 | 0 | return rc; |
1281 | 0 | } |
1282 | | |
1283 | | |
1284 | | |
1285 | | static int |
1286 | | netsnmp_dtlsudp_close(netsnmp_transport *t) |
1287 | 0 | { |
1288 | | /* XXX: issue a proper dtls closure notification(s) */ |
1289 | |
|
1290 | 0 | bio_cache *cachep = NULL; |
1291 | 0 | _netsnmpTLSBaseData *tlsbase = NULL; |
1292 | |
|
1293 | 0 | DEBUGTRACETOK("9:dtlsudp"); |
1294 | |
|
1295 | 0 | DEBUGMSGTL(("dtlsudp:close", "closing dtlsudp transport %p\n", t)); |
1296 | | |
1297 | | /* RFC5953: section 5.4, step 1: |
1298 | | 1) Increment either the snmpTlstmSessionClientCloses or the |
1299 | | snmpTlstmSessionServerCloses counter as appropriate. |
1300 | | */ |
1301 | 0 | snmp_increment_statistic(STAT_TLSTM_SNMPTLSTMSESSIONCLIENTCLOSES); |
1302 | | |
1303 | | /* RFC5953: section 5.4, step 2: |
1304 | | 2) Look up the session using the tmSessionID. |
1305 | | */ |
1306 | | /* Implementation notes: |
1307 | | + Our session id is stored as the t->data pointer |
1308 | | */ |
1309 | 0 | if (NULL != t->data && t->data_length == sizeof(_netsnmpTLSBaseData)) { |
1310 | 0 | tlsbase = t->data; |
1311 | |
|
1312 | 0 | if (tlsbase->addr) |
1313 | 0 | cachep = find_bio_cache(&tlsbase->addr->remote_addr); |
1314 | 0 | } |
1315 | | |
1316 | | /* RFC5953: section 5.4, step 3: |
1317 | | 3) If there is no open session associated with the tmSessionID, then |
1318 | | closeSession processing is completed. |
1319 | | */ |
1320 | 0 | if (NULL == cachep) |
1321 | 0 | return netsnmp_socketbase_close(t); |
1322 | | |
1323 | | /* if we have any remaining packets to send, try to send them */ |
1324 | 0 | if (cachep->write_cache_len > 0) { |
1325 | 0 | int i = 0; |
1326 | 0 | char buf[8192]; |
1327 | 0 | int rc; |
1328 | 0 | void *opaque = NULL; |
1329 | 0 | int opaque_len = 0; |
1330 | 0 | fd_set readfs; |
1331 | 0 | NETSNMP_SELECT_TIMEVAL tv; |
1332 | | |
1333 | 0 | DEBUGMSGTL(("dtlsudp:close", |
1334 | 0 | "%" NETSNMP_PRIz "d bytes remain in write_cache\n", |
1335 | 0 | cachep->write_cache_len)); |
1336 | | |
1337 | | /* |
1338 | | * if negotiations have completed and we've received data, try and |
1339 | | * send any queued packets. |
1340 | | */ |
1341 | 0 | if (1) { |
1342 | | /* make configurable: |
1343 | | - do this at all? |
1344 | | - retries |
1345 | | - timeout |
1346 | | */ |
1347 | 0 | for (i = 0; i < 6 && cachep->write_cache_len != 0; ++i) { |
1348 | | |
1349 | | /* first see if we can send out what we have */ |
1350 | 0 | _netsnmp_bio_try_and_write_buffered(t, cachep); |
1351 | 0 | if (cachep->write_cache_len == 0) |
1352 | 0 | break; |
1353 | | |
1354 | | /* if we've failed that, we probably need to wait for packets */ |
1355 | 0 | FD_ZERO(&readfs); |
1356 | 0 | FD_SET(t->sock, &readfs); |
1357 | 0 | tv.tv_sec = 0; |
1358 | 0 | tv.tv_usec = 50000; |
1359 | 0 | rc = select(t->sock+1, &readfs, NULL, NULL, &tv); |
1360 | 0 | if (rc > 0) { |
1361 | | /* junk recv for catching negotiations still in play */ |
1362 | 0 | opaque_len = 0; |
1363 | 0 | rc = netsnmp_dtlsudp_recv(t, buf, sizeof(buf), |
1364 | 0 | &opaque, &opaque_len); |
1365 | 0 | DEBUGMSGTL(("dtlsudp:close", |
1366 | 0 | "netsnmp_dtlsudp_recv() returned %d\n", rc)); |
1367 | 0 | SNMP_FREE(opaque); |
1368 | 0 | } |
1369 | 0 | } /* for loop */ |
1370 | 0 | } |
1371 | | |
1372 | | /** dump anything that wasn't sent */ |
1373 | 0 | if (cachep->write_cache_len > 0) { |
1374 | 0 | DEBUGMSGTL(("dtlsudp:close", |
1375 | 0 | "dumping %" NETSNMP_PRIz "d bytes from write_cache\n", |
1376 | 0 | cachep->write_cache_len)); |
1377 | 0 | SNMP_FREE(cachep->write_cache); |
1378 | 0 | cachep->write_cache_len = 0; |
1379 | 0 | } |
1380 | 0 | } |
1381 | | |
1382 | | /* RFC5953: section 5.4, step 4: |
1383 | | 4) Have (D)TLS close the specified connection. This MUST include |
1384 | | sending a close_notify TLS Alert to inform the other side that |
1385 | | session cleanup may be performed. |
1386 | | */ |
1387 | 0 | if (NULL != cachep->tlsdata && NULL != cachep->tlsdata->ssl) { |
1388 | |
|
1389 | 0 | DEBUGMSGTL(("dtlsudp:close", "closing SSL socket\n")); |
1390 | 0 | SSL_shutdown(cachep->tlsdata->ssl); |
1391 | | |
1392 | | /* send the close_notify we maybe generated in step 4 */ |
1393 | 0 | if (BIO_ctrl_pending(cachep->write_bio) > 0) |
1394 | 0 | _netsnmp_send_queued_dtls_pkts(t, cachep); |
1395 | 0 | } |
1396 | |
|
1397 | 0 | remove_and_free_bio_cache(cachep); |
1398 | |
|
1399 | 0 | return netsnmp_socketbase_close(t); |
1400 | 0 | } |
1401 | | |
1402 | | static char * |
1403 | | netsnmp_dtlsudp_fmtaddr(netsnmp_transport *t, const void *data, int len, |
1404 | | const char *pfx, |
1405 | | char *(*fmt_base_addr)(const char *pfx, |
1406 | | netsnmp_transport *t, |
1407 | | const void *data, int len)) |
1408 | 0 | { |
1409 | 0 | if (!data) { |
1410 | 0 | data = t->data; |
1411 | 0 | len = t->data_length; |
1412 | 0 | } |
1413 | |
|
1414 | 0 | switch (data ? len : 0) { |
1415 | 0 | case sizeof(netsnmp_indexed_addr_pair): |
1416 | 0 | return netsnmp_ipv4_fmtaddr(pfx, t, data, len); |
1417 | 0 | case sizeof(netsnmp_tmStateReference): { |
1418 | 0 | const netsnmp_tmStateReference *r = data; |
1419 | 0 | const netsnmp_indexed_addr_pair *p = &r->addresses; |
1420 | 0 | netsnmp_transport *bt = t->base_transport; |
1421 | |
|
1422 | 0 | if (r->have_addresses) { |
1423 | 0 | return fmt_base_addr("DTLSUDP", t, p, sizeof(*p)); |
1424 | 0 | } else if (bt && t->data_length == sizeof(_netsnmpTLSBaseData)) { |
1425 | 0 | _netsnmpTLSBaseData *tlsdata = t->data; |
1426 | 0 | netsnmp_indexed_addr_pair *tls_addr = tlsdata->addr; |
1427 | |
|
1428 | 0 | return bt->f_fmtaddr(bt, tls_addr, sizeof(*tls_addr)); |
1429 | 0 | } else if (bt) { |
1430 | 0 | return bt->f_fmtaddr(bt, t->data, t->data_length); |
1431 | 0 | } else { |
1432 | 0 | return strdup("DTLSUDP: unknown"); |
1433 | 0 | } |
1434 | 0 | } |
1435 | 0 | case sizeof(_netsnmpTLSBaseData): { |
1436 | 0 | const _netsnmpTLSBaseData *b = data; |
1437 | 0 | char *buf; |
1438 | |
|
1439 | 0 | if (asprintf(&buf, "DTLSUDP: %s", b->addr_string) < 0) |
1440 | 0 | buf = NULL; |
1441 | 0 | return buf; |
1442 | 0 | } |
1443 | 0 | case 0: |
1444 | 0 | return strdup("DTLSUDP: unknown"); |
1445 | 0 | default: { |
1446 | 0 | char *buf; |
1447 | |
|
1448 | 0 | if (asprintf(&buf, "DTLSUDP: len %d", len) < 0) |
1449 | 0 | buf = NULL; |
1450 | 0 | return buf; |
1451 | 0 | } |
1452 | 0 | } |
1453 | 0 | } |
1454 | | |
1455 | | static char * |
1456 | | netsnmp_dtlsudp4_fmtaddr(netsnmp_transport *t, const void *data, int len) |
1457 | 0 | { |
1458 | 0 | return netsnmp_dtlsudp_fmtaddr(t, data, len, "DTLSUDP", |
1459 | 0 | netsnmp_ipv4_fmtaddr); |
1460 | 0 | } |
1461 | | |
1462 | | /* |
1463 | | * Open a DTLS-based transport for SNMP. Local is TRUE if addr is the local |
1464 | | * address to bind to (i.e. this is a server-type session); otherwise addr is |
1465 | | * the remote address to send things to. |
1466 | | */ |
1467 | | |
1468 | | static netsnmp_transport * |
1469 | | _transport_common(netsnmp_transport *t, int local) |
1470 | 0 | { |
1471 | 0 | char *tmp = NULL; |
1472 | 0 | int tmp_len; |
1473 | |
|
1474 | 0 | DEBUGTRACETOK("9:dtlsudp"); |
1475 | |
|
1476 | 0 | if (NULL == t) |
1477 | 0 | return NULL; |
1478 | | |
1479 | | /** save base transport for clients; need in send/recv functions later */ |
1480 | 0 | if (t->data) { /* don't copy data */ |
1481 | 0 | tmp = t->data; |
1482 | 0 | tmp_len = t->data_length; |
1483 | 0 | t->data = NULL; |
1484 | 0 | } |
1485 | 0 | t->base_transport = netsnmp_transport_copy(t); |
1486 | |
|
1487 | 0 | if (tmp) { |
1488 | 0 | t->data = tmp; |
1489 | 0 | t->data_length = tmp_len; |
1490 | 0 | } |
1491 | 0 | if (NULL != t->data && |
1492 | 0 | t->data_length == sizeof(netsnmp_indexed_addr_pair)) { |
1493 | 0 | _netsnmpTLSBaseData *tlsdata = |
1494 | 0 | netsnmp_tlsbase_allocate_tlsdata(t, local); |
1495 | 0 | tlsdata->addr = t->data; |
1496 | 0 | t->data = tlsdata; |
1497 | 0 | t->data_length = sizeof(_netsnmpTLSBaseData); |
1498 | 0 | } |
1499 | | |
1500 | | /* |
1501 | | * Set Domain |
1502 | | */ |
1503 | 0 | t->domain = netsnmpDTLSUDPDomain; |
1504 | 0 | t->domain_length = netsnmpDTLSUDPDomain_len; |
1505 | |
|
1506 | 0 | t->f_recv = netsnmp_dtlsudp_recv; |
1507 | 0 | t->f_send = netsnmp_dtlsudp_send; |
1508 | 0 | t->f_close = netsnmp_dtlsudp_close; |
1509 | 0 | t->f_config = netsnmp_tlsbase_config; |
1510 | 0 | t->f_setup_session = netsnmp_tlsbase_session_init; |
1511 | 0 | t->f_accept = NULL; |
1512 | 0 | t->f_fmtaddr = netsnmp_dtlsudp4_fmtaddr; |
1513 | 0 | t->f_get_taddr = netsnmp_ipv4_get_taddr; |
1514 | |
|
1515 | 0 | t->flags = NETSNMP_TRANSPORT_FLAG_TUNNELED; |
1516 | |
|
1517 | 0 | return t; |
1518 | 0 | } |
1519 | | |
1520 | | netsnmp_transport * |
1521 | | netsnmp_dtlsudp_transport(const struct netsnmp_ep *ep, int local) |
1522 | 0 | { |
1523 | 0 | const struct sockaddr_in *addr = &ep->a.sin; |
1524 | 0 | netsnmp_transport *t, *t2; |
1525 | |
|
1526 | 0 | DEBUGTRACETOK("dtlsudp"); |
1527 | |
|
1528 | 0 | t = netsnmp_udp_transport(ep, local); |
1529 | 0 | if (NULL == t) |
1530 | 0 | return NULL; |
1531 | | |
1532 | 0 | t2 = _transport_common(t, local); |
1533 | 0 | if (!t2) { |
1534 | 0 | netsnmp_transport_free(t); |
1535 | 0 | return NULL; |
1536 | 0 | } |
1537 | | |
1538 | 0 | if (!local) { |
1539 | | /* dtls needs to bind the socket for SSL_write to work */ |
1540 | 0 | if (connect(t->sock, (const struct sockaddr *)addr, sizeof(*addr)) < 0) |
1541 | 0 | snmp_log(LOG_ERR, "dtls: failed to connect\n"); |
1542 | 0 | } |
1543 | |
|
1544 | 0 | return t2; |
1545 | 0 | } |
1546 | | |
1547 | | |
1548 | | #ifdef NETSNMP_TRANSPORT_UDPIPV6_DOMAIN |
1549 | | |
1550 | | static char * |
1551 | | netsnmp_dtlsudp6_fmtaddr(netsnmp_transport *t, const void *data, int len) |
1552 | 0 | { |
1553 | 0 | return netsnmp_dtlsudp_fmtaddr(t, data, len, "DTLSUDP6", |
1554 | 0 | netsnmp_ipv6_fmtaddr); |
1555 | 0 | } |
1556 | | |
1557 | | /* |
1558 | | * Open a DTLS-based transport for SNMP. Local is TRUE if addr is the local |
1559 | | * address to bind to (i.e. this is a server-type session); otherwise addr is |
1560 | | * the remote address to send things to. |
1561 | | */ |
1562 | | |
1563 | | netsnmp_transport * |
1564 | | netsnmp_dtlsudp6_transport(const struct netsnmp_ep *ep, int local) |
1565 | 0 | { |
1566 | 0 | const struct sockaddr_in6 *addr = &ep->a.sin6; |
1567 | 0 | netsnmp_transport *t, *t2; |
1568 | |
|
1569 | 0 | DEBUGTRACETOK("dtlsudp"); |
1570 | |
|
1571 | 0 | t = netsnmp_udp6_transport(ep, local); |
1572 | 0 | if (NULL == t) |
1573 | 0 | return NULL; |
1574 | | |
1575 | 0 | t2 = _transport_common(t, local); |
1576 | 0 | if (!t2) { |
1577 | 0 | netsnmp_transport_free(t); |
1578 | 0 | return NULL; |
1579 | 0 | } |
1580 | | |
1581 | 0 | if (!local) { |
1582 | | /* dtls needs to bind the socket for SSL_write to work */ |
1583 | 0 | if (connect(t->sock, (const struct sockaddr *)addr, sizeof(*addr)) < 0) |
1584 | 0 | snmp_log(LOG_ERR, "dtls: failed to connect\n"); |
1585 | 0 | } |
1586 | | |
1587 | | /* XXX: Potentially set sock opts here (SO_SNDBUF/SO_RCV_BUF) */ |
1588 | | /* XXX: and buf size */ |
1589 | |
|
1590 | 0 | t2->f_fmtaddr = netsnmp_dtlsudp6_fmtaddr; |
1591 | 0 | t2->f_get_taddr = netsnmp_ipv6_get_taddr; |
1592 | |
|
1593 | 0 | return t2; |
1594 | 0 | } |
1595 | | #endif |
1596 | | |
1597 | | |
1598 | | netsnmp_transport * |
1599 | | netsnmp_dtlsudp_create_tstring(const char *str, int isserver, |
1600 | | const char *default_target) |
1601 | 0 | { |
1602 | 0 | struct netsnmp_ep ep; |
1603 | 0 | netsnmp_transport *t; |
1604 | 0 | _netsnmpTLSBaseData *tlsdata; |
1605 | 0 | char buf[SPRINT_MAX_LEN], *cp; |
1606 | |
|
1607 | 0 | if (netsnmp_sockaddr_in3(&ep, str, default_target)) |
1608 | 0 | t = netsnmp_dtlsudp_transport(&ep, isserver); |
1609 | 0 | #ifdef NETSNMP_TRANSPORT_UDPIPV6_DOMAIN |
1610 | 0 | else if (netsnmp_sockaddr_in6_3(&ep, str, default_target)) |
1611 | 0 | t = netsnmp_dtlsudp6_transport(&ep, isserver); |
1612 | 0 | #endif |
1613 | 0 | else |
1614 | 0 | return NULL; |
1615 | | |
1616 | | |
1617 | | /* see if we can extract the remote hostname */ |
1618 | 0 | if (!isserver && t && t->data && str) { |
1619 | 0 | tlsdata = t->data; |
1620 | | /* search for a : */ |
1621 | 0 | if (NULL != (cp = strrchr(str, ':'))) { |
1622 | 0 | sprintf(buf, "%.*s", (int) SNMP_MIN(cp - str, sizeof(buf) - 1), |
1623 | 0 | str); |
1624 | 0 | } else { |
1625 | | /* else the entire spec is a host name only */ |
1626 | 0 | strlcpy(buf, str, sizeof(buf)); |
1627 | 0 | } |
1628 | 0 | tlsdata->their_hostname = strdup(buf); |
1629 | 0 | } |
1630 | 0 | return t; |
1631 | 0 | } |
1632 | | |
1633 | | |
1634 | | netsnmp_transport * |
1635 | | netsnmp_dtlsudp_create_ostring(const void *o, size_t o_len, int local) |
1636 | 0 | { |
1637 | 0 | struct netsnmp_ep ep; |
1638 | |
|
1639 | 0 | memset(&ep, 0, sizeof(ep)); |
1640 | 0 | if (netsnmp_ipv4_ostring_to_sockaddr(&ep.a.sin, o, o_len)) |
1641 | 0 | return netsnmp_dtlsudp_transport(&ep, local); |
1642 | 0 | #ifdef NETSNMP_TRANSPORT_UDPIPV6_DOMAIN |
1643 | 0 | else if (netsnmp_ipv6_ostring_to_sockaddr(&ep.a.sin6, o, o_len)) |
1644 | 0 | return netsnmp_dtlsudp6_transport(&ep, local); |
1645 | 0 | #endif |
1646 | 0 | else |
1647 | 0 | return NULL; |
1648 | 0 | } |
1649 | | |
1650 | | void |
1651 | | netsnmp_dtlsudp_ctor(void) |
1652 | 3.81k | { |
1653 | 3.81k | static const char indexname[] = "_netsnmp_addr_info"; |
1654 | 3.81k | static const char *prefixes[] = { "dtlsudp", "dtls" |
1655 | 3.81k | #ifdef NETSNMP_TRANSPORT_UDPIPV6_DOMAIN |
1656 | 3.81k | , "dtlsudp6", "dtls6" |
1657 | 3.81k | #endif |
1658 | 3.81k | }; |
1659 | 3.81k | int i, num_prefixes = sizeof(prefixes) / sizeof(char *); |
1660 | 3.81k | #ifdef NETSNMP_TRANSPORT_UDPIPV6_DOMAIN |
1661 | 3.81k | static const char indexname6[] = "_netsnmp_addr_info6"; |
1662 | 3.81k | #endif |
1663 | | |
1664 | 3.81k | DEBUGMSGTL(("dtlsudp", "registering DTLS constructor\n")); |
1665 | | |
1666 | | /* config settings */ |
1667 | | |
1668 | 3.81k | #ifdef NETSNMP_TRANSPORT_UDPIPV6_DOMAIN |
1669 | 3.81k | if (!openssl_addr_index6) |
1670 | 3 | openssl_addr_index6 = |
1671 | 3 | SSL_get_ex_new_index(0, NETSNMP_REMOVE_CONST(void *, indexname6), |
1672 | 3.81k | NULL, NULL, NULL); |
1673 | 3.81k | #endif |
1674 | | |
1675 | 3.81k | dtlsudpDomain.name = netsnmpDTLSUDPDomain; |
1676 | 3.81k | dtlsudpDomain.name_length = netsnmpDTLSUDPDomain_len; |
1677 | 3.81k | dtlsudpDomain.prefix = calloc(num_prefixes + 1, sizeof(char *)); |
1678 | 3.81k | if (!dtlsudpDomain.prefix) { |
1679 | 0 | snmp_log(LOG_ERR, "calloc() failed - out of memory\n"); |
1680 | 0 | return; |
1681 | 0 | } |
1682 | 19.0k | for (i = 0; i < num_prefixes; ++ i) |
1683 | 15.2k | dtlsudpDomain.prefix[i] = prefixes[i]; |
1684 | | |
1685 | 3.81k | dtlsudpDomain.f_create_from_tstring_new = netsnmp_dtlsudp_create_tstring; |
1686 | 3.81k | dtlsudpDomain.f_create_from_ostring = netsnmp_dtlsudp_create_ostring; |
1687 | | |
1688 | 3.81k | if (!openssl_addr_index) |
1689 | 3 | openssl_addr_index = |
1690 | 3 | SSL_get_ex_new_index(0, NETSNMP_REMOVE_CONST(void *, indexname), |
1691 | 3.81k | NULL, NULL, NULL); |
1692 | | |
1693 | 3.81k | netsnmp_tdomain_register(&dtlsudpDomain); |
1694 | 3.81k | } |
1695 | | |
1696 | | /* |
1697 | | * Much of the code below was taken from the OpenSSL example code |
1698 | | * and is subject to the OpenSSL copyright. |
1699 | | */ |
1700 | 0 | #define NETSNMP_COOKIE_SECRET_LENGTH 16 |
1701 | | int cookie_initialized=0; |
1702 | | unsigned char cookie_secret[NETSNMP_COOKIE_SECRET_LENGTH]; |
1703 | | |
1704 | | #ifdef HAVE_SSL_CTX_SET_COOKIE_GENERATE_CB |
1705 | | int netsnmp_dtls_gen_cookie(SSL *ssl, unsigned char *cookie, |
1706 | | unsigned int *cookie_len) |
1707 | 0 | { |
1708 | 0 | unsigned char *buffer, result[EVP_MAX_MD_SIZE]; |
1709 | 0 | unsigned int length, resultlength; |
1710 | 0 | bio_cache *cachep = NULL; |
1711 | 0 | const netsnmp_sockaddr_storage *peer; |
1712 | | |
1713 | | /* Initialize a random secret */ |
1714 | 0 | if (!cookie_initialized) { |
1715 | 0 | if (!RAND_bytes(cookie_secret, NETSNMP_COOKIE_SECRET_LENGTH)) { |
1716 | 0 | snmp_log(LOG_ERR, "dtls: error setting random cookie secret\n"); |
1717 | 0 | return 0; |
1718 | 0 | } |
1719 | 0 | MAKE_MEM_DEFINED(cookie_secret, NETSNMP_COOKIE_SECRET_LENGTH); |
1720 | 0 | cookie_initialized = 1; |
1721 | 0 | } |
1722 | | |
1723 | 0 | DEBUGMSGT(("dtlsudp:cookie", "generating cookie...\n")); |
1724 | | |
1725 | | /* Read peer information */ |
1726 | 0 | cachep = SSL_get_ex_data(ssl, openssl_addr_index); |
1727 | 0 | if (!cachep) { |
1728 | 0 | snmp_log(LOG_ERR, "dtls: failed to get the peer address\n"); |
1729 | 0 | return 0; |
1730 | 0 | } |
1731 | 0 | peer = &cachep->sas; |
1732 | | |
1733 | | /* Create buffer with peer's address and port */ |
1734 | 0 | length = 0; |
1735 | 0 | switch (peer->sa.sa_family) { |
1736 | 0 | case AF_INET: |
1737 | 0 | length += sizeof(struct in_addr); |
1738 | 0 | length += sizeof(peer->sin.sin_port); |
1739 | 0 | break; |
1740 | 0 | #ifdef NETSNMP_TRANSPORT_UDPIPV6_DOMAIN |
1741 | 0 | case AF_INET6: |
1742 | 0 | length += sizeof(struct in6_addr); |
1743 | 0 | length += sizeof(peer->sin6.sin6_port); |
1744 | 0 | break; |
1745 | 0 | #endif |
1746 | 0 | default: |
1747 | 0 | snmp_log(LOG_ERR, "dtls generating cookie: unknown family: %d\n", |
1748 | 0 | peer->sa.sa_family); |
1749 | 0 | return 0; |
1750 | 0 | } |
1751 | 0 | buffer = malloc(length); |
1752 | 0 | if (buffer == NULL) { |
1753 | 0 | snmp_log(LOG_ERR,"dtls: out of memory\n"); |
1754 | 0 | return 0; |
1755 | 0 | } |
1756 | | |
1757 | 0 | switch (peer->sa.sa_family) { |
1758 | 0 | case AF_INET: |
1759 | 0 | memcpy(buffer, |
1760 | 0 | &peer->sin.sin_port, |
1761 | 0 | sizeof(peer->sin.sin_port)); |
1762 | 0 | memcpy(buffer + sizeof(peer->sin.sin_port), |
1763 | 0 | &peer->sin.sin_addr, |
1764 | 0 | sizeof(struct in_addr)); |
1765 | 0 | break; |
1766 | 0 | #ifdef NETSNMP_TRANSPORT_UDPIPV6_DOMAIN |
1767 | 0 | case AF_INET6: |
1768 | 0 | memcpy(buffer, |
1769 | 0 | &peer->sin6.sin6_port, |
1770 | 0 | sizeof(peer->sin6.sin6_port)); |
1771 | 0 | memcpy(buffer + sizeof(peer->sin6.sin6_port), |
1772 | 0 | &peer->sin6.sin6_addr, |
1773 | 0 | sizeof(struct in6_addr)); |
1774 | 0 | break; |
1775 | 0 | #endif |
1776 | 0 | default: |
1777 | 0 | snmp_log(LOG_ERR, "dtls: unknown address family generating a cookie\n"); |
1778 | 0 | free(buffer); |
1779 | 0 | return 0; |
1780 | 0 | } |
1781 | | |
1782 | | /* Calculate HMAC of buffer using the secret */ |
1783 | 0 | HMAC(EVP_sha1(), cookie_secret, NETSNMP_COOKIE_SECRET_LENGTH, |
1784 | 0 | buffer, length, result, &resultlength); |
1785 | 0 | free(buffer); |
1786 | |
|
1787 | 0 | memcpy(cookie, result, resultlength); |
1788 | 0 | *cookie_len = resultlength; |
1789 | |
|
1790 | 0 | DEBUGMSGT(("9:dtlsudp:cookie", "generated %d byte cookie\n", *cookie_len)); |
1791 | |
|
1792 | 0 | return 1; |
1793 | 0 | } |
1794 | | |
1795 | | int netsnmp_dtls_verify_cookie(SSL *ssl, |
1796 | | SECOND_APPVERIFY_COOKIE_CB_ARG_QUALIFIER |
1797 | | unsigned char *cookie, |
1798 | | unsigned int cookie_len) |
1799 | 0 | { |
1800 | 0 | unsigned char *buffer, result[EVP_MAX_MD_SIZE]; |
1801 | 0 | unsigned int length, resultlength, rc; |
1802 | 0 | bio_cache *cachep = NULL; |
1803 | 0 | const netsnmp_sockaddr_storage *peer; |
1804 | | |
1805 | | /* If secret isn't initialized yet, the cookie can't be valid */ |
1806 | 0 | if (!cookie_initialized) |
1807 | 0 | return 0; |
1808 | | |
1809 | 0 | DEBUGMSGT(("9:dtlsudp:cookie", "verifying %d byte cookie\n", cookie_len)); |
1810 | |
|
1811 | 0 | cachep = SSL_get_ex_data(ssl, openssl_addr_index); |
1812 | 0 | if (!cachep) { |
1813 | 0 | snmp_log(LOG_ERR, "dtls: failed to get the peer address\n"); |
1814 | 0 | return 0; |
1815 | 0 | } |
1816 | 0 | peer = &cachep->sas; |
1817 | | |
1818 | | /* Create buffer with peer's address and port */ |
1819 | 0 | length = 0; |
1820 | 0 | switch (peer->sa.sa_family) { |
1821 | 0 | case AF_INET: |
1822 | 0 | length += sizeof(struct in_addr); |
1823 | 0 | length += sizeof(peer->sin.sin_port); |
1824 | 0 | break; |
1825 | 0 | #ifdef NETSNMP_TRANSPORT_UDPIPV6_DOMAIN |
1826 | 0 | case AF_INET6: |
1827 | 0 | length += sizeof(struct in6_addr); |
1828 | 0 | length += sizeof(peer->sin6.sin6_port); |
1829 | 0 | break; |
1830 | 0 | #endif |
1831 | 0 | default: |
1832 | 0 | snmp_log(LOG_ERR, |
1833 | 0 | "dtls: unknown address family %d generating a cookie\n", |
1834 | 0 | peer->sa.sa_family); |
1835 | 0 | return 0; |
1836 | 0 | } |
1837 | 0 | buffer = malloc(length); |
1838 | 0 | if (buffer == NULL) { |
1839 | 0 | snmp_log(LOG_ERR, "dtls: unknown address family generating a cookie\n"); |
1840 | 0 | return 0; |
1841 | 0 | } |
1842 | | |
1843 | 0 | switch (peer->sa.sa_family) { |
1844 | 0 | case AF_INET: |
1845 | 0 | memcpy(buffer, |
1846 | 0 | &peer->sin.sin_port, |
1847 | 0 | sizeof(peer->sin.sin_port)); |
1848 | 0 | memcpy(buffer + sizeof(peer->sin.sin_port), |
1849 | 0 | &peer->sin.sin_addr, |
1850 | 0 | sizeof(struct in_addr)); |
1851 | 0 | break; |
1852 | 0 | #ifdef NETSNMP_TRANSPORT_UDPIPV6_DOMAIN |
1853 | 0 | case AF_INET6: |
1854 | 0 | memcpy(buffer, |
1855 | 0 | &peer->sin6.sin6_port, |
1856 | 0 | sizeof(peer->sin6.sin6_port)); |
1857 | 0 | memcpy(buffer + sizeof(peer->sin6.sin6_port), |
1858 | 0 | &peer->sin6.sin6_addr, |
1859 | 0 | sizeof(struct in6_addr)); |
1860 | 0 | break; |
1861 | 0 | #endif |
1862 | 0 | default: |
1863 | 0 | snmp_log(LOG_ERR, |
1864 | 0 | "dtls: unknown address family %d generating a cookie\n", |
1865 | 0 | peer->sa.sa_family); |
1866 | 0 | free(buffer); |
1867 | 0 | return 0; |
1868 | 0 | } |
1869 | | |
1870 | | /* Calculate HMAC of buffer using the secret */ |
1871 | 0 | HMAC(EVP_sha1(), cookie_secret, NETSNMP_COOKIE_SECRET_LENGTH, |
1872 | 0 | buffer, length, result, &resultlength); |
1873 | 0 | free(buffer); |
1874 | |
|
1875 | 0 | if (cookie_len != resultlength || memcmp(result, cookie, resultlength) != 0) |
1876 | 0 | rc = 0; |
1877 | 0 | else { |
1878 | 0 | rc = 1; |
1879 | 0 | cachep->flags |= NETSNMP_BIO_HAVE_COOKIE; |
1880 | 0 | } |
1881 | |
|
1882 | 0 | DEBUGMSGT(("dtlsudp:cookie", "verify cookie: %d\n", rc)); |
1883 | |
|
1884 | 0 | return rc; |
1885 | 0 | } |
1886 | | #endif /* #ifdef HAVE_SSL_CTX_SET_COOKIE_GENERATE_CB */ |
1887 | | |
1888 | | #endif /* HAVE_LIBSSL_DTLS */ |