Blame snmplib/transports/snmpTLSTCPDomain.c

Packit fcad23
/* Portions of this file are subject to the following copyright(s).  See
Packit fcad23
 * the Net-SNMP's COPYING file for more details and other copyrights
Packit fcad23
 * that may apply:
Packit fcad23
 */
Packit fcad23
/* 
Packit fcad23
 * See the following web pages for useful documentation on this transport:
Packit fcad23
 * http://www.net-snmp.org/wiki/index.php/TUT:Using_TLS
Packit fcad23
 * http://www.net-snmp.org/wiki/index.php/Using_DTLS
Packit fcad23
 */
Packit fcad23
#include <net-snmp/net-snmp-config.h>
Packit fcad23
Packit fcad23
#include <net-snmp/net-snmp-features.h>
Packit fcad23
Packit fcad23
netsnmp_feature_require(cert_util)
Packit fcad23
Packit fcad23
#include <stdio.h>
Packit fcad23
#include <sys/types.h>
Packit fcad23
#include <ctype.h>
Packit fcad23
#include <errno.h>
Packit fcad23
Packit fcad23
#if HAVE_STRING_H
Packit fcad23
#include <string.h>
Packit fcad23
#else
Packit fcad23
#include <strings.h>
Packit fcad23
#endif
Packit fcad23
#if HAVE_STDLIB_H
Packit fcad23
#include <stdlib.h>
Packit fcad23
#endif
Packit fcad23
#if HAVE_UNISTD_H
Packit fcad23
#include <unistd.h>
Packit fcad23
#endif
Packit fcad23
#if HAVE_SYS_SOCKET_H
Packit fcad23
#include <sys/socket.h>
Packit fcad23
#endif
Packit fcad23
#if HAVE_NETINET_IN_H
Packit fcad23
#include <netinet/in.h>
Packit fcad23
#endif
Packit fcad23
#if HAVE_ARPA_INET_H
Packit fcad23
#include <arpa/inet.h>
Packit fcad23
#endif
Packit fcad23
#if HAVE_NETDB_H
Packit fcad23
#include <netdb.h>
Packit fcad23
#endif
Packit fcad23
#if HAVE_SYS_UIO_H
Packit fcad23
#include <sys/uio.h>
Packit fcad23
#endif
Packit fcad23
Packit fcad23
#if HAVE_ARPA_INET_H
Packit fcad23
#include <arpa/inet.h>
Packit fcad23
#endif
Packit fcad23
Packit fcad23
#if HAVE_DMALLOC_H
Packit fcad23
#include <dmalloc.h>
Packit fcad23
#endif
Packit fcad23
Packit fcad23
#include <net-snmp/types.h>
Packit fcad23
#include <net-snmp/output_api.h>
Packit fcad23
#include <net-snmp/config_api.h>
Packit fcad23
#include <net-snmp/library/snmp_assert.h>
Packit fcad23
#include <net-snmp/library/snmpIPv4BaseDomain.h>
Packit fcad23
#include <net-snmp/library/snmpSocketBaseDomain.h>
Packit fcad23
#include <net-snmp/library/snmpTLSBaseDomain.h>
Packit fcad23
#include <net-snmp/library/snmpTLSTCPDomain.h>
Packit fcad23
#include <net-snmp/library/system.h>
Packit fcad23
#include <net-snmp/library/tools.h>
Packit fcad23
#include <net-snmp/library/cert_util.h>
Packit fcad23
#include <net-snmp/library/snmp_openssl.h>
Packit fcad23
#include <net-snmp/library/callback.h>
Packit fcad23
Packit fcad23
#include "openssl/bio.h"
Packit fcad23
#include "openssl/ssl.h"
Packit fcad23
#include "openssl/err.h"
Packit fcad23
Packit fcad23
#ifndef INADDR_NONE
Packit fcad23
#define INADDR_NONE	-1
Packit fcad23
#endif
Packit fcad23
Packit fcad23
#define WE_ARE_SERVER 0
Packit fcad23
#define WE_ARE_CLIENT 1
Packit fcad23
Packit fcad23
oid             netsnmpTLSTCPDomain[] = { TRANSPORT_DOMAIN_TLS_TCP_IP };
Packit fcad23
size_t          netsnmpTLSTCPDomain_len = OID_LENGTH(netsnmpTLSTCPDomain);
Packit fcad23
Packit fcad23
static netsnmp_tdomain tlstcpDomain;
Packit fcad23
Packit fcad23
/*
Packit fcad23
 * Return a string representing the address in data, or else the "far end"
Packit fcad23
 * address if data is NULL.  
Packit fcad23
 */
Packit fcad23
Packit fcad23
static char *
Packit fcad23
netsnmp_tlstcp_fmtaddr(netsnmp_transport *t, const void *data, int len)
Packit fcad23
{
Packit fcad23
    if (t && !data) {
Packit fcad23
        data = t->data;
Packit fcad23
        len = t->data_length;
Packit fcad23
    }
Packit fcad23
Packit fcad23
    switch (data ? len : 0) {
Packit fcad23
    case sizeof(netsnmp_indexed_addr_pair):
Packit fcad23
        return netsnmp_ipv4_fmtaddr("TLSTCP", t, data, len);
Packit fcad23
    case sizeof(netsnmp_tmStateReference): {
Packit fcad23
        const netsnmp_tmStateReference *r = data;
Packit fcad23
        const netsnmp_indexed_addr_pair *p = &r->addresses;
Packit fcad23
Packit fcad23
        return netsnmp_ipv4_fmtaddr("TLSTCP", t, p, sizeof(*p));
Packit fcad23
    }
Packit fcad23
    case sizeof(_netsnmpTLSBaseData): {
Packit fcad23
        const _netsnmpTLSBaseData *b = data;
Packit fcad23
        char *buf;
Packit fcad23
Packit fcad23
        if (asprintf(&buf, "TLSTCP: %s", b->addr_string) < 0)
Packit fcad23
            buf = NULL;
Packit fcad23
        return buf;
Packit fcad23
    }
Packit fcad23
    case 0:
Packit fcad23
        return strdup("TLSTCP: unknown");
Packit fcad23
    default: {
Packit fcad23
        char *buf;
Packit fcad23
Packit fcad23
        if (asprintf(&buf, "TLSTCP: len %d", len) < 0)
Packit fcad23
            buf = NULL;
Packit fcad23
        return buf;
Packit fcad23
    }
Packit fcad23
    }
Packit fcad23
}
Packit fcad23
Packit fcad23
static void netsnmp_tlstcp_get_taddr(struct netsnmp_transport_s *t,
Packit fcad23
                                     void **addr, size_t *addr_len)
Packit fcad23
{
Packit fcad23
    *addr_len = t->remote_length;
Packit fcad23
    *addr = netsnmp_memdup(t->remote, *addr_len);
Packit fcad23
}
Packit fcad23
Packit fcad23
/*
Packit fcad23
 * You can write something into opaque that will subsequently get passed back 
Packit fcad23
 * to your send function if you like.  For instance, you might want to
Packit fcad23
 * remember where a PDU came from, so that you can send a reply there...  
Packit fcad23
 */
