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