/src/net-snmp/snmplib/transports/snmpTLSTCPDomain.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 | | * See the following web pages for useful documentation on this transport: |
7 | | * http://www.net-snmp.org/wiki/index.php/TUT:Using_TLS |
8 | | * http://www.net-snmp.org/wiki/index.php/Using_DTLS |
9 | | */ |
10 | | #include <net-snmp/net-snmp-config.h> |
11 | | |
12 | | #include <net-snmp/net-snmp-features.h> |
13 | | |
14 | | netsnmp_feature_require(cert_util); |
15 | | |
16 | | #include <stdio.h> |
17 | | #include <sys/types.h> |
18 | | #include <ctype.h> |
19 | | #include <errno.h> |
20 | | |
21 | | #ifdef HAVE_STRING_H |
22 | | #include <string.h> |
23 | | #else |
24 | | #include <strings.h> |
25 | | #endif |
26 | | #ifdef HAVE_STDLIB_H |
27 | | #include <stdlib.h> |
28 | | #endif |
29 | | #ifdef HAVE_UNISTD_H |
30 | | #include <unistd.h> |
31 | | #endif |
32 | | #ifdef HAVE_SYS_SOCKET_H |
33 | | #include <sys/socket.h> |
34 | | #endif |
35 | | #ifdef HAVE_NETINET_IN_H |
36 | | #include <netinet/in.h> |
37 | | #endif |
38 | | #ifdef HAVE_ARPA_INET_H |
39 | | #include <arpa/inet.h> |
40 | | #endif |
41 | | #ifdef HAVE_NETDB_H |
42 | | #include <netdb.h> |
43 | | #endif |
44 | | #ifdef HAVE_SYS_UIO_H |
45 | | #include <sys/uio.h> |
46 | | #endif |
47 | | |
48 | | #ifdef HAVE_ARPA_INET_H |
49 | | #include <arpa/inet.h> |
50 | | #endif |
51 | | |
52 | | #include "../memcheck.h" |
53 | | |
54 | | #include <net-snmp/types.h> |
55 | | #include <net-snmp/output_api.h> |
56 | | #include <net-snmp/config_api.h> |
57 | | #include <net-snmp/library/snmp_assert.h> |
58 | | #include <net-snmp/library/snmp_impl.h> |
59 | | #include <net-snmp/library/snmpIPv4BaseDomain.h> |
60 | | #include <net-snmp/library/snmpSocketBaseDomain.h> |
61 | | #include <net-snmp/library/snmpTLSBaseDomain.h> |
62 | | #include <net-snmp/library/snmpTLSTCPDomain.h> |
63 | | #include <net-snmp/library/system.h> |
64 | | #include <net-snmp/library/tools.h> |
65 | | #include <net-snmp/library/cert_util.h> |
66 | | #include <net-snmp/library/snmp_openssl.h> |
67 | | #include <net-snmp/library/callback.h> |
68 | | |
69 | | #include "openssl/bio.h" |
70 | | #include "openssl/ssl.h" |
71 | | #include "openssl/err.h" |
72 | | |
73 | | #ifndef INADDR_NONE |
74 | | #define INADDR_NONE -1 |
75 | | #endif |
76 | | |
77 | | #define WE_ARE_SERVER 0 |
78 | | #define WE_ARE_CLIENT 1 |
79 | | |
80 | | const oid netsnmpTLSTCPDomain[] = { TRANSPORT_DOMAIN_TLS_TCP_IP }; |
81 | | size_t netsnmpTLSTCPDomain_len = OID_LENGTH(netsnmpTLSTCPDomain); |
82 | | |
83 | | static netsnmp_tdomain tlstcpDomain; |
84 | | |
85 | | /* |
86 | | * Return a string representing the address in data, or else the "far end" |
87 | | * address if data is NULL. |
88 | | */ |
89 | | |
90 | | static char * |
91 | | netsnmp_tlstcp_fmtaddr(netsnmp_transport *t, const void *data, int len) |
92 | 0 | { |
93 | 0 | if (t && !data) { |
94 | 0 | data = t->data; |
95 | 0 | len = t->data_length; |
96 | 0 | } |
97 | |
|
98 | 0 | switch (data ? len : 0) { |
99 | 0 | case sizeof(netsnmp_indexed_addr_pair): |
100 | 0 | return netsnmp_ipv4_fmtaddr("TLSTCP", t, data, len); |
101 | 0 | case sizeof(netsnmp_tmStateReference): { |
102 | 0 | const netsnmp_tmStateReference *r = data; |
103 | 0 | const netsnmp_indexed_addr_pair *p = &r->addresses; |
104 | |
|
105 | 0 | return netsnmp_ipv4_fmtaddr("TLSTCP", t, p, sizeof(*p)); |
106 | 0 | } |
107 | 0 | case sizeof(_netsnmpTLSBaseData): { |
108 | 0 | const _netsnmpTLSBaseData *b = data; |
109 | 0 | char *buf; |
110 | |
|
111 | 0 | if (asprintf(&buf, "TLSTCP: %s", b->addr_string) < 0) |
112 | 0 | buf = NULL; |
113 | 0 | return buf; |
114 | 0 | } |
115 | 0 | case 0: |
116 | 0 | return strdup("TLSTCP: unknown"); |
117 | 0 | default: { |
118 | 0 | char *buf; |
119 | |
|
120 | 0 | if (asprintf(&buf, "TLSTCP: len %d", len) < 0) |
121 | 0 | buf = NULL; |
122 | 0 | return buf; |
123 | 0 | } |
124 | 0 | } |
125 | 0 | } |
126 | | |
127 | | static void netsnmp_tlstcp_get_taddr(struct netsnmp_transport_s *t, |
128 | | void **addr, size_t *addr_len) |
129 | 0 | { |
130 | 0 | *addr_len = t->remote_length; |
131 | 0 | *addr = netsnmp_memdup(t->remote, *addr_len); |
132 | 0 | } |
133 | | |
134 | | /* |
135 | | * You can write something into opaque that will subsequently get passed back |
136 | | * to your send function if you like. For instance, you might want to |
137 | | * remember where a PDU came from, so that you can send a reply there... |
138 | | */ |
139 | | |
140 | | static int |
141 | | netsnmp_tlstcp_copy(const netsnmp_transport *oldt, netsnmp_transport *newt) |
142 | 0 | { |
143 | 0 | _netsnmpTLSBaseData *oldtlsdata = (_netsnmpTLSBaseData *) oldt->data; |
144 | 0 | _netsnmpTLSBaseData *newtlsdata = (_netsnmpTLSBaseData *) newt->data; |
145 | 0 | oldtlsdata->accepted_bio = NULL; |
146 | 0 | oldtlsdata->ssl = NULL; |
147 | 0 | newtlsdata->ssl_context = NULL; |
148 | | |
149 | 0 | if (oldtlsdata->addr_string) |
150 | 0 | newtlsdata->addr_string = strdup(oldtlsdata->addr_string); |
151 | 0 | if (oldtlsdata->securityName) |
152 | 0 | newtlsdata->securityName = strdup(oldtlsdata->securityName); |
153 | 0 | if (oldtlsdata->our_identity) |
154 | 0 | newtlsdata->our_identity = strdup(oldtlsdata->our_identity); |
155 | 0 | if (oldtlsdata->their_identity) |
156 | 0 | newtlsdata->their_identity = strdup(oldtlsdata->their_identity); |
157 | 0 | if (oldtlsdata->their_fingerprint) |
158 | 0 | newtlsdata->their_fingerprint = strdup(oldtlsdata->their_fingerprint); |
159 | 0 | if (oldtlsdata->their_hostname) |
160 | 0 | newtlsdata->their_hostname = strdup(oldtlsdata->their_hostname); |
161 | 0 | if (oldtlsdata->trust_cert) |
162 | 0 | newtlsdata->trust_cert = strdup(oldtlsdata->trust_cert); |
163 | 0 | if (oldtlsdata->addr) |
164 | 0 | newtlsdata->addr = netsnmp_memdup(oldtlsdata->addr, |
165 | 0 | sizeof(*oldtlsdata->addr)); |
166 | |
|
167 | 0 | return 0; |
168 | 0 | } |
169 | | |
170 | | static int |
171 | | netsnmp_tlstcp_recv(netsnmp_transport *t, void *buf, int size, |
172 | | void **opaque, int *olength) |
173 | 0 | { |
174 | 0 | int rc = -1; |
175 | 0 | netsnmp_tmStateReference *tmStateRef = NULL; |
176 | 0 | _netsnmpTLSBaseData *tlsdata; |
177 | |
|
178 | 0 | if (NULL == t || t->sock < 0 || NULL == t->data) { |
179 | 0 | snmp_log(LOG_ERR, |
180 | 0 | "tlstcp received an invalid invocation with missing data\n"); |
181 | 0 | DEBUGMSGTL(("tlstcp", "recvfrom fd %d err %d (\"%s\")\n", |
182 | 0 | (t ? t->sock : -1), errno, strerror(errno))); |
183 | 0 | if (t) |
184 | 0 | DEBUGMSGTL(("tlstcp", " tdata = %p", t->data)); |
185 | 0 | DEBUGMSGTL(("tlstcp", "\n")); |
186 | 0 | return -1; |
187 | 0 | } |
188 | | |
189 | | /* RFC5953 Section 5.1.2 step 1: |
190 | | 1) Determine the tlstmSessionID for the incoming message. The |
191 | | tlstmSessionID MUST be a unique session identifier for this |
192 | | (D)TLS connection. The contents and format of this identifier |
193 | | are implementation-dependent as long as it is unique to the |
194 | | session. A session identifier MUST NOT be reused until all |
195 | | references to it are no longer in use. The tmSessionID is |
196 | | equal to the tlstmSessionID discussed in Section 5.1.1. |
197 | | tmSessionID refers to the session identifier when stored in the |
198 | | tmStateReference and tlstmSessionID refers to the session |
199 | | identifier when stored in the LCD. They MUST always be equal |
200 | | when processing a given session's traffic. |
201 | | */ |
202 | | /* For this implementation we use the t->data memory pointer as |
203 | | the sessionID. As it's a pointer to session specific data tied |
204 | | with the transport object we know it'll never be reallocated |
205 | | (ie, duplicated) until release by this transport object and is |
206 | | safe to use as a unique session identifier. */ |
207 | | |
208 | 0 | tlsdata = t->data; |
209 | 0 | if (NULL == tlsdata->ssl) { |
210 | 0 | snmp_log(LOG_ERR, |
211 | 0 | "tlstcp received an invalid invocation without ssl data\n"); |
212 | 0 | return -1; |
213 | 0 | } |
214 | | |
215 | | /* RFC5953 Section 5.1.2 step 1, part2: |
216 | | * This part (incrementing the counter) is done in the |
217 | | netsnmp_tlstcp_accept function. |
218 | | */ |
219 | | |
220 | | |
221 | | /* RFC5953 Section 5.1.2 step 2: |
222 | | * Create a tmStateReference cache for the subsequent reference and |
223 | | assign the following values within it: |
224 | | |
225 | | tmTransportDomain = snmpTLSTCPDomain or snmpDTLSUDPDomain as |
226 | | appropriate. |
227 | | |
228 | | tmTransportAddress = The address the message originated from. |
229 | | |
230 | | tmSecurityLevel = The derived tmSecurityLevel for the session, |
231 | | as discussed in Section 3.1.2 and Section 5.3. |
232 | | |
233 | | tmSecurityName = The fderived tmSecurityName for the session as |
234 | | discussed in Section 5.3. This value MUST |
235 | | be constant during the lifetime of the |
236 | | session. |
237 | | |
238 | | tmSessionID = The tlstmSessionID described in step 1 above. |
239 | | */ |
240 | | |
241 | | /* Implementation notes: |
242 | | * - The tmTransportDomain is represented by the transport object |
243 | | * - The tmpSessionID is represented by the tlsdata pointer (as |
244 | | discussed above) |
245 | | * - The following items are handled later in netsnmp_tlsbase_wrapup_recv: |
246 | | - tmSecurityLevel |
247 | | - tmSecurityName |
248 | | - tmSessionID |
249 | | */ |
250 | | |
251 | | /* create a tmStateRef cache for slow fill-in */ |
252 | 0 | tmStateRef = SNMP_MALLOC_TYPEDEF(netsnmp_tmStateReference); |
253 | |
|
254 | 0 | if (tmStateRef == NULL) { |
255 | 0 | *opaque = NULL; |
256 | 0 | *olength = 0; |
257 | 0 | return -1; |
258 | 0 | } |
259 | | |
260 | | /* Set the transportDomain */ |
261 | 0 | memcpy(tmStateRef->transportDomain, |
262 | 0 | netsnmpTLSTCPDomain, sizeof(netsnmpTLSTCPDomain[0]) * |
263 | 0 | netsnmpTLSTCPDomain_len); |
264 | 0 | tmStateRef->transportDomainLen = netsnmpTLSTCPDomain_len; |
265 | | |
266 | | /* Set the tmTransportAddress */ |
267 | 0 | tmStateRef->have_addresses = 1; |
268 | | |
269 | | /* RFC5953 Section 5.1.2 step 1: |
270 | | * 3) The incomingMessage and incomingMessageLength are assigned values |
271 | | from the (D)TLS processing. |
272 | | */ |
273 | | |
274 | | /* Implementation notes: |
275 | | - incomingMessage = buf pointer |
276 | | - incomingMessageLength = rc |
277 | | */ |
278 | | |
279 | | /* read the packet from openssl */ |
280 | 0 | do { |
281 | 0 | rc = SSL_read(tlsdata->ssl, buf, size); |
282 | 0 | MAKE_MEM_DEFINED(&rc, sizeof(rc)); |
283 | 0 | if (rc > 0) |
284 | 0 | MAKE_MEM_DEFINED(buf, rc); |
285 | 0 | if (rc == 0) { |
286 | | /* XXX closed connection */ |
287 | 0 | DEBUGMSGTL(("tlstcp", "remote side closed connection\n")); |
288 | | /* XXX: openssl cleanup */ |
289 | 0 | SNMP_FREE(tmStateRef); |
290 | 0 | return -1; |
291 | 0 | } |
292 | 0 | if (rc == -1) { |
293 | 0 | int err = SSL_get_error(tlsdata->ssl, rc); |
294 | 0 | if (err != SSL_ERROR_WANT_READ && err != SSL_ERROR_WANT_WRITE) { |
295 | | /* error detected */ |
296 | 0 | _openssl_log_error(rc, tlsdata->ssl, "SSL_read"); |
297 | 0 | SNMP_FREE(tmStateRef); |
298 | 0 | return rc; |
299 | 0 | } |
300 | 0 | } |
301 | | /* retry read for SSL_ERROR_WANT_READ || SSL_ERROR_WANT_WRITE */ |
302 | 0 | } while (rc <= 0); |
303 | | |
304 | 0 | DEBUGMSGTL(("tlstcp", "received %d decoded bytes from tls\n", rc)); |
305 | | |
306 | | /* log the packet */ |
307 | 0 | DEBUGIF("tlstcp") { |
308 | 0 | char *str = netsnmp_tlstcp_fmtaddr(t, NULL, 0); |
309 | 0 | DEBUGMSGTL(("tlstcp", |
310 | 0 | "recvfrom fd %d got %d bytes (from %s)\n", |
311 | 0 | t->sock, rc, str)); |
312 | 0 | free(str); |
313 | 0 | } |
314 | | |
315 | | /* Other wrap-up things common to TLS and DTLS */ |
316 | 0 | if (netsnmp_tlsbase_wrapup_recv(tmStateRef, tlsdata, opaque, olength) != |
317 | 0 | SNMPERR_SUCCESS) |
318 | 0 | return SNMPERR_GENERR; |
319 | | |
320 | | /* RFC5953 Section 5.1.2 step 1: |
321 | | * 4) The TLS Transport Model passes the transportDomain, |
322 | | transportAddress, incomingMessage, and incomingMessageLength to |
323 | | the Dispatcher using the receiveMessage ASI: |
324 | | */ |
325 | | |
326 | | /* In our implementation, this is done simply by returning */ |
327 | 0 | return rc; |
328 | 0 | } |
329 | | |
330 | | |
331 | | |
332 | | static int |
333 | | netsnmp_tlstcp_send(netsnmp_transport *t, const void *buf, int size, |
334 | | void **opaque, int *olength) |
335 | 0 | { |
336 | 0 | int rc = -1; |
337 | 0 | const netsnmp_tmStateReference *tmStateRef = NULL; |
338 | 0 | _netsnmpTLSBaseData *tlsdata; |
339 | | |
340 | 0 | DEBUGTRACETOK("tlstcp"); |
341 | | |
342 | | /* RFC5953 section 5.2: |
343 | | 1) If tmStateReference does not refer to a cache containing values |
344 | | for tmTransportDomain, tmTransportAddress, tmSecurityName, |
345 | | tmRequestedSecurityLevel, and tmSameSecurity, then increment the |
346 | | snmpTlstmSessionInvalidCaches counter, discard the message, and |
347 | | return the error indication in the statusInformation. Processing |
348 | | of this message stops. |
349 | | */ |
350 | | /* Implementation Notes: the tmStateReference is stored in the opaque ptr */ |
351 | 0 | if (opaque != NULL && *opaque != NULL && |
352 | 0 | *olength == sizeof(netsnmp_tmStateReference)) { |
353 | 0 | tmStateRef = (const netsnmp_tmStateReference *) *opaque; |
354 | 0 | } else { |
355 | 0 | snmp_log(LOG_ERR, "TLSTCP was called with an invalid state; possibly the wrong security model is in use. It should be 'tsm'.\n"); |
356 | 0 | snmp_increment_statistic(STAT_TLSTM_SNMPTLSTMSESSIONINVALIDCACHES); |
357 | 0 | return SNMPERR_GENERR; |
358 | 0 | } |
359 | | |
360 | | /* RFC5953 section 5.2: |
361 | | 2) Extract the tmSessionID, tmTransportDomain, tmTransportAddress, |
362 | | tmSecurityName, tmRequestedSecurityLevel, and tmSameSecurity |
363 | | values from the tmStateReference. Note: The tmSessionID value |
364 | | may be undefined if no session exists yet over which the message |
365 | | can be sent. |
366 | | */ |
367 | | /* Implementation Notes: |
368 | | - Our session will always exist by now as it's created when the |
369 | | transport object is created. Auto-session creation is handled |
370 | | higher in the stack. |
371 | | - We don't "extract" per say since we just leave the data in |
372 | | the structure. |
373 | | - The sessionID is stored in the t->data memory pointer. |
374 | | */ |
375 | | |
376 | | /* RFC5953 section 5.2: |
377 | | 3) If tmSameSecurity is true and either tmSessionID is undefined or |
378 | | refers to a session that is no longer open then increment the |
379 | | snmpTlstmSessionNoSessions counter, discard the message and |
380 | | return the error indication in the statusInformation. Processing |
381 | | of this message stops. |
382 | | */ |
383 | | /* Implementation Notes: |
384 | | - We would never get here if the sessionID was either undefined |
385 | | or different. We tie packets directly to the transport |
386 | | object and it could never be sent back over a different |
387 | | transport, which is what the above text is trying to prevent. |
388 | | */ |
389 | | |
390 | | /* RFC5953 section 5.2: |
391 | | 4) If tmSameSecurity is false and tmSessionID refers to a session |
392 | | that is no longer available then an implementation SHOULD open a |
393 | | new session using the openSession() ASI (described in greater |
394 | | detail in step 5b). Instead of opening a new session an |
395 | | implementation MAY return a snmpTlstmSessionNoSessions error to |
396 | | the calling module and stop processing of the message. |
397 | | */ |
398 | | /* Implementation Notes: |
399 | | - We would never get here if the sessionID was either undefined |
400 | | or different. We tie packets directly to the transport |
401 | | object and it could never be sent back over a different |
402 | | transport, which is what the above text is trying to prevent. |
403 | | - Auto-connections are handled higher in the Net-SNMP library stack |
404 | | */ |
405 | | |
406 | | /* RFC5953 section 5.2: |
407 | | 5) If tmSessionID is undefined, then use tmTransportDomain, |
408 | | tmTransportAddress, tmSecurityName and tmRequestedSecurityLevel |
409 | | to see if there is a corresponding entry in the LCD suitable to |
410 | | send the message over. |
411 | | |
412 | | 5a) If there is a corresponding LCD entry, then this session |
413 | | will be used to send the message. |
414 | | |
415 | | 5b) If there is not a corresponding LCD entry, then open a |
416 | | session using the openSession() ASI (discussed further in |
417 | | Section 5.3.1). Implementations MAY wish to offer message |
418 | | buffering to prevent redundant openSession() calls for the |
419 | | same cache entry. If an error is returned from |
420 | | openSession(), then discard the message, discard the |
421 | | tmStateReference, increment the snmpTlstmSessionOpenErrors, |
422 | | return an error indication to the calling module and stop |
423 | | processing of the message. |
424 | | */ |
425 | | /* Implementation Notes: |
426 | | - We would never get here if the sessionID was either undefined |
427 | | or different. We tie packets directly to the transport |
428 | | object and it could never be sent back over a different |
429 | | transport, which is what the above text is trying to prevent. |
430 | | - Auto-connections are handled higher in the Net-SNMP library stack |
431 | | */ |
432 | | |
433 | | /* our session pointer is functionally t->data */ |
434 | 0 | if (NULL == t->data) { |
435 | 0 | snmp_log(LOG_ERR, "netsnmp_tlstcp_send received no incoming data\n"); |
436 | 0 | return -1; |
437 | 0 | } |
438 | | |
439 | 0 | tlsdata = t->data; |
440 | | |
441 | 0 | if (tlsdata->ssl == NULL) { |
442 | 0 | snmp_log(LOG_ERR, "tlstcp_send was called without a SSL connection.\n"); |
443 | 0 | return SNMPERR_GENERR; |
444 | 0 | } |
445 | | |
446 | | /* If the first packet and we have no secname, then copy the |
447 | | important securityName data into the longer-lived session |
448 | | reference information. */ |
449 | 0 | if ((tlsdata->flags & NETSNMP_TLSBASE_IS_CLIENT) && |
450 | 0 | !tlsdata->securityName && tmStateRef && tmStateRef->securityNameLen > 0) |
451 | 0 | tlsdata->securityName = strdup(tmStateRef->securityName); |
452 | | |
453 | | |
454 | | /* RFC5953 section 5.2: |
455 | | 6) Using either the session indicated by the tmSessionID if there |
456 | | was one or the session resulting from a previous step (4 or 5), |
457 | | pass the outgoingMessage to (D)TLS for encapsulation and |
458 | | transmission. |
459 | | */ |
460 | 0 | rc = SSL_write(tlsdata->ssl, buf, size); |
461 | 0 | DEBUGMSGTL(("tlstcp", "wrote %d bytes\n", size)); |
462 | 0 | if (rc < 0) { |
463 | 0 | _openssl_log_error(rc, tlsdata->ssl, "SSL_write"); |
464 | 0 | } |
465 | |
|
466 | 0 | return rc; |
467 | 0 | } |
468 | | |
469 | | |
470 | | |
471 | | static int |
472 | | netsnmp_tlstcp_close(netsnmp_transport *t) |
473 | 0 | { |
474 | 0 | _netsnmpTLSBaseData *tlsdata; |
475 | |
|
476 | 0 | if (NULL == t || NULL == t->data) |
477 | 0 | return -1; |
478 | | |
479 | | /* RFC5953 Section 5.4. Closing a Session |
480 | | |
481 | | 1) Increment either the snmpTlstmSessionClientCloses or the |
482 | | snmpTlstmSessionServerCloses counter as appropriate. |
483 | | */ |
484 | 0 | if (t->flags & NETSNMP_TLSBASE_IS_CLIENT) |
485 | 0 | snmp_increment_statistic(STAT_TLSTM_SNMPTLSTMSESSIONCLIENTCLOSES); |
486 | 0 | else |
487 | 0 | snmp_increment_statistic(STAT_TLSTM_SNMPTLSTMSESSIONSERVERCLOSES); |
488 | | |
489 | | /* RFC5953 Section 5.4. Closing a Session |
490 | | 2) Look up the session using the tmSessionID. |
491 | | */ |
492 | 0 | tlsdata = (_netsnmpTLSBaseData *) t->data; |
493 | | |
494 | | /* RFC5953 Section 5.4. Closing a Session |
495 | | 3) If there is no open session associated with the tmSessionID, then |
496 | | closeSession processing is completed. |
497 | | */ |
498 | | /* Implementation notes: if we have a non-zero tlsdata then it's |
499 | | always true */ |
500 | | |
501 | | /* RFC5953 Section 5.3.1: Establishing a Session as a Client |
502 | | 4) Have (D)TLS close the specified connection. This SHOULD include |
503 | | sending a close_notify TLS Alert to inform the other side that |
504 | | session cleanup may be performed. |
505 | | */ |
506 | |
|
507 | 0 | DEBUGMSGTL(("tlstcp", "Shutting down SSL connection\n")); |
508 | 0 | if (tlsdata->ssl) { |
509 | 0 | SSL_shutdown(tlsdata->ssl); |
510 | 0 | } |
511 | |
|
512 | 0 | netsnmp_tlsbase_free_tlsdata(tlsdata); |
513 | |
|
514 | 0 | t->data = NULL; |
515 | 0 | return netsnmp_socketbase_close(t); |
516 | 0 | } |
517 | | |
518 | | static int |
519 | | netsnmp_tlstcp_accept(netsnmp_transport *t) |
520 | 0 | { |
521 | 0 | BIO *accepted_bio; |
522 | 0 | int rc; |
523 | 0 | SSL_CTX *ctx; |
524 | 0 | SSL *ssl; |
525 | 0 | _netsnmpTLSBaseData *tlsdata = NULL; |
526 | | |
527 | 0 | DEBUGMSGTL(("tlstcp", "netsnmp_tlstcp_accept called\n")); |
528 | |
|
529 | 0 | tlsdata = (_netsnmpTLSBaseData *) t->data; |
530 | |
|
531 | 0 | rc = BIO_do_accept(tlsdata->accept_bio); |
532 | |
|
533 | 0 | if (rc <= 0) { |
534 | 0 | snmp_log(LOG_ERR, "BIO_do_accept failed\n"); |
535 | 0 | _openssl_log_error(rc, NULL, "BIO_do_accept"); |
536 | | /* XXX: need to close the listening connection here? */ |
537 | 0 | return -1; |
538 | 0 | } |
539 | | |
540 | 0 | tlsdata->accepted_bio = accepted_bio = BIO_pop(tlsdata->accept_bio); |
541 | 0 | if (!accepted_bio) { |
542 | 0 | snmp_log(LOG_ERR, "Failed to pop an accepted bio off the bio stack\n"); |
543 | | /* XXX: need to close the listening connection here? */ |
544 | 0 | return -1; |
545 | 0 | } |
546 | | |
547 | | /* create the OpenSSL TLS context */ |
548 | 0 | ctx = tlsdata->ssl_context; |
549 | | |
550 | | /* create the server's main SSL bio */ |
551 | 0 | ssl = tlsdata->ssl = SSL_new(ctx); |
552 | 0 | if (!tlsdata->ssl) { |
553 | 0 | snmp_log(LOG_ERR, "TLSTCP: Failed to create a SSL BIO\n"); |
554 | 0 | BIO_free(accepted_bio); |
555 | 0 | tlsdata->accepted_bio = NULL; |
556 | 0 | return -1; |
557 | 0 | } |
558 | | |
559 | 0 | SSL_set_bio(ssl, accepted_bio, accepted_bio); |
560 | | |
561 | 0 | if ((rc = SSL_accept(ssl)) <= 0) { |
562 | 0 | snmp_log(LOG_ERR, "TLSTCP: Failed SSL_accept\n"); |
563 | 0 | _openssl_log_error(rc, ssl, "SSL_accept"); |
564 | 0 | SSL_shutdown(tlsdata->ssl); |
565 | 0 | SSL_free(tlsdata->ssl); |
566 | 0 | tlsdata->accepted_bio = NULL; /* freed by SSL_free */ |
567 | 0 | tlsdata->ssl = NULL; |
568 | 0 | return -1; |
569 | 0 | } |
570 | | |
571 | | /* |
572 | | * currently netsnmp_tlsbase_wrapup_recv is where we check for |
573 | | * algorithm compliance, but for tls we know the algorithms |
574 | | * at this point, so we could bail earlier... |
575 | | */ |
576 | | #if 0 /* moved checks to netsnmp_tlsbase_wrapup_recv */ |
577 | | netsnmp_openssl_null_checks(tlsdata->ssl, &no_auth, NULL); |
578 | | if (no_auth != 0) { /* null/unknown authentication */ |
579 | | /* xxx-rks: snmp_increment_statistic(STAT_???); */ |
580 | | snmp_log(LOG_ERR, "tlstcp: connection with NULL authentication\n"); |
581 | | SSL_shutdown(tlsdata->ssl); |
582 | | SSL_free(tlsdata->ssl); |
583 | | tlsdata->accepted_bio = NULL; /* freed by SSL_free */ |
584 | | tlsdata->ssl = NULL; |
585 | | return -1; |
586 | | } |
587 | | #endif |
588 | | |
589 | | /* RFC5953 Section 5.3.2: Accepting a Session as a Server |
590 | | A (D)TLS server should accept new session connections from any client |
591 | | that it is able to verify the client's credentials for. This is done |
592 | | by authenticating the client's presented certificate through a |
593 | | certificate path validation process (e.g. [RFC5280]) or through |
594 | | certificate fingerprint verification using fingerprints configured in |
595 | | the snmpTlstmCertToTSNTable. Afterward the server will determine the |
596 | | identity of the remote entity using the following procedures. |
597 | | |
598 | | The (D)TLS server identifies the authenticated identity from the |
599 | | (D)TLS client's principal certificate using configuration information |
600 | | from the snmpTlstmCertToTSNTable mapping table. The (D)TLS server |
601 | | MUST request and expect a certificate from the client and MUST NOT |
602 | | accept SNMP messages over the (D)TLS connection until the client has |
603 | | sent a certificate and it has been authenticated. The resulting |
604 | | derived tmSecurityName is recorded in the tmStateReference cache as |
605 | | tmSecurityName. The details of the lookup process are fully |
606 | | described in the DESCRIPTION clause of the snmpTlstmCertToTSNTable |
607 | | MIB object. If any verification fails in any way (for example |
608 | | because of failures in cryptographic verification or because of the |
609 | | lack of an appropriate row in the snmpTlstmCertToTSNTable) then the |
610 | | session establishment MUST fail, and the |
611 | | snmpTlstmSessionInvalidClientCertificates object is incremented. If |
612 | | the session can not be opened for any reason at all, including |
613 | | cryptographic verification failures, then the |
614 | | snmpTlstmSessionOpenErrors counter is incremented and processing |
615 | | stops. |
616 | | |
617 | | Servers that wish to support multiple principals at a particular port |
618 | | SHOULD make use of a (D)TLS extension that allows server-side |
619 | | principal selection like the Server Name Indication extension defined |
620 | | in Section 3.1 of [RFC4366]. Supporting this will allow, for |
621 | | example, sending notifications to a specific principal at a given TCP |
622 | | or UDP port. |
623 | | */ |
624 | | /* Implementation notes: |
625 | | - we expect fingerprints to be stored in the transport config |
626 | | - we do not currently support multiple principals and only offer one |
627 | | */ |
628 | 0 | if ((rc = netsnmp_tlsbase_verify_client_cert(ssl, tlsdata)) |
629 | 0 | != SNMPERR_SUCCESS) { |
630 | | /* XXX: free needed memory */ |
631 | 0 | snmp_log(LOG_ERR, "TLSTCP: Failed checking client certificate\n"); |
632 | 0 | snmp_increment_statistic(STAT_TLSTM_SNMPTLSTMSESSIONINVALIDCLIENTCERTIFICATES); |
633 | 0 | SSL_shutdown(tlsdata->ssl); |
634 | 0 | SSL_free(tlsdata->ssl); |
635 | 0 | tlsdata->accepted_bio = NULL; /* freed by SSL_free */ |
636 | 0 | tlsdata->ssl = NULL; |
637 | 0 | return -1; |
638 | 0 | } |
639 | | |
640 | | |
641 | | /* XXX: check acceptance criteria here */ |
642 | | |
643 | 0 | DEBUGMSGTL(("tlstcp", "accept succeeded on sock %d\n", t->sock)); |
644 | | |
645 | | /* RFC5953 Section 5.1.2 step 1, part2:: |
646 | | * If this is the first message received through this session and |
647 | | the session does not have an assigned tlstmSessionID yet then the |
648 | | snmpTlstmSessionAccepts counter is incremented and a |
649 | | tlstmSessionID for the session is created. This will only happen |
650 | | on the server side of a connection because a client would have |
651 | | already assigned a tlstmSessionID during the openSession() |
652 | | invocation. Implementations may have performed the procedures |
653 | | described in Section 5.3.2 prior to this point or they may |
654 | | perform them now, but the procedures described in Section 5.3.2 |
655 | | MUST be performed before continuing beyond this point. |
656 | | */ |
657 | | /* We're taking option 2 and incrementing the session accepts here |
658 | | rather than upon receiving the first packet */ |
659 | 0 | snmp_increment_statistic(STAT_TLSTM_SNMPTLSTMSESSIONACCEPTS); |
660 | | |
661 | | /* XXX: check that it returns something so we can free stuff? */ |
662 | 0 | return BIO_get_fd(tlsdata->accepted_bio, NULL); |
663 | 0 | } |
664 | | |
665 | | static netsnmp_transport * |
666 | | netsnmp_tlstcp_open_client(netsnmp_transport *t) |
667 | 0 | { |
668 | 0 | _netsnmpTLSBaseData *tlsdata = t->data; |
669 | 0 | BIO *bio; |
670 | 0 | SSL_CTX *ctx; |
671 | 0 | SSL *ssl; |
672 | 0 | int rc = 0; |
673 | 0 | _netsnmp_verify_info *verify_info; |
674 | | |
675 | | /* RFC5953 Section 5.3.1: Establishing a Session as a Client |
676 | | * 1) The snmpTlstmSessionOpens counter is incremented. |
677 | | */ |
678 | 0 | snmp_increment_statistic(STAT_TLSTM_SNMPTLSTMSESSIONOPENS); |
679 | | |
680 | | /* RFC5953 Section 5.3.1: Establishing a Session as a Client |
681 | | 2) The client selects the appropriate certificate and cipher_suites |
682 | | for the key agreement based on the tmSecurityName and the |
683 | | tmRequestedSecurityLevel for the session. For sessions being |
684 | | established as a result of a SNMP-TARGET-MIB based operation, the |
685 | | certificate will potentially have been identified via the |
686 | | snmpTlstmParamsTable mapping and the cipher_suites will have to |
687 | | be taken from system-wide or implementation-specific |
688 | | configuration. If no row in the snmpTlstmParamsTable exists then |
689 | | implementations MAY choose to establish the connection using a |
690 | | default client certificate available to the application. |
691 | | Otherwise, the certificate and appropriate cipher_suites will |
692 | | need to be passed to the openSession() ASI as supplemental |
693 | | information or configured through an implementation-dependent |
694 | | mechanism. It is also implementation-dependent and possibly |
695 | | policy-dependent how tmRequestedSecurityLevel will be used to |
696 | | influence the security capabilities provided by the (D)TLS |
697 | | connection. However this is done, the security capabilities |
698 | | provided by (D)TLS MUST be at least as high as the level of |
699 | | security indicated by the tmRequestedSecurityLevel parameter. |
700 | | The actual security level of the session is reported in the |
701 | | tmStateReference cache as tmSecurityLevel. For (D)TLS to provide |
702 | | strong authentication, each principal acting as a command |
703 | | generator SHOULD have its own certificate. |
704 | | */ |
705 | | /* |
706 | | Implementation notes: we do most of this in the |
707 | | sslctx_client_setup The transport should have been |
708 | | f_config()ed with the proper fingerprints to use (which is |
709 | | stored in tlsdata), or we'll use the default identity |
710 | | fingerprint if that can be found. |
711 | | */ |
712 | | |
713 | | /* XXX: check securityLevel and ensure no NULL fingerprints are used */ |
714 | | |
715 | | /* set up the needed SSL context */ |
716 | 0 | tlsdata->ssl_context = ctx = sslctx_client_setup(TLS_method(), tlsdata); |
717 | 0 | if (!ctx) { |
718 | 0 | snmp_log(LOG_ERR, "failed to create TLS context\n"); |
719 | 0 | return NULL; |
720 | 0 | } |
721 | | |
722 | | /* RFC5953 Section 5.3.1: Establishing a Session as a Client |
723 | | 3) Using the destTransportDomain and destTransportAddress values, |
724 | | the client will initiate the (D)TLS handshake protocol to |
725 | | establish session keys for message integrity and encryption. |
726 | | */ |
727 | | /* Implementation note: |
728 | | The transport domain and address are pre-processed by this point |
729 | | */ |
730 | | |
731 | | /* Create a BIO connection for it */ |
732 | 0 | DEBUGMSGTL(("tlstcp", "connecting to tlstcp %s\n", |
733 | 0 | tlsdata->addr_string)); |
734 | 0 | t->remote = strdup(tlsdata->addr_string); |
735 | 0 | t->remote_length = strlen(tlsdata->addr_string) + 1; |
736 | |
|
737 | 0 | bio = BIO_new_connect(tlsdata->addr_string); |
738 | | |
739 | | /* RFC5953 Section 5.3.1: Establishing a Session as a Client |
740 | | 3) continued: |
741 | | If the attempt to establish a session is unsuccessful, then |
742 | | snmpTlstmSessionOpenErrors is incremented, an error indication is |
743 | | returned, and processing stops. |
744 | | */ |
745 | |
|
746 | 0 | if (NULL == bio) { |
747 | 0 | snmp_increment_statistic(STAT_TLSTM_SNMPTLSTMSESSIONOPENERRORS); |
748 | 0 | snmp_log(LOG_ERR, "tlstcp: failed to create bio\n"); |
749 | 0 | _openssl_log_error(rc, NULL, "BIO creation"); |
750 | 0 | return NULL; |
751 | 0 | } |
752 | | |
753 | | /* Tell the BIO to actually do the connection */ |
754 | 0 | rc = BIO_do_connect(bio); |
755 | 0 | if (rc <= 0) { |
756 | 0 | snmp_increment_statistic(STAT_TLSTM_SNMPTLSTMSESSIONOPENERRORS); |
757 | 0 | snmp_log(LOG_ERR, "tlstcp: failed to connect to %s\n", |
758 | 0 | tlsdata->addr_string); |
759 | 0 | _openssl_log_error(rc, NULL, "BIO_do_connect"); |
760 | 0 | BIO_free(bio); |
761 | 0 | return NULL; |
762 | 0 | } |
763 | | |
764 | | /* Create the SSL layer on top of the socket bio */ |
765 | 0 | ssl = tlsdata->ssl = SSL_new(ctx); |
766 | 0 | if (NULL == ssl) { |
767 | 0 | snmp_increment_statistic(STAT_TLSTM_SNMPTLSTMSESSIONOPENERRORS); |
768 | 0 | snmp_log(LOG_ERR, "tlstcp: failed to create a SSL connection\n"); |
769 | 0 | BIO_free(bio); |
770 | 0 | return NULL; |
771 | 0 | } |
772 | | |
773 | | /* Bind the SSL layer to the BIO */ |
774 | 0 | SSL_set_bio(ssl, bio, bio); |
775 | 0 | SSL_set_mode(ssl, SSL_MODE_AUTO_RETRY); |
776 | |
|
777 | 0 | verify_info = SNMP_MALLOC_TYPEDEF(_netsnmp_verify_info); |
778 | 0 | if (NULL == verify_info) { |
779 | 0 | snmp_increment_statistic(STAT_TLSTM_SNMPTLSTMSESSIONOPENERRORS); |
780 | 0 | snmp_log(LOG_ERR, "tlstcp: failed to create a SSL connection\n"); |
781 | 0 | SSL_shutdown(ssl); |
782 | 0 | BIO_free(bio); |
783 | 0 | return NULL; |
784 | 0 | } |
785 | | |
786 | 0 | SSL_set_ex_data(ssl, tls_get_verify_info_index(), verify_info); |
787 | | |
788 | | /* Then have SSL do it's connection over the BIO */ |
789 | 0 | rc = SSL_connect(ssl); |
790 | 0 | if (rc <= 0) { |
791 | 0 | snmp_increment_statistic(STAT_TLSTM_SNMPTLSTMSESSIONOPENERRORS); |
792 | 0 | snmp_log(LOG_ERR, "tlstcp: failed to ssl_connect\n"); |
793 | 0 | BIO_free(bio); |
794 | 0 | return NULL; |
795 | 0 | } |
796 | | |
797 | | /* RFC5953 Section 5.3.1: Establishing a Session as a Client |
798 | | 3) continued: |
799 | | If the session failed to open because the presented |
800 | | server certificate was unknown or invalid then the |
801 | | snmpTlstmSessionUnknownServerCertificate or |
802 | | snmpTlstmSessionInvalidServerCertificates MUST be |
803 | | incremented and a snmpTlstmServerCertificateUnknown or |
804 | | snmpTlstmServerInvalidCertificate notification SHOULD be |
805 | | sent as appropriate. Reasons for server certificate |
806 | | invalidation includes, but is not limited to, |
807 | | cryptographic validation failures and an unexpected |
808 | | presented certificate identity. |
809 | | */ |
810 | | |
811 | | /* RFC5953 Section 5.3.1: Establishing a Session as a Client |
812 | | 4) The (D)TLS client MUST then verify that the (D)TLS server's |
813 | | presented certificate is the expected certificate. The (D)TLS |
814 | | client MUST NOT transmit SNMP messages until the server |
815 | | certificate has been authenticated, the client certificate has |
816 | | been transmitted and the TLS connection has been fully |
817 | | established. |
818 | | |
819 | | If the connection is being established from configuration based |
820 | | on SNMP-TARGET-MIB configuration, then the snmpTlstmAddrTable |
821 | | DESCRIPTION clause describes how the verification is done (using |
822 | | either a certificate fingerprint, or an identity authenticated |
823 | | via certification path validation). |
824 | | |
825 | | If the connection is being established for reasons other than |
826 | | configuration found in the SNMP-TARGET-MIB then configuration and |
827 | | procedures outside the scope of this document should be followed. |
828 | | Configuration mechanisms SHOULD be similar in nature to those |
829 | | defined in the snmpTlstmAddrTable to ensure consistency across |
830 | | management configuration systems. For example, a command-line |
831 | | tool for generating SNMP GETs might support specifying either the |
832 | | server's certificate fingerprint or the expected host name as a |
833 | | command line argument. |
834 | | */ |
835 | | |
836 | | /* Implementation notes: |
837 | | - All remote certificate fingerprints are expected to be |
838 | | stored in the transport's config information. This is |
839 | | true both for CLI clients and TARGET-MIB sessions. |
840 | | - netsnmp_tlsbase_verify_server_cert implements these checks |
841 | | */ |
842 | 0 | if (netsnmp_tlsbase_verify_server_cert(ssl, tlsdata) != SNMPERR_SUCCESS) { |
843 | | /* XXX: unknown vs invalid; two counters */ |
844 | 0 | snmp_increment_statistic(STAT_TLSTM_SNMPTLSTMSESSIONUNKNOWNSERVERCERTIFICATE); |
845 | 0 | snmp_log(LOG_ERR, "tlstcp: failed to verify ssl certificate\n"); |
846 | 0 | SSL_shutdown(ssl); |
847 | 0 | BIO_free(bio); |
848 | 0 | return NULL; |
849 | 0 | } |
850 | | |
851 | | /* RFC5953 Section 5.3.1: Establishing a Session as a Client |
852 | | 5) (D)TLS provides assurance that the authenticated identity has |
853 | | been signed by a trusted configured certification authority. If |
854 | | verification of the server's certificate fails in any way (for |
855 | | example because of failures in cryptographic verification or the |
856 | | presented identity did not match the expected named entity) then |
857 | | the session establishment MUST fail, the |
858 | | snmpTlstmSessionInvalidServerCertificates object is incremented. |
859 | | If the session can not be opened for any reason at all, including |
860 | | cryptographic verification failures, then the |
861 | | snmpTlstmSessionOpenErrors counter is incremented and processing |
862 | | stops. |
863 | | |
864 | | */ |
865 | | /* XXX: add snmpTlstmSessionInvalidServerCertificates on |
866 | | crypto failure */ |
867 | | |
868 | | /* RFC5953 Section 5.3.1: Establishing a Session as a Client |
869 | | 6) The TLSTM-specific session identifier (tlstmSessionID) is set in |
870 | | the tmSessionID of the tmStateReference passed to the TLS |
871 | | Transport Model to indicate that the session has been established |
872 | | successfully and to point to a specific (D)TLS connection for |
873 | | future use. The tlstmSessionID is also stored in the LCD for |
874 | | later lookup during processing of incoming messages |
875 | | (Section 5.1.2). |
876 | | */ |
877 | | /* Implementation notes: |
878 | | - the tlsdata pointer is used as our session identifier, as |
879 | | noted in the netsnmp_tlstcp_recv() function comments. |
880 | | */ |
881 | | |
882 | 0 | t->sock = BIO_get_fd(bio, NULL); |
883 | |
|
884 | 0 | return t; |
885 | 0 | } |
886 | | |
887 | | static netsnmp_transport * |
888 | | netsnmp_tlstcp_open_server(netsnmp_transport *t) |
889 | 0 | { |
890 | 0 | _netsnmpTLSBaseData *tlsdata; |
891 | 0 | int rc; |
892 | |
|
893 | 0 | tlsdata = t->data; |
894 | |
|
895 | 0 | #ifndef NETSNMP_NO_LISTEN_SUPPORT |
896 | | /* Create the socket bio */ |
897 | 0 | DEBUGMSGTL(("tlstcp", "listening on tlstcp port %s\n", |
898 | 0 | tlsdata->addr_string)); |
899 | 0 | tlsdata->accept_bio = BIO_new_accept(tlsdata->addr_string); |
900 | 0 | t->local = strdup(tlsdata->addr_string); |
901 | 0 | t->local_length = strlen(tlsdata->addr_string) + 1; |
902 | 0 | if (NULL == tlsdata->accept_bio) { |
903 | 0 | snmp_log(LOG_ERR, "TLSTCP: Failed to create a accept BIO\n"); |
904 | 0 | return NULL; |
905 | 0 | } |
906 | | |
907 | | /* openssl requires an initial accept to bind() the socket */ |
908 | 0 | rc = BIO_do_accept(tlsdata->accept_bio); |
909 | 0 | if (rc <= 0) { |
910 | 0 | _openssl_log_error(rc, tlsdata->ssl, "BIO_do_accept"); |
911 | 0 | snmp_log(LOG_ERR, |
912 | 0 | "TLSTCP: Failed to do first accept on the TLS accept BIO\n"); |
913 | 0 | return NULL; |
914 | 0 | } |
915 | | |
916 | | /* create the OpenSSL TLS context */ |
917 | 0 | tlsdata->ssl_context = sslctx_server_setup(TLS_method()); |
918 | |
|
919 | 0 | t->sock = BIO_get_fd(tlsdata->accept_bio, NULL); |
920 | 0 | t->flags |= NETSNMP_TRANSPORT_FLAG_LISTEN; |
921 | | #else /* NETSNMP_NO_LISTEN_SUPPORT */ |
922 | | return NULL; |
923 | | #endif /* NETSNMP_NO_LISTEN_SUPPORT */ |
924 | |
|
925 | 0 | return t; |
926 | 0 | } |
927 | | |
928 | | netsnmp_transport * |
929 | | netsnmp_tlstcp_open(netsnmp_transport *t) |
930 | 0 | { |
931 | 0 | _netsnmpTLSBaseData *tlsdata; |
932 | |
|
933 | 0 | netsnmp_assert_or_return(t != NULL, NULL); |
934 | 0 | netsnmp_assert_or_return(t->data != NULL, NULL); |
935 | 0 | netsnmp_assert_or_return(sizeof(_netsnmpTLSBaseData) == t->data_length, |
936 | 0 | NULL); |
937 | | |
938 | 0 | tlsdata = t->data; |
939 | |
|
940 | 0 | if (tlsdata->flags & NETSNMP_TLSBASE_IS_CLIENT) |
941 | 0 | return netsnmp_tlstcp_open_client(t); |
942 | 0 | else |
943 | 0 | return netsnmp_tlstcp_open_server(t); |
944 | 0 | } |
945 | | |
946 | | /* |
947 | | * Create a TLS-based transport for SNMP. Local is TRUE if addr is the local |
948 | | * address to bind to (i.e. this is a server-type session); otherwise addr is |
949 | | * the remote address to send things to. |
950 | | */ |
951 | | |
952 | | netsnmp_transport * |
953 | | netsnmp_tlstcp_transport(const char *addr_string, int isserver) |
954 | 0 | { |
955 | 0 | netsnmp_transport *t = NULL; |
956 | 0 | _netsnmpTLSBaseData *tlsdata; |
957 | 0 | char *cp; |
958 | 0 | char buf[SPRINT_MAX_LEN]; |
959 | | |
960 | | #ifdef NETSNMP_NO_LISTEN_SUPPORT |
961 | | if (isserver) |
962 | | return NULL; |
963 | | #endif /* NETSNMP_NO_LISTEN_SUPPORT */ |
964 | | |
965 | | /* allocate our transport structure */ |
966 | 0 | t = SNMP_MALLOC_TYPEDEF(netsnmp_transport); |
967 | 0 | if (NULL == t) { |
968 | 0 | return NULL; |
969 | 0 | } |
970 | | |
971 | | /* allocate our TLS specific data */ |
972 | 0 | if (NULL == (tlsdata = netsnmp_tlsbase_allocate_tlsdata(t, isserver))) |
973 | 0 | return NULL; |
974 | | |
975 | 0 | if (!isserver) |
976 | 0 | t->flags |= NETSNMP_TLSBASE_IS_CLIENT; |
977 | |
|
978 | 0 | tlsdata->addr_string = strdup(addr_string); |
979 | | |
980 | | /* see if we can extract the remote hostname */ |
981 | 0 | if (!isserver && tlsdata && addr_string) { |
982 | | /* search for a : */ |
983 | 0 | if (NULL != (cp = strrchr(addr_string, ':'))) { |
984 | 0 | sprintf(buf, "%.*s", |
985 | 0 | (int) SNMP_MIN(cp - addr_string, sizeof(buf) - 1), |
986 | 0 | addr_string); |
987 | 0 | } else { |
988 | | /* else the entire spec is a host name only */ |
989 | 0 | strlcpy(buf, addr_string, sizeof(buf)); |
990 | 0 | } |
991 | 0 | tlsdata->their_hostname = strdup(buf); |
992 | 0 | } |
993 | |
|
994 | 0 | t->data = tlsdata; |
995 | 0 | t->data_length = sizeof(_netsnmpTLSBaseData); |
996 | | |
997 | | /* |
998 | | * Set Domain |
999 | | */ |
1000 | 0 | t->domain = netsnmpTLSTCPDomain; |
1001 | 0 | t->domain_length = netsnmpTLSTCPDomain_len; |
1002 | | |
1003 | | /* |
1004 | | * 16-bit length field, 8 byte TLS header, 20 byte IPv4 header |
1005 | | */ |
1006 | |
|
1007 | 0 | t->msgMaxSize = 0xffff - 8 - 20; |
1008 | 0 | t->f_recv = netsnmp_tlstcp_recv; |
1009 | 0 | t->f_send = netsnmp_tlstcp_send; |
1010 | 0 | t->f_open = netsnmp_tlstcp_open; |
1011 | 0 | t->f_close = netsnmp_tlstcp_close; |
1012 | 0 | t->f_accept = netsnmp_tlstcp_accept; |
1013 | 0 | t->f_copy = netsnmp_tlstcp_copy; |
1014 | 0 | t->f_config = netsnmp_tlsbase_config; |
1015 | 0 | t->f_setup_session = netsnmp_tlsbase_session_init; |
1016 | 0 | t->f_fmtaddr = netsnmp_tlstcp_fmtaddr; |
1017 | 0 | t->f_get_taddr = netsnmp_tlstcp_get_taddr; |
1018 | |
|
1019 | 0 | t->flags |= NETSNMP_TRANSPORT_FLAG_TUNNELED | NETSNMP_TRANSPORT_FLAG_STREAM; |
1020 | |
|
1021 | 0 | return t; |
1022 | 0 | } |
1023 | | |
1024 | | netsnmp_transport * |
1025 | | netsnmp_tlstcp_create_tstring(const char *str, int local, |
1026 | | const char *default_target) |
1027 | 0 | { |
1028 | 0 | char buf[SPRINT_MAX_LEN]; |
1029 | |
|
1030 | 0 | if (str == NULL || *str == '\0') |
1031 | 0 | str = default_target + 1; /* drop the leading : */ |
1032 | 0 | else if (!strchr(str, ':')) { |
1033 | | /* it's either :port or :address. Try to guess which. */ |
1034 | 0 | const char *cp; |
1035 | 0 | int isport = 1; |
1036 | 0 | for(cp = str; *cp != '\0'; cp++) { |
1037 | | /* if ALL numbers, it must be just a port */ |
1038 | | /* if it contains anything else, assume a host or ip address */ |
1039 | 0 | if (!isdigit(0xFF & *cp)) { |
1040 | 0 | isport = 0; |
1041 | 0 | break; |
1042 | 0 | } |
1043 | 0 | } |
1044 | 0 | if (isport) { |
1045 | | /* Just :NNN can be passed to openssl */ |
1046 | 0 | snprintf(buf, sizeof(buf)-1, "0.0.0.0:%s", str); |
1047 | 0 | } else { |
1048 | | /* add the default port */ |
1049 | 0 | snprintf(buf, sizeof(buf)-1, "%s%s", str, default_target); |
1050 | 0 | } |
1051 | 0 | str = buf; |
1052 | 0 | } |
1053 | 0 | return netsnmp_tlstcp_transport(str, local); |
1054 | 0 | } |
1055 | | |
1056 | | |
1057 | | netsnmp_transport * |
1058 | | netsnmp_tlstcp_create_ostring(const void *o, size_t o_len, int local) |
1059 | 0 | { |
1060 | 0 | char buf[SPRINT_MAX_LEN]; |
1061 | | |
1062 | | /* ensure buf is big enough */ |
1063 | 0 | if (o_len > SPRINT_MAX_LEN - 1) |
1064 | 0 | return NULL; |
1065 | | |
1066 | 0 | memcpy(buf, o, o_len); |
1067 | 0 | buf[o_len] = '\0'; |
1068 | |
|
1069 | 0 | return netsnmp_tlstcp_transport(buf, local); |
1070 | 0 | } |
1071 | | |
1072 | | void |
1073 | | netsnmp_tlstcp_ctor(void) |
1074 | 3.82k | { |
1075 | 3.82k | DEBUGMSGTL(("tlstcp", "registering TLS constructor\n")); |
1076 | | |
1077 | | /* config settings */ |
1078 | | |
1079 | 3.82k | tlstcpDomain.name = netsnmpTLSTCPDomain; |
1080 | 3.82k | tlstcpDomain.name_length = netsnmpTLSTCPDomain_len; |
1081 | 3.82k | tlstcpDomain.prefix = calloc(3, sizeof(char *)); |
1082 | 3.82k | if (!tlstcpDomain.prefix) { |
1083 | 0 | snmp_log(LOG_ERR, "calloc() failed - out of memory\n"); |
1084 | 0 | return; |
1085 | 0 | } |
1086 | 3.82k | tlstcpDomain.prefix[0] = "tlstcp"; |
1087 | 3.82k | tlstcpDomain.prefix[1] = "tls"; |
1088 | | |
1089 | 3.82k | tlstcpDomain.f_create_from_tstring_new = netsnmp_tlstcp_create_tstring; |
1090 | 3.82k | tlstcpDomain.f_create_from_ostring = netsnmp_tlstcp_create_ostring; |
1091 | | |
1092 | 3.82k | netsnmp_tdomain_register(&tlstcpDomain); |
1093 | 3.82k | } |