Packit fcad23
Packit fcad23
static int
Packit fcad23
netsnmp_tlstcp_copy(const netsnmp_transport *oldt, netsnmp_transport *newt)
Packit fcad23
{
Packit fcad23
    _netsnmpTLSBaseData *oldtlsdata = (_netsnmpTLSBaseData *) oldt->data;
Packit fcad23
    _netsnmpTLSBaseData *newtlsdata = (_netsnmpTLSBaseData *) newt->data;
Packit fcad23
    oldtlsdata->accepted_bio = NULL;
Packit fcad23
    oldtlsdata->ssl = NULL;
Packit fcad23
    newtlsdata->ssl_context = NULL;
Packit fcad23
    
Packit fcad23
    if (oldtlsdata->addr_string)
Packit fcad23
        newtlsdata->addr_string = strdup(oldtlsdata->addr_string);
Packit fcad23
    if (oldtlsdata->securityName)
Packit fcad23
        newtlsdata->securityName = strdup(oldtlsdata->securityName);
Packit fcad23
    if (oldtlsdata->our_identity)
Packit fcad23
        newtlsdata->our_identity = strdup(oldtlsdata->our_identity);
Packit fcad23
    if (oldtlsdata->their_identity)
Packit fcad23
        newtlsdata->their_identity = strdup(oldtlsdata->their_identity);
Packit fcad23
    if (oldtlsdata->their_fingerprint)
Packit fcad23
        newtlsdata->their_fingerprint = strdup(oldtlsdata->their_fingerprint);
Packit fcad23
    if (oldtlsdata->their_hostname)
Packit fcad23
        newtlsdata->their_hostname = strdup(oldtlsdata->their_hostname);
Packit fcad23
    if (oldtlsdata->trust_cert)
Packit fcad23
        newtlsdata->trust_cert = strdup(oldtlsdata->trust_cert);
Packit fcad23
    if (oldtlsdata->addr)
Packit fcad23
        newtlsdata->addr = netsnmp_memdup(oldtlsdata->addr,
Packit fcad23
                                          sizeof(*oldtlsdata->addr));
Packit fcad23
Packit fcad23
    return 0;
Packit fcad23
}
Packit fcad23
Packit fcad23
static int
Packit fcad23
netsnmp_tlstcp_recv(netsnmp_transport *t, void *buf, int size,
Packit fcad23
                    void **opaque, int *olength)
Packit fcad23
{
Packit fcad23
    int             rc = -1;
Packit fcad23
    netsnmp_tmStateReference *tmStateRef = NULL;
Packit fcad23
    _netsnmpTLSBaseData *tlsdata;
Packit fcad23
Packit fcad23
    if (NULL == t || t->sock < 0 || NULL == t->data) {
Packit fcad23
        snmp_log(LOG_ERR,
Packit fcad23
                 "tlstcp received an invalid invocation with missing data\n");
Packit fcad23
        DEBUGMSGTL(("tlstcp", "recvfrom fd %d err %d (\"%s\")\n",
Packit fcad23
                    (t ? t->sock : -1), errno, strerror(errno)));
Packit fcad23
        if (t)
Packit fcad23
            DEBUGMSGTL(("tlstcp", "  tdata = %p", t->data));
Packit fcad23
        DEBUGMSGTL(("tlstcp", "\n"));
Packit fcad23
        return -1;
Packit fcad23
    }
Packit fcad23
        
Packit fcad23
    /* RFC5953 Section 5.1.2 step 1:
Packit fcad23
    1) Determine the tlstmSessionID for the incoming message. The
Packit fcad23
       tlstmSessionID MUST be a unique session identifier for this
Packit fcad23
       (D)TLS connection.  The contents and format of this identifier
Packit fcad23
       are implementation-dependent as long as it is unique to the
Packit fcad23
       session.  A session identifier MUST NOT be reused until all
Packit fcad23
       references to it are no longer in use.  The tmSessionID is
Packit fcad23
       equal to the tlstmSessionID discussed in Section 5.1.1.
Packit fcad23
       tmSessionID refers to the session identifier when stored in the
Packit fcad23
       tmStateReference and tlstmSessionID refers to the session
Packit fcad23
       identifier when stored in the LCD.  They MUST always be equal
Packit fcad23
       when processing a given session's traffic.
Packit fcad23
     */
Packit fcad23
    /* For this implementation we use the t->data memory pointer as
Packit fcad23
       the sessionID.  As it's a pointer to session specific data tied
Packit fcad23
       with the transport object we know it'll never be realloated
Packit fcad23
       (ie, duplicated) until release by this transport object and is
Packit fcad23
       safe to use as a unique session identifier. */
Packit fcad23
Packit fcad23
    tlsdata = t->data;
Packit fcad23
    if (NULL == tlsdata->ssl) {
Packit fcad23
        snmp_log(LOG_ERR,
Packit fcad23
                 "tlstcp received an invalid invocation without ssl data\n");
Packit fcad23
        return -1;
Packit fcad23
    }
Packit fcad23
Packit fcad23
    /* RFC5953 Section 5.1.2 step 1, part2:
Packit fcad23
     * This part (incrementing the counter) is done in the
Packit fcad23
       netsnmp_tlstcp_accept function.
Packit fcad23
     */
Packit fcad23
Packit fcad23
Packit fcad23
    /* RFC5953 Section 5.1.2 step 2:
Packit fcad23
     * Create a tmStateReference cache for the subsequent reference and
Packit fcad23
       assign the following values within it:
Packit fcad23
Packit fcad23
       tmTransportDomain  = snmpTLSTCPDomain or snmpDTLSUDPDomain as
Packit fcad23
                            appropriate.
Packit fcad23
Packit fcad23
       tmTransportAddress = The address the message originated from.
Packit fcad23
Packit fcad23
       tmSecurityLevel    = The derived tmSecurityLevel for the session,
Packit fcad23
                            as discussed in Section 3.1.2 and Section 5.3.
Packit fcad23
Packit fcad23
       tmSecurityName     = The fderived tmSecurityName for the session as
Packit fcad23
                            discussed in Section 5.3.  This value MUST
Packit fcad23
                            be constant during the lifetime of the
Packit fcad23
                            session.
Packit fcad23
Packit fcad23
       tmSessionID        = The tlstmSessionID described in step 1 above.
Packit fcad23
     */
Packit fcad23
Packit fcad23
    /* Implementation notes:
Packit fcad23
     * - The tmTransportDomain is represented by the transport object
Packit fcad23
     * - The tmpSessionID is represented by the tlsdata pointer (as
Packit fcad23
         discussed above)
Packit fcad23
     * - The following items are handled later in netsnmp_tlsbase_wrapup_recv:
Packit fcad23
         - tmSecurityLevel
Packit fcad23
         - tmSecurityName
Packit fcad23
         - tmSessionID
Packit fcad23
    */
Packit fcad23
Packit fcad23
    /* create a tmStateRef cache for slow fill-in */
Packit fcad23
    tmStateRef = SNMP_MALLOC_TYPEDEF(netsnmp_tmStateReference);
Packit fcad23
Packit fcad23
    if (tmStateRef == NULL) {
Packit fcad23
        *opaque = NULL;
Packit fcad23
        *olength = 0;
Packit fcad23
        return -1;
Packit fcad23
    }
Packit fcad23
Packit fcad23
    /* Set the transportDomain */
Packit fcad23
    memcpy(tmStateRef->transportDomain,
Packit fcad23
           netsnmpTLSTCPDomain, sizeof(netsnmpTLSTCPDomain[0]) *
Packit fcad23
           netsnmpTLSTCPDomain_len);
Packit fcad23
    tmStateRef->transportDomainLen = netsnmpTLSTCPDomain_len;
Packit fcad23
Packit fcad23
    /* Set the tmTransportAddress */
Packit fcad23
    tmStateRef->have_addresses = 1;
Packit fcad23
Packit fcad23
    /* RFC5953 Section 5.1.2 step 1:
Packit fcad23
     * 3)  The incomingMessage and incomingMessageLength are assigned values
Packit fcad23
       from the (D)TLS processing.
Packit fcad23
     */
Packit fcad23
Packit fcad23
    /* Implementation notes:
Packit fcad23
       - incomingMessage       = buf pointer
Packit fcad23
       - incomingMessageLength = rc
Packit fcad23
    */
Packit fcad23
Packit fcad23
    /* read the packet from openssl */
Packit fcad23
    do {
Packit fcad23
        rc = SSL_read(tlsdata->ssl, buf, size);
Packit fcad23
        if (rc == 0) {
Packit fcad23
            /* XXX closed connection */
Packit fcad23
            DEBUGMSGTL(("tlstcp", "remote side closed connection\n"));
Packit fcad23
            /* XXX: openssl cleanup */
Packit fcad23
            SNMP_FREE(tmStateRef);
Packit fcad23
            return -1;
Packit fcad23
        }
Packit fcad23
        if (rc == -1) {
Packit fcad23
            int err = SSL_get_error(tlsdata->ssl, rc);
Packit fcad23
            if (err != SSL_ERROR_WANT_READ && err != SSL_ERROR_WANT_WRITE) {
Packit fcad23
                /* error detected */
Packit fcad23
                _openssl_log_error(rc, tlsdata->ssl, "SSL_read");
Packit fcad23
                SNMP_FREE(tmStateRef);
Packit fcad23
                return rc;
Packit fcad23
            }
Packit fcad23
        }
Packit fcad23
        /* retry read for SSL_ERROR_WANT_READ || SSL_ERROR_WANT_WRITE */
Packit fcad23
    } while (rc <= 0); 
Packit fcad23
Packit fcad23
    DEBUGMSGTL(("tlstcp", "received %d decoded bytes from tls\n", rc));
Packit fcad23
Packit fcad23
    /* log the packet */
Packit fcad23
    DEBUGIF("tlstcp") {
Packit fcad23
        char *str = netsnmp_tlstcp_fmtaddr(t, NULL, 0);
Packit fcad23
        DEBUGMSGTL(("tlstcp",
Packit fcad23
                    "recvfrom fd %d got %d bytes (from %s)\n",
Packit fcad23
                    t->sock, rc, str));
Packit fcad23
        free(str);
Packit fcad23
    }
Packit fcad23
Packit fcad23
    /* Other wrap-up things common to TLS and DTLS */
Packit fcad23
    if (netsnmp_tlsbase_wrapup_recv(tmStateRef, tlsdata, opaque, olength) !=
Packit fcad23
        SNMPERR_SUCCESS)
Packit fcad23
        return SNMPERR_GENERR;
Packit fcad23
Packit fcad23
    /* RFC5953 Section 5.1.2 step 1:
Packit fcad23
     * 4)  The TLS Transport Model passes the transportDomain,
Packit fcad23
       transportAddress, incomingMessage, and incomingMessageLength to
Packit fcad23
       the Dispatcher using the receiveMessage ASI:
Packit fcad23
    */
Packit fcad23
Packit fcad23
    /* In our implementation, this is done simply by returning */
Packit fcad23
    return rc;
Packit fcad23
}
Packit fcad23
Packit fcad23
Packit fcad23
Packit fcad23
static int
Packit fcad23
netsnmp_tlstcp_send(netsnmp_transport *t, const void *buf, int size,
Packit fcad23
                    void **opaque, int *olength)
