Coverage Report

Created: 2024-02-25 06:16

/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
}