Packit fcad23
{
Packit fcad23
    int rc = -1;
Packit fcad23
    const netsnmp_tmStateReference *tmStateRef = NULL;
Packit fcad23
    _netsnmpTLSBaseData *tlsdata;
Packit fcad23
    
Packit fcad23
    DEBUGTRACETOK("tlstcp");
Packit fcad23
Packit fcad23
    /* RFC5953 section 5.2: 
Packit fcad23
      1)  If tmStateReference does not refer to a cache containing values
Packit fcad23
      for tmTransportDomain, tmTransportAddress, tmSecurityName,
Packit fcad23
      tmRequestedSecurityLevel, and tmSameSecurity, then increment the
Packit fcad23
      snmpTlstmSessionInvalidCaches counter, discard the message, and
Packit fcad23
      return the error indication in the statusInformation.  Processing
Packit fcad23
      of this message stops.
Packit fcad23
    */
Packit fcad23
    /* Implementation Notes: the tmStateReference is stored in the opaque ptr */
Packit fcad23
    if (opaque != NULL && *opaque != NULL &&
Packit fcad23
        *olength == sizeof(netsnmp_tmStateReference)) {
Packit fcad23
        tmStateRef = (const netsnmp_tmStateReference *) *opaque;
Packit fcad23
    } else {
Packit fcad23
        snmp_log(LOG_ERR, "TLSTCP was called with an invalid state; possibly the wrong security model is in use.  It should be 'tsm'.\n");
Packit fcad23
        snmp_increment_statistic(STAT_TLSTM_SNMPTLSTMSESSIONINVALIDCACHES);
Packit fcad23
        return SNMPERR_GENERR;
Packit fcad23
    }
Packit fcad23
Packit fcad23
    /* RFC5953 section 5.2: 
Packit fcad23
       2)  Extract the tmSessionID, tmTransportDomain, tmTransportAddress,
Packit fcad23
       tmSecurityName, tmRequestedSecurityLevel, and tmSameSecurity
Packit fcad23
       values from the tmStateReference.  Note: The tmSessionID value
Packit fcad23
       may be undefined if no session exists yet over which the message
Packit fcad23
       can be sent.
Packit fcad23
    */
Packit fcad23
    /* Implementation Notes:
Packit fcad23
       - Our session will always exist by now as it's created when the
Packit fcad23
         transport object is created. Auto-session creation is handled
Packit fcad23
         higher in the stack.
Packit fcad23
       - We don't "extract" per say since we just leave the data in
Packit fcad23
         the structure.
Packit fcad23
       - The sessionID is stored in the t->data memory pointer.
Packit fcad23
    */
Packit fcad23
Packit fcad23
    /* RFC5953 section 5.2: 
Packit fcad23
       3)  If tmSameSecurity is true and either tmSessionID is undefined or
Packit fcad23
           refers to a session that is no longer open then increment the
Packit fcad23
           snmpTlstmSessionNoSessions counter, discard the message and
Packit fcad23
           return the error indication in the statusInformation.  Processing
Packit fcad23
           of this message stops.
Packit fcad23
    */
Packit fcad23
    /* Implementation Notes:
Packit fcad23
       - We would never get here if the sessionID was either undefined
Packit fcad23
         or different.  We tie packets directly to the transport
Packit fcad23
         object and it could never be sent back over a different
Packit fcad23
         transport, which is what the above text is trying to prevent.
Packit fcad23
     */
Packit fcad23
Packit fcad23
    /* RFC5953 section 5.2: 
Packit fcad23
       4)  If tmSameSecurity is false and tmSessionID refers to a session
Packit fcad23
           that is no longer available then an implementation SHOULD open a
Packit fcad23
           new session using the openSession() ASI (described in greater
Packit fcad23
           detail in step 5b).  Instead of opening a new session an
Packit fcad23
           implementation MAY return a snmpTlstmSessionNoSessions error to
Packit fcad23
           the calling module and stop processing of the message.
Packit fcad23
    */
Packit fcad23
    /* Implementation Notes:
Packit fcad23
       - We would never get here if the sessionID was either undefined
Packit fcad23
         or different.  We tie packets directly to the transport
Packit fcad23
         object and it could never be sent back over a different
Packit fcad23
         transport, which is what the above text is trying to prevent.
Packit fcad23
       - Auto-connections are handled higher in the Net-SNMP library stack
Packit fcad23
     */
Packit fcad23
    
Packit fcad23
    /* RFC5953 section 5.2: 
Packit fcad23
       5)  If tmSessionID is undefined, then use tmTransportDomain,
Packit fcad23
           tmTransportAddress, tmSecurityName and tmRequestedSecurityLevel
Packit fcad23
           to see if there is a corresponding entry in the LCD suitable to
Packit fcad23
           send the message over.
Packit fcad23
Packit fcad23
           5a)  If there is a corresponding LCD entry, then this session
Packit fcad23
                will be used to send the message.
Packit fcad23
Packit fcad23
           5b)  If there is not a corresponding LCD entry, then open a
Packit fcad23
                session using the openSession() ASI (discussed further in
Packit fcad23
                Section 5.3.1).  Implementations MAY wish to offer message
Packit fcad23
                buffering to prevent redundant openSession() calls for the
Packit fcad23
                same cache entry.  If an error is returned from
Packit fcad23
                openSession(), then discard the message, discard the
Packit fcad23
                tmStateReference, increment the snmpTlstmSessionOpenErrors,
Packit fcad23
                return an error indication to the calling module and stop
Packit fcad23
                processing of the message.
Packit fcad23
    */
Packit fcad23
    /* Implementation Notes:
Packit fcad23
       - We would never get here if the sessionID was either undefined
Packit fcad23
         or different.  We tie packets directly to the transport
Packit fcad23
         object and it could never be sent back over a different
Packit fcad23
         transport, which is what the above text is trying to prevent.
Packit fcad23
       - Auto-connections are handled higher in the Net-SNMP library stack
Packit fcad23
     */
Packit fcad23
Packit fcad23
    /* our session pointer is functionally t->data */
Packit fcad23
    if (NULL == t->data) {
Packit fcad23
        snmp_log(LOG_ERR, "netsnmp_tlstcp_send received no incoming data\n");
Packit fcad23
        return -1;
Packit fcad23
    }
Packit fcad23
Packit fcad23
    tlsdata = t->data;
Packit fcad23
    
Packit fcad23
    if (tlsdata->ssl == NULL) {
Packit fcad23
        snmp_log(LOG_ERR, "tlstcp_send was called without a SSL connection.\n");
Packit fcad23
        return SNMPERR_GENERR;
Packit fcad23
    }
Packit fcad23
Packit fcad23
    /* If the first packet and we have no secname, then copy the
Packit fcad23
       important securityName data into the longer-lived session
Packit fcad23
       reference information. */
Packit fcad23
    if ((tlsdata->flags & NETSNMP_TLSBASE_IS_CLIENT) &&
Packit fcad23
        !tlsdata->securityName && tmStateRef && tmStateRef->securityNameLen > 0)
Packit fcad23
        tlsdata->securityName = strdup(tmStateRef->securityName);
Packit fcad23
        
Packit fcad23
        
Packit fcad23
    /* RFC5953 section 5.2: 
Packit fcad23
       6)  Using either the session indicated by the tmSessionID if there
Packit fcad23
           was one or the session resulting from a previous step (4 or 5),
Packit fcad23
           pass the outgoingMessage to (D)TLS for encapsulation and
Packit fcad23
           transmission.
Packit fcad23
    */
Packit fcad23
    rc = SSL_write(tlsdata->ssl, buf, size);
Packit fcad23
    DEBUGMSGTL(("tlstcp", "wrote %d bytes\n", size));
Packit fcad23
    if (rc < 0) {
Packit fcad23
        _openssl_log_error(rc, tlsdata->ssl, "SSL_write");
Packit fcad23
    }
Packit fcad23
Packit fcad23
    return rc;
Packit fcad23
}
Packit fcad23
Packit fcad23
Packit fcad23
Packit fcad23
static int
Packit fcad23
netsnmp_tlstcp_close(netsnmp_transport *t)
Packit fcad23
{
Packit fcad23
    _netsnmpTLSBaseData *tlsdata;
Packit fcad23
Packit fcad23
    if (NULL == t || NULL == t->data)
Packit fcad23
        return -1;
Packit fcad23
Packit fcad23
    /* RFC5953 Section 5.4.  Closing a Session
Packit fcad23
Packit fcad23
       1)  Increment either the snmpTlstmSessionClientCloses or the
Packit fcad23
           snmpTlstmSessionServerCloses counter as appropriate.
Packit fcad23
    */
Packit fcad23
    if (t->flags & NETSNMP_TLSBASE_IS_CLIENT)
Packit fcad23
        snmp_increment_statistic(STAT_TLSTM_SNMPTLSTMSESSIONCLIENTCLOSES);
Packit fcad23
    else 
Packit fcad23
        snmp_increment_statistic(STAT_TLSTM_SNMPTLSTMSESSIONSERVERCLOSES);
Packit fcad23
Packit fcad23
    /* RFC5953 Section 5.4.  Closing a Session
Packit fcad23
       2)  Look up the session using the tmSessionID.
Packit fcad23
    */
Packit fcad23
    tlsdata = (_netsnmpTLSBaseData *) t->data;
Packit fcad23
Packit fcad23
    /* RFC5953 Section 5.4.  Closing a Session
Packit fcad23
       3)  If there is no open session associated with the tmSessionID, then
Packit fcad23
           closeSession processing is completed.
Packit fcad23
    */
Packit fcad23
    /* Implementation notes: if we have a non-zero tlsdata then it's
Packit fcad23
       always true */
Packit fcad23
Packit fcad23
    /* RFC5953 Section 5.3.1: Establishing a Session as a Client
Packit fcad23
       4)  Have (D)TLS close the specified connection.  This SHOULD include
Packit fcad23
           sending a close_notify TLS Alert to inform the other side that
Packit fcad23
           session cleanup may be performed.
Packit fcad23
    */
Packit fcad23
Packit fcad23
    DEBUGMSGTL(("tlstcp", "Shutting down SSL connection\n"));
Packit fcad23
    if (tlsdata->ssl) {
Packit fcad23
        SSL_shutdown(tlsdata->ssl);
Packit fcad23
    }
Packit fcad23
Packit fcad23
    netsnmp_tlsbase_free_tlsdata(tlsdata);
Packit fcad23
Packit fcad23
    t->data = NULL;
Packit fcad23
    return netsnmp_socketbase_close(t);
Packit fcad23
}
Packit fcad23
Packit fcad23
static int
Packit fcad23
netsnmp_tlstcp_accept(netsnmp_transport *t)
Packit fcad23
{
Packit fcad23
    BIO            *accepted_bio;
Packit fcad23
    int             rc;
Packit fcad23
    SSL_CTX *ctx;
Packit fcad23
    SSL     *ssl;
Packit fcad23
    _netsnmpTLSBaseData *tlsdata = NULL;
Packit fcad23
    
Packit fcad23
    DEBUGMSGTL(("tlstcp", "netsnmp_tlstcp_accept called\n"));
Packit fcad23
Packit fcad23
    tlsdata = (_netsnmpTLSBaseData *) t->data;
Packit fcad23
Packit fcad23
    rc = BIO_do_accept(tlsdata->accept_bio);
Packit fcad23
Packit fcad23
    if (rc <= 0) {
Packit fcad23
        snmp_log(LOG_ERR, "BIO_do_accept failed\n");
Packit fcad23
        _openssl_log_error(rc, NULL, "BIO_do_accept");
Packit fcad23
        /* XXX: need to close the listening connection here? */
Packit fcad23
        return -1;
Packit fcad23
    }
Packit fcad23
Packit fcad23
    tlsdata->accepted_bio = accepted_bio = BIO_pop(tlsdata->accept_bio);
Packit fcad23
    if (!accepted_bio) {
Packit fcad23
        snmp_log(LOG_ERR, "Failed to pop an accepted bio off the bio stack\n");
Packit fcad23
        /* XXX: need to close the listening connection here? */
Packit fcad23
        return -1;
Packit fcad23
    }
Packit fcad23
Packit fcad23
    /* create the OpenSSL TLS context */
Packit fcad23
    ctx = tlsdata->ssl_context;
Packit fcad23
Packit fcad23
    /* create the server's main SSL bio */
Packit fcad23
    ssl = tlsdata->ssl = SSL_new(ctx);
Packit fcad23
    if (!tlsdata->ssl) {
Packit fcad23
        snmp_log(LOG_ERR, "TLSTCP: Failed to create a SSL BIO\n");
Packit fcad23
        BIO_free(accepted_bio);
Packit fcad23
        tlsdata->accepted_bio = NULL;
Packit fcad23
        return -1;
Packit fcad23
    }
Packit fcad23
        
Packit fcad23
    SSL_set_bio(ssl, accepted_bio, accepted_bio);
Packit fcad23
        
Packit fcad23
    if ((rc = SSL_accept(ssl)) <= 0) {
Packit fcad23
        snmp_log(LOG_ERR, "TLSTCP: Failed SSL_accept\n");
Packit fcad23
        _openssl_log_error(rc, ssl, "SSL_accept");
Packit fcad23
        SSL_shutdown(tlsdata->ssl);
Packit fcad23
        SSL_free(tlsdata->ssl);
Packit fcad23
        tlsdata->accepted_bio = NULL; /* freed by SSL_free */
Packit fcad23
        tlsdata->ssl = NULL;
Packit fcad23
        return -1;
Packit fcad23
    }   
Packit fcad23
Packit fcad23
    /*
Packit fcad23
     * currently netsnmp_tlsbase_wrapup_recv is where we check for
Packit fcad23
     * algorithm compliance, but for tls we know the algorithms
Packit fcad23
     * at this point, so we could bail earlier...
Packit fcad23
     */
Packit fcad23
#if 0 /* moved checks to netsnmp_tlsbase_wrapup_recv */
Packit fcad23
    netsnmp_openssl_null_checks(tlsdata->ssl, &no_auth, NULL);
Packit fcad23
    if (no_auth != 0) { /* null/unknown authentication */
Packit fcad23
        /* xxx-rks: snmp_increment_statistic(STAT_???); */
Packit fcad23
        snmp_log(LOG_ERR, "tlstcp: connection with NULL authentication\n");
Packit fcad23
        SSL_shutdown(tlsdata->ssl);
Packit fcad23
        SSL_free(tlsdata->ssl);
Packit fcad23
        tlsdata->accepted_bio = NULL; /* freed by SSL_free */
Packit fcad23
        tlsdata->ssl = NULL;
Packit fcad23
        return -1;
Packit fcad23
    }
Packit fcad23
#endif
Packit fcad23
Packit fcad23
    /* RFC5953 Section 5.3.2: Accepting a Session as a Server
Packit fcad23
       A (D)TLS server should accept new session connections from any client
Packit fcad23
       that it is able to verify the client's credentials for.  This is done
Packit fcad23
       by authenticating the client's presented certificate through a
Packit fcad23
       certificate path validation process (e.g.  [RFC5280]) or through
Packit fcad23
       certificate fingerprint verification using fingerprints configured in
Packit fcad23
       the snmpTlstmCertToTSNTable.  Afterward the server will determine the
Packit fcad23
       identity of the remote entity using the following procedures.
Packit fcad23
Packit fcad23
       The (D)TLS server identifies the authenticated identity from the
Packit fcad23
       (D)TLS client's principal certificate using configuration information
Packit fcad23
       from the snmpTlstmCertToTSNTable mapping table.  The (D)TLS server
Packit fcad23
       MUST request and expect a certificate from the client and MUST NOT
Packit fcad23
       accept SNMP messages over the (D)TLS connection until the client has
Packit fcad23
       sent a certificate and it has been authenticated.  The resulting
Packit fcad23
       derived tmSecurityName is recorded in the tmStateReference cache as
Packit fcad23
       tmSecurityName.  The details of the lookup process are fully
Packit fcad23
       described in the DESCRIPTION clause of the snmpTlstmCertToTSNTable
Packit fcad23
       MIB object.  If any verification fails in any way (for example
Packit fcad23
       because of failures in cryptographic verification or because of the
Packit fcad23
       lack of an appropriate row in the snmpTlstmCertToTSNTable) then the
Packit fcad23
       session establishment MUST fail, and the
Packit fcad23
       snmpTlstmSessionInvalidClientCertificates object is incremented.  If
Packit fcad23
       the session can not be opened for any reason at all, including
Packit fcad23
       cryptographic verification failures, then the
Packit fcad23
       snmpTlstmSessionOpenErrors counter is incremented and processing
Packit fcad23
       stops.
Packit fcad23
Packit fcad23
       Servers that wish to support multiple principals at a particular port
Packit fcad23
       SHOULD make use of a (D)TLS extension that allows server-side
Packit fcad23
       principal selection like the Server Name Indication extension defined
Packit fcad23
       in Section 3.1 of [RFC4366].  Supporting this will allow, for
Packit fcad23
       example, sending notifications to a specific principal at a given TCP
Packit fcad23
       or UDP port.
Packit fcad23
    */
Packit fcad23
    /* Implementation notes:
Packit fcad23
       - we expect fingerprints to be stored in the transport config
Packit fcad23
       - we do not currently support mulitple principals and only offer one
Packit fcad23
    */
Packit fcad23
    if ((rc = netsnmp_tlsbase_verify_client_cert(ssl, tlsdata))
Packit fcad23
        != SNMPERR_SUCCESS) {
Packit fcad23
        /* XXX: free needed memory */
Packit fcad23
        snmp_log(LOG_ERR, "TLSTCP: Falied checking client certificate\n");
Packit fcad23
        snmp_increment_statistic(STAT_TLSTM_SNMPTLSTMSESSIONINVALIDCLIENTCERTIFICATES);
Packit fcad23
        SSL_shutdown(tlsdata->ssl);
Packit fcad23
        SSL_free(tlsdata->ssl);
Packit fcad23
        tlsdata->accepted_bio = NULL; /* freed by SSL_free */
Packit fcad23
        tlsdata->ssl = NULL;
Packit fcad23
        return -1;
Packit fcad23
    }
Packit fcad23
Packit fcad23
Packit fcad23
    /* XXX: check acceptance criteria here */
Packit fcad23
Packit fcad23
    DEBUGMSGTL(("tlstcp", "accept succeeded on sock %d\n", t->sock));
Packit fcad23
Packit fcad23
    /* RFC5953 Section 5.1.2 step 1, part2::
Packit fcad23
     * If this is the first message received through this session and
Packit fcad23
     the session does not have an assigned tlstmSessionID yet then the
Packit fcad23
     snmpTlstmSessionAccepts counter is incremented and a
Packit fcad23
     tlstmSessionID for the session is created.  This will only happen
Packit fcad23
     on the server side of a connection because a client would have
Packit fcad23
     already assigned a tlstmSessionID during the openSession()
Packit fcad23
     invocation.  Implementations may have performed the procedures
Packit fcad23
     described in Section 5.3.2 prior to this point or they may
Packit fcad23
     perform them now, but the procedures described in Section 5.3.2
Packit fcad23
     MUST be performed before continuing beyond this point.
Packit fcad23
    */
Packit fcad23
    /* We're taking option 2 and incrementing the session accepts here
Packit fcad23
       rather than upon receiving the first packet */
Packit fcad23
    snmp_increment_statistic(STAT_TLSTM_SNMPTLSTMSESSIONACCEPTS);
Packit fcad23
Packit fcad23
    /* XXX: check that it returns something so we can free stuff? */
Packit fcad23
    return BIO_get_fd(tlsdata->accepted_bio, NULL);
Packit fcad23
}
Packit fcad23
Packit fcad23
Packit fcad23
netsnmp_transport *
Packit fcad23
netsnmp_tlstcp_open(netsnmp_transport *t)
Packit fcad23
{
Packit fcad23
    _netsnmpTLSBaseData *tlsdata;
Packit fcad23
    BIO *bio;
Packit fcad23
    SSL_CTX *ctx;
Packit fcad23
    SSL *ssl;
Packit fcad23
    int rc = 0;
Packit fcad23
    _netsnmp_verify_info *verify_info;
Packit fcad23
Packit fcad23
    netsnmp_assert_or_return(t != NULL, NULL);
Packit fcad23
    netsnmp_assert_or_return(t->data != NULL, NULL);
Packit fcad23
    netsnmp_assert_or_return(sizeof(_netsnmpTLSBaseData) == t->data_length,
Packit fcad23
                             NULL);
Packit fcad23
Packit fcad23
    tlsdata = t->data;
Packit fcad23
    
Packit fcad23
    if (tlsdata->flags & NETSNMP_TLSBASE_IS_CLIENT) {
Packit fcad23
        /* Is the client */
Packit fcad23
Packit fcad23
        /* RFC5953 Section 5.3.1:  Establishing a Session as a Client
Packit fcad23
         *    1)  The snmpTlstmSessionOpens counter is incremented.
Packit fcad23
         */
Packit fcad23
        snmp_increment_statistic(STAT_TLSTM_SNMPTLSTMSESSIONOPENS);
Packit fcad23
Packit fcad23
        /* RFC5953 Section 5.3.1:  Establishing a Session as a Client
Packit fcad23
          2)  The client selects the appropriate certificate and cipher_suites
Packit fcad23
              for the key agreement based on the tmSecurityName and the
Packit fcad23
              tmRequestedSecurityLevel for the session.  For sessions being
Packit fcad23
              established as a result of a SNMP-TARGET-MIB based operation, the
Packit fcad23
              certificate will potentially have been identified via the
Packit fcad23
              snmpTlstmParamsTable mapping and the cipher_suites will have to
Packit fcad23
              be taken from system-wide or implementation-specific
Packit fcad23
              configuration.  If no row in the snmpTlstmParamsTable exists then
Packit fcad23
              implementations MAY choose to establish the connection using a
Packit fcad23
              default client certificate available to the application.
Packit fcad23
              Otherwise, the certificate and appropriate cipher_suites will
Packit fcad23
              need to be passed to the openSession() ASI as supplemental
Packit fcad23
              information or configured through an implementation-dependent
Packit fcad23
              mechanism.  It is also implementation-dependent and possibly
Packit fcad23
              policy-dependent how tmRequestedSecurityLevel will be used to
Packit fcad23
              influence the security capabilities provided by the (D)TLS
Packit fcad23
              connection.  However this is done, the security capabilities
Packit fcad23
              provided by (D)TLS MUST be at least as high as the level of
Packit fcad23
              security indicated by the tmRequestedSecurityLevel parameter.
Packit fcad23
              The actual security level of the session is reported in the
Packit fcad23
              tmStateReference cache as tmSecurityLevel.  For (D)TLS to provide
Packit fcad23
              strong authentication, each principal acting as a command
Packit fcad23
              generator SHOULD have its own certificate.
Packit fcad23
        */
Packit fcad23
        /*
Packit fcad23
          Implementation notes: we do most of this in the
Packit fcad23
          sslctx_client_setup The transport should have been
Packit fcad23
          f_config()ed with the proper fingerprints to use (which is
Packit fcad23
          stored in tlsdata), or we'll use the default identity
Packit fcad23
          fingerprint if that can be found.
Packit fcad23
        */
Packit fcad23
Packit fcad23
        /* XXX: check securityLevel and ensure no NULL fingerprints are used */
Packit fcad23
Packit fcad23
        /* set up the needed SSL context */
Packit fcad23
        tlsdata->ssl_context = ctx = sslctx_client_setup(TLS_method(), tlsdata);
Packit fcad23
        if (!ctx) {
Packit fcad23
            snmp_log(LOG_ERR, "failed to create TLS context\n");
Packit fcad23
            return NULL;
Packit fcad23
        }
Packit fcad23
Packit fcad23
        /* RFC5953 Section 5.3.1:  Establishing a Session as a Client
Packit fcad23
           3)  Using the destTransportDomain and destTransportAddress values,
Packit fcad23
               the client will initiate the (D)TLS handshake protocol to
Packit fcad23
               establish session keys for message integrity and encryption.
Packit fcad23
        */
Packit fcad23
        /* Implementation note:
Packit fcad23
           The transport domain and address are pre-processed by this point
Packit fcad23
        */
Packit fcad23
Packit fcad23
        /* Create a BIO connection for it */
Packit fcad23
        DEBUGMSGTL(("tlstcp", "connecting to tlstcp %s\n",
Packit fcad23
		    tlsdata->addr_string));
Packit fcad23
        t->remote = (void *) strdup(tlsdata->addr_string);
Packit fcad23
        t->remote_length = strlen(tlsdata->addr_string) + 1;
Packit fcad23
Packit fcad23
        bio = BIO_new_connect(tlsdata->addr_string);
Packit fcad23
Packit fcad23
        /* RFC5953 Section 5.3.1:  Establishing a Session as a Client
Packit fcad23
           3) continued:
Packit fcad23
              If the attempt to establish a session is unsuccessful, then
Packit fcad23
              snmpTlstmSessionOpenErrors is incremented, an error indication is
Packit fcad23
              returned, and processing stops.
Packit fcad23
        */
Packit fcad23
Packit fcad23
        if (NULL == bio) {
Packit fcad23
            snmp_increment_statistic(STAT_TLSTM_SNMPTLSTMSESSIONOPENERRORS);
Packit fcad23
            snmp_log(LOG_ERR, "tlstcp: failed to create bio\n");
Packit fcad23
            _openssl_log_error(rc, NULL, "BIO creation");
Packit fcad23
            return NULL;
Packit fcad23
        }
Packit fcad23
            
Packit fcad23
Packit fcad23
        /* Tell the BIO to actually do the connection */
Packit fcad23
        if ((rc = BIO_do_connect(bio)) <= 0) {
Packit fcad23
            snmp_increment_statistic(STAT_TLSTM_SNMPTLSTMSESSIONOPENERRORS);
Packit fcad23
            snmp_log(LOG_ERR, "tlstcp: failed to connect to %s\n",
Packit fcad23
		     tlsdata->addr_string);
Packit fcad23
            _openssl_log_error(rc, NULL, "BIO_do_connect");
Packit fcad23
            BIO_free(bio);
Packit fcad23
            return NULL;
Packit fcad23
        }
Packit fcad23
Packit fcad23
        /* Create the SSL layer on top of the socket bio */
Packit fcad23
        ssl = tlsdata->ssl = SSL_new(ctx);
Packit fcad23
        if (NULL == ssl) {
Packit fcad23
            snmp_increment_statistic(STAT_TLSTM_SNMPTLSTMSESSIONOPENERRORS);
Packit fcad23
            snmp_log(LOG_ERR, "tlstcp: failed to create a SSL connection\n");
Packit fcad23
            BIO_free(bio);
Packit fcad23
            return NULL;
Packit fcad23
        }
Packit fcad23
        
Packit fcad23
        /* Bind the SSL layer to the BIO */ 
Packit fcad23
        SSL_set_bio(ssl, bio, bio);
Packit fcad23
        SSL_set_mode(ssl, SSL_MODE_AUTO_RETRY);
Packit fcad23
Packit fcad23
        verify_info = SNMP_MALLOC_TYPEDEF(_netsnmp_verify_info);
Packit fcad23
        if (NULL == verify_info) {
Packit fcad23
            snmp_increment_statistic(STAT_TLSTM_SNMPTLSTMSESSIONOPENERRORS);
Packit fcad23
            snmp_log(LOG_ERR, "tlstcp: failed to create a SSL connection\n");
Packit fcad23
            SSL_shutdown(ssl);
Packit fcad23
            BIO_free(bio);
Packit fcad23
            return NULL;
Packit fcad23
        }
Packit fcad23
Packit fcad23
        SSL_set_ex_data(ssl, tls_get_verify_info_index(), verify_info);
Packit fcad23
Packit fcad23
        /* Then have SSL do it's connection over the BIO */
Packit fcad23
        if ((rc = SSL_connect(ssl)) <= 0) {
Packit fcad23
            snmp_increment_statistic(STAT_TLSTM_SNMPTLSTMSESSIONOPENERRORS);
Packit fcad23
            snmp_log(LOG_ERR, "tlstcp: failed to ssl_connect\n");
Packit fcad23
            BIO_free(bio);
Packit fcad23
            return NULL;
Packit fcad23
        }
Packit fcad23
Packit fcad23
        /* RFC5953 Section 5.3.1: Establishing a Session as a Client
Packit fcad23
           3) continued:
Packit fcad23
              If the session failed to open because the presented
Packit fcad23
              server certificate was unknown or invalid then the
Packit fcad23
              snmpTlstmSessionUnknownServerCertificate or
Packit fcad23
              snmpTlstmSessionInvalidServerCertificates MUST be
Packit fcad23
              incremented and a snmpTlstmServerCertificateUnknown or
Packit fcad23
              snmpTlstmServerInvalidCertificate notification SHOULD be
Packit fcad23
              sent as appropriate.  Reasons for server certificate
Packit fcad23
              invalidation includes, but is not limited to,
Packit fcad23
              cryptographic validation failures and an unexpected
Packit fcad23
              presented certificate identity.
Packit fcad23
        */
Packit fcad23
Packit fcad23
        /* RFC5953 Section 5.3.1: Establishing a Session as a Client
Packit fcad23
           4)  The (D)TLS client MUST then verify that the (D)TLS server's
Packit fcad23
               presented certificate is the expected certificate.  The (D)TLS
Packit fcad23
               client MUST NOT transmit SNMP messages until the server
Packit fcad23
               certificate has been authenticated, the client certificate has
Packit fcad23
               been transmitted and the TLS connection has been fully
Packit fcad23
               established.
Packit fcad23
Packit fcad23
               If the connection is being established from configuration based
Packit fcad23
               on SNMP-TARGET-MIB configuration, then the snmpTlstmAddrTable
Packit fcad23
               DESCRIPTION clause describes how the verification is done (using
Packit fcad23
               either a certificate fingerprint, or an identity authenticated
Packit fcad23
               via certification path validation).
Packit fcad23
Packit fcad23
               If the connection is being established for reasons other than
Packit fcad23
               configuration found in the SNMP-TARGET-MIB then configuration and
Packit fcad23
               procedures outside the scope of this document should be followed.
Packit fcad23
               Configuration mechanisms SHOULD be similar in nature to those
Packit fcad23
               defined in the snmpTlstmAddrTable to ensure consistency across
Packit fcad23
               management configuration systems.  For example, a command-line
Packit fcad23
               tool for generating SNMP GETs might support specifying either the
Packit fcad23
               server's certificate fingerprint or the expected host name as a
Packit fcad23
               command line argument.
Packit fcad23
        */
Packit fcad23
Packit fcad23
        /* Implementation notes:
Packit fcad23
           - All remote certificate fingerprints are expected to be
Packit fcad23
             stored in the transport's config information.  This is
Packit fcad23
             true both for CLI clients and TARGET-MIB sessions.
Packit fcad23
           - netsnmp_tlsbase_verify_server_cert implements these checks
Packit fcad23
        */
Packit fcad23
        if (netsnmp_tlsbase_verify_server_cert(ssl, tlsdata) != SNMPERR_SUCCESS) {
Packit fcad23
            /* XXX: unknown vs invalid; two counters */
Packit fcad23
            snmp_increment_statistic(STAT_TLSTM_SNMPTLSTMSESSIONUNKNOWNSERVERCERTIFICATE);
Packit fcad23
            snmp_log(LOG_ERR, "tlstcp: failed to verify ssl certificate\n");
Packit fcad23
            SSL_shutdown(ssl);
Packit fcad23
            BIO_free(bio);
Packit fcad23
            return NULL;
Packit fcad23
        }
Packit fcad23
Packit fcad23
        /* RFC5953 Section 5.3.1: Establishing a Session as a Client
Packit fcad23
           5)  (D)TLS provides assurance that the authenticated identity has
Packit fcad23
               been signed by a trusted configured certification authority.  If
Packit fcad23
               verification of the server's certificate fails in any way (for
Packit fcad23
               example because of failures in cryptographic verification or the
Packit fcad23
               presented identity did not match the expected named entity) then
Packit fcad23
               the session establishment MUST fail, the
Packit fcad23
               snmpTlstmSessionInvalidServerCertificates object is incremented.
Packit fcad23
               If the session can not be opened for any reason at all, including
Packit fcad23
               cryptographic verification failures, then the
Packit fcad23
               snmpTlstmSessionOpenErrors counter is incremented and processing
Packit fcad23
               stops.
Packit fcad23
Packit fcad23
        */
Packit fcad23
        /* XXX: add snmpTlstmSessionInvalidServerCertificates on
Packit fcad23
           crypto failure */
Packit fcad23
Packit fcad23
        /* RFC5953 Section 5.3.1: Establishing a Session as a Client
Packit fcad23
           6)  The TLSTM-specific session identifier (tlstmSessionID) is set in
Packit fcad23
           the tmSessionID of the tmStateReference passed to the TLS
Packit fcad23
           Transport Model to indicate that the session has been established
Packit fcad23
           successfully and to point to a specific (D)TLS connection for
Packit fcad23
           future use.  The tlstmSessionID is also stored in the LCD for
Packit fcad23
           later lookup during processing of incoming messages
Packit fcad23
           (Section 5.1.2).
Packit fcad23
        */
Packit fcad23
        /* Implementation notes:
Packit fcad23
           - the tlsdata pointer is used as our session identifier, as
Packit fcad23
             noted in the netsnmp_tlstcp_recv() function comments.
Packit fcad23
        */
Packit fcad23
Packit fcad23
        t->sock = BIO_get_fd(bio, NULL);
Packit fcad23
Packit fcad23
    } else {
Packit fcad23
#ifndef NETSNMP_NO_LISTEN_SUPPORT
Packit fcad23
        /* Is the server */
Packit fcad23
        
Packit fcad23
        /* Create the socket bio */
Packit fcad23
        DEBUGMSGTL(("tlstcp", "listening on tlstcp port %s\n",
Packit fcad23
		    tlsdata->addr_string));
Packit fcad23
        tlsdata->accept_bio = BIO_new_accept(tlsdata->addr_string);
Packit fcad23
        t->local = (void *) strdup(tlsdata->addr_string);
Packit fcad23
        t->local_length = strlen(tlsdata->addr_string)+1;
Packit fcad23
        if (NULL == tlsdata->accept_bio) {
Packit fcad23
            snmp_log(LOG_ERR, "TLSTCP: Falied to create a accept BIO\n");
Packit fcad23
            return NULL;
Packit fcad23
        }
Packit fcad23
Packit fcad23
        /* openssl requires an initial accept to bind() the socket */
Packit fcad23
        if (BIO_do_accept(tlsdata->accept_bio) <= 0) {
Packit fcad23
	    _openssl_log_error(rc, tlsdata->ssl, "BIO_do__accept");
Packit fcad23
            snmp_log(LOG_ERR, "TLSTCP: Falied to do first accept on the TLS accept BIO\n");
Packit fcad23
            return NULL;
Packit fcad23
        }
Packit fcad23
Packit fcad23
        /* create the OpenSSL TLS context */
Packit fcad23
        tlsdata->ssl_context = sslctx_server_setup(TLS_method());
Packit fcad23
Packit fcad23
        t->sock = BIO_get_fd(tlsdata->accept_bio, NULL);
Packit fcad23
        t->flags |= NETSNMP_TRANSPORT_FLAG_LISTEN;
Packit fcad23
#else /* NETSNMP_NO_LISTEN_SUPPORT */
Packit fcad23
        return NULL;
Packit fcad23
#endif /* NETSNMP_NO_LISTEN_SUPPORT */
Packit fcad23
    }
Packit fcad23
    return t;
Packit fcad23
}
Packit fcad23
Packit fcad23
/*
Packit fcad23
 * Create a TLS-based transport for SNMP.  Local is TRUE if addr is the local
Packit fcad23
 * address to bind to (i.e. this is a server-type session); otherwise addr is 
Packit fcad23
 * the remote address to send things to.  
Packit fcad23
 */
Packit fcad23
Packit fcad23
netsnmp_transport *
Packit fcad23
netsnmp_tlstcp_transport(const char *addr_string, int isserver)
Packit fcad23
{
Packit fcad23
    netsnmp_transport *t = NULL;
Packit fcad23
    _netsnmpTLSBaseData *tlsdata;
Packit fcad23
    char *cp;
Packit fcad23
    char buf[SPRINT_MAX_LEN];
Packit fcad23
    
Packit fcad23
#ifdef NETSNMP_NO_LISTEN_SUPPORT
Packit fcad23
    if (isserver)
Packit fcad23
        return NULL;
Packit fcad23
#endif /* NETSNMP_NO_LISTEN_SUPPORT */
Packit fcad23
Packit fcad23
    /* allocate our transport structure */
Packit fcad23
    t = SNMP_MALLOC_TYPEDEF(netsnmp_transport);
Packit fcad23
    if (NULL == t) {
Packit fcad23
        return NULL;
Packit fcad23
    }
Packit fcad23
Packit fcad23
    /* allocate our TLS specific data */
Packit fcad23
    if (NULL == (tlsdata = netsnmp_tlsbase_allocate_tlsdata(t, isserver)))
Packit fcad23
        return NULL;
Packit fcad23
Packit fcad23
    if (!isserver)
Packit fcad23
        t->flags |= NETSNMP_TLSBASE_IS_CLIENT;
Packit fcad23
Packit fcad23
    tlsdata->addr_string = strdup(addr_string);
Packit fcad23
Packit fcad23
    /* see if we can extract the remote hostname */
Packit fcad23
    if (!isserver && tlsdata && addr_string) {
Packit fcad23
        /* search for a : */
Packit fcad23
        if (NULL != (cp = strrchr(addr_string, ':'))) {
Packit fcad23
            sprintf(buf, "%.*s",
Packit fcad23
                    (int) SNMP_MIN(cp - addr_string, sizeof(buf) - 1),
Packit fcad23
                    addr_string);
Packit fcad23
        } else {
Packit fcad23
            /* else the entire spec is a host name only */
Packit fcad23
            strlcpy(buf, addr_string, sizeof(buf));
Packit fcad23
        }
Packit fcad23
        tlsdata->their_hostname = strdup(buf);
Packit fcad23
    }
Packit fcad23
Packit fcad23
    t->data = tlsdata;
Packit fcad23
    t->data_length = sizeof(_netsnmpTLSBaseData);
Packit fcad23
Packit fcad23
    /*
Packit fcad23
     * Set Domain
Packit fcad23
     */
Packit fcad23
    t->domain = netsnmpTLSTCPDomain;                                     
Packit fcad23
    t->domain_length = netsnmpTLSTCPDomain_len;     
Packit fcad23
Packit fcad23
    /*
Packit fcad23
     * 16-bit length field, 8 byte TLS header, 20 byte IPv4 header  
Packit fcad23
     */
Packit fcad23
Packit fcad23
    t->msgMaxSize      = 0xffff - 8 - 20;
Packit fcad23
    t->f_recv          = netsnmp_tlstcp_recv;
Packit fcad23
    t->f_send          = netsnmp_tlstcp_send;
Packit fcad23
    t->f_open          = netsnmp_tlstcp_open;
Packit fcad23
    t->f_close         = netsnmp_tlstcp_close;
Packit fcad23
    t->f_accept        = netsnmp_tlstcp_accept;
Packit fcad23
    t->f_copy          = netsnmp_tlstcp_copy;
Packit fcad23
    t->f_config        = netsnmp_tlsbase_config;
Packit fcad23
    t->f_setup_session = netsnmp_tlsbase_session_init;
Packit fcad23
    t->f_fmtaddr       = netsnmp_tlstcp_fmtaddr;
Packit fcad23
    t->f_get_taddr     = netsnmp_tlstcp_get_taddr;
Packit fcad23
Packit fcad23
    t->flags |= NETSNMP_TRANSPORT_FLAG_TUNNELED | NETSNMP_TRANSPORT_FLAG_STREAM;
Packit fcad23
Packit fcad23
    return t;
Packit fcad23
}
Packit fcad23
Packit fcad23
netsnmp_transport *
Packit fcad23
netsnmp_tlstcp_create_tstring(const char *str, int local,
Packit fcad23
                               const char *default_target)
Packit fcad23
{
Packit fcad23
    char buf[SPRINT_MAX_LEN];
Packit fcad23
Packit fcad23
    if (str == NULL || *str == '\0')
Packit fcad23
        str = default_target + 1; /* drop the leading : */
Packit fcad23
    else if (!strchr(str, ':')) {
Packit fcad23
        /* it's either :port or :address.  Try to guess which. */
Packit fcad23
        const char *cp;
Packit fcad23
        int isport = 1;
Packit fcad23
        for(cp = str; *cp != '\0'; cp++) {
Packit fcad23
            /* if ALL numbers, it must be just a port */
Packit fcad23
            /* if it contains anything else, assume a host or ip address */
Packit fcad23
            if (!isdigit(0xFF & *cp)) {
Packit fcad23
                isport = 0;
Packit fcad23
                break;
Packit fcad23
            }
Packit fcad23
        }
Packit fcad23
        if (isport) {
Packit fcad23
            /* Just :NNN can be passed to openssl */
Packit fcad23
            snprintf(buf, sizeof(buf)-1, "0.0.0.0:%s", str);
Packit fcad23
        } else {
Packit fcad23
            /* add the default port */
Packit fcad23
            snprintf(buf, sizeof(buf)-1, "%s%s", str, default_target);
Packit fcad23
        }
Packit fcad23
        str = buf;
Packit fcad23
    }
Packit fcad23
    return netsnmp_tlstcp_transport(str, local);
Packit fcad23
}
Packit fcad23
Packit fcad23
Packit fcad23
netsnmp_transport *
Packit fcad23
netsnmp_tlstcp_create_ostring(const void *o, size_t o_len, int local)
Packit fcad23
{
Packit fcad23
    char buf[SPRINT_MAX_LEN];
Packit fcad23
Packit fcad23
    /* ensure buf is big enough */
Packit fcad23
    if (o_len > SPRINT_MAX_LEN - 1)
Packit fcad23
        return NULL;
Packit fcad23
Packit fcad23
    memcpy(buf, o, o_len);
Packit fcad23
    buf[o_len] = '\0';
Packit fcad23
Packit fcad23
    return netsnmp_tlstcp_transport(buf, local);
Packit fcad23
}
Packit fcad23
Packit fcad23
void
Packit fcad23
netsnmp_tlstcp_ctor(void)
Packit fcad23
{
Packit fcad23
    DEBUGMSGTL(("tlstcp", "registering TLS constructor\n"));
Packit fcad23
Packit fcad23
    /* config settings */
Packit fcad23
Packit fcad23
    tlstcpDomain.name = netsnmpTLSTCPDomain;
Packit fcad23
    tlstcpDomain.name_length = netsnmpTLSTCPDomain_len;
Packit fcad23
    tlstcpDomain.prefix = (const char**)calloc(3, sizeof(char *));
Packit fcad23
    tlstcpDomain.prefix[0] = "tlstcp";
Packit fcad23
    tlstcpDomain.prefix[1] = "tls";
Packit fcad23
Packit fcad23
    tlstcpDomain.f_create_from_tstring     = NULL;
Packit fcad23
    tlstcpDomain.f_create_from_tstring_new = netsnmp_tlstcp_create_tstring;
Packit fcad23
    tlstcpDomain.f_create_from_ostring     = netsnmp_tlstcp_create_ostring;
Packit fcad23
Packit fcad23
    netsnmp_tdomain_register(&tlstcpDomain);
Packit fcad23